/* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2000 John Califf * Copyright (c) 2001 Toshitaka Fujioka * Copyright (c) 2002, 2003 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // TQt #include #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include // KOffice #include #include #include #include #include #include #include #include // Local #include #include #include "kis_annotation.h" #include "kis_types.h" #include "kis_config.h" #include "kis_debug_areas.h" #include "kis_doc.h" #include "kis_factory.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_nameserver.h" #include "kis_painter.h" #include "kis_selection.h" #include "kis_fill_painter.h" #include "kis_command.h" #include "kis_view.h" #include "kis_colorspace.h" #include "kis_colorspace_factory_registry.h" #include "kis_profile.h" #include "kis_id.h" #include "kis_part_layer.h" #include "kis_doc_iface.h" #include "kis_paint_device_action.h" #include "kis_custom_image_widget.h" #include "kis_load_visitor.h" #include "kis_save_visitor.h" #include "kis_savexml_visitor.h" static const char *CURRENT_DTD_VERSION = "1.3"; /** * Mime type for this app - not same as file type, but file types * can be associated with a mime type and are opened with applications * associated with the same mime type */ #define APP_MIMETYPE "application/x-chalk" /** * Mime type for native file format */ #define NATIVE_MIMETYPE "application/x-kra" namespace { class KisCommandImageMv : public KisCommand { typedef KisCommand super; public: KisCommandImageMv(KisDoc *doc, KisUndoAdapter *adapter, const TQString& name, const TQString& oldName) : super(i18n("Rename Image"), adapter) { m_doc = doc; m_name = name; m_oldName = oldName; } virtual ~KisCommandImageMv() { } virtual void execute() { adapter()->setUndo(false); m_doc->renameImage(m_oldName, m_name); adapter()->setUndo(true); } virtual void unexecute() { adapter()->setUndo(false); m_doc->renameImage(m_name, m_oldName); adapter()->setUndo(true); } private: KisDoc *m_doc; TQString m_name; TQString m_oldName; }; } KisDoc::KisDoc(TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name, bool singleViewMode) : super(parentWidget, widgetName, parent, name, singleViewMode) { m_undo = false; m_dcop = 0; m_cmdHistory = 0; m_nserver = 0; m_currentImage = 0; m_currentMacro = 0; m_macroNestDepth = 0; m_ioProgressBase = 0; m_ioProgressTotalSteps = 0; setInstance( KisFactory::instance(), false ); setTemplateType( "chalk_template" ); init(); if (name) dcopObject(); } KisDoc::~KisDoc() { delete m_cmdHistory; delete m_nserver; m_undoListeners.setAutoDelete(false); delete m_dcop; } TQCString KisDoc::mimeType() const { return APP_MIMETYPE; } DCOPObject *KisDoc::dcopObject() { if (!m_dcop) { m_dcop = new KisDocIface(this); TQ_CHECK_PTR(m_dcop); } return m_dcop; } bool KisDoc::initDoc(InitDocFlags flags, TQWidget* parentWidget) { if (!init()) return false; bool ok = false; TQString file; KoTemplateChooseDia::DialogType dlgtype; if (flags != KoDocument::InitDocFileNew) { dlgtype = KoTemplateChooseDia::Everything; } else { dlgtype = KoTemplateChooseDia::OnlyTemplates; } KoTemplateChooseDia::ReturnType ret = KoTemplateChooseDia::choose(KisFactory::instance(), file, dlgtype, "chalk_template", parentWidget); setUndo(false); if (ret == KoTemplateChooseDia::Template) { resetURL(); ok = loadNativeFormat( file ); setEmpty(); ok = true; } else if (ret == KoTemplateChooseDia::File) { KURL url( file ); ok = openURL(url); } else if (ret == KoTemplateChooseDia::Empty) { setEmpty(); ok = true; } setModified(false); KisConfig cfg; setUndo(cfg.undoEnabled()); return ok; } void KisDoc::openExistingFile(const TQString& file) { setUndo(false); KoDocument::openExistingFile(file); setUndo(true); } void KisDoc::openTemplate(const TQString& file) { setUndo(false); KoDocument::openTemplate(file); setUndo(true); } bool KisDoc::init() { if (m_cmdHistory) { delete m_cmdHistory; m_cmdHistory = 0; } if (m_nserver) { delete m_nserver; m_nserver = 0; } m_cmdHistory = new KoCommandHistory(actionCollection(), true); TQ_CHECK_PTR(m_cmdHistory); connect(m_cmdHistory, TQT_SIGNAL(documentRestored()), this, TQT_SLOT(slotDocumentRestored())); connect(m_cmdHistory, TQT_SIGNAL(commandExecuted(KCommand *)), this, TQT_SLOT(slotCommandExecuted(KCommand *))); setUndo(true); m_nserver = new KisNameServer(i18n("Image %1"), 1); TQ_CHECK_PTR(m_nserver); if (!KisMetaRegistry::instance()->csRegistry()->exists(KisID("RGBA",""))) { KMessageBox::sorry(0, i18n("No colorspace modules loaded: cannot run Chalk")); return false; } m_undoListeners.setAutoDelete(false); return true; } TQDomDocument KisDoc::saveXML() { TQDomDocument doc = createDomDocument("DOC", CURRENT_DTD_VERSION); TQDomElement root = doc.documentElement(); root.setAttribute("editor", "Chalk"); root.setAttribute("depth", sizeof(TQ_UINT8)); root.setAttribute("syntaxVersion", "1"); root.appendChild(saveImage(doc, m_currentImage)); return doc; } bool KisDoc::loadOasis( const TQDomDocument&, KoOasisStyles&, const TQDomDocument&, KoStore* ) { //XXX: todo (and that includes defining an OASIS format for layered 2D raster data!) return false; } bool KisDoc::saveOasis( KoStore*, KoXmlWriter* ) { //XXX: todo (and that includes defining an OASIS format for layered 2D raster data!) return false; } bool KisDoc::loadXML(TQIODevice *, const TQDomDocument& doc) { TQDomElement root; TQString attr; TQDomNode node; KisImageSP img; if (!init()) return false; if (doc.doctype().name() != "DOC") return false; root = doc.documentElement(); attr = root.attribute("syntaxVersion"); if (attr.toInt() > 1) return false; if ((attr = root.attribute("depth")).isNull()) return false; m_conversionDepth = attr.toInt(); if (!root.hasChildNodes()) { return false; // XXX used to be: return slotNewImage(); } setUndo(false); for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement()) { if (node.nodeName() == "IMAGE") { TQDomElement elem = node.toElement(); if (!(img = loadImage(elem))) return false; m_currentImage = img; } else { return false; } } } emit loadingFinished(); return true; } bool KisDoc::loadChildren(KoStore* store) { TQPtrListIterator it(children()); for( ; it.current(); ++it ) { if (!it.current()->loadDocument(store)) { return false; } } return true; } TQDomElement KisDoc::saveImage(TQDomDocument& doc, KisImageSP img) { TQDomElement image = doc.createElement("IMAGE"); Q_ASSERT(img); image.setAttribute("name", img->name()); image.setAttribute("mime", "application/x-kra"); image.setAttribute("width", img->width()); image.setAttribute("height", img->height()); image.setAttribute("colorspacename", img->colorSpace()->id().id()); image.setAttribute("description", img->description()); // XXX: Save profile as blob inside the image, instead of the product name. if (img->getProfile() && img->getProfile()-> valid()) image.setAttribute("profile", img->getProfile()->productName()); image.setAttribute("x-res", img->xRes()); image.setAttribute("y-res", img->yRes()); TQ_UINT32 count=0; KisSaveXmlVisitor visitor(doc, image, count, true); m_currentImage->rootLayer()->accept(visitor); return image; } KisImageSP KisDoc::loadImage(const TQDomElement& element) { KisConfig cfg; TQString attr; TQDomNode node; TQDomNode child; KisImageSP img; TQString name; TQ_INT32 width; TQ_INT32 height; TQString description; TQString profileProductName; double xres; double yres; TQString colorspacename; KisColorSpace * cs; if ((attr = element.attribute("mime")) == NATIVE_MIMETYPE) { if ((name = element.attribute("name")).isNull()) return 0; if ((attr = element.attribute("width")).isNull()) return 0; width = attr.toInt(); if ((attr = element.attribute("height")).isNull()) return 0; height = attr.toInt(); description = element.attribute("description"); if ((attr = element.attribute("x-res")).isNull()) xres = 100.0; xres = attr.toDouble(); if ((attr = element.attribute("y-res")).isNull()) yres = 100.0; yres = attr.toDouble(); if ((colorspacename = element.attribute("colorspacename")).isNull()) { // An old file: take a reasonable default. // Chalk didn't support anything else in those // days anyway. colorspacename = "RGBA"; } // A hack for an old colorspacename if (colorspacename == "Grayscale + Alpha") colorspacename = "GRAYA"; if ((profileProductName = element.attribute("profile")).isNull()) { // no mention of profile so get default profile cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(colorspacename,""); } else { cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(colorspacename, profileProductName); } if (cs == 0) { kdWarning(DBG_AREA_FILE) << "Could not open colorspace\n"; return 0; } img = new KisImage(this, width, height, cs, name); img->blockSignals(true); // Don't send out signals while we're building the image TQ_CHECK_PTR(img); connect( img, TQT_SIGNAL( sigImageModified() ), this, TQT_SLOT( slotImageUpdated() )); img->setDescription(description); img->setResolution(xres, yres); loadLayers(element, img, img->rootLayer().data()); } img->notifyImageLoaded(); return img; } void KisDoc::loadLayers(const TQDomElement& element, KisImageSP img, KisGroupLayerSP parent) { TQDomNode node = element.firstChild(); TQDomNode child; if(!node.isNull()) { if (node.isElement()) { if (node.nodeName() == "LAYERS") { for (child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { KisLayerSP layer = loadLayer(child.toElement(), img); if (!layer) { kdWarning(DBG_AREA_FILE) << "Could not load layer\n"; } else { img->nextLayerName(); // Make sure the nameserver is current with the number of layers. img->addLayer(layer, parent, 0); } } } } } } KisLayerSP KisDoc::loadLayer(const TQDomElement& element, KisImageSP img) { // Nota bene: If you add new properties to layers, you should // ALWAYS define a default value in case the property is not // present in the layer definition: this helps a LOT with backward // compatibilty. TQString attr; TQString name; TQ_INT32 x; TQ_INT32 y; TQ_INT32 opacity; bool visible; bool locked; if ((name = element.attribute("name")).isNull()) return 0; if ((attr = element.attribute("x")).isNull()) return 0; x = attr.toInt(); if ((attr = element.attribute("y")).isNull()) return 0; y = attr.toInt(); if ((attr = element.attribute("opacity")).isNull()) return 0; if ((opacity = attr.toInt()) < 0 || opacity > TQ_UINT8_MAX) opacity = OPACITY_OPAQUE; TQString compositeOpName = element.attribute("compositeop"); KisCompositeOp compositeOp; if (compositeOpName.isNull()) { compositeOp = COMPOSITE_OVER; } else { compositeOp = KisCompositeOp(compositeOpName); } if (!compositeOp.isValid()) { return 0; } if ((attr = element.attribute("visible")).isNull()) attr = "1"; visible = attr == "0" ? false : true; if ((attr = element.attribute("locked")).isNull()) attr = "0"; locked = attr == "0" ? false : true; // Now find out the layer type and do specific handling if ((attr = element.attribute("layertype")).isNull()) return loadPaintLayer(element, img, name, x, y, opacity, visible, locked, compositeOp) ; if(attr == "paintlayer") return loadPaintLayer(element, img, name, x, y, opacity, visible, locked, compositeOp); if(attr == "grouplayer") return loadGroupLayer(element, img, name, x, y, opacity, visible, locked, compositeOp).data(); if(attr == "adjustmentlayer") return loadAdjustmentLayer(element, img, name, x, y, opacity, visible, locked, compositeOp).data(); if(attr == "partlayer") return loadPartLayer(element, img, name, x, y, opacity, visible, locked, compositeOp).data(); kdWarning(DBG_AREA_FILE) << "Specified layertype is not recognised\n"; return 0; } KisLayerSP KisDoc::loadPaintLayer(const TQDomElement& element, KisImageSP img, TQString name, TQ_INT32 x, TQ_INT32 y, TQ_INT32 opacity, bool visible, bool locked, KisCompositeOp compositeOp) { TQString attr; KisPaintLayerSP layer; KisColorSpace * cs; TQString colorspacename; TQString profileProductName; if ((colorspacename = element.attribute("colorspacename")).isNull()) cs = img->colorSpace(); else // use default profile - it will be replaced later in completLoading cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(colorspacename,""); layer = new KisPaintLayer(img, name, opacity, cs); TQ_CHECK_PTR(layer); layer->setCompositeOp(compositeOp); layer->setVisible(visible); layer->setLocked(locked); layer->setX(x); layer->setY(y); if ((element.attribute("filename")).isNull()) m_layerFilenames[layer.data()] = name; else m_layerFilenames[layer.data()] = TQString(element.attribute("filename")); if ((attr = element.attribute("hasmask")).isNull()) attr = "0"; if (attr == "1") { // We add a mask, but we'll fill in the actual mask later in completeLoading with the visitor layer->createMask(); } // Load exif info for( TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling() ) { TQDomElement e = node.toElement(); if ( !e.isNull() && e.tagName() == "ExifInfo" ) { layer->paintDevice()->exifInfo()->load(e); } } return layer.data(); } KisGroupLayerSP KisDoc::loadGroupLayer(const TQDomElement& element, KisImageSP img, TQString name, TQ_INT32 x, TQ_INT32 y, TQ_INT32 opacity, bool visible, bool locked, KisCompositeOp compositeOp) { TQString attr; KisGroupLayerSP layer; layer = new KisGroupLayer(img, name, opacity); TQ_CHECK_PTR(layer); layer->setCompositeOp(compositeOp); layer->setVisible(visible); layer->setLocked(locked); layer->setX(x); layer->setY(y); loadLayers(element, img, layer); return layer; } KisAdjustmentLayerSP KisDoc::loadAdjustmentLayer(const TQDomElement& element, KisImageSP img, TQString name, TQ_INT32 x, TQ_INT32 y, TQ_INT32 opacity, bool visible, bool locked, KisCompositeOp compositeOp) { TQString attr; KisAdjustmentLayerSP layer; TQString filtername; if ((filtername = element.attribute("filtername")).isNull()) { // XXX: Invalid adjustmentlayer! We should warn about it! kdWarning(DBG_AREA_FILE) << "No filter in adjustment layer" << endl; return 0; } KisFilter * f = KisFilterRegistry::instance()->get(filtername); if (!f) { kdWarning(DBG_AREA_FILE) << "No filter for filtername " << filtername << "\n"; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfiguration * kfc = f->configuration(); // We'll load the configuration and the selection later. layer = new KisAdjustmentLayer(img, name, kfc, 0); TQ_CHECK_PTR(layer); layer->setCompositeOp(compositeOp); layer->setVisible(visible); layer->setLocked(locked); layer->setX(x); layer->setY(y); layer->setOpacity(opacity); if ((element.attribute("filename")).isNull()) m_layerFilenames[layer.data()] = name; else m_layerFilenames[layer.data()] = TQString(element.attribute("filename")); return layer; } KisPartLayerSP KisDoc::loadPartLayer(const TQDomElement& element, KisImageSP img, TQString name, TQ_INT32 /*x*/, TQ_INT32 /*y*/, TQ_INT32 opacity, bool visible, bool locked, KisCompositeOp compositeOp) { KisChildDoc* child = new KisChildDoc(this); TQString filename(element.attribute("filename")); TQDomElement partElement = element.namedItem("object").toElement(); if (partElement.isNull()) { kdWarning() << "loadPartLayer failed with partElement isNull" << endl; return 0; } child->load(partElement); insertChild(child); KisPartLayerSP layer = new KisPartLayerImpl(img, child); TQ_CHECK_PTR(layer); layer->setCompositeOp(compositeOp); layer->setVisible(visible); layer->setLocked(locked); layer->setOpacity(opacity); layer->setName(name); return layer; } bool KisDoc::completeSaving(KoStore *store) { TQString uri = url().url(); TQString location; bool external = isStoredExtern(); TQ_INT32 totalSteps = 0; if (!m_currentImage) return false; totalSteps = (m_currentImage)->nlayers(); setIOSteps(totalSteps + 1); // Save the layers data TQ_UINT32 count=0; KisSaveVisitor visitor(m_currentImage, store, count); if(external) visitor.setExternalUri(uri); m_currentImage->rootLayer()->accept(visitor); // saving annotations // XXX this only saves EXIF and ICC info. This would probably need // a redesign of the dtd of the chalk file to do this more generally correct // e.g. have tags or so. KisAnnotationSP annotation = (m_currentImage)->annotation("exif"); if (annotation) { location = external ? TQString() : uri; location += (m_currentImage)->name() + "/annotations/exif"; if (store->open(location)) { store->write(annotation->annotation()); store->close(); } } if (m_currentImage->getProfile()) { annotation = m_currentImage->getProfile()->annotation(); if (annotation) { location = external ? TQString() : uri; location += m_currentImage->name() + "/annotations/icc"; if (store->open(location)) { store->write(annotation->annotation()); store->close(); } } } IODone(); return true; } bool KisDoc::completeLoading(KoStore *store) { TQString uri = url().url(); TQString location; bool external = isStoredExtern(); TQ_INT32 totalSteps = 0; totalSteps = (m_currentImage)->nlayers(); setIOSteps(totalSteps); // Load the layers data KisLoadVisitor visitor(m_currentImage, store, m_layerFilenames); if(external) visitor.setExternalUri(uri); m_currentImage->rootLayer()->accept(visitor); // annotations // exif location = external ? TQString() : uri; location += (m_currentImage)->name() + "/annotations/exif"; if (store->hasFile(location)) { TQByteArray data; store->open(location); data = store->read(store->size()); store->close(); (m_currentImage)->addAnnotation(new KisAnnotation("exif", "", data)); } // icc profile location = external ? TQString() : uri; location += (m_currentImage)->name() + "/annotations/icc"; if (store->hasFile(location)) { TQByteArray data; store->open(location); data = store->read(store->size()); store->close(); (m_currentImage)->setProfile(new KisProfile(data)); } IODone(); setModified( false ); setUndo(true); return true; } TQWidget* KisDoc::createCustomDocumentWidget(TQWidget *parent) { KisConfig cfg; int w = cfg.defImgWidth(); int h = cfg.defImgHeight(); TQSize sz = KisClipboard::instance()->clipSize(); if (sz.isValid() && sz.width() != 0 && sz.height() != 0) { w = sz.width(); h = sz.height(); } return new KisCustomImageWidget(parent, this, w, h, cfg.defImgResolution(), cfg.workingColorSpace(),"unnamed"); } KoDocument* KisDoc::hitTest(const TQPoint &pos, const TQWMatrix& matrix) { KoDocument* doc = super::hitTest(pos, matrix); if (doc && doc != this) { // We hit a child document. We will only acknowledge we hit it, if the hit child // is the currently active parts layer. KisPartLayerImpl* partLayer = dynamic_cast(currentImage()->activeLayer().data()); if (!partLayer) return this; if (doc == partLayer->childDoc()->document()) { return doc; } return this; } return doc; } void KisDoc::renameImage(const TQString& oldName, const TQString& newName) { (m_currentImage)->setName(newName); if (undo()) addCommand(new KisCommandImageMv(this, this, newName, oldName)); } KisImageSP KisDoc::newImage(const TQString& name, TQ_INT32 width, TQ_INT32 height, KisColorSpace * colorstrategy) { if (!init()) return 0; setUndo(false); KisImageSP img = new KisImage(this, width, height, colorstrategy, name); TQ_CHECK_PTR(img); connect( img, TQT_SIGNAL( sigImageModified() ), this, TQT_SLOT( slotImageUpdated() )); KisPaintLayer *layer = new KisPaintLayer(img, img->nextLayerName(), OPACITY_OPAQUE,colorstrategy); TQ_CHECK_PTR(layer); KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, KisColor(TQt::white, cs), OPACITY_OPAQUE); painter.end(); img->addLayer(layer, img->rootLayer(), 0); img->activate(layer); m_currentImage = img; setUndo(true); return img; } bool KisDoc::newImage(const TQString& name, TQ_INT32 width, TQ_INT32 height, KisColorSpace * cs, const KisColor &bgColor, const TQString &imgDescription, const double imgResolution) { if (!init()) return false; KisConfig cfg; TQ_UINT8 opacity = OPACITY_OPAQUE;//bgColor.getAlpha(); KisImageSP img; KisPaintLayer *layer; if (!cs) return false; setUndo(false); img = new KisImage(this, width, height, cs, name); TQ_CHECK_PTR(img); connect( img, TQT_SIGNAL( sigImageModified() ), this, TQT_SLOT( slotImageUpdated() )); img->setResolution(imgResolution, imgResolution); img->setDescription(imgDescription); img->setProfile(cs->getProfile()); layer = new KisPaintLayer(img, img->nextLayerName(), OPACITY_OPAQUE, cs); TQ_CHECK_PTR(layer); KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, bgColor, opacity); painter.end(); TQValueVector actions = KisMetaRegistry::instance() -> csRegistry()->paintDeviceActionsFor(cs); for (uint i = 0; i < actions.count(); i++) actions.at(i)->act(layer->paintDevice(), img->width(), img->height()); img->setBackgroundColor(bgColor); img->addLayer(layer, img->rootLayer(), 0); img->activate(layer); m_currentImage = img; cfg.defImgWidth(width); cfg.defImgHeight(height); cfg.defImgResolution(imgResolution); setUndo(true); return true; } KoView* KisDoc::createViewInstance(TQWidget* parent, const char *name) { KisView * v = new KisView(this, this, parent, name); TQ_CHECK_PTR(v); return v; } void KisDoc::paintContent(TQPainter& painter, const TQRect& rc, bool transparent, double zoomX, double zoomY) { KisConfig cfg; TQString monitorProfileName = cfg.monitorProfile(); KisProfile * profile = KisMetaRegistry::instance()->csRegistry()->getProfileByName(monitorProfileName); painter.scale(zoomX, zoomY); TQRect rect = rc & m_currentImage->bounds(); KisImage::PaintFlags paintFlags; if (transparent) { paintFlags = KisImage::PAINT_SELECTION; } else { paintFlags = (KisImage::PaintFlags)(KisImage::PAINT_BACKGROUND|KisImage::PAINT_SELECTION); } paintFlags = (KisImage::PaintFlags)(paintFlags | KisImage::PAINT_EMBEDDED_RECT); m_currentImage->renderToPainter(rect.left(), rect.top(), rect.right(), rect.bottom(), painter, profile, paintFlags); } void KisDoc::slotImageUpdated() { emit docUpdated(); setModified(true); } void KisDoc::slotImageUpdated(const TQRect& rect) { emit docUpdated(rect); } void KisDoc::beginMacro(const TQString& macroName) { if (m_undo) { if (m_macroNestDepth == 0) { Q_ASSERT(m_currentMacro == 0); m_currentMacro = new KMacroCommand(macroName); TQ_CHECK_PTR(m_currentMacro); } m_macroNestDepth++; } } void KisDoc::endMacro() { if (m_undo) { Q_ASSERT(m_macroNestDepth > 0); if (m_macroNestDepth > 0) { m_macroNestDepth--; if (m_macroNestDepth == 0) { Q_ASSERT(m_currentMacro != 0); m_cmdHistory->addCommand(m_currentMacro, false); m_currentMacro = 0; emit sigCommandExecuted(); } } } } void KisDoc::setCommandHistoryListener(const KisCommandHistoryListener * l) { // Never have more than one instance of a listener around. TQt should prove a Set class for this... m_undoListeners.removeRef(l); m_undoListeners.append(l); } void KisDoc::removeCommandHistoryListener(const KisCommandHistoryListener * l) { m_undoListeners.removeRef(l); } KCommand * KisDoc::presentCommand() { return m_cmdHistory->presentCommand(); } void KisDoc::addCommand(KCommand *cmd) { Q_ASSERT(cmd); KisCommandHistoryListener* l = 0; for (l = m_undoListeners.first(); l; l = m_undoListeners.next()) { l->notifyCommandAdded(cmd); } setModified(true); if (m_undo) { if (m_currentMacro) m_currentMacro->addCommand(cmd); else { m_cmdHistory->addCommand(cmd, false); emit sigCommandExecuted(); } } else { kdDebug() << "Deleting command\n"; delete cmd; } } void KisDoc::setUndo(bool undo) { m_undo = undo; if (m_undo && m_cmdHistory->undoLimit() == 50 /*default*/) { KisConfig cfg; setUndoLimit( cfg.defUndoLimit() ); } } TQ_INT32 KisDoc::undoLimit() const { return m_cmdHistory->undoLimit(); } void KisDoc::setUndoLimit(TQ_INT32 limit) { m_cmdHistory->setUndoLimit(limit); } TQ_INT32 KisDoc::redoLimit() const { return m_cmdHistory->redoLimit(); } void KisDoc::setRedoLimit(TQ_INT32 limit) { m_cmdHistory->setRedoLimit(limit); } void KisDoc::slotDocumentRestored() { setModified(false); } void KisDoc::slotCommandExecuted(KCommand *command) { setModified(true); emit sigCommandExecuted(); KisCommandHistoryListener* l = 0; for (l = m_undoListeners.first(); l; l = m_undoListeners.next()) { l->notifyCommandExecuted(command); } } void KisDoc::slotUpdate(KisImageSP, TQ_UINT32 x, TQ_UINT32 y, TQ_UINT32 w, TQ_UINT32 h) { TQRect rc(x, y, w, h); emit docUpdated(rc); } bool KisDoc::undo() const { return m_undo; } void KisDoc::setIOSteps(TQ_INT32 nsteps) { m_ioProgressTotalSteps = nsteps * 100; m_ioProgressBase = 0; emitProgress(0); } void KisDoc::IOCompletedStep() { m_ioProgressBase += 100; } void KisDoc::IODone() { emitProgress(-1); } void KisDoc::slotIOProgress(TQ_INT8 percentage) { TDEApplication *app = TDEApplication::kApplication(); Q_ASSERT(app); if (app->hasPendingEvents()) app->processEvents(); int totalPercentage = ((m_ioProgressBase + percentage) * 100) / m_ioProgressTotalSteps; emitProgress(totalPercentage); } KisChildDoc * KisDoc::createChildDoc( const TQRect & rect, KoDocument* childDoc ) { KisChildDoc * ch = new KisChildDoc( this, rect, childDoc ); insertChild( ch ); ch->document()->setStoreInternal(true); return ch; } void KisDoc::prepareForImport() { if (m_nserver == 0) init(); setUndo(false); } KisImageSP KisDoc::currentImage() { return m_currentImage; } void KisDoc::setCurrentImage(KisImageSP image) { m_currentImage = image; setUndo(true); image->notifyImageLoaded(); emit loadingFinished(); } void KisDoc::initEmpty() { KisConfig cfg; KisColorSpace * rgb = KisMetaRegistry::instance()->csRegistry()->getRGB8(); newImage("", cfg.defImgWidth(), cfg.defImgHeight(), rgb); } #include "kis_doc.moc"