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
658 KisFilterConfiguration * kfc = f->configuration();
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
1024 KisCommandHistoryListener* l = 0;
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
1084 KisCommandHistoryListener* l = 0;
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
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.
void fillRect(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const KisColor &c)
Fill a rectangle with a certain color.
virtual KisFilterConfiguration * configuration(TQWidget *)
_T get(const KisID &name) const
This function allow to get an object from its KisID.
KCommand * end()
Finish painting on the current device.
void begin(KisPaintDeviceSP device)
Start painting on the specified device.
virtual KisChildDoc * childDoc() const
Returns the childDoc so that we can access the doc from other places, if need be (KisDoc).