diff --git a/krita/krita.action b/krita/krita.action index f024c2375ba..a702640923a 100644 --- a/krita/krita.action +++ b/krita/krita.action @@ -1,340 +1,329 @@ - - - - - - - - - - - diff --git a/krita/krita.rc b/krita/krita.rc index 4ee1ad114f8..bb2935fa7c5 100644 --- a/krita/krita.rc +++ b/krita/krita.rc @@ -1,333 +1,324 @@ &File &Edit &View &Canvas - - Grid Spacing - - - - - - - &Image &Rotate &Layer New &Import/Export Import &Convert S&plit Alpha &Rotate &Select Filte&r &Tools Recording Macros Setti&ngs &Help File Brushes and Stuff diff --git a/krita/ui/KisDocument.cpp b/krita/ui/KisDocument.cpp index 4f86f25c618..e6991866c02 100644 --- a/krita/ui/KisDocument.cpp +++ b/krita/ui/KisDocument.cpp @@ -1,2636 +1,2641 @@ /* This file is part of the Krita project * * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" // XXX: remove #include // XXX: remove #include "KisApplication.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPart.h" #include "KisView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kundo2stack.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Krita Image #include #include #include #include #include #include #include #include #include #include #include #include #include // Local #include "kis_factory2.h" #include "KisViewManager.h" #include "kis_clipboard.h" #include "widgets/kis_custom_image_widget.h" #include "canvas/kis_canvas2.h" #include "flake/kis_shape_controller.h" #include "kra/kis_kra_loader.h" #include "kra/kis_kra_saver.h" #include "kis_statusbar.h" #include "widgets/kis_progress_widget.h" #include "kis_canvas_resource_provider.h" #include "kis_resource_server_provider.h" #include "kis_node_manager.h" #include "KisPart.h" static const char CURRENT_DTD_VERSION[] = "2.0"; // Define the protocol used here for embedded documents' URL // This used to "store" but KUrl didn't like it, // so let's simply make it "tar" ! #define STORE_PROTOCOL "tar" // The internal path is a hack to make KUrl happy and for document children #define INTERNAL_PROTOCOL "intern" #define INTERNAL_PREFIX "intern:/" // Warning, keep it sync in koStore.cc #include using namespace std; /********************************************************** * * KisDocument * **********************************************************/ namespace { class DocumentProgressProxy : public KoProgressProxy { public: KisMainWindow *m_mainWindow; DocumentProgressProxy(KisMainWindow *mainWindow) : m_mainWindow(mainWindow) { } ~DocumentProgressProxy() { // signal that the job is done setValue(-1); } int maximum() const { return 100; } void setValue(int value) { if (m_mainWindow) { m_mainWindow->slotProgress(value); } } void setRange(int /*minimum*/, int /*maximum*/) { } void setFormat(const QString &/*format*/) { } }; } //static QString KisDocument::newObjectName() { static int s_docIFNumber = 0; QString name; name.setNum(s_docIFNumber++); name.prepend("document_"); return name; } class UndoStack : public KUndo2Stack { public: UndoStack(KisDocument *doc) : m_doc(doc) { } void setIndex(int idx) { KisImageWSP image = this->image(); image->requestStrokeCancellation(); if(image->tryBarrierLock()) { KUndo2Stack::setIndex(idx); image->unlock(); } } void undo() { KisImageWSP image = this->image(); image->requestUndoDuringStroke(); if(image->tryBarrierLock()) { KUndo2Stack::undo(); image->unlock(); } } void redo() { KisImageWSP image = this->image(); if(image->tryBarrierLock()) { KUndo2Stack::redo(); image->unlock(); } } private: KisImageWSP image() { KisImageWSP currentImage = m_doc->image(); Q_ASSERT(currentImage); return currentImage; } private: KisDocument *m_doc; }; class KisDocument::Private { public: Private(KisDocument *document) : document(document), // XXX: the part should _not_ be modified from the document docInfo(0), progressUpdater(0), progressProxy(0), profileStream(0), filterManager(0), specialOutputFlag(0), // default is native format isImporting(false), isExporting(false), password(QString()), modifiedAfterAutosave(false), autosaving(false), shouldCheckAutoSaveFile(true), autoErrorHandlingEnabled(true), backupFile(true), backupPath(QString()), doNotSaveExtDoc(false), storeInternal(false), isLoading(false), undoStack(0), modified(false), readwrite(true), disregardAutosaveFailure(false), nserver(0), macroNestDepth(0), kraLoader(0) { m_job = 0; m_statJob = 0; m_uploadJob = 0; m_saveOk = false; m_waitForSave = false; m_duringSaveAs = false; m_bTemp = false; m_bAutoDetectedMime = false; confirmNonNativeSave[0] = true; confirmNonNativeSave[1] = true; if (KGlobal::locale()->measureSystem() == KLocale::Imperial) { unit = KoUnit::Inch; } else { unit = KoUnit::Centimeter; } } ~Private() { // Don't delete m_d->shapeController because it's in a QObject hierarchy. delete nserver; } KisDocument *document; KoDocumentInfo *docInfo; KoProgressUpdater *progressUpdater; KoProgressProxy *progressProxy; QTextStream *profileStream; QTime profileReferenceTime; KoUnit unit; KisImportExportManager *filterManager; // The filter-manager to use when loading/saving [for the options] QByteArray mimeType; // The actual mimetype of the document QByteArray outputMimeType; // The mimetype to use when saving bool confirmNonNativeSave [2]; // used to pop up a dialog when saving for the // first time if the file is in a foreign format // (Save/Save As, Export) int specialOutputFlag; // See KoFileDialog in koMainWindow.cc bool isImporting; bool isExporting; // File --> Import/Export vs File --> Open/Save QString password; // The password used to encrypt an encrypted document QTimer autoSaveTimer; QString lastErrorMessage; // see openFile() int autoSaveDelay; // in seconds, 0 to disable. bool modifiedAfterAutosave; bool autosaving; bool shouldCheckAutoSaveFile; // usually true bool autoErrorHandlingEnabled; // usually true bool backupFile; QString backupPath; bool doNotSaveExtDoc; // makes it possible to save only internally stored child documents bool storeInternal; // Store this doc internally even if url is external bool isLoading; // True while loading (openUrl is async) KUndo2Stack *undoStack; KoGridData gridData; KoGuidesData guidesData; bool isEmpty; KoPageLayout pageLayout; KIO::FileCopyJob * m_job; KIO::StatJob * m_statJob; KIO::FileCopyJob * m_uploadJob; KUrl m_originalURL; // for saveAs QString m_originalFilePath; // for saveAs bool m_saveOk : 1; bool m_waitForSave : 1; bool m_duringSaveAs : 1; bool m_bTemp: 1; // If @p true, @p m_file is a temporary file that needs to be deleted later. bool m_bAutoDetectedMime : 1; // whether the mimetype in the arguments was detected by the part itself KUrl m_url; // Remote (or local) url - the one displayed to the user. QString m_file; // Local file - the only one the part implementation should deal with. QEventLoop m_eventLoop; bool modified; bool readwrite; bool disregardAutosaveFailure; KisNameServer *nserver; qint32 macroNestDepth; KisImageSP image; KisNodeSP preActivatedNode; KisShapeController* shapeController; KoShapeController* koShapeController; KisKraLoader* kraLoader; KisKraSaver* kraSaver; QList assistants; bool openFile() { DocumentProgressProxy *progressProxy = 0; if (!document->progressProxy()) { KisMainWindow *mainWindow = 0; if (KisPart::instance()->mainWindows().count() > 0) { mainWindow = KisPart::instance()->mainWindows()[0]; } progressProxy = new DocumentProgressProxy(mainWindow); document->setProgressProxy(progressProxy); } document->setUrl(m_url); bool ok = document->openFile(); if (progressProxy) { document->setProgressProxy(0); delete progressProxy; } return ok; } bool openLocalFile() { m_bTemp = false; // set the mimetype only if it was not already set (for example, by the host application) if (mimeType.isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion KMimeType::Ptr mime = KMimeType::findByUrl(m_url, 0, true /* local file*/); if (mime) { mimeType = mime->name().toLocal8Bit(); m_bAutoDetectedMime = true; } } const bool ret = openFile(); if (ret) { emit document->completed(); } else { emit document->canceled(QString()); } return ret; } void openRemoteFile() { m_bTemp = true; // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice) QString fileName = m_url.fileName(); QFileInfo fileInfo(fileName); QString ext = fileInfo.completeSuffix(); QString extension; if (!ext.isEmpty() && m_url.query().isNull()) // not if the URL has a query, e.g. cgi.pl?something extension = '.'+ext; // keep the '.' KTemporaryFile tempFile; tempFile.setSuffix(extension); tempFile.setAutoRemove(false); tempFile.open(); m_file = tempFile.fileName(); KUrl destURL; destURL.setPath( m_file ); KIO::JobFlags flags = KIO::DefaultFlags; flags |= KIO::Overwrite; m_job = KIO::file_copy(m_url, destURL, 0600, flags); QObject::connect(m_job, SIGNAL(result(KJob*)), document, SLOT(_k_slotJobFinished(KJob*))); QObject::connect(m_job, SIGNAL(mimetype(KIO::Job*,QString)), document, SLOT(_k_slotGotMimeType(KIO::Job*,QString))); } // Set m_file correctly for m_url void prepareSaving() { // Local file if ( m_url.isLocalFile() ) { if ( m_bTemp ) // get rid of a possible temp file first { // (happens if previous url was remote) QFile::remove( m_file ); m_bTemp = false; } m_file = m_url.toLocalFile(); } else { // Remote file // We haven't saved yet, or we did but locally - provide a temp file if ( m_file.isEmpty() || !m_bTemp ) { KTemporaryFile tempFile; tempFile.setAutoRemove(false); tempFile.open(); m_file = tempFile.fileName(); m_bTemp = true; } // otherwise, we already had a temp file } } void _k_slotJobFinished( KJob * job ) { Q_ASSERT( job == m_job ); m_job = 0; if (job->error()) emit document->canceled( job->errorString() ); else { if ( openFile() ) { emit document->completed(); } else { emit document->canceled(QString()); } } } void _k_slotStatJobFinished(KJob * job) { Q_ASSERT(job == m_statJob); m_statJob = 0; // this could maybe confuse some apps? So for now we'll just fallback to KIO::get // and error again. Well, maybe this even helps with wrong stat results. if (!job->error()) { #if KDE_IS_VERSION(4,4,0) const KUrl localUrl = static_cast(job)->mostLocalUrl(); #else const KUrl localUrl = static_cast(job)->url(); #endif if (localUrl.isLocalFile()) { m_file = localUrl.toLocalFile(); openLocalFile(); return; } } openRemoteFile(); } void _k_slotGotMimeType(KIO::Job *job, const QString &mime) { kDebug(1000) << mime; Q_ASSERT(job == m_job); Q_UNUSED(job); // set the mimetype only if it was not already set (for example, by the host application) if (mimeType.isEmpty()) { mimeType = mime.toLocal8Bit(); m_bAutoDetectedMime = true; } } void _k_slotUploadFinished( KJob * ) { if (m_uploadJob->error()) { QFile::remove(m_uploadJob->srcUrl().toLocalFile()); m_uploadJob = 0; if (m_duringSaveAs) { document->setUrl(m_originalURL); m_file = m_originalFilePath; } } else { KUrl dirUrl( m_url ); dirUrl.setPath( dirUrl.directory() ); ::org::kde::KDirNotify::emitFilesAdded( dirUrl.url() ); m_uploadJob = 0; document->setModified( false ); emit document->completed(); m_saveOk = true; } m_duringSaveAs = false; m_originalURL = KUrl(); m_originalFilePath.clear(); if (m_waitForSave) { m_eventLoop.quit(); } } }; KisDocument::KisDocument() : d(new Private(this)) { d->undoStack = new UndoStack(this); d->undoStack->setParent(this); d->isEmpty = true; d->filterManager = new KisImportExportManager(this, d->progressUpdater); connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setAutoSave(defaultAutoSave()); setObjectName(newObjectName()); d->docInfo = new KoDocumentInfo(this); d->pageLayout.width = 0; d->pageLayout.height = 0; d->pageLayout.topMargin = 0; d->pageLayout.bottomMargin = 0; d->pageLayout.leftMargin = 0; d->pageLayout.rightMargin = 0; KConfigGroup cfgGrp(KisFactory::componentData().config(), "Undo"); d->undoStack->setUndoLimit(cfgGrp.readEntry("UndoLimit", 1000)); connect(d->undoStack, SIGNAL(indexChanged(int)), this, SLOT(slotUndoStackIndexChanged(int))); // preload the krita resources KisResourceServerProvider::instance(); init(); undoStack()->setUndoLimit(KisConfig().undoStackLimit()); setBackupFile(KisConfig().backupFile()); + + gridData().setShowGrid(false); + KisConfig cfg; + gridData().setGrid(cfg.getGridHSpacing(), cfg.getGridVSpacing()); + } KisDocument::~KisDocument() { /** * Push a timebomb, which will try to release the memory after * the document has been deleted */ KisPaintDevice::createMemoryReleaseObject()->deleteLater(); d->autoSaveTimer.disconnect(this); d->autoSaveTimer.stop(); delete d->filterManager; // Despite being QObject they needs to be deleted before the image delete d->shapeController; delete d->koShapeController; if (d->image) { d->image->notifyAboutToBeDeleted(); } // The following line trigger the deletion of the image d->image.clear(); delete d; } void KisDocument::init() { delete d->nserver; d->nserver = 0; d->nserver = new KisNameServer(1); Q_CHECK_PTR(d->nserver); d->shapeController = new KisShapeController(this, d->nserver); d->koShapeController = new KoShapeController(0, d->shapeController); d->kraSaver = 0; d->kraLoader = 0; } bool KisDocument::reload() { // XXX: reimplement! return false; } bool KisDocument::exportDocument(const KUrl & _url) { bool ret; d->isExporting = true; // // Preserve a lot of state here because we need to restore it in order to // be able to fake a File --> Export. Can't do this in saveFile() because, // for a start, KParts has already set url and m_file and because we need // to restore the modified flag etc. and don't want to put a load on anyone // reimplementing saveFile() (Note: importDocument() and exportDocument() // will remain non-virtual). // KUrl oldURL = url(); QString oldFile = localFilePath(); bool wasModified = isModified(); QByteArray oldMimeType = mimeType(); // save... ret = saveAs(_url); // // This is sooooo hacky :( // Hopefully we will restore enough state. // kDebug(30003) << "Restoring KisDocument state to before export"; // always restore url & m_file because KParts has changed them // (regardless of failure or success) setUrl(oldURL); setLocalFilePath(oldFile); // on successful export we need to restore modified etc. too // on failed export, mimetype/modified hasn't changed anyway if (ret) { setModified(wasModified); d->mimeType = oldMimeType; } d->isExporting = false; return ret; } bool KisDocument::saveFile() { kDebug(30003) << "doc=" << url().url(); // Save it to be able to restore it after a failed save const bool wasModified = isModified(); // The output format is set by koMainWindow, and by openFile QByteArray outputMimeType = d->outputMimeType; if (outputMimeType.isEmpty()) outputMimeType = d->outputMimeType = nativeFormatMimeType(); QApplication::setOverrideCursor(Qt::WaitCursor); if (backupFile()) { if (url().isLocalFile()) KSaveFile::backupFile(url().toLocalFile(), d->backupPath); else { KIO::UDSEntry entry; if (KIO::NetAccess::stat(url(), entry, KisPart::instance()->currentMainwindow())) { // this file exists => backup emit statusBarMessage(i18n("Making backup...")); KUrl backup; if (d->backupPath.isEmpty()) backup = url(); else backup = d->backupPath + '/' + url().fileName(); backup.setPath(backup.path() + QString::fromLatin1("~")); KFileItem item(entry, url()); Q_ASSERT(item.name() == url().fileName()); KIO::FileCopyJob *job = KIO::file_copy(url(), backup, item.permissions(), KIO::Overwrite | KIO::HideProgressInfo); job->exec(); } } } emit statusBarMessage(i18n("Saving...")); qApp->processEvents(); bool ret = false; bool suppressErrorDialog = false; if (!isNativeFormat(outputMimeType)) { kDebug(30003) << "Saving to format" << outputMimeType << "in" << localFilePath(); // Not native format : save using export filter KisImportExportFilter::ConversionStatus status = d->filterManager->exportDocument(localFilePath(), outputMimeType); ret = status == KisImportExportFilter::OK; suppressErrorDialog = (status == KisImportExportFilter::UserCancelled || status == KisImportExportFilter::BadConversionGraph); } else { // Native format => normal save Q_ASSERT(!localFilePath().isEmpty()); ret = saveNativeFormat(localFilePath()); } if (ret) { d->undoStack->setClean(); removeAutoSaveFiles(); // Restart the autosave timer // (we don't want to autosave again 2 seconds after a real save) setAutoSave(d->autoSaveDelay); } QApplication::restoreOverrideCursor(); if (!ret) { if (!suppressErrorDialog) { if (errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", localFilePath()) + "\n\n" + i18n("Most likely a layer is still processing effects.")); } else if (errorMessage() != "USER_CANCELED") { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", localFilePath(), errorMessage())); } } // couldn't save file so this new URL is invalid // FIXME: we should restore the current document's true URL instead of // setting it to nothing otherwise anything that depends on the URL // being correct will not work (i.e. the document will be called // "Untitled" which may not be true) // // Update: now the URL is restored in KisMainWindow but really, this // should still be fixed in KisDocument/KParts (ditto for file). // We still resetURL() here since we may or may not have been called // by KisMainWindow - Clarence resetURL(); // As we did not save, restore the "was modified" status setModified(wasModified); } if (ret) { d->mimeType = outputMimeType; setConfirmNonNativeSave(isExporting(), false); } emit clearStatusBarMessage(); return ret; } QByteArray KisDocument::mimeType() const { return d->mimeType; } void KisDocument::setMimeType(const QByteArray & mimeType) { d->mimeType = mimeType; } void KisDocument::setOutputMimeType(const QByteArray & mimeType, int specialOutputFlag) { d->outputMimeType = mimeType; d->specialOutputFlag = specialOutputFlag; } QByteArray KisDocument::outputMimeType() const { return d->outputMimeType; } int KisDocument::specialOutputFlag() const { return d->specialOutputFlag; } bool KisDocument::confirmNonNativeSave(const bool exporting) const { // "exporting ? 1 : 0" is different from "exporting" because a bool is // usually implemented like an "int", not "unsigned : 1" return d->confirmNonNativeSave [ exporting ? 1 : 0 ]; } void KisDocument::setConfirmNonNativeSave(const bool exporting, const bool on) { d->confirmNonNativeSave [ exporting ? 1 : 0] = on; } bool KisDocument::saveInBatchMode() const { return d->filterManager->getBatchMode(); } void KisDocument::setSaveInBatchMode(const bool batchMode) { d->filterManager->setBatchMode(batchMode); } bool KisDocument::isImporting() const { return d->isImporting; } bool KisDocument::isExporting() const { return d->isExporting; } void KisDocument::setCheckAutoSaveFile(bool b) { d->shouldCheckAutoSaveFile = b; } void KisDocument::setAutoErrorHandlingEnabled(bool b) { d->autoErrorHandlingEnabled = b; } bool KisDocument::isAutoErrorHandlingEnabled() const { return d->autoErrorHandlingEnabled; } void KisDocument::slotAutoSave() { if (d->modified && d->modifiedAfterAutosave && !d->isLoading) { // Give a warning when trying to autosave an encrypted file when no password is known (should not happen) if (d->specialOutputFlag == SaveEncrypted && d->password.isNull()) { // That advice should also fix this error from occurring again emit statusBarMessage(i18n("The password of this encrypted document is not known. Autosave aborted! Please save your work manually.")); } else { connect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); emit statusBarMessage(i18n("Autosaving...")); d->autosaving = true; bool ret = saveNativeFormat(autoSaveFile(localFilePath())); setModified(true); if (ret) { d->modifiedAfterAutosave = false; d->autoSaveTimer.stop(); // until the next change } d->autosaving = false; emit clearStatusBarMessage(); disconnect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); if (!ret && !d->disregardAutosaveFailure) { emit statusBarMessage(i18n("Error during autosave! Partition full?")); } } } } void KisDocument::setReadWrite(bool readwrite) { d->readwrite = readwrite; setAutoSave(d->autoSaveDelay); foreach(KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) { mainWindow->setReadWrite(readwrite); } } void KisDocument::setAutoSave(int delay) { d->autoSaveDelay = delay; if (isReadWrite() && d->autoSaveDelay > 0) d->autoSaveTimer.start(d->autoSaveDelay * 1000); else d->autoSaveTimer.stop(); } KoDocumentInfo *KisDocument::documentInfo() const { return d->docInfo; } bool KisDocument::isModified() const { return d->modified; } bool KisDocument::saveNativeFormat(const QString & file) { const int realAutoSaveInterval = KisConfig().autoSaveInterval(); const int emergencyAutoSaveInterval = 10; // sec if (!d->image->tryBarrierLock()) { if (isAutosaving()) { setDisregardAutosaveFailure(true); if (realAutoSaveInterval) { setAutoSave(emergencyAutoSaveInterval); } return false; } else { d->image->requestStrokeEnd(); QApplication::processEvents(); if (!d->image->tryBarrierLock()) { return false; } } } setDisregardAutosaveFailure(false); d->lastErrorMessage.clear(); //kDebug(30003) <<"Saving to store"; KoStore::Backend backend = KoStore::Auto; if (d->specialOutputFlag == SaveAsDirectoryStore) { backend = KoStore::Directory; kDebug(30003) << "Saving as uncompressed XML, using directory store."; } else if (d->specialOutputFlag == SaveAsFlatXML) { kDebug(30003) << "Saving as a flat XML file."; QFile f(file); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { bool success = saveToStream(&f); f.close(); return success; } else return false; } kDebug(30003) << "KisDocument::saveNativeFormat nativeFormatMimeType=" << nativeFormatMimeType(); // TODO: use std::auto_ptr or create store on stack [needs API fixing], // to remove all the 'delete store' in all the branches KoStore *store = KoStore::createStore(file, KoStore::Write, d->outputMimeType, backend); if (d->specialOutputFlag == SaveEncrypted && !d->password.isNull()) { store->setPassword(d->password); } if (store->bad()) { d->lastErrorMessage = i18n("Could not create the file for saving"); // more details needed? delete store; d->image->unlock(); setAutoSave(realAutoSaveInterval); return false; } d->image->unlock(); setAutoSave(realAutoSaveInterval); return saveNativeFormatCalligra(store); } bool KisDocument::saveNativeFormatCalligra(KoStore *store) { kDebug(30003) << "Saving root"; if (store->open("root")) { KoStoreDevice dev(store); if (!saveToStream(&dev) || !store->close()) { kDebug(30003) << "saveToStream failed"; delete store; return false; } } else { d->lastErrorMessage = i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml")); delete store; return false; } if (store->open("documentinfo.xml")) { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = d->docInfo->save(doc); KoStoreDevice dev(store); QByteArray s = doc.toByteArray(); // this is already Utf8! (void)dev.write(s.data(), s.size()); (void)store->close(); } if (store->open("preview.png")) { // ### TODO: missing error checking (The partition could be full!) savePreview(store); (void)store->close(); } if (!completeSaving(store)) { delete store; return false; } kDebug(30003) << "Saving done of url:" << url().url(); if (!store->finalize()) { delete store; return false; } // Success delete store; return true; } bool KisDocument::saveToStream(QIODevice *dev) { QDomDocument doc = saveXML(); // Save to buffer QByteArray s = doc.toByteArray(); // utf8 already dev->open(QIODevice::WriteOnly); int nwritten = dev->write(s.data(), s.size()); if (nwritten != (int)s.size()) kWarning(30003) << "wrote " << nwritten << "- expected" << s.size(); return nwritten == (int)s.size(); } QString KisDocument::checkImageMimeTypes(const QString &mimeType, const KUrl &url) const { if (!url.isLocalFile()) return mimeType; if (url.toLocalFile().endsWith(".kpp")) return "image/png"; QStringList imageMimeTypes; imageMimeTypes << "image/jpeg" << "image/x-psd" << "image/photoshop" << "image/x-photoshop" << "image/x-vnd.adobe.photoshop" << "image/vnd.adobe.photoshop" << "image/x-portable-pixmap" << "image/x-portable-graymap" << "image/x-portable-bitmap" << "application/pdf" << "image/x-exr" << "image/x-xcf" << "image/x-eps" << "image/png" << "image/bmp" << "image/x-xpixmap" << "image/gif" << "image/x-xbitmap" << "image/tiff" << "image/jp2"; if (!imageMimeTypes.contains(mimeType)) return mimeType; int accuracy = 0; QFile f(url.toLocalFile()); if (!f.open(QIODevice::ReadOnly)) { qWarning() << "Could not open file to check the mimetype" << url; } QByteArray ba = f.read(qMin(f.size(), (qint64)512)); // should be enough for images KMimeType::Ptr mime = KMimeType::findByContent(ba, &accuracy); f.close(); if (!mime) { return mimeType; } // Checking the content failed as well, so let's fall back on the extension again if (mime->name() == "application/octet-stream") { return mimeType; } return mime->name(); } // Called for embedded documents bool KisDocument::saveToStore(KoStore *_store, const QString & _path) { kDebug(30003) << "Saving document to store" << _path; _store->pushDirectory(); // Use the path as the internal url if (_path.startsWith(STORE_PROTOCOL)) setUrl(KUrl(_path)); else // ugly hack to pass a relative URI setUrl(KUrl(INTERNAL_PREFIX + _path)); // In the current directory we're the king :-) if (_store->open("root")) { KoStoreDevice dev(_store); if (!saveToStream(&dev)) { _store->close(); return false; } if (!_store->close()) return false; } if (!completeSaving(_store)) return false; // Now that we're done leave the directory again _store->popDirectory(); kDebug(30003) << "Saved document to store"; return true; } bool KisDocument::savePreview(KoStore *store) { QPixmap pix = generatePreview(QSize(256, 256)); const QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly)); KoStoreDevice io(store); if (!io.open(QIODevice::WriteOnly)) return false; if (! preview.save(&io, "PNG")) // ### TODO What is -9 in quality terms? return false; io.close(); return true; } QPixmap KisDocument::generatePreview(const QSize& size) { if (d->image) { QRect bounds = d->image->bounds(); QSize newSize = bounds.size(); newSize.scale(size, Qt::KeepAspectRatio); QImage image; if (bounds.width() < 10000 && bounds.height() < 10000) { image = d->image->convertToQImage(d->image->bounds(), 0); } else { image = d->image->convertToQImage(QRect(0, 0, newSize.width(), newSize.height()), newSize, 0); } image = image.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); return QPixmap::fromImage(image); } return QPixmap(size); } QString KisDocument::autoSaveFile(const QString & path) const { QString retval; // Using the extension allows to avoid relying on the mime magic when opening KMimeType::Ptr mime = KMimeType::mimeType(nativeFormatMimeType()); if (! mime) { qFatal("It seems your installation is broken/incomplete because we failed to load the native mimetype \"%s\".", nativeFormatMimeType().constData()); } QString extension = mime->property("X-KDE-NativeExtension").toString(); if (extension.isEmpty()) extension = mime->mainExtension(); if (path.isEmpty()) { // Never saved? #ifdef Q_OS_WIN // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) retval = QString("%1/.%2-%3-%4-autosave%5").arg(QDir::tempPath()).arg(KisFactory::componentName()).arg(qApp->applicationPid()).arg(objectName()).arg(extension); #else // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file retval = QString("%1/.%2-%3-%4-autosave%5").arg(QDir::homePath()).arg(KisFactory::componentName()).arg(qApp->applicationPid()).arg(objectName()).arg(extension); #endif } else { KUrl url = KUrl::fromPath(path); Q_ASSERT(url.isLocalFile()); QString dir = url.directory(KUrl::AppendTrailingSlash); QString filename = url.fileName(); retval = QString("%1.%2-autosave%3").arg(dir).arg(filename).arg(extension); } return retval; } void KisDocument::setDisregardAutosaveFailure(bool disregardFailure) { d->disregardAutosaveFailure = disregardFailure; } bool KisDocument::importDocument(const KUrl & _url) { bool ret; kDebug(30003) << "url=" << _url.url(); d->isImporting = true; // open... ret = openUrl(_url); // reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a // File --> Import if (ret) { kDebug(30003) << "success, resetting url"; resetURL(); setTitleModified(); } d->isImporting = false; return ret; } bool KisDocument::openUrl(const KUrl & _url) { kDebug(30003) << "url=" << _url.url(); d->lastErrorMessage.clear(); // Reimplemented, to add a check for autosave files and to improve error reporting if (!_url.isValid()) { d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ? return false; } KUrl url(_url); bool autosaveOpened = false; d->isLoading = true; if (url.isLocalFile() && d->shouldCheckAutoSaveFile) { QString file = url.toLocalFile(); QString asf = autoSaveFile(file); if (QFile::exists(asf)) { //kDebug(30003) <<"asf=" << asf; // ## TODO compare timestamps ? int res = QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("An autosaved file exists for this document.\nDo you want to open it instead?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : url.setPath(asf); autosaveOpened = true; break; case QMessageBox::No : QFile::remove(asf); break; default: // Cancel d->isLoading = false; return false; } } } bool ret = openUrlInternal(url); if (autosaveOpened) { resetURL(); // Force save to act like 'Save As' setReadWrite(true); // enable save button setModified(true); } else { KisPart::instance()->addRecentURLToAllMainWindows(_url); if (ret) { // Detect readonly local-files; remote files are assumed to be writable, unless we add a KIO::stat here (async). KFileItem file(url, mimeType(), KFileItem::Unknown); setReadWrite(file.isWritable()); } } return ret; } bool KisDocument::openFile() { //kDebug(30003) <<"for" << localFilePath(); if (!QFile::exists(localFilePath())) { QApplication::restoreOverrideCursor(); if (d->autoErrorHandlingEnabled) // Maybe offer to create a new document with that name ? QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath())); d->isLoading = false; return false; } QApplication::setOverrideCursor(Qt::WaitCursor); d->specialOutputFlag = 0; QByteArray _native_format = nativeFormatMimeType(); KUrl u(localFilePath()); QString typeName = mimeType(); if (typeName.isEmpty()) { typeName = KMimeType::findByUrl(u, 0, true)->name(); } // for images, always check content. typeName = checkImageMimeTypes(typeName, u); //kDebug(30003) << "mimetypes 4:" << typeName; // Allow to open backup files, don't keep the mimetype application/x-trash. if (typeName == "application/x-trash") { QString path = u.path(); KMimeType::Ptr mime = KMimeType::mimeType(typeName); const QStringList patterns = mime ? mime->patterns() : QStringList(); // Find the extension that makes it a backup file, and remove it for (QStringList::ConstIterator it = patterns.begin(); it != patterns.end(); ++it) { QString ext = *it; if (!ext.isEmpty() && ext[0] == '*') { ext.remove(0, 1); if (path.endsWith(ext)) { path.chop(ext.length()); break; } } } typeName = KMimeType::findByPath(path, 0, true)->name(); } // Special case for flat XML files (e.g. using directory store) if (u.fileName() == "maindoc.xml" || u.fileName() == "content.xml" || typeName == "inode/directory") { typeName = _native_format; // Hmm, what if it's from another app? ### Check mimetype d->specialOutputFlag = SaveAsDirectoryStore; kDebug(30003) << "loading" << u.fileName() << ", using directory store for" << localFilePath() << "; typeName=" << typeName; } kDebug(30003) << localFilePath() << "type:" << typeName; QString importedFile = localFilePath(); // create the main progress monitoring object for loading, this can // contain subtasks for filtering and loading KoProgressProxy *progressProxy = 0; if (d->progressProxy) { progressProxy = d->progressProxy; } d->progressUpdater = new KoProgressUpdater(progressProxy, KoProgressUpdater::Unthreaded, d->profileStream); d->progressUpdater->setReferenceTime(d->profileReferenceTime); d->progressUpdater->start(100, i18n("Opening Document")); setupOpenFileSubProgress(); if (!isNativeFormat(typeName.toLatin1())) { KisImportExportFilter::ConversionStatus status; importedFile = d->filterManager->importDocument(localFilePath(), typeName, status); if (status != KisImportExportFilter::OK) { QApplication::restoreOverrideCursor(); QString msg; switch (status) { case KisImportExportFilter::OK: break; case KisImportExportFilter::FilterCreationError: msg = i18n("Could not create the filter plugin"); break; case KisImportExportFilter::CreationError: msg = i18n("Could not create the output document"); break; case KisImportExportFilter::FileNotFound: msg = i18n("File not found"); break; case KisImportExportFilter::StorageCreationError: msg = i18n("Cannot create storage"); break; case KisImportExportFilter::BadMimeType: msg = i18n("Bad MIME type"); break; case KisImportExportFilter::EmbeddedDocError: msg = i18n("Error in embedded document"); break; case KisImportExportFilter::WrongFormat: msg = i18n("Format not recognized"); break; case KisImportExportFilter::NotImplemented: msg = i18n("Not implemented"); break; case KisImportExportFilter::ParsingError: msg = i18n("Parsing error"); break; case KisImportExportFilter::PasswordProtected: msg = i18n("Document is password protected"); break; case KisImportExportFilter::InvalidFormat: msg = i18n("Invalid file format"); break; case KisImportExportFilter::InternalError: case KisImportExportFilter::UnexpectedEOF: case KisImportExportFilter::UnexpectedOpcode: case KisImportExportFilter::StupidError: // ?? what is this ?? case KisImportExportFilter::UsageError: msg = i18n("Internal error"); break; case KisImportExportFilter::OutOfMemory: msg = i18n("Out of memory"); break; case KisImportExportFilter::FilterEntryNull: msg = i18n("Empty Filter Plugin"); break; case KisImportExportFilter::NoDocumentCreated: msg = i18n("Trying to load into the wrong kind of document"); break; case KisImportExportFilter::DownloadFailed: msg = i18n("Failed to download remote file"); break; case KisImportExportFilter::UserCancelled: case KisImportExportFilter::BadConversionGraph: // intentionally we do not prompt the error message here break; default: msg = i18n("Unknown error"); break; } if (d->autoErrorHandlingEnabled && !msg.isEmpty()) { QString errorMsg(i18n("Could not open %2.\nReason: %1.\n%3", msg, prettyPathOrUrl(), errorMessage())); QMessageBox::critical(0, i18nc("@title:window", "Krita"), errorMsg); } d->isLoading = false; delete d->progressUpdater; d->progressUpdater = 0; return false; } d->isEmpty = false; kDebug(30003) << "importedFile" << importedFile << "status:" << static_cast(status); } QApplication::restoreOverrideCursor(); bool ok = true; if (!importedFile.isEmpty()) { // Something to load (tmp or native file) ? // The filter, if any, has been applied. It's all native format now. if (!loadNativeFormat(importedFile)) { ok = false; if (d->autoErrorHandlingEnabled) { showLoadingErrorDialog(); } } } if (importedFile != localFilePath()) { // We opened a temporary file (result of an import filter) // Set document URL to empty - we don't want to save in /tmp ! // But only if in readwrite mode (no saving problem otherwise) // -- // But this isn't true at all. If this is the result of an // import, then importedFile=temporary_file.kwd and // file/m_url=foreignformat.ext so m_url is correct! // So don't resetURL() or else the caption won't be set when // foreign files are opened (an annoying bug). // - Clarence // #if 0 if (isReadWrite()) resetURL(); #endif // remove temp file - uncomment this to debug import filters if (!importedFile.isEmpty()) { #ifndef NDEBUG if (!getenv("CALLIGRA_DEBUG_FILTERS")) #endif QFile::remove(importedFile); } } if (ok) { setMimeTypeAfterLoading(typeName); emit sigLoadingFinished(); } if (progressUpdater()) { QPointer updater = progressUpdater()->startSubtask(1, "clear undo stack"); updater->setProgress(0); undoStack()->clear(); updater->setProgress(100); } delete d->progressUpdater; d->progressUpdater = 0; d->isLoading = false; return ok; } KoProgressUpdater *KisDocument::progressUpdater() const { return d->progressUpdater; } void KisDocument::setProgressProxy(KoProgressProxy *progressProxy) { d->progressProxy = progressProxy; } KoProgressProxy* KisDocument::progressProxy() const { if (!d->progressProxy) { KisMainWindow *mainWindow = 0; if (KisPart::instance()->mainwindowCount() > 0) { mainWindow = KisPart::instance()->mainWindows()[0]; } d->progressProxy = new DocumentProgressProxy(mainWindow); } return d->progressProxy; } // shared between openFile and koMainWindow's "create new empty document" code void KisDocument::setMimeTypeAfterLoading(const QString& mimeType) { d->mimeType = mimeType.toLatin1(); d->outputMimeType = d->mimeType; const bool needConfirm = !isNativeFormat(d->mimeType); setConfirmNonNativeSave(false, needConfirm); setConfirmNonNativeSave(true, needConfirm); } // The caller must call store->close() if loadAndParse returns true. bool KisDocument::oldLoadAndParse(KoStore *store, const QString& filename, KoXmlDocument& doc) { //kDebug(30003) <<"Trying to open" << filename; if (!store->open(filename)) { kWarning(30003) << "Entry " << filename << " not found!"; d->lastErrorMessage = i18n("Could not find %1", filename); return false; } // Error variables for QDomDocument::setContent QString errorMsg; int errorLine, errorColumn; bool ok = doc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn); store->close(); if (!ok) { kError(30003) << "Parsing error in " << filename << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; d->lastErrorMessage = i18n("Parsing error in %1 at line %2, column %3\nError message: %4" , filename , errorLine, errorColumn , QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0, QCoreApplication::UnicodeUTF8)); return false; } kDebug(30003) << "File" << filename << " loaded and parsed"; return true; } bool KisDocument::loadNativeFormat(const QString & file_) { QString file = file_; QFileInfo fileInfo(file); if (!fileInfo.exists()) { // check duplicated from openUrl, but this is useful for templates d->lastErrorMessage = i18n("The file %1 does not exist.", file); return false; } if (!fileInfo.isFile()) { file += "/content.xml"; QFileInfo fileInfo2(file); if (!fileInfo2.exists() || !fileInfo2.isFile()) { d->lastErrorMessage = i18n("%1 is not a file." , file_); return false; } } QApplication::setOverrideCursor(Qt::WaitCursor); kDebug(30003) << file; QFile in; bool isRawXML = false; if (d->specialOutputFlag != SaveAsDirectoryStore) { // Don't try to open a directory ;) in.setFileName(file); if (!in.open(QIODevice::ReadOnly)) { QApplication::restoreOverrideCursor(); d->lastErrorMessage = i18n("Could not open the file for reading (check read permissions)."); return false; } char buf[6]; buf[5] = 0; int pos = 0; do { if (in.read(buf + pos , 1) < 1) { QApplication::restoreOverrideCursor(); in.close(); d->lastErrorMessage = i18n("Could not read the beginning of the file."); return false; } if (QChar(buf[pos]).isSpace()) continue; pos++; } while (pos < 5); isRawXML = (qstrnicmp(buf, "lastErrorMessage = i18n("parsing error in the main document at line %1, column %2\nError message: %3", errorLine, errorColumn, i18n(errorMsg.toUtf8())); res = false; } QApplication::restoreOverrideCursor(); in.close(); d->isEmpty = false; return res; } else { // It's a calligra store (tar.gz, zip, directory, etc.) in.close(); KoStore::Backend backend = (d->specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto; KoStore *store = KoStore::createStore(file, KoStore::Read, "", backend); if (store->bad()) { d->lastErrorMessage = i18n("Not a valid Krita file: %1", file); delete store; QApplication::restoreOverrideCursor(); return false; } // Remember that the file was encrypted if (d->specialOutputFlag == 0 && store->isEncrypted() && !d->isImporting) d->specialOutputFlag = SaveEncrypted; const bool success = loadNativeFormatFromStoreInternal(store); // Retrieve the password after loading the file, only then is it guaranteed to exist if (success && store->isEncrypted() && !d->isImporting) d->password = store->password(); delete store; return success; } } bool KisDocument::loadNativeFormatFromByteArray(QByteArray &data) { bool succes; KoStore::Backend backend = (d->specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto; QBuffer buffer(&data); KoStore *store = KoStore::createStore(&buffer, KoStore::Read, "", backend); if (store->bad()) { delete store; return false; } // Remember that the file was encrypted if (d->specialOutputFlag == 0 && store->isEncrypted() && !d->isImporting) d->specialOutputFlag = SaveEncrypted; succes = loadNativeFormatFromStoreInternal(store); // Retrieve the password after loading the file, only then is it guaranteed to exist if (succes && store->isEncrypted() && !d->isImporting) d->password = store->password(); delete store; return succes; } bool KisDocument::loadNativeFormatFromStoreInternal(KoStore *store) { if (store->hasFile("root") || store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml) KoXmlDocument doc = KoXmlDocument(true); bool ok = oldLoadAndParse(store, "root", doc); if (ok) ok = loadXML(doc, store); if (!ok) { QApplication::restoreOverrideCursor(); return false; } } else { kError(30003) << "ERROR: No maindoc.xml" << endl; d->lastErrorMessage = i18n("Invalid document: no file 'maindoc.xml'."); QApplication::restoreOverrideCursor(); return false; } if (store->hasFile("documentinfo.xml")) { KoXmlDocument doc = KoXmlDocument(true); if (oldLoadAndParse(store, "documentinfo.xml", doc)) { d->docInfo->load(doc); } } else { //kDebug( 30003 ) <<"cannot open document info"; delete d->docInfo; d->docInfo = new KoDocumentInfo(this); } bool res = completeLoading(store); QApplication::restoreOverrideCursor(); d->isEmpty = false; return res; } // For embedded documents bool KisDocument::loadFromStore(KoStore *_store, const QString& url) { if (_store->open(url)) { KoXmlDocument doc = KoXmlDocument(true); doc.setContent(_store->device()); if (!loadXML(doc, _store)) { _store->close(); return false; } _store->close(); } else { kWarning() << "couldn't open " << url; } _store->pushDirectory(); // Store as document URL if (url.startsWith(STORE_PROTOCOL)) { setUrl(url); } else { setUrl(KUrl(INTERNAL_PREFIX + url)); _store->enterDirectory(url); } bool result = completeLoading(_store); // Restore the "old" path _store->popDirectory(); return result; } bool KisDocument::loadOdf(KoOdfReadStore & odfStore) { Q_UNUSED(odfStore); setErrorMessage(i18n("Krita does not support the OpenDocument file format.")); return false; } bool KisDocument::saveOdf(SavingContext &documentContext) { Q_UNUSED(documentContext); setErrorMessage(i18n("Krita does not support the OpenDocument file format.")); return false; } bool KisDocument::isStoredExtern() const { return !storeInternal() && hasExternURL(); } void KisDocument::setModified() { d->modified = true; } void KisDocument::setModified(bool mod) { if (isAutosaving()) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { kError(1000) << "Can't set a read-only document to 'modified' !" << endl; return; } //kDebug(30003)<<" url:" << url.path(); //kDebug(30003)<<" mod="<image) { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("Unknown error.")); } else { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); } return false; } d->kraLoader->loadBinaryData(store, d->image, url().url(), isStoredExtern()); bool retval = true; if (!d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); retval = false; } if (retval) { vKisNodeSP preselectedNodes = d->kraLoader->selectedNodes(); if (preselectedNodes.size() > 0) { d->preActivatedNode = preselectedNodes.first(); } // before deleting the kraloader, get the list with preloaded assistants and save it d->assistants = d->kraLoader->assistants(); d->shapeController->setImage(d->image); connect(d->image.data(), SIGNAL(sigImageModified()), this, SLOT(setImageModified())); if (d->image) { d->image->initialRefreshGraph(); } setAutoSave(KisConfig().autoSaveInterval()); emit sigLoadingFinished(); } delete d->kraLoader; d->kraLoader = 0; return retval; } bool KisDocument::completeSaving(KoStore* store) { QString uri = url().url(); d->kraSaver->saveBinaryData(store, d->image, url().url(), isStoredExtern(), isAutosaving()); bool retval = true; if (!d->kraSaver->errorMessages().isEmpty()) { setErrorMessage(d->kraSaver->errorMessages().join(".\n")); retval = false; } delete d->kraSaver; d->kraSaver = 0; emit sigSavingFinished(); return retval; } QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const { return createDomDocument(KisFactory::componentName(), tagName, version); } //static QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version) { QDomImplementation impl; QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version); QDomDocumentType dtype = impl.createDocumentType(tagName, QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version), url); // The namespace URN doesn't need to include the version number. QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName); QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype); doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement()); return doc; } bool KisDocument::loadXML(const KoXmlDocument& doc, KoStore */*store*/) { if (d->image) { d->shapeController->setImage(0); d->image = 0; } KoXmlElement root; KoXmlNode node; KisImageWSP image; init(); if (doc.doctype().name() != "DOC") { setErrorMessage(i18n("The format is not supported or the file is corrupted")); return false; } root = doc.documentElement(); int syntaxVersion = root.attribute("syntaxVersion", "3").toInt(); if (syntaxVersion > 2) { setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion)); return false; } if (!root.hasChildNodes()) { setErrorMessage(i18n("The file has no layers.")); return false; } if (d->kraLoader) delete d->kraLoader; d->kraLoader = new KisKraLoader(this, syntaxVersion); // Legacy from the multi-image .kra file period. for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement()) { if (node.nodeName() == "IMAGE") { KoXmlElement elem = node.toElement(); if (!(image = d->kraLoader->loadXML(elem))) { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("Unknown error.")); } else { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); } return false; } } else { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("The file does not contain an image.")); } return false; } } } if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); } d->image = image; return true; } QDomDocument KisDocument::saveXML() { dbgFile << url(); QDomDocument doc = createDomDocument("DOC", CURRENT_DTD_VERSION); QDomElement root = doc.documentElement(); root.setAttribute("editor", "Krita"); root.setAttribute("syntaxVersion", "2"); if (d->kraSaver) delete d->kraSaver; d->kraSaver = new KisKraSaver(this); root.appendChild(d->kraSaver->saveXML(doc, d->image)); if (!d->kraSaver->errorMessages().isEmpty()) { setErrorMessage(d->kraSaver->errorMessages().join(".\n")); } return doc; } bool KisDocument::isNativeFormat(const QByteArray& mimetype) const { if (mimetype == nativeFormatMimeType()) return true; return extraNativeMimeTypes().contains(mimetype); } int KisDocument::supportedSpecialFormats() const { return 0; // we don't support encryption. } void KisDocument::setErrorMessage(const QString& errMsg) { d->lastErrorMessage = errMsg; } QString KisDocument::errorMessage() const { return d->lastErrorMessage; } void KisDocument::showLoadingErrorDialog() { if (errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open\n%1", localFilePath())); } else if (errorMessage() != "USER_CANCELED") { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open %1\nReason: %2", localFilePath(), errorMessage())); } } bool KisDocument::isAutosaving() const { return d->autosaving; } bool KisDocument::isLoading() const { return d->isLoading; } void KisDocument::removeAutoSaveFiles() { // Eliminate any auto-save file QString asf = autoSaveFile(localFilePath()); // the one in the current dir if (QFile::exists(asf)) QFile::remove(asf); asf = autoSaveFile(QString()); // and the one in $HOME if (QFile::exists(asf)) QFile::remove(asf); } void KisDocument::setBackupFile(bool _b) { d->backupFile = _b; } bool KisDocument::backupFile()const { return d->backupFile; } void KisDocument::setBackupPath(const QString & _path) { d->backupPath = _path; } QString KisDocument::backupPath()const { return d->backupPath; } bool KisDocument::storeInternal() const { return d->storeInternal; } void KisDocument::setStoreInternal(bool i) { d->storeInternal = i; //kDebug(30003)<<"="<storeInternal<<" doc:"<pageLayout; } void KisDocument::setPageLayout(const KoPageLayout &pageLayout) { d->pageLayout = pageLayout; } KoUnit KisDocument::unit() const { return d->unit; } void KisDocument::setUnit(const KoUnit &unit) { if (d->unit != unit) { d->unit = unit; emit unitChanged(unit); } } void KisDocument::saveUnitOdf(KoXmlWriter *settingsWriter) const { settingsWriter->addConfigItem("unit", unit().symbol()); } KUndo2Stack *KisDocument::undoStack() { return d->undoStack; } void KisDocument::addCommand(KUndo2Command *command) { if (command) d->undoStack->push(command); } void KisDocument::beginMacro(const KUndo2MagicString & text) { d->undoStack->beginMacro(text); } void KisDocument::endMacro() { d->undoStack->endMacro(); } void KisDocument::slotUndoStackIndexChanged(int idx) { // even if the document was already modified, call setModified to re-start autosave timer setModified(idx != d->undoStack->cleanIndex()); } void KisDocument::setProfileStream(QTextStream *profilestream) { d->profileStream = profilestream; } void KisDocument::setProfileReferenceTime(const QTime& referenceTime) { d->profileReferenceTime = referenceTime; } void KisDocument::clearUndoHistory() { d->undoStack->clear(); } KoGridData &KisDocument::gridData() { return d->gridData; } KoGuidesData &KisDocument::guidesData() { return d->guidesData; } bool KisDocument::isEmpty() const { return d->isEmpty; } void KisDocument::setEmpty() { d->isEmpty = true; } // static int KisDocument::defaultAutoSave() { return 300; } void KisDocument::resetURL() { setUrl(KUrl()); setLocalFilePath(QString()); } int KisDocument::pageCount() const { return 1; } void KisDocument::setupOpenFileSubProgress() {} KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } KUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( d->document->isReadWrite() && d->document->isModified()) { foreach(KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { if (!view->queryClose()) { return false; } } } } } // Not modified => ok and delete temp file. d->mimeType = QByteArray(); if ( d->m_bTemp ) { QFile::remove( d->m_file ); d->m_bTemp = false; } // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } bool KisDocument::saveAs( const KUrl & kurl ) { if (!kurl.isValid()) { kError(1000) << "saveAs: Malformed URL " << kurl.url() << endl; return false; } d->m_duringSaveAs = true; d->m_originalURL = d->m_url; d->m_originalFilePath = d->m_file; d->m_url = kurl; // Store where to upload in saveToURL d->prepareSaving(); bool result = save(); // Save local file and upload local file if (!result) { d->m_url = d->m_originalURL; d->m_file = d->m_originalFilePath; d->m_duringSaveAs = false; d->m_originalURL = KUrl(); d->m_originalFilePath.clear(); } return result; } bool KisDocument::save() { d->m_saveOk = false; if ( d->m_file.isEmpty() ) // document was created empty d->prepareSaving(); DocumentProgressProxy *progressProxy = 0; if (!d->document->progressProxy()) { KisMainWindow *mainWindow = 0; if (KisPart::instance()->mainwindowCount() > 0) { mainWindow = KisPart::instance()->mainWindows()[0]; } progressProxy = new DocumentProgressProxy(mainWindow); d->document->setProgressProxy(progressProxy); } d->document->setUrl(url()); // THIS IS WRONG! KisDocument::saveFile should move here, and whoever subclassed KisDocument to // reimplement saveFile shold now subclass KisPart. bool ok = d->document->saveFile(); if (progressProxy) { d->document->setProgressProxy(0); delete progressProxy; } if (ok) { return saveToUrl(); } else { emit canceled(QString()); } return false; } bool KisDocument::waitSaveComplete() { if (!d->m_uploadJob) return d->m_saveOk; d->m_waitForSave = true; d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); d->m_waitForSave = false; return d->m_saveOk; } void KisDocument::setUrl(const KUrl &url) { d->m_url = url; } QString KisDocument::localFilePath() const { return d->m_file; } void KisDocument::setLocalFilePath( const QString &localFilePath ) { d->m_file = localFilePath; } bool KisDocument::saveToUrl() { if ( d->m_url.isLocalFile() ) { d->document->setModified( false ); emit completed(); // if m_url is a local file there won't be a temp file -> nothing to remove Q_ASSERT( !d->m_bTemp ); d->m_saveOk = true; d->m_duringSaveAs = false; d->m_originalURL = KUrl(); d->m_originalFilePath.clear(); return true; // Nothing to do } #ifndef Q_OS_WIN else { if (d->m_uploadJob) { QFile::remove(d->m_uploadJob->srcUrl().toLocalFile()); d->m_uploadJob->kill(); d->m_uploadJob = 0; } KTemporaryFile *tempFile = new KTemporaryFile(); tempFile->open(); QString uploadFile = tempFile->fileName(); delete tempFile; KUrl uploadUrl; uploadUrl.setPath( uploadFile ); // Create hardlink if (::link(QFile::encodeName(d->m_file), QFile::encodeName(uploadFile)) != 0) { // Uh oh, some error happened. return false; } d->m_uploadJob = KIO::file_move( uploadUrl, d->m_url, -1, KIO::Overwrite ); connect( d->m_uploadJob, SIGNAL(result(KJob*)), this, SLOT(_k_slotUploadFinished(KJob*)) ); return true; } #else return false; #endif } bool KisDocument::openUrlInternal(const KUrl &url) { if ( !url.isValid() ) return false; if (d->m_bAutoDetectedMime) { d->mimeType = QByteArray(); d->m_bAutoDetectedMime = false; } QByteArray mimetype = d->mimeType; if ( !closeUrl() ) return false; d->mimeType = mimetype; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); return d->openLocalFile(); } else { d->openRemoteFile(); return true; } } KisImageWSP KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* colorspace) { KoColor backgroundColor(Qt::white, colorspace); /** * FIXME: check whether this is a good value */ double defaultResolution=1.; newImage(name, width, height, colorspace, backgroundColor, "", defaultResolution); return image(); } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, const QString &imageDescription, const double imageResolution) { return newImage(name, width, height, cs, bgColor, false, 1, imageDescription, imageResolution); } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &description, const double imageResolution) { Q_ASSERT(cs); init(); KisConfig cfg; KisImageWSP image; KisPaintLayerSP layer; if (!cs) return false; QApplication::setOverrideCursor(Qt::BusyCursor); image = new KisImage(createUndoStore(), width, height, cs, name); Q_CHECK_PTR(image); connect(image.data(), SIGNAL(sigImageModified()), this, SLOT(setImageModified())); image->setResolution(imageResolution, imageResolution); image->assignImageProfile(cs->profile()); documentInfo()->setAboutInfo("title", name); if (name != i18n("Unnamed") && !name.isEmpty()) { setUrl(KUrl(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) + '/' + name + ".kra")); } documentInfo()->setAboutInfo("comments", description); layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs); Q_CHECK_PTR(layer); if (backgroundAsLayer) { image->setDefaultProjectionColor(KoColor(cs)); if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) { layer->paintDevice()->setDefaultPixel(bgColor.data()); } else { // Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8()); } } else { image->setDefaultProjectionColor(bgColor); } layer->setDirty(QRect(0, 0, width, height)); image->addNode(layer.data(), image->rootLayer().data()); setCurrentImage(image); for(int i = 1; i < numberOfLayers; ++i) { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs); image->addNode(layer, image->root(), i); layer->setDirty(QRect(0, 0, width, height)); } cfg.defImageWidth(width); cfg.defImageHeight(height); cfg.defImageResolution(imageResolution); cfg.defColorModel(image->colorSpace()->colorModelId().id()); cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id()); cfg.defColorProfile(image->colorSpace()->profile()->name()); QApplication::restoreOverrideCursor(); return true; } KoShapeBasedDocumentBase *KisDocument::shapeController() const { return d->shapeController; } KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const { return d->shapeController->shapeForNode(layer); } vKisNodeSP KisDocument::activeNodes() const { vKisNodeSP nodes; foreach(KisView *v, KisPart::instance()->views()) { if (v->document() == this && v->viewManager()) { KisNodeSP activeNode = v->viewManager()->activeNode(); if (activeNode && !nodes.contains(activeNode)) { if (activeNode->inherits("KisMask")) { activeNode = activeNode->parent(); } nodes.append(activeNode); } } } return nodes; } QList KisDocument::assistants() { QList assistants; foreach(KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { KisPaintingAssistantsDecoration* assistantsDecoration = view->canvasBase()->paintingAssistantsDecoration(); assistants.append(assistantsDecoration->assistants()); } } return assistants; } QList KisDocument::preLoadedAssistants() { return d->assistants; } void KisDocument::setPreActivatedNode(KisNodeSP activatedNode) { d->preActivatedNode = activatedNode; } KisNodeSP KisDocument::preActivatedNode() const { return d->preActivatedNode; } void KisDocument::prepareForImport() { if (d->nserver == 0) { init(); } } KisImageWSP KisDocument::image() const { return d->image; } void KisDocument::setCurrentImage(KisImageWSP image) { if (!image || !image.isValid()) return; if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); d->shapeController->setImage(0); } d->image = image; d->shapeController->setImage(image); setModified(false); connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified())); d->image->initialRefreshGraph(); setAutoSave(KisConfig().autoSaveInterval()); } void KisDocument::initEmpty() { KisConfig cfg; const KoColorSpace * rgb = KoColorSpaceRegistry::instance()->rgb8(); newImage("", cfg.defImageWidth(), cfg.defImageHeight(), rgb); } void KisDocument::setImageModified() { setModified(true); } KisUndoStore* KisDocument::createUndoStore() { return new KisDocumentUndoStore(this); } #include diff --git a/krita/ui/canvas/kis_grid_manager.cpp b/krita/ui/canvas/kis_grid_manager.cpp index 02f7f01fd30..8ec248843cd 100644 --- a/krita/ui/canvas/kis_grid_manager.cpp +++ b/krita/ui/canvas/kis_grid_manager.cpp @@ -1,228 +1,106 @@ /* * This file is part of Krita * * Copyright (c) 2006 Cyrille Berger * * 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. */ #include "kis_grid_manager.h" #include #include #include #include #include #include "kis_coordinates_converter.h" #include "kis_config.h" #include "kis_grid_painter_configuration.h" #include "kis_grid_decoration.h" #include "kis_image.h" #include "KisViewManager.h" #include "KisDocument.h" #include "KisView.h" #include "kis_action.h" KisGridManager::KisGridManager(KisViewManager * parent) : QObject(parent) { } KisGridManager::~KisGridManager() { } void KisGridManager::setup(KisActionManager* actionManager) { - //there is no grid by default -// m_view->document()->gridData().setShowGrid(false); -// -// KisConfig config; -// m_view->document()->gridData().setGrid(config.getGridHSpacing(), config.getGridVSpacing()); - m_toggleGrid = new KisAction(themedIcon("view-grid"), i18n("Show Grid"), 0); m_toggleGrid->setCheckable(true); m_toggleGrid->setActivationFlags(KisAction::ACTIVE_NODE); m_toggleGrid->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Apostrophe)); actionManager->addAction("view_grid", m_toggleGrid); m_toggleSnapToGrid = new KisAction(i18n("Snap To Grid"), this); m_toggleSnapToGrid->setCheckable(true); m_toggleSnapToGrid->setActivationFlags(KisAction::ACTIVE_NODE); m_toggleSnapToGrid->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Semicolon)); //actionManager->addAction("view_snap_to_grid", m_toggleSnapToGrid); connect(m_toggleSnapToGrid, SIGNAL(triggered()), this, SLOT(toggleSnapToGrid())); - - // Fast grid config - m_gridFastConfig1x1 = new KisAction(i18n("1x1"), this); - m_gridFastConfig1x1->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_1x1", m_gridFastConfig1x1); - connect(m_gridFastConfig1x1, SIGNAL(triggered()), this, SLOT(fastConfig1x1())); - - m_gridFastConfig2x2 = new KisAction(i18n("2x2"), this); - m_gridFastConfig2x2->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_2x2", m_gridFastConfig2x2); - connect(m_gridFastConfig2x2, SIGNAL(triggered()), this, SLOT(fastConfig2x2())); - - m_gridFastConfig4x4 = new KisAction(i18n("4x4"), this); - m_gridFastConfig4x4->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_4x4", m_gridFastConfig4x4); - connect(m_gridFastConfig4x4, SIGNAL(triggered()), this, SLOT(fastConfig4x4())); - - m_gridFastConfig5x5 = new KisAction(i18n("5x5"), this); - m_gridFastConfig5x5->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_5x5", m_gridFastConfig5x5); - connect(m_gridFastConfig5x5, SIGNAL(triggered()), this, SLOT(fastConfig5x5())); - - m_gridFastConfig8x8 = new KisAction(i18n("8x8"), this); - m_gridFastConfig8x8->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_8x8", m_gridFastConfig8x8); - connect(m_gridFastConfig8x8, SIGNAL(triggered()), this, SLOT(fastConfig8x8())); - - m_gridFastConfig10x10 = new KisAction(i18n("10x10"), this); - m_gridFastConfig10x10->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_10x10", m_gridFastConfig10x10); - connect(m_gridFastConfig10x10, SIGNAL(triggered()), this, SLOT(fastConfig10x10())); - - m_gridFastConfig16x16 = new KisAction(i18n("16x16"), this); - m_gridFastConfig16x16->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_16x16", m_gridFastConfig16x16); - connect(m_gridFastConfig16x16, SIGNAL(triggered()), this, SLOT(fastConfig16x16())); - - m_gridFastConfig20x20 = new KisAction(i18n("20x20"), this); - m_gridFastConfig20x20->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_20x20", m_gridFastConfig20x20); - connect(m_gridFastConfig20x20, SIGNAL(triggered()), this, SLOT(fastConfig20x20())); - - m_gridFastConfig32x32 = new KisAction(i18n("32x32"), this); - m_gridFastConfig32x32->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_32x32", m_gridFastConfig32x32); - connect(m_gridFastConfig32x32, SIGNAL(triggered()), this, SLOT(fastConfig32x32())); - - m_gridFastConfig40x40 = new KisAction(i18n("40x40"), this); - m_gridFastConfig40x40->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_40x40", m_gridFastConfig40x40); - connect(m_gridFastConfig40x40, SIGNAL(triggered()), this, SLOT(fastConfig40x40())); - - m_gridFastConfig64x64 = new KisAction(i18n("64x64"), this); - m_gridFastConfig64x64->setActivationFlags(KisAction::ACTIVE_NODE); - actionManager->addAction("view_fast_grid_64x64", m_gridFastConfig64x64); - connect(m_gridFastConfig64x64, SIGNAL(triggered()), this, SLOT(fastConfig64x64())); } void KisGridManager::updateGUI() { } -void KisGridManager::setView(QPointer< KisView > imageView) +void KisGridManager::setView(QPointer imageView) { + qDebug() << "KisGridManager::setView();" << imageView; + if (m_imageView) { m_toggleGrid->disconnect(); m_gridDecoration = 0; } + m_imageView = imageView; + if (imageView) { m_gridDecoration = qobject_cast(imageView->canvasBase()->decoration("grid")); if (!m_gridDecoration) { m_gridDecoration = new KisGridDecoration(imageView); imageView->canvasBase()->addDecoration(m_gridDecoration); } checkVisibilityAction(m_gridDecoration->visible()); connect(m_toggleGrid, SIGNAL(triggered()), m_gridDecoration, SLOT(toggleVisibility())); } } void KisGridManager::checkVisibilityAction(bool check) { m_toggleGrid->setChecked(check); } void KisGridManager::toggleSnapToGrid() { if (m_imageView) { m_imageView->document()->gridData().setSnapToGrid(m_toggleSnapToGrid->isChecked()); m_imageView->canvasBase()->updateCanvas(); } } -void KisGridManager::setFastConfig(int size) -{ - if (m_imageView) { - m_imageView->document()->gridData().setGrid(size, size); - m_imageView->canvasBase()->updateCanvas(); - } -} - -void KisGridManager::fastConfig1x1() -{ - setFastConfig(1); -} - -void KisGridManager::fastConfig2x2() -{ - setFastConfig(2); -} - -void KisGridManager::fastConfig5x5() -{ - setFastConfig(5); -} - -void KisGridManager::fastConfig10x10() -{ - setFastConfig(10); -} - -void KisGridManager::fastConfig20x20() -{ - setFastConfig(20); -} - -void KisGridManager::fastConfig40x40() -{ - setFastConfig(40); -} - -void KisGridManager::fastConfig4x4() -{ - setFastConfig(4); -} - -void KisGridManager::fastConfig8x8() -{ - setFastConfig(4); -} - -void KisGridManager::fastConfig16x16() -{ - setFastConfig(16); -} - -void KisGridManager::fastConfig32x32() -{ - setFastConfig(32); -} - -void KisGridManager::fastConfig64x64() -{ - setFastConfig(64); -} - - #include "kis_grid_manager.moc" diff --git a/krita/ui/canvas/kis_grid_manager.h b/krita/ui/canvas/kis_grid_manager.h index 0d7c439a405..9b9d5a41a14 100644 --- a/krita/ui/canvas/kis_grid_manager.h +++ b/krita/ui/canvas/kis_grid_manager.h @@ -1,89 +1,67 @@ /* * This file is part of Krita * * Copyright (c) 2006 Cyrille Berger * * 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. */ #ifndef KIS_GRID_MANAGER_H #define KIS_GRID_MANAGER_H #include "kis_canvas_decoration.h" #include #include "kis_types.h" #include #include "kis_action_manager.h" #include "kis_action.h" class KisGridDecoration; class KisViewManager; class KRITAUI_EXPORT KisGridManager : public QObject { Q_OBJECT public: KisGridManager(KisViewManager * parent); virtual ~KisGridManager(); public: void setup(KisActionManager * actionManager); void setView(QPointerimageView); public Q_SLOTS: void updateGUI(); void checkVisibilityAction(bool check); private Q_SLOTS: void toggleSnapToGrid(); - void fastConfig1x1(); - void fastConfig2x2(); - void fastConfig5x5(); - void fastConfig10x10(); - void fastConfig20x20(); - void fastConfig40x40(); - void fastConfig4x4(); - void fastConfig8x8(); - void fastConfig16x16(); - void fastConfig32x32(); - void fastConfig64x64(); private: void setFastConfig(int size); KisAction *m_toggleGrid; KisAction* m_toggleSnapToGrid; - KisAction* m_gridFastConfig1x1; - KisAction* m_gridFastConfig2x2; - KisAction* m_gridFastConfig5x5; - KisAction* m_gridFastConfig10x10; - KisAction* m_gridFastConfig20x20; - KisAction* m_gridFastConfig40x40; - KisAction* m_gridFastConfig4x4; - KisAction* m_gridFastConfig8x8; - KisAction* m_gridFastConfig16x16; - KisAction* m_gridFastConfig32x32; - KisAction* m_gridFastConfig64x64; QPointer m_imageView; KisGridDecoration* m_gridDecoration; }; #endif