chalk

kis_doc.cpp
1/*
2 * Copyright (c) 1999 Matthias Elter <me@kde.org>
3 * Copyright (c) 2000 John Califf <jcaliff@compuzone.net>
4 * Copyright (c) 2001 Toshitaka Fujioka <fujioka@kde.org>
5 * Copyright (c) 2002, 2003 Patrick Julien <freak@codepimps.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22// TQt
23#include <tqapplication.h>
24#include <tqdom.h>
25#include <tqimage.h>
26#include <tqpainter.h>
27#include <tqtl.h>
28#include <tqstringlist.h>
29#include <tqwidget.h>
30#include <tqpaintdevicemetrics.h>
31
32// KDE
33#include <dcopobject.h>
34#include <tdeapplication.h>
35#include <kcommand.h>
36#include <kdebug.h>
37#include <kimageio.h>
38#include <tdefiledialog.h>
39#include <tdeglobal.h>
40#include <kmimetype.h>
41#include <knotifyclient.h>
42#include <tdelocale.h>
43#include <tdemessagebox.h>
44
45// KOffice
46#include <KoFilterManager.h>
47#include <KoMainWindow.h>
48#include <KoQueryTrader.h>
49#include <KoStore.h>
50#include <KoStoreDevice.h>
51#include <KoTemplateChooseDia.h>
52#include <KoApplication.h>
53#include <KoCommandHistory.h>
54
55// Local
56#include <kis_clipboard.h>
57#include <kis_meta_registry.h>
58#include "kis_annotation.h"
59#include "kis_types.h"
60#include "kis_config.h"
61#include "kis_debug_areas.h"
62#include "kis_doc.h"
63#include "kis_factory.h"
64#include "kis_image.h"
65#include "kis_layer.h"
66#include "kis_paint_layer.h"
67#include "kis_nameserver.h"
68#include "kis_painter.h"
69#include "kis_selection.h"
70#include "kis_fill_painter.h"
71#include "kis_command.h"
72#include "kis_view.h"
73#include "kis_colorspace.h"
74#include "kis_colorspace_factory_registry.h"
75#include "kis_profile.h"
76#include "kis_id.h"
77#include "kis_part_layer.h"
78#include "kis_doc_iface.h"
79#include "kis_paint_device_action.h"
80#include "kis_custom_image_widget.h"
81#include "kis_load_visitor.h"
82#include "kis_save_visitor.h"
83#include "kis_savexml_visitor.h"
84
85static const char *CURRENT_DTD_VERSION = "1.3";
86
92#define APP_MIMETYPE "application/x-chalk"
93
97#define NATIVE_MIMETYPE "application/x-kra"
98
99namespace {
100 class KisCommandImageMv : public KisCommand {
101 typedef KisCommand super;
102
103 public:
104 KisCommandImageMv(KisDoc *doc,
105 KisUndoAdapter *adapter,
106 const TQString& name,
107 const TQString& oldName) : super(i18n("Rename Image"), adapter)
108 {
109 m_doc = doc;
110 m_name = name;
111 m_oldName = oldName;
112 }
113
114 virtual ~KisCommandImageMv()
115 {
116 }
117
118 virtual void execute()
119 {
120 adapter()->setUndo(false);
121 m_doc->renameImage(m_oldName, m_name);
122 adapter()->setUndo(true);
123 }
124
125 virtual void unexecute()
126 {
127 adapter()->setUndo(false);
128 m_doc->renameImage(m_name, m_oldName);
129 adapter()->setUndo(true);
130 }
131
132 private:
133 KisDoc *m_doc;
134 TQString m_name;
135 TQString m_oldName;
136 };
137
138}
139
140KisDoc::KisDoc(TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name, bool singleViewMode) :
141 super(parentWidget, widgetName, parent, name, singleViewMode)
142{
143
144 m_undo = false;
145 m_dcop = 0;
146 m_cmdHistory = 0;
147 m_nserver = 0;
148 m_currentImage = 0;
149 m_currentMacro = 0;
150 m_macroNestDepth = 0;
151 m_ioProgressBase = 0;
152 m_ioProgressTotalSteps = 0;
153
154 setInstance( KisFactory::instance(), false );
155 setTemplateType( "chalk_template" );
156
157 init();
158
159 if (name)
160 dcopObject();
161}
162
163KisDoc::~KisDoc()
164{
165 delete m_cmdHistory;
166 delete m_nserver;
167 m_undoListeners.setAutoDelete(false);
168 delete m_dcop;
169}
170
171TQCString KisDoc::mimeType() const
172{
173 return APP_MIMETYPE;
174}
175
176DCOPObject *KisDoc::dcopObject()
177{
178 if (!m_dcop) {
179 m_dcop = new KisDocIface(this);
180 TQ_CHECK_PTR(m_dcop);
181 }
182 return m_dcop;
183}
184
185bool KisDoc::initDoc(InitDocFlags flags, TQWidget* parentWidget)
186{
187 if (!init())
188 return false;
189
190 bool ok = false;
191
192 TQString file;
193 KoTemplateChooseDia::DialogType dlgtype;
194
195 if (flags != KoDocument::InitDocFileNew) {
196 dlgtype = KoTemplateChooseDia::Everything;
197 } else {
198 dlgtype = KoTemplateChooseDia::OnlyTemplates;
199 }
200
201 KoTemplateChooseDia::ReturnType ret =
202 KoTemplateChooseDia::choose(KisFactory::instance(),
203 file,
204 dlgtype,
205 "chalk_template",
206 parentWidget);
207 setUndo(false);
208
209 if (ret == KoTemplateChooseDia::Template) {
210 resetURL();
211 ok = loadNativeFormat( file );
212 setEmpty();
213 ok = true;
214
215 } else if (ret == KoTemplateChooseDia::File) {
216 KURL url( file );
217 ok = openURL(url);
218 } else if (ret == KoTemplateChooseDia::Empty) {
219 setEmpty();
220 ok = true;
221 }
222
223 setModified(false);
224 KisConfig cfg;
225 setUndo(cfg.undoEnabled());
226
227 return ok;
228}
229
230void KisDoc::openExistingFile(const TQString& file)
231{
232 setUndo(false);
233
234 KoDocument::openExistingFile(file);
235
236 setUndo(true);
237}
238
239void KisDoc::openTemplate(const TQString& file)
240{
241 setUndo(false);
242
243 KoDocument::openTemplate(file);
244
245 setUndo(true);
246}
247
248bool KisDoc::init()
249{
250 if (m_cmdHistory) {
251 delete m_cmdHistory;
252 m_cmdHistory = 0;
253 }
254
255 if (m_nserver) {
256 delete m_nserver;
257 m_nserver = 0;
258 }
259
260 m_cmdHistory = new KoCommandHistory(actionCollection(), true);
261 TQ_CHECK_PTR(m_cmdHistory);
262
263 connect(m_cmdHistory, TQ_SIGNAL(documentRestored()), this, TQ_SLOT(slotDocumentRestored()));
264 connect(m_cmdHistory, TQ_SIGNAL(commandExecuted(KCommand *)), this, TQ_SLOT(slotCommandExecuted(KCommand *)));
265 setUndo(true);
266
267 m_nserver = new KisNameServer(i18n("Image %1"), 1);
268 TQ_CHECK_PTR(m_nserver);
269
270 if (!KisMetaRegistry::instance()->csRegistry()->exists(KisID("RGBA",""))) {
271 KMessageBox::sorry(0, i18n("No colorspace modules loaded: cannot run Chalk"));
272 return false;
273 }
274
275 m_undoListeners.setAutoDelete(false);
276
277 return true;
278}
279
280TQDomDocument KisDoc::saveXML()
281{
282 TQDomDocument doc = createDomDocument("DOC", CURRENT_DTD_VERSION);
283 TQDomElement root = doc.documentElement();
284
285 root.setAttribute("editor", "Chalk");
286 root.setAttribute("depth", sizeof(TQ_UINT8));
287 root.setAttribute("syntaxVersion", "1");
288
289 root.appendChild(saveImage(doc, m_currentImage));
290
291 return doc;
292}
293
294bool KisDoc::loadOasis( const TQDomDocument&, KoOasisStyles&, const TQDomDocument&, KoStore* )
295{
296 //XXX: todo (and that includes defining an OASIS format for layered 2D raster data!)
297 return false;
298}
299
300
301bool KisDoc::saveOasis( KoStore*, KoXmlWriter* )
302{
303 //XXX: todo (and that includes defining an OASIS format for layered 2D raster data!)
304 return false;
305}
306
307bool KisDoc::loadXML(TQIODevice *, const TQDomDocument& doc)
308{
309 TQDomElement root;
310 TQString attr;
311 TQDomNode node;
312 KisImageSP img;
313
314 if (!init())
315 return false;
316 if (doc.doctype().name() != "DOC")
317 return false;
318 root = doc.documentElement();
319 attr = root.attribute("syntaxVersion");
320 if (attr.toInt() > 1)
321 return false;
322 if ((attr = root.attribute("depth")).isNull())
323 return false;
324 m_conversionDepth = attr.toInt();
325
326 if (!root.hasChildNodes()) {
327 return false; // XXX used to be: return slotNewImage();
328 }
329
330 setUndo(false);
331
332 for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) {
333 if (node.isElement()) {
334 if (node.nodeName() == "IMAGE") {
335 TQDomElement elem = node.toElement();
336 if (!(img = loadImage(elem)))
337 return false;
338 m_currentImage = img;
339 } else {
340 return false;
341 }
342 }
343 }
344
345 emit loadingFinished();
346 return true;
347}
348
349bool KisDoc::loadChildren(KoStore* store) {
350 TQPtrListIterator<KoDocumentChild> it(children());
351 for( ; it.current(); ++it ) {
352 if (!it.current()->loadDocument(store)) {
353 return false;
354 }
355 }
356 return true;
357}
358
359TQDomElement KisDoc::saveImage(TQDomDocument& doc, KisImageSP img)
360{
361 TQDomElement image = doc.createElement("IMAGE");
362
363 Q_ASSERT(img);
364 image.setAttribute("name", img->name());
365 image.setAttribute("mime", "application/x-kra");
366 image.setAttribute("width", img->width());
367 image.setAttribute("height", img->height());
368 image.setAttribute("colorspacename", img->colorSpace()->id().id());
369 image.setAttribute("description", img->description());
370 // XXX: Save profile as blob inside the image, instead of the product name.
371 if (img->getProfile() && img->getProfile()-> valid())
372 image.setAttribute("profile", img->getProfile()->productName());
373 image.setAttribute("x-res", img->xRes());
374 image.setAttribute("y-res", img->yRes());
375
376 TQ_UINT32 count=0;
377 KisSaveXmlVisitor visitor(doc, image, count, true);
378
379 m_currentImage->rootLayer()->accept(visitor);
380
381 return image;
382}
383
384KisImageSP KisDoc::loadImage(const TQDomElement& element)
385{
386
387 KisConfig cfg;
388 TQString attr;
389 TQDomNode node;
390 TQDomNode child;
391 KisImageSP img;
392 TQString name;
393 TQ_INT32 width;
394 TQ_INT32 height;
395 TQString description;
396 TQString profileProductName;
397 double xres;
398 double yres;
399 TQString colorspacename;
400 KisColorSpace * cs;
401
402 if ((attr = element.attribute("mime")) == NATIVE_MIMETYPE) {
403 if ((name = element.attribute("name")).isNull())
404 return 0;
405 if ((attr = element.attribute("width")).isNull())
406 return 0;
407 width = attr.toInt();
408 if ((attr = element.attribute("height")).isNull())
409 return 0;
410 height = attr.toInt();
411
412 description = element.attribute("description");
413
414 if ((attr = element.attribute("x-res")).isNull())
415 xres = 100.0;
416 xres = attr.toDouble();
417
418 if ((attr = element.attribute("y-res")).isNull())
419 yres = 100.0;
420 yres = attr.toDouble();
421
422 if ((colorspacename = element.attribute("colorspacename")).isNull())
423 {
424 // An old file: take a reasonable default.
425 // Chalk didn't support anything else in those
426 // days anyway.
427 colorspacename = "RGBA";
428 }
429
430 // A hack for an old colorspacename
431 if (colorspacename == "Grayscale + Alpha")
432 colorspacename = "GRAYA";
433
434 if ((profileProductName = element.attribute("profile")).isNull()) {
435 // no mention of profile so get default profile
436 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(colorspacename,"");
437 }
438 else {
439 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(colorspacename, profileProductName);
440 }
441
442 if (cs == 0) {
443 kdWarning(DBG_AREA_FILE) << "Could not open colorspace\n";
444 return 0;
445 }
446
447 img = new KisImage(this, width, height, cs, name);
448 img->blockSignals(true); // Don't send out signals while we're building the image
449 TQ_CHECK_PTR(img);
450 connect( img, TQ_SIGNAL( sigImageModified() ), this, TQ_SLOT( slotImageUpdated() ));
451 img->setDescription(description);
452 img->setResolution(xres, yres);
453
454 loadLayers(element, img, img->rootLayer().data());
455
456 }
457
458 img->notifyImageLoaded();
459
460 return img;
461}
462
463void KisDoc::loadLayers(const TQDomElement& element, KisImageSP img, KisGroupLayerSP parent)
464{
465 TQDomNode node = element.firstChild();
466 TQDomNode child;
467
468 if(!node.isNull())
469 {
470 if (node.isElement()) {
471 if (node.nodeName() == "LAYERS") {
472 for (child = node.firstChild(); !child.isNull(); child = child.nextSibling()) {
473 KisLayerSP layer = loadLayer(child.toElement(), img);
474
475 if (!layer) {
476 kdWarning(DBG_AREA_FILE) << "Could not load layer\n";
477 }
478 else {
479 img->nextLayerName(); // Make sure the nameserver is current with the number of layers.
480 img->addLayer(layer, parent, 0);
481 }
482 }
483 }
484 }
485 }
486}
487
488KisLayerSP KisDoc::loadLayer(const TQDomElement& element, KisImageSP img)
489{
490 // Nota bene: If you add new properties to layers, you should
491 // ALWAYS define a default value in case the property is not
492 // present in the layer definition: this helps a LOT with backward
493 // compatibilty.
494 TQString attr;
495 TQString name;
496 TQ_INT32 x;
497 TQ_INT32 y;
498 TQ_INT32 opacity;
499 bool visible;
500 bool locked;
501
502 if ((name = element.attribute("name")).isNull())
503 return 0;
504
505 if ((attr = element.attribute("x")).isNull())
506 return 0;
507 x = attr.toInt();
508
509 if ((attr = element.attribute("y")).isNull())
510 return 0;
511
512 y = attr.toInt();
513
514 if ((attr = element.attribute("opacity")).isNull())
515 return 0;
516
517 if ((opacity = attr.toInt()) < 0 || opacity > TQ_UINT8_MAX)
518 opacity = OPACITY_OPAQUE;
519
520
521 TQString compositeOpName = element.attribute("compositeop");
522 KisCompositeOp compositeOp;
523
524 if (compositeOpName.isNull()) {
525 compositeOp = COMPOSITE_OVER;
526 } else {
527 compositeOp = KisCompositeOp(compositeOpName);
528 }
529
530 if (!compositeOp.isValid()) {
531 return 0;
532 }
533
534 if ((attr = element.attribute("visible")).isNull())
535 attr = "1";
536
537 visible = attr == "0" ? false : true;
538
539 if ((attr = element.attribute("locked")).isNull())
540 attr = "0";
541
542 locked = attr == "0" ? false : true;
543
544 // Now find out the layer type and do specific handling
545 if ((attr = element.attribute("layertype")).isNull())
546 return loadPaintLayer(element, img, name, x, y, opacity, visible, locked, compositeOp) ;
547
548 if(attr == "paintlayer")
549 return loadPaintLayer(element, img, name, x, y, opacity, visible, locked, compositeOp);
550
551 if(attr == "grouplayer")
552 return loadGroupLayer(element, img, name, x, y, opacity, visible, locked, compositeOp).data();
553
554 if(attr == "adjustmentlayer")
555 return loadAdjustmentLayer(element, img, name, x, y, opacity, visible, locked, compositeOp).data();
556
557 if(attr == "partlayer")
558 return loadPartLayer(element, img, name, x, y, opacity, visible, locked, compositeOp).data();
559
560 kdWarning(DBG_AREA_FILE) << "Specified layertype is not recognised\n";
561 return 0;
562}
563
564
565KisLayerSP KisDoc::loadPaintLayer(const TQDomElement& element, KisImageSP img,
566 TQString name, TQ_INT32 x, TQ_INT32 y,
567 TQ_INT32 opacity, bool visible, bool locked, KisCompositeOp compositeOp)
568{
569 TQString attr;
570 KisPaintLayerSP layer;
571 KisColorSpace * cs;
572
573 TQString colorspacename;
574 TQString profileProductName;
575
576 if ((colorspacename = element.attribute("colorspacename")).isNull())
577 cs = img->colorSpace();
578 else
579 // use default profile - it will be replaced later in completLoading
580 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(colorspacename,"");
581
582 layer = new KisPaintLayer(img, name, opacity, cs);
583 TQ_CHECK_PTR(layer);
584
585 layer->setCompositeOp(compositeOp);
586 layer->setVisible(visible);
587 layer->setLocked(locked);
588 layer->setX(x);
589 layer->setY(y);
590
591 if ((element.attribute("filename")).isNull())
592 m_layerFilenames[layer.data()] = name;
593 else
594 m_layerFilenames[layer.data()] = TQString(element.attribute("filename"));
595
596 if ((attr = element.attribute("hasmask")).isNull())
597 attr = "0";
598
599 if (attr == "1") {
600 // We add a mask, but we'll fill in the actual mask later in completeLoading with the visitor
601 layer->createMask();
602 }
603
604
605 // Load exif info
606 for( TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling() )
607 {
608 TQDomElement e = node.toElement();
609 if ( !e.isNull() && e.tagName() == "ExifInfo" )
610 {
611 layer->paintDevice()->exifInfo()->load(e);
612 }
613 }
614 return layer.data();
615}
616
617KisGroupLayerSP KisDoc::loadGroupLayer(const TQDomElement& element, KisImageSP img,
618 TQString name, TQ_INT32 x, TQ_INT32 y, TQ_INT32 opacity, bool visible, bool locked,
619 KisCompositeOp compositeOp)
620{
621 TQString attr;
622 KisGroupLayerSP layer;
623
624 layer = new KisGroupLayer(img, name, opacity);
625 TQ_CHECK_PTR(layer);
626
627 layer->setCompositeOp(compositeOp);
628 layer->setVisible(visible);
629 layer->setLocked(locked);
630 layer->setX(x);
631 layer->setY(y);
632
633 loadLayers(element, img, layer);
634
635 return layer;
636}
637
638KisAdjustmentLayerSP KisDoc::loadAdjustmentLayer(const TQDomElement& element, KisImageSP img,
639 TQString name, TQ_INT32 x, TQ_INT32 y, TQ_INT32 opacity, bool visible, bool locked,
640 KisCompositeOp compositeOp)
641{
642 TQString attr;
643 KisAdjustmentLayerSP layer;
644 TQString filtername;
645
646 if ((filtername = element.attribute("filtername")).isNull()) {
647 // XXX: Invalid adjustmentlayer! We should warn about it!
648 kdWarning(DBG_AREA_FILE) << "No filter in adjustment layer" << endl;
649 return 0;
650 }
651
652 KisFilter * f = KisFilterRegistry::instance()->get(filtername);
653 if (!f) {
654 kdWarning(DBG_AREA_FILE) << "No filter for filtername " << filtername << "\n";
655 return 0; // XXX: We don't have this filter. We should warn about it!
656 }
657
659
660 // We'll load the configuration and the selection later.
661 layer = new KisAdjustmentLayer(img, name, kfc, 0);
662 TQ_CHECK_PTR(layer);
663
664 layer->setCompositeOp(compositeOp);
665 layer->setVisible(visible);
666 layer->setLocked(locked);
667 layer->setX(x);
668 layer->setY(y);
669 layer->setOpacity(opacity);
670
671 if ((element.attribute("filename")).isNull())
672 m_layerFilenames[layer.data()] = name;
673 else
674 m_layerFilenames[layer.data()] = TQString(element.attribute("filename"));
675
676 return layer;
677}
678
679KisPartLayerSP KisDoc::loadPartLayer(const TQDomElement& element, KisImageSP img,
680 TQString name, TQ_INT32 /*x*/, TQ_INT32 /*y*/, TQ_INT32 opacity,
681 bool visible, bool locked,
682 KisCompositeOp compositeOp) {
683 KisChildDoc* child = new KisChildDoc(this);
684 TQString filename(element.attribute("filename"));
685 TQDomElement partElement = element.namedItem("object").toElement();
686
687 if (partElement.isNull()) {
688 kdWarning() << "loadPartLayer failed with partElement isNull" << endl;
689 return 0;
690 }
691
692 child->load(partElement);
693 insertChild(child);
694
695 KisPartLayerSP layer = new KisPartLayerImpl(img, child);
696 TQ_CHECK_PTR(layer);
697
698 layer->setCompositeOp(compositeOp);
699 layer->setVisible(visible);
700 layer->setLocked(locked);
701 layer->setOpacity(opacity);
702 layer->setName(name);
703
704 return layer;
705}
706
707bool KisDoc::completeSaving(KoStore *store)
708{
709 TQString uri = url().url();
710 TQString location;
711 bool external = isStoredExtern();
712 TQ_INT32 totalSteps = 0;
713
714 if (!m_currentImage) return false;
715
716 totalSteps = (m_currentImage)->nlayers();
717
718
719 setIOSteps(totalSteps + 1);
720
721 // Save the layers data
722 TQ_UINT32 count=0;
723 KisSaveVisitor visitor(m_currentImage, store, count);
724
725 if(external)
726 visitor.setExternalUri(uri);
727
728 m_currentImage->rootLayer()->accept(visitor);
729
730 // saving annotations
731 // XXX this only saves EXIF and ICC info. This would probably need
732 // a redesign of the dtd of the chalk file to do this more generally correct
733 // e.g. have <ANNOTATION> tags or so.
734 KisAnnotationSP annotation = (m_currentImage)->annotation("exif");
735 if (annotation) {
736 location = external ? TQString() : uri;
737 location += (m_currentImage)->name() + "/annotations/exif";
738 if (store->open(location)) {
739 store->write(annotation->annotation());
740 store->close();
741 }
742 }
743 if (m_currentImage->getProfile()) {
744 annotation = m_currentImage->getProfile()->annotation();
745
746 if (annotation) {
747 location = external ? TQString() : uri;
748 location += m_currentImage->name() + "/annotations/icc";
749 if (store->open(location)) {
750 store->write(annotation->annotation());
751 store->close();
752 }
753 }
754 }
755
756 IODone();
757 return true;
758}
759
760bool KisDoc::completeLoading(KoStore *store)
761{
762 TQString uri = url().url();
763 TQString location;
764 bool external = isStoredExtern();
765 TQ_INT32 totalSteps = 0;
766
767 totalSteps = (m_currentImage)->nlayers();
768
769 setIOSteps(totalSteps);
770
771 // Load the layers data
772 KisLoadVisitor visitor(m_currentImage, store, m_layerFilenames);
773
774 if(external)
775 visitor.setExternalUri(uri);
776
777 m_currentImage->rootLayer()->accept(visitor);
778
779 // annotations
780 // exif
781 location = external ? TQString() : uri;
782 location += (m_currentImage)->name() + "/annotations/exif";
783 if (store->hasFile(location)) {
784 TQByteArray data;
785 store->open(location);
786 data = store->read(store->size());
787 store->close();
788 (m_currentImage)->addAnnotation(new KisAnnotation("exif", "", data));
789 }
790 // icc profile
791 location = external ? TQString() : uri;
792 location += (m_currentImage)->name() + "/annotations/icc";
793 if (store->hasFile(location)) {
794 TQByteArray data;
795 store->open(location);
796 data = store->read(store->size());
797 store->close();
798 (m_currentImage)->setProfile(new KisProfile(data));
799 }
800
801 IODone();
802
803 setModified( false );
804 setUndo(true);
805 return true;
806}
807
808TQWidget* KisDoc::createCustomDocumentWidget(TQWidget *parent)
809{
810
811 KisConfig cfg;
812
813 int w = cfg.defImgWidth();
814 int h = cfg.defImgHeight();
815
816 TQSize sz = KisClipboard::instance()->clipSize();
817 if (sz.isValid() && sz.width() != 0 && sz.height() != 0) {
818 w = sz.width();
819 h = sz.height();
820 }
821 return new KisCustomImageWidget(parent, this, w, h, cfg.defImgResolution(), cfg.workingColorSpace(),"unnamed");
822}
823
824
825KoDocument* KisDoc::hitTest(const TQPoint &pos, const TQWMatrix& matrix) {
826 KoDocument* doc = super::hitTest(pos, matrix);
827 if (doc && doc != this) {
828 // We hit a child document. We will only acknowledge we hit it, if the hit child
829 // is the currently active parts layer.
830 KisPartLayerImpl* partLayer
831 = dynamic_cast<KisPartLayerImpl*>(currentImage()->activeLayer().data());
832
833 if (!partLayer)
834 return this;
835
836 if (doc == partLayer->childDoc()->document()) {
837 return doc;
838 }
839 return this;
840 }
841 return doc;
842}
843
844void KisDoc::renameImage(const TQString& oldName, const TQString& newName)
845{
846 (m_currentImage)->setName(newName);
847
848 if (undo())
849 addCommand(new KisCommandImageMv(this, this, newName, oldName));
850}
851
852
853KisImageSP KisDoc::newImage(const TQString& name, TQ_INT32 width, TQ_INT32 height, KisColorSpace * colorstrategy)
854{
855 if (!init())
856 return 0;
857
858 setUndo(false);
859
860 KisImageSP img = new KisImage(this, width, height, colorstrategy, name);
861 TQ_CHECK_PTR(img);
862 connect( img, TQ_SIGNAL( sigImageModified() ), this, TQ_SLOT( slotImageUpdated() ));
863
864 KisPaintLayer *layer = new KisPaintLayer(img, img->nextLayerName(), OPACITY_OPAQUE,colorstrategy);
865 TQ_CHECK_PTR(layer);
866
867 KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8();
868 KisFillPainter painter;
869
870 painter.begin(layer->paintDevice());
871 painter.fillRect(0, 0, width, height, KisColor(TQt::white, cs), OPACITY_OPAQUE);
872 painter.end();
873
874 img->addLayer(layer, img->rootLayer(), 0);
875 img->activate(layer);
876
877 m_currentImage = img;
878
879 setUndo(true);
880
881 return img;
882}
883
884bool KisDoc::newImage(const TQString& name, TQ_INT32 width, TQ_INT32 height, KisColorSpace * cs, const KisColor &bgColor, const TQString &imgDescription, const double imgResolution)
885{
886 if (!init())
887 return false;
888
889 KisConfig cfg;
890
891 TQ_UINT8 opacity = OPACITY_OPAQUE;//bgColor.getAlpha();
892 KisImageSP img;
893 KisPaintLayer *layer;
894
895 if (!cs) return false;
896
897 setUndo(false);
898
899 img = new KisImage(this, width, height, cs, name);
900 TQ_CHECK_PTR(img);
901 connect( img, TQ_SIGNAL( sigImageModified() ), this, TQ_SLOT( slotImageUpdated() ));
902 img->setResolution(imgResolution, imgResolution);
903 img->setDescription(imgDescription);
904 img->setProfile(cs->getProfile());
905
906 layer = new KisPaintLayer(img, img->nextLayerName(), OPACITY_OPAQUE, cs);
907 TQ_CHECK_PTR(layer);
908
909 KisFillPainter painter;
910 painter.begin(layer->paintDevice());
911 painter.fillRect(0, 0, width, height, bgColor, opacity);
912 painter.end();
913
914 TQValueVector<KisPaintDeviceAction *> actions = KisMetaRegistry::instance() ->
915 csRegistry()->paintDeviceActionsFor(cs);
916 for (uint i = 0; i < actions.count(); i++)
917 actions.at(i)->act(layer->paintDevice(), img->width(), img->height());
918
919 img->setBackgroundColor(bgColor);
920 img->addLayer(layer, img->rootLayer(), 0);
921 img->activate(layer);
922
923 m_currentImage = img;
924
925 cfg.defImgWidth(width);
926 cfg.defImgHeight(height);
927 cfg.defImgResolution(imgResolution);
928
929 setUndo(true);
930
931 return true;
932}
933
934KoView* KisDoc::createViewInstance(TQWidget* parent, const char *name)
935{
936 KisView * v = new KisView(this, this, parent, name);
937 TQ_CHECK_PTR(v);
938
939 return v;
940}
941
942void KisDoc::paintContent(TQPainter& painter, const TQRect& rc, bool transparent, double zoomX, double zoomY)
943{
944 KisConfig cfg;
945 TQString monitorProfileName = cfg.monitorProfile();
946 KisProfile * profile = KisMetaRegistry::instance()->csRegistry()->getProfileByName(monitorProfileName);
947 painter.scale(zoomX, zoomY);
948 TQRect rect = rc & m_currentImage->bounds();
949 KisImage::PaintFlags paintFlags;
950 if (transparent) {
951 paintFlags = KisImage::PAINT_SELECTION;
952 } else {
953 paintFlags = (KisImage::PaintFlags)(KisImage::PAINT_BACKGROUND|KisImage::PAINT_SELECTION);
954 }
955
956 paintFlags = (KisImage::PaintFlags)(paintFlags | KisImage::PAINT_EMBEDDED_RECT);
957
958 m_currentImage->renderToPainter(rect.left(), rect.top(), rect.right(), rect.bottom(), painter, profile, paintFlags);
959}
960
961void KisDoc::slotImageUpdated()
962{
963 emit docUpdated();
964 setModified(true);
965}
966
967void KisDoc::slotImageUpdated(const TQRect& rect)
968{
969 emit docUpdated(rect);
970}
971
972void KisDoc::beginMacro(const TQString& macroName)
973{
974 if (m_undo) {
975 if (m_macroNestDepth == 0) {
976 Q_ASSERT(m_currentMacro == 0);
977 m_currentMacro = new KMacroCommand(macroName);
978 TQ_CHECK_PTR(m_currentMacro);
979 }
980
981 m_macroNestDepth++;
982 }
983}
984
985void KisDoc::endMacro()
986{
987 if (m_undo) {
988 Q_ASSERT(m_macroNestDepth > 0);
989 if (m_macroNestDepth > 0) {
990 m_macroNestDepth--;
991
992 if (m_macroNestDepth == 0) {
993 Q_ASSERT(m_currentMacro != 0);
994
995 m_cmdHistory->addCommand(m_currentMacro, false);
996 m_currentMacro = 0;
997 emit sigCommandExecuted();
998 }
999 }
1000 }
1001}
1002
1003void KisDoc::setCommandHistoryListener(const KisCommandHistoryListener * l)
1004{
1005 // Never have more than one instance of a listener around. TQt should prove a Set class for this...
1006 m_undoListeners.removeRef(l);
1007 m_undoListeners.append(l);
1008}
1009
1010void KisDoc::removeCommandHistoryListener(const KisCommandHistoryListener * l)
1011{
1012 m_undoListeners.removeRef(l);
1013}
1014
1015KCommand * KisDoc::presentCommand()
1016{
1017 return m_cmdHistory->presentCommand();
1018}
1019
1020void KisDoc::addCommand(KCommand *cmd)
1021{
1022 Q_ASSERT(cmd);
1023
1025
1026 for (l = m_undoListeners.first(); l; l = m_undoListeners.next()) {
1027 l->notifyCommandAdded(cmd);
1028 }
1029
1030 setModified(true);
1031
1032 if (m_undo) {
1033 if (m_currentMacro)
1034 m_currentMacro->addCommand(cmd);
1035 else {
1036 m_cmdHistory->addCommand(cmd, false);
1037 emit sigCommandExecuted();
1038 }
1039 } else {
1040 kdDebug() << "Deleting command\n";
1041 delete cmd;
1042 }
1043}
1044
1045void KisDoc::setUndo(bool undo)
1046{
1047 m_undo = undo;
1048 if (m_undo && m_cmdHistory->undoLimit() == 50 /*default*/) {
1049 KisConfig cfg;
1050 setUndoLimit( cfg.defUndoLimit() );
1051 }
1052}
1053
1054TQ_INT32 KisDoc::undoLimit() const
1055{
1056 return m_cmdHistory->undoLimit();
1057}
1058
1059void KisDoc::setUndoLimit(TQ_INT32 limit)
1060{
1061 m_cmdHistory->setUndoLimit(limit);
1062}
1063
1064TQ_INT32 KisDoc::redoLimit() const
1065{
1066 return m_cmdHistory->redoLimit();
1067}
1068
1069void KisDoc::setRedoLimit(TQ_INT32 limit)
1070{
1071 m_cmdHistory->setRedoLimit(limit);
1072}
1073
1074void KisDoc::slotDocumentRestored()
1075{
1076 setModified(false);
1077}
1078
1079void KisDoc::slotCommandExecuted(KCommand *command)
1080{
1081 setModified(true);
1082 emit sigCommandExecuted();
1083
1085
1086 for (l = m_undoListeners.first(); l; l = m_undoListeners.next()) {
1087 l->notifyCommandExecuted(command);
1088 }
1089
1090}
1091
1092void KisDoc::slotUpdate(KisImageSP, TQ_UINT32 x, TQ_UINT32 y, TQ_UINT32 w, TQ_UINT32 h)
1093{
1094 TQRect rc(x, y, w, h);
1095
1096 emit docUpdated(rc);
1097}
1098
1099bool KisDoc::undo() const
1100{
1101 return m_undo;
1102}
1103
1104void KisDoc::setIOSteps(TQ_INT32 nsteps)
1105{
1106 m_ioProgressTotalSteps = nsteps * 100;
1107 m_ioProgressBase = 0;
1108 emitProgress(0);
1109}
1110
1111void KisDoc::IOCompletedStep()
1112{
1113 m_ioProgressBase += 100;
1114}
1115
1116void KisDoc::IODone()
1117{
1118 emitProgress(-1);
1119}
1120
1121void KisDoc::slotIOProgress(TQ_INT8 percentage)
1122{
1123 TDEApplication *app = TDEApplication::kApplication();
1124
1125 Q_ASSERT(app);
1126
1127 if (app->hasPendingEvents())
1128 app->processEvents();
1129
1130 int totalPercentage = ((m_ioProgressBase + percentage) * 100) / m_ioProgressTotalSteps;
1131
1132 emitProgress(totalPercentage);
1133}
1134
1135KisChildDoc * KisDoc::createChildDoc( const TQRect & rect, KoDocument* childDoc )
1136{
1137 KisChildDoc * ch = new KisChildDoc( this, rect, childDoc );
1138 insertChild( ch );
1139 ch->document()->setStoreInternal(true);
1140 return ch;
1141}
1142
1143void KisDoc::prepareForImport()
1144{
1145 if (m_nserver == 0)
1146 init();
1147 setUndo(false);
1148}
1149
1150KisImageSP KisDoc::currentImage()
1151{
1152 return m_currentImage;
1153}
1154
1155void KisDoc::setCurrentImage(KisImageSP image)
1156{
1157 m_currentImage = image;
1158 setUndo(true);
1159 image->notifyImageLoaded();
1160 emit loadingFinished();
1161}
1162
1163void KisDoc::initEmpty()
1164{
1165 KisConfig cfg;
1166 KisColorSpace * rgb = KisMetaRegistry::instance()->csRegistry()->getRGB8();
1167 newImage("", cfg.defImgWidth(), cfg.defImgHeight(), rgb);
1168}
1169
1170#include "kis_doc.moc"
1171
Class that contains a KisFilter and optionally a KisSelection.
An data extension mechanism for Chalk.
The child document is responsible for saving and loading the embedded layers.
KisColorSpace * getRGB8()
Convenience method to get an RGB colorspace with the default lcms profile.
KisColorSpace * getColorSpace(const KisID &csID, const TQString &profileName)
Return the colorspace + profile as named, or NULL if impossible combination.
KisProfile * getProfileByName(const TQString &name)
Return the profile associated with the given product name, or 0.
A colorspace is the definition of a certain color model in Chalk.
virtual KisProfile * getProfile() const =0
Return the profile of this color space.
A KisColor describes a color in a certain colorspace.
Definition kis_color.h:33
Undo listeners want to be notified of undo and redo actions.
The 'Custom Document' widget in the Chalk startup widget.
This painter can be used to fill paint devices in different ways.
void fillRect(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const KisColor &c)
Fill a rectangle with a certain color.
A KisFilterConfiguration is the serializable representation of the filter parameters.
Basic interface of a Chalk filter.
Definition kis_filter.h:46
virtual KisFilterConfiguration * configuration(TQWidget *)
A KisLayer that bundles child layers into a single layer.
Chalk has a large number of extensible resources.
Definition kis_id.h:57
This layer is of a type that can be painted on.
KCommand * end()
Finish painting on the current device.
void begin(KisPaintDeviceSP device)
Start painting on the specified device.
A PartLayer is a layer that contains a KOffice Part like a KWord document or a KSpread spreadsheet.
virtual KisChildDoc * childDoc() const
Returns the childDoc so that we can access the doc from other places, if need be (KisDoc)