You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1172 lines
31 KiB
1172 lines
31 KiB
14 years ago
|
/*
|
||
|
* Copyright (c) 1999 Matthias Elter <me@kde.org>
|
||
|
* Copyright (c) 2000 John Califf <jcaliff@compuzone.net>
|
||
|
* Copyright (c) 2001 Toshitaka Fujioka <fujioka@kde.org>
|
||
|
* Copyright (c) 2002, 2003 Patrick Julien <freak@codepimps.org>
|
||
|
*
|
||
|
* 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 <tqapplication.h>
|
||
|
#include <tqdom.h>
|
||
|
#include <tqimage.h>
|
||
|
#include <tqpainter.h>
|
||
|
#include <tqtl.h>
|
||
|
#include <tqstringlist.h>
|
||
|
#include <tqwidget.h>
|
||
|
#include <tqpaintdevicemetrics.h>
|
||
|
|
||
|
// KDE
|
||
|
#include <dcopobject.h>
|
||
12 years ago
|
#include <tdeapplication.h>
|
||
14 years ago
|
#include <kcommand.h>
|
||
|
#include <kdebug.h>
|
||
|
#include <kimageio.h>
|
||
12 years ago
|
#include <tdefiledialog.h>
|
||
12 years ago
|
#include <tdeglobal.h>
|
||
14 years ago
|
#include <kmimetype.h>
|
||
|
#include <knotifyclient.h>
|
||
12 years ago
|
#include <tdelocale.h>
|
||
|
#include <tdemessagebox.h>
|
||
14 years ago
|
|
||
|
// KOffice
|
||
|
#include <KoFilterManager.h>
|
||
|
#include <KoMainWindow.h>
|
||
|
#include <KoQueryTrader.h>
|
||
|
#include <KoStore.h>
|
||
|
#include <KoStoreDevice.h>
|
||
|
#include <KoTemplateChooseDia.h>
|
||
|
#include <KoApplication.h>
|
||
|
#include <KoCommandHistory.h>
|
||
|
|
||
|
// Local
|
||
|
#include <kis_clipboard.h>
|
||
|
#include <kis_meta_registry.h>
|
||
|
#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;
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
13 years ago
|
KisDoc::KisDoc(TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name, bool singleViewMode) :
|
||
|
super(parentWidget, widgetName, parent, name, singleViewMode)
|
||
14 years ago
|
{
|
||
|
|
||
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(m_dcop);
|
||
14 years ago
|
}
|
||
|
return m_dcop;
|
||
|
}
|
||
|
|
||
14 years ago
|
bool KisDoc::initDoc(InitDocFlags flags, TQWidget* parentWidget)
|
||
14 years ago
|
{
|
||
|
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",
|
||
14 years ago
|
parentWidget);
|
||
14 years ago
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(m_cmdHistory);
|
||
14 years ago
|
|
||
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(m_nserver);
|
||
14 years ago
|
|
||
|
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) {
|
||
13 years ago
|
TQPtrListIterator<KoDocumentChild> it(children());
|
||
14 years ago
|
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
|
||
13 years ago
|
TQ_CHECK_PTR(img);
|
||
14 years ago
|
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;
|
||
|
}
|
||
|
|
||
13 years ago
|
void KisDoc::loadLayers(const TQDomElement& element, KisImageSP img, KisGroupLayerSP parent)
|
||
14 years ago
|
{
|
||
|
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.
|
||
13 years ago
|
img->addLayer(layer, parent, 0);
|
||
14 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(layer);
|
||
14 years ago
|
|
||
|
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"));
|
||
|
|
||
13 years ago
|
if ((attr = element.attribute("hasmask")).isNull())
|
||
14 years ago
|
attr = "0";
|
||
|
|
||
|
if (attr == "1") {
|
||
13 years ago
|
// We add a mask, but we'll fill in the actual mask later in completeLoading with the visitor
|
||
14 years ago
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(layer);
|
||
14 years ago
|
|
||
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(layer);
|
||
14 years ago
|
|
||
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(layer);
|
||
14 years ago
|
|
||
|
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 <ANNOTATION> 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;
|
||
|
}
|
||
|
|
||
13 years ago
|
TQWidget* KisDoc::createCustomDocumentWidget(TQWidget *parent)
|
||
14 years ago
|
{
|
||
|
|
||
|
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();
|
||
|
}
|
||
13 years ago
|
return new KisCustomImageWidget(parent, this, w, h, cfg.defImgResolution(), cfg.workingColorSpace(),"unnamed");
|
||
14 years ago
|
}
|
||
|
|
||
|
|
||
|
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<KisPartLayerImpl*>(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);
|
||
13 years ago
|
TQ_CHECK_PTR(img);
|
||
14 years ago
|
connect( img, TQT_SIGNAL( sigImageModified() ), this, TQT_SLOT( slotImageUpdated() ));
|
||
|
|
||
|
KisPaintLayer *layer = new KisPaintLayer(img, img->nextLayerName(), OPACITY_OPAQUE,colorstrategy);
|
||
13 years ago
|
TQ_CHECK_PTR(layer);
|
||
14 years ago
|
|
||
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(img);
|
||
14 years ago
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(layer);
|
||
14 years ago
|
|
||
|
KisFillPainter painter;
|
||
|
painter.begin(layer->paintDevice());
|
||
|
painter.fillRect(0, 0, width, height, bgColor, opacity);
|
||
|
painter.end();
|
||
|
|
||
|
TQValueVector<KisPaintDeviceAction *> 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;
|
||
|
}
|
||
|
|
||
13 years ago
|
KoView* KisDoc::createViewInstance(TQWidget* parent, const char *name)
|
||
14 years ago
|
{
|
||
13 years ago
|
KisView * v = new KisView(this, this, parent, name);
|
||
13 years ago
|
TQ_CHECK_PTR(v);
|
||
14 years ago
|
|
||
|
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);
|
||
13 years ago
|
TQ_CHECK_PTR(m_currentMacro);
|
||
14 years ago
|
}
|
||
|
|
||
|
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)
|
||
|
{
|
||
12 years ago
|
TDEApplication *app = TDEApplication::kApplication();
|
||
14 years ago
|
|
||
|
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"
|
||
|
|