diff --git a/krita/gemini/MainWindow.cpp b/krita/gemini/MainWindow.cpp index b38f2d1804..646de3c486 100644 --- a/krita/gemini/MainWindow.cpp +++ b/krita/gemini/MainWindow.cpp @@ -1,787 +1,787 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * Copyright (C) 2012 KO GmbH. Contact: Boudewijn Rempt * Copyright (C) 2013 Dan Leinir Turthra Jensen * * 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 "MainWindow.h" #include "desktopviewproxy.h" #include "ViewModeSwitchEvent.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 "filter/kis_filter.h" #include "filter/kis_filter_registry.h" #include #include #include #include #include #include #include #include #include #include #include "sketch/SketchDeclarativeView.h" #include "sketch/RecentFileManager.h" #include "sketch/DocumentManager.h" #include "sketch/QmlGlobalEngine.h" #include "sketch/Settings.h" #ifdef Q_OS_WIN // Slate mode/docked detection stuff #include #define SM_CONVERTIBLESLATEMODE 0x2003 #define SM_SYSTEMDOCKED 0x2004 #endif class MainWindow::Private { public: Private(MainWindow* qq) : q(qq) , allowClose(true) , sketchView(0) , desktopWindow(0) , currentView(0) , desktopCursorStyle(CURSOR_STYLE_NO_CURSOR) , slateMode(false) , docked(false) , sketchKisView(0) , desktopKisView(0) , desktopViewProxy(0) , forceFullScreen(false) , forceDesktop(false) , forceSketch(false) , temporaryFile(false) , syncObject(0) , toDesktop(0) , toSketch(0) , switcher(0) { #ifdef Q_OS_WIN // slateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); // docked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0); #endif centerer = new QTimer(q); centerer->setInterval(10); centerer->setSingleShot(true); connect(centerer, SIGNAL(timeout()), q, SLOT(adjustZoomOnDocumentChangedAndStuff())); } MainWindow* q; bool allowClose; SketchDeclarativeView* sketchView; KisMainWindow* desktopWindow; QObject* currentView; CursorStyle desktopCursorStyle; bool slateMode; bool docked; QString currentSketchPage; KisView* sketchKisView; KisView* desktopKisView; DesktopViewProxy* desktopViewProxy; bool forceFullScreen; bool forceDesktop; bool forceSketch; bool temporaryFile; ViewModeSynchronisationObject* syncObject; QTimer* centerer; QAction * toDesktop; QAction * toSketch; QToolButton* switcher; void initSketchView(QObject* parent) { sketchView = new SketchDeclarativeView(); QmlGlobalEngine::instance()->setEngine(sketchView->engine()); sketchView->engine()->rootContext()->setContextProperty("mainWindow", parent); #ifdef Q_OS_WIN QDir appdir(qApp->applicationDirPath()); // Corrects for mismatched case errors in path (qtdeclarative fails to load) wchar_t buffer[1024]; QString absolute = appdir.absolutePath(); DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024); rv = ::GetLongPathName(buffer, buffer, 1024); QString correctedPath((QChar *)buffer); appdir.setPath(correctedPath); // for now, the app in bin/ and we still use the env.bat script appdir.cdUp(); sketchView->engine()->addImportPath(appdir.canonicalPath() + "/lib/calligra/imports"); sketchView->engine()->addImportPath(appdir.canonicalPath() + "/lib64/calligra/imports"); QString mainqml = appdir.canonicalPath() + "/share/apps/kritagemini/kritagemini.qml"; #else sketchView->engine()->addImportPath(KoResourcePaths::findDirs("lib", "calligra/imports").value(0)); QString mainqml = KoResourcePaths::findResource("data", "kritagemini/kritagemini.qml"); #endif Q_ASSERT(QFile::exists(mainqml)); if (!QFile::exists(mainqml)) { QMessageBox::warning(0, i18nc("@title:window", "Krita: No QML Found"), i18n("%1 doesn't exist.", mainqml)); } QFileInfo fi(mainqml); sketchView->setSource(QUrl::fromLocalFile(fi.canonicalFilePath())); sketchView->setResizeMode( QDeclarativeView::SizeRootObjectToView ); if (sketchView->errors().count() > 0) { Q_FOREACH (const QDeclarativeError &error, sketchView->errors()) { dbgKrita << error.toString(); } } toDesktop = new QAction(q); toDesktop->setEnabled(false); toDesktop->setText(tr("Switch to Desktop")); // useful for monkey-testing to crash... //toDesktop->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D); //q->addAction(toDesktop); //connect(toDesktop, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchDesktopForced())); connect(toDesktop, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchToDesktop())); sketchView->engine()->rootContext()->setContextProperty("switchToDesktopAction", toDesktop); } void initDesktopView() { // Initialize all Calligra directories etc. KoGlobal::initialize(); // The default theme is not what we want for Gemini KConfigGroup group(KGlobal::config(), "theme"); if(group.readEntry("Theme", "no-theme-is-set") == QLatin1String("no-theme-is-set")) { group.writeEntry("Theme", "Krita-dark"); } desktopWindow = new KisMainWindow(); toSketch = new QAction(desktopWindow); toSketch->setEnabled(false); toSketch->setText(tr("Switch to Sketch")); toSketch->setIcon(KisIconUtils::loadIcon("system-reboot")); toSketch->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_S); //connect(toSketch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchSketchForced())); connect(toSketch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchToSketch())); desktopWindow->actionCollection()->addAction("SwitchToSketchView", toSketch); switcher = new QToolButton(); switcher->setEnabled(false); switcher->setText(tr("Switch to Sketch")); switcher->setIcon(KisIconUtils::loadIcon("system-reboot")); switcher->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); //connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchDesktopForced())); connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchToSketch())); desktopWindow->menuBar()->setCornerWidget(switcher); // DesktopViewProxy connects itself up to everything appropriate on construction, // and destroys itself again when the view is removed desktopViewProxy = new DesktopViewProxy(q, desktopWindow); connect(desktopViewProxy, SIGNAL(documentSaved()), q, SIGNAL(documentSaved())); connect(desktopViewProxy, SIGNAL(documentSaved()), q, SLOT(resetWindowTitle())); } void notifySlateModeChange(); void notifyDockingModeChange(); bool queryClose(); }; MainWindow::MainWindow(QStringList fileNames, QWidget* parent, Qt::WindowFlags flags ) : QMainWindow( parent, flags ), d( new Private(this) ) { qApp->setActiveWindow( this ); setWindowTitle(i18n("Krita Gemini")); setWindowIcon(KisIconUtils::loadIcon("kritagemini")); // Load filters and other plugins in the gui thread Q_UNUSED(KisFilterRegistry::instance()); Q_UNUSED(KisPaintOpRegistry::instance()); KisConfig cfg; // Store the current setting before we do "things", and heuristic our way to a reasonable // default if it's no cursor (that's most likely due to a broken config) if (cfg.newCursorStyle() != CURSOR_STYLE_NO_CURSOR) d->desktopCursorStyle = cfg.newCursorStyle(); cfg.setNewCursorStyle(CURSOR_STYLE_NO_CURSOR); cfg.setUseOpenGL(true); Q_FOREACH (QString fileName, fileNames) { DocumentManager::instance()->recentFileManager()->addRecent( QDir::current().absoluteFilePath( fileName ) ); } connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(documentChanged())); connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(resetWindowTitle())); connect(DocumentManager::instance(), SIGNAL(documentSaved()), SLOT(resetWindowTitle())); d->initSketchView(this); // Set the initial view to sketch... because reasons. // Really, this allows us to show the pleasant welcome screen from Sketch switchToSketch(); if(!fileNames.isEmpty()) { //It feels a little hacky, but call a QML function to open files. //This saves a lot of hassle required to change state for loading dialogs etc. QMetaObject::invokeMethod(d->sketchView->rootObject(), "openFile", Q_ARG(QVariant, fileNames.at(0))); } } void MainWindow::resetWindowTitle() { QUrl url(DocumentManager::instance()->settingsManager()->currentFile()); QString fileName = url.fileName(); if(url.scheme() == "temp") fileName = i18n("Untitled"); KoDialog::CaptionFlags flags = KoDialog::HIGCompliantCaption; KisDocument* document = DocumentManager::instance()->document(); if (document && document->isModified() ) { flags |= KoDialog::ModifiedCaption; } setWindowTitle( KoDialog::makeStandardCaption(fileName, this, flags) ); } void MainWindow::switchDesktopForced() { if (d->slateMode) d->forceDesktop = true; d->forceSketch = false; } void MainWindow::switchSketchForced() { if (!d->slateMode) d->forceSketch = true; d->forceDesktop = false; } void MainWindow::switchToSketch() { if (d->toSketch) { d->toSketch->setEnabled(false); d->switcher->setEnabled(false); } d->syncObject = new ViewModeSynchronisationObject; KisViewManager* view = 0; KisConfig cfg; if (d->desktopWindow && centralWidget() == d->desktopWindow) { d->desktopCursorStyle = cfg.newCursorStyle(); view = qobject_cast(d->desktopWindow->activeView()); //Notify the view we are switching away from that we are about to switch away from it //giving it the possibility to set up the synchronisation object. ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, view, d->sketchView, d->syncObject); QApplication::sendEvent(view, &aboutToSwitchEvent); d->desktopWindow->setParent(0); } setCentralWidget(d->sketchView); if (d->slateMode) { setWindowState(windowState() | Qt::WindowFullScreen); if (d->syncObject->initialized) QTimer::singleShot(50, this, SLOT(sketchChange())); } else QTimer::singleShot(50, this, SLOT(sketchChange())); if (view && view->document()) { - view->document()->setSaveInBatchMode(true); + view->document()->setFileBatchMode(true); } } void MainWindow::sketchChange() { if (centralWidget() != d->sketchView || !d->syncObject) return; if (d->desktopWindow) { if (!d->sketchKisView || !d->sketchView->canvasWidget()) { QTimer::singleShot(100, this, SLOT(sketchChange())); return; } qApp->processEvents(); KisViewManager* view = qobject_cast(d->desktopWindow->activeView()); //Notify the new view that we just switched to it, passing our synchronisation object //so it can use those values to sync with the old view. ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToSketchModeEvent, view, d->sketchView, d->syncObject); QApplication::sendEvent(d->sketchView, &switchedEvent); d->syncObject = 0; qApp->processEvents(); KisConfig cfg; cfg.setNewCursorStyle(CURSOR_STYLE_NO_CURSOR); emit switchedToSketch(); } if (d->toDesktop) { qApp->processEvents(); d->toDesktop->setEnabled(true); } } void MainWindow::switchToDesktop(bool justLoaded) { if (d->toDesktop) d->toDesktop->setEnabled(false); ViewModeSynchronisationObject* syncObject = new ViewModeSynchronisationObject; KisViewManager* view = 0; if (d->desktopWindow) { view = qobject_cast(d->desktopWindow->activeView()); } //Notify the view we are switching away from that we are about to switch away from it //giving it the possibility to set up the synchronisation object. ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, d->sketchView, view, syncObject); QApplication::sendEvent(d->sketchView, &aboutToSwitchEvent); qApp->processEvents(); if (d->currentSketchPage == "MainPage") { d->sketchView->setParent(0); setCentralWidget(d->desktopWindow); } if (!d->forceFullScreen) { setWindowState(windowState() & ~Qt::WindowFullScreen); } if (view) { //Notify the new view that we just switched to it, passing our synchronisation object //so it can use those values to sync with the old view. ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToDesktopModeEvent, d->sketchView, view, syncObject); QApplication::sendEvent(view, &switchedEvent); KisConfig cfg; cfg.setNewCursorStyle(d->desktopCursorStyle); } if (d->toSketch && !justLoaded) { qApp->processEvents(); d->toSketch->setEnabled(true); d->switcher->setEnabled(true); } if (view && view->document()) { - view->document()->setSaveInBatchMode(false); + view->document()->setFileBatchMode(false); } } void MainWindow::adjustZoomOnDocumentChangedAndStuff() { if (d->desktopWindow && centralWidget() == d->desktopWindow) { KisView* view = qobject_cast(d->desktopWindow->activeView()); // We have to set the focus on the view here, otherwise the toolmanager is unaware of which // canvas should be handled. view->canvasController()->setFocus(); view->setFocus(); QPoint center = view->rect().center(); view->canvasController()->zoomRelativeToPoint(center, 0.9); qApp->processEvents(); d->toSketch->setEnabled(true); d->switcher->setEnabled(true); } else if (d->sketchKisView && centralWidget() == d->sketchView) { qApp->processEvents(); d->sketchKisView->zoomController()->setZoom(KoZoomMode::ZOOM_PAGE, 1.0); qApp->processEvents(); QPoint center = d->sketchKisView->rect().center(); d->sketchKisView->canvasController()->zoomRelativeToPoint(center, 0.9); qApp->processEvents(); d->toDesktop->setEnabled(true); } // Ensure that we do, in fact, have the brush tool selected on the currently active canvas KoToolManager::instance()->switchToolRequested( "InteractionTool" ); qApp->processEvents(); KoToolManager::instance()->switchToolRequested( "KritaShape/KisToolBrush" ); } void MainWindow::documentChanged() { if (d->desktopWindow) { d->desktopWindow->setNoCleanup(true); d->desktopWindow->deleteLater(); d->desktopWindow = 0; } d->initDesktopView(); //d->desktopWindow->setRootDocument(DocumentManager::instance()->document(), DocumentManager::instance()->part(), false); qApp->processEvents(); d->desktopKisView = qobject_cast(d->desktopWindow->activeView()); //d->desktopKisView->setQtMainWindow(d->desktopWindow); // Define new actions here KXMLGUIFactory* factory = d->desktopWindow->factory(); factory->removeClient(d->desktopWindow); factory->addClient(d->desktopWindow); d->desktopViewProxy->documentChanged(); connect(d->desktopKisView, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start())); connect(d->desktopKisView, SIGNAL(sigSavingFinished()), this, SLOT(resetWindowTitle())); if (d->desktopKisView && d->desktopKisView->canvasBase() && d->desktopKisView->canvasBase()->resourceManager()) { connect(d->desktopKisView->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), this, SLOT(resourceChanged(int, const QVariant&))); } if (!d->forceSketch && !d->slateMode) switchToDesktop(true); } bool MainWindow::allowClose() const { return d->allowClose; } void MainWindow::setAllowClose(bool allow) { d->allowClose = allow; } bool MainWindow::slateMode() const { return d->slateMode; } void MainWindow::setSlateMode(bool newValue) { d->slateMode = newValue; } QString MainWindow::currentSketchPage() const { return d->currentSketchPage; } void MainWindow::setCurrentSketchPage(QString newPage) { d->currentSketchPage = newPage; emit currentSketchPageChanged(); if (newPage == "MainPage") { if (!d->forceSketch && !d->slateMode) { // Just loaded to desktop, do nothing } else { //QTimer::singleShot(3000, this, SLOT(adjustZoomOnDocumentChangedAndStuff())); } } } bool MainWindow::temporaryFile() const { return d->temporaryFile; } void MainWindow::setTemporaryFile(bool newValue) { d->temporaryFile = newValue; emit temporaryFileChanged(); } QString MainWindow::openImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Document")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); KisDocumentEntry entry = KisDocumentEntry::queryByMimeType("application/x-krita"); KService::Ptr service = entry.service(); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import, service->property("X-KDE-ExtraNativeMimeTypes").toStringList())); dialog.setHideNameFilterDetailsOption(); return dialog.filename(); } void MainWindow::resourceChanged(int key, const QVariant& v) { Q_UNUSED(key); if(centralWidget() == d->sketchView) return; KisPaintOpPresetSP preset = v.value(); if(preset && d->sketchKisView != 0) { KisPaintOpPresetSP clone = preset; d->sketchKisView->resourceProvider()->setPaintOpPreset(clone); } } void MainWindow::resourceChangedSketch(int key, const QVariant& v) { Q_UNUSED(key); if(centralWidget() == d->desktopWindow) return; KisPaintOpPresetSP preset = v.value(); if(preset && d->desktopKisView != 0) { KisPaintOpPresetSP clone = preset; d->desktopKisView->resourceProvider()->setPaintOpPreset(clone); } } QObject* MainWindow::sketchKisView() const { return d->sketchKisView; } void MainWindow::setSketchKisView(QObject* newView) { if (d->sketchKisView) { d->sketchKisView->disconnect(this); d->sketchKisView->canvasBase()->resourceManager()->disconnect(this); } if (d->sketchKisView != newView) { d->sketchKisView = qobject_cast(newView); if(d->sketchKisView) { d->sketchView->addActions(d->sketchKisView->actions()); // d->sketchKisView->setQtMainWindow(this); connect(d->sketchKisView, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start())); connect(d->sketchKisView->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), this, SLOT(resourceChangedSketch(int, const QVariant&))); d->centerer->start(); } emit sketchKisViewChanged(); } } void MainWindow::minimize() { setWindowState(windowState() ^ Qt::WindowMinimized); } void MainWindow::closeWindow() { if (d->desktopWindow) { // This situation shouldn't occur, but protecting potentially dangerous call d->desktopWindow->setNoCleanup(true); } //For some reason, close() does not work even if setAllowClose(true) was called just before this method. //So instead just completely quit the application, since we are using a single window anyway. DocumentManager::instance()->closeDocument(); qApp->processEvents(); QApplication::instance()->quit(); } bool MainWindow::Private::queryClose() { if (desktopWindow) { // This situation shouldn't occur, but protecting potentially dangerous call desktopWindow->setNoCleanup(true); } if (DocumentManager::instance()->document() == 0) return true; // main doc + internally stored child documents if (DocumentManager::instance()->document()->isModified()) { QString name; if (DocumentManager::instance()->document()->documentInfo()) { name = DocumentManager::instance()->document()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = DocumentManager::instance()->document()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = QMessageBox::warning(q, i18nc("@title:window", "Krita"), i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : { if (DocumentManager::instance()->isTemporaryFile()) { if(!desktopViewProxy->fileSaveAs()) return false; } else if (!DocumentManager::instance()->save()) { return false; } break; } case QMessageBox::No : DocumentManager::instance()->document()->removeAutoSaveFiles(); DocumentManager::instance()->document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case QMessageBox::Cancel : return false; } } return true; } void MainWindow::closeEvent(QCloseEvent* event) { if (centralWidget() == d->desktopWindow) { if (DocumentManager::instance()->document()->isLoading()) { event->ignore(); return; } d->allowClose = d->queryClose(); } if (d->allowClose) { if (d->desktopWindow) { d->desktopWindow->setNoCleanup(true); d->desktopWindow->close(); } event->accept(); } else { event->ignore(); emit closeRequested(); } } MainWindow::~MainWindow() { delete d; KisConfig cfg; cfg.setNewCursorStyle(d->desktopCursorStyle); } #ifdef Q_OS_WIN bool MainWindow::winEvent( MSG * message, long * result ) { if (message && message->message == WM_SETTINGCHANGE && message->lParam) { if (wcscmp(TEXT("ConvertibleSlateMode"), (TCHAR *) message->lParam) == 0) d->notifySlateModeChange(); else if (wcscmp(TEXT("SystemDockMode"), (TCHAR *) message->lParam) == 0) d->notifyDockingModeChange(); *result = 0; return true; } return false; } #endif void MainWindow::Private::notifySlateModeChange() { #ifdef Q_OS_WIN bool bSlateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); if (slateMode != bSlateMode) { slateMode = bSlateMode; emit q->slateModeChanged(); if (forceSketch || (slateMode && !forceDesktop)) { if (!toSketch || (toSketch && toSketch->isEnabled())) q->switchToSketch(); } else { q->switchToDesktop(); } //dbgKrita << "Slate mode is now" << slateMode; } #endif } void MainWindow::Private::notifyDockingModeChange() { #ifdef Q_OS_WIN bool bDocked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0); if (docked != bDocked) { docked = bDocked; //dbgKrita << "Docking mode is now" << docked; } #endif } void MainWindow::cloneResources(KisCanvasResourceProvider *from, KisCanvasResourceProvider *to) { to->setBGColor(from->bgColor()); to->setFGColor(from->fgColor()); to->setHDRExposure(from->HDRExposure()); to->setHDRGamma(from->HDRGamma()); to->setCurrentCompositeOp(from->currentCompositeOp()); to->slotPatternActivated(from->currentPattern()); to->slotGradientActivated(from->currentGradient()); to->slotNodeActivated(from->currentNode()); to->setPaintOpPreset(from->currentPreset()); to->setOpacity(from->opacity()); to->setGlobalAlphaLock(from->globalAlphaLock()); } bool MainWindow::forceFullScreen() { return d->forceFullScreen; } void MainWindow::forceFullScreen(bool newValue) { d->forceFullScreen = newValue; } diff --git a/krita/sketch/DocumentManager.cpp b/krita/sketch/DocumentManager.cpp index f584ceb297..55bcbab967 100644 --- a/krita/sketch/DocumentManager.cpp +++ b/krita/sketch/DocumentManager.cpp @@ -1,314 +1,314 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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 "DocumentManager.h" #include "ProgressProxy.h" #include "Settings.h" #include "RecentFileManager.h" #include #include #include #include #include #include #include #include class DocumentManager::Private { public: Private() : proxy(0) , document(0) , settingsManager(0) , recentFileManager(0) , newDocWidth(0) , newDocHeight(0) , newDocResolution(0) , importingDocument(false) , temporaryFile(false) { } ProgressProxy* proxy; QPointer document; Settings* settingsManager; RecentFileManager* recentFileManager; QString saveAsFilename; QString openDocumentFilename; int newDocWidth, newDocHeight; float newDocResolution; bool importingDocument; QVariantMap newDocOptions; bool temporaryFile; }; DocumentManager *DocumentManager::sm_instance = 0; KisDocument* DocumentManager::document() const { return d->document; } ProgressProxy* DocumentManager::progressProxy() const { return d->proxy; } Settings* DocumentManager::settingsManager() const { return d->settingsManager; } void DocumentManager::setSettingsManager(Settings* newManager) { d->settingsManager = newManager; } RecentFileManager* DocumentManager::recentFileManager() const { return d->recentFileManager; } bool DocumentManager::isTemporaryFile() const { return d->temporaryFile; } void DocumentManager::newDocument(int width, int height, float resolution) { closeDocument(); d->newDocWidth = width; d->newDocHeight = height; d->newDocResolution = resolution; QTimer::singleShot(300, this, SLOT(delayedNewDocument())); } void DocumentManager::newDocument(const QVariantMap& options) { closeDocument(); d->newDocOptions = options; QTimer::singleShot(300, this, SLOT(delayedNewDocument())); } void DocumentManager::delayedNewDocument() { d->document = KisPart::instance()->createDocument(); d->document->setProgressProxy(d->proxy); if (qAppName().contains("sketch")) { - d->document->setSaveInBatchMode(true); + d->document->setFileBatchMode(true); } if(d->newDocOptions.isEmpty()) { d->document->newImage("New Image", d->newDocWidth, d->newDocHeight, KoColorSpaceRegistry::instance()->rgb8()); d->document->image()->setResolution(d->newDocResolution, d->newDocResolution); d->document->resetURL(); } else if(d->newDocOptions.contains("template")) { QUrl url(d->newDocOptions.value("template").toString().remove("template://")); bool ok = d->document->loadNativeFormat(url.toLocalFile()); d->document->setModified(false); d->document->undoStack()->clear(); if (ok) { QMimeDatabase db; QString mimeType = db.mimeTypeForFile( url.path(), QMimeDatabase::MatchExtension).name(); // in case this is a open document template remove the -template from the end mimeType.remove( QRegExp( "-template$" ) ); d->document->setMimeTypeAfterLoading(mimeType); d->document->resetURL(); d->document->setEmpty(); } else { d->document->showLoadingErrorDialog(); d->document->initEmpty(); } } else { QString name = d->newDocOptions.value("name", "New Image").toString(); int width = d->newDocOptions.value("width").toInt(); int height = d->newDocOptions.value("height").toInt(); // internal resolution is pixels per point, not ppi float res = d->newDocOptions.value("resolution", 72.0f).toFloat() / 72.0f; QString colorModelId = d->newDocOptions.value("colorModelId").toString(); QString colorDepthId = d->newDocOptions.value("colorDepthId").toString(); QString colorProfileId = d->newDocOptions.value("colorProfileId").toString(); const KoColorSpace* profile; if(colorModelId.isEmpty() || colorDepthId.isEmpty() || colorProfileId.isEmpty()) { profile = KoColorSpaceRegistry::instance()->rgb8(); } else { profile = KoColorSpaceRegistry::instance()->colorSpace(colorModelId, colorDepthId, colorProfileId); } QColor background = d->newDocOptions.value("backgroundColor", QColor("white")).value(); background.setAlphaF(d->newDocOptions.value("backgroundOpacity", 1.0f).toFloat()); KoColor bg(background, profile); d->document->newImage(name, width, height, profile, bg, QString(), res); d->document->resetURL(); } KisPart::instance()->addDocument(d->document); d->temporaryFile = true; emit documentChanged(); } void DocumentManager::openDocument(const QString& document, bool import) { closeDocument(); d->openDocumentFilename = document; d->importingDocument = import; QTimer::singleShot(300, this, SLOT(delayedOpenDocument())); } void DocumentManager::delayedOpenDocument() { d->document = KisPart::instance()->createDocument(); d->document->setProgressProxy(d->proxy); if (qAppName().contains("sketch")) { - d->document->setSaveInBatchMode(true); + d->document->setFileBatchMode(true); } connect(d->document, SIGNAL(completed()), this, SLOT(onLoadCompleted())); connect(d->document, SIGNAL(canceled(QString)), this, SLOT(onLoadCanceled(QString))); // TODO: still needed? d->document->setModified(false); if (d->importingDocument) d->document->importDocument(QUrl::fromLocalFile(d->openDocumentFilename)); else d->document->openUrl(QUrl::fromLocalFile(d->openDocumentFilename)); // TODO: handle fail of open/import d->recentFileManager->addRecent(d->openDocumentFilename); KisPart::instance()->addDocument(d->document); d->temporaryFile = false; } // Separate from openDocument to handle async loading (remote URLs) void DocumentManager::onLoadCompleted() { KisDocument *newdoc = qobject_cast(sender()); disconnect(newdoc, SIGNAL(completed()), this, SLOT(onLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(onLoadCanceled(QString))); emit documentChanged(); } void DocumentManager::onLoadCanceled(const QString &errMsg) { // if (!errMsg.isEmpty()) // empty when canceled by user // QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); // ... can't delete the document, it's the one who emitted the signal... KisDocument* newdoc = qobject_cast(sender()); Q_ASSERT(newdoc); disconnect(newdoc, SIGNAL(completed()), this, SLOT(onLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(onLoadCanceled(QString))); } void DocumentManager::closeDocument() { if (d->document) { emit aboutToDeleteDocument(); d->document->closeUrl(false); //d->document->deleteLater(); d->document = 0; } } bool DocumentManager::save() { if (d->document->save()) { d->recentFileManager->addRecent(d->document->url().toLocalFile()); d->settingsManager->setCurrentFile(d->document->url().toLocalFile()); emit documentSaved(); return true; } return false; } void DocumentManager::saveAs(const QString &filename, const QString &mimetype) { d->document->setOutputMimeType(mimetype.toLatin1()); d->saveAsFilename = filename; // Yes. This is a massive hack. Basically, we need to wait a little while, to ensure // the save call happens late enough for a variety of UI things to happen first. // A second seems like a long time, but well, we do have file system interaction here, // so for now, we can get away with it. QTimer::singleShot(300, this, SLOT(delayedSaveAs())); } void DocumentManager::delayedSaveAs() { d->document->saveAs(QUrl::fromLocalFile(d->saveAsFilename)); d->settingsManager->setCurrentFile(d->saveAsFilename); d->recentFileManager->addRecent(d->saveAsFilename); emit documentSaved(); } void DocumentManager::reload() { QUrl url = d->document->url(); closeDocument(); d->openDocumentFilename = url.toLocalFile(); QTimer::singleShot(0, this, SLOT(delayedOpenDocument())); } void DocumentManager::setTemporaryFile(bool temp) { d->temporaryFile = temp; emit documentSaved(); } DocumentManager* DocumentManager::instance() { if (!sm_instance) { sm_instance = new DocumentManager(QCoreApplication::instance()); } return sm_instance; } DocumentManager::DocumentManager(QObject* parent) : QObject(parent), d(new Private) { d->proxy = new ProgressProxy(this); d->recentFileManager = new RecentFileManager(this); } DocumentManager::~DocumentManager() { delete d; } diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp index 6029e59661..411c0b804b 100644 --- a/libs/ui/KisDocument.cpp +++ b/libs/ui/KisDocument.cpp @@ -1,2506 +1,2465 @@ /* 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 #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 #include #include #include #include #include #include #include // Krita Image #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local #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" #include "KisApplication.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPart.h" #include "KisView.h" #include "kis_async_action_feedback.h" #include "kis_grid_config.h" #include "kis_guides_config.h" static const char CURRENT_DTD_VERSION[] = "2.0"; // Define the protocol used here for embedded documents' URL // This used to "store" but QUrl didn't like it, // so let's simply make it "tar" ! #define STORE_PROTOCOL "tar" // The internal path is a hack to make QUrl 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 notifySetIndexChangedOneCommand() { KisImageWSP image = this->image(); image->unlock(); image->barrierLock(); } 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 Q_DECL_HIDDEN KisDocument::Private { public: Private(KisDocument *document) : document(document), // XXX: the part should _not_ be modified from the document docInfo(0), progressUpdater(0), progressProxy(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), m_saveOk(false), m_waitForSave(false), m_duringSaveAs(false), m_bTemp(false), m_bAutoDetectedMime(false), modified(false), readwrite(true), disregardAutosaveFailure(false), nserver(0), macroNestDepth(0), imageIdleWatcher(2000 /*ms*/), - kraLoader(0) + kraLoader(0), + suppressProgress(false), + fileProgressProxy(0) { if (QLocale().measurementSystem() == QLocale::ImperialSystem) { 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; 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] = {true, true}; // 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; KisGuidesConfig guidesConfig; bool isEmpty; KoPageLayout pageLayout; QUrl 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 QUrl 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; QDateTime firstMod; QDateTime lastMod; bool disregardAutosaveFailure; KisNameServer *nserver; qint32 macroNestDepth; KisImageSP image; KisNodeSP preActivatedNode; KisShapeController* shapeController; KoShapeController* koShapeController; KisIdleWatcher imageIdleWatcher; QScopedPointer imageIdleConnection; KisKraLoader* kraLoader; KisKraSaver* kraSaver; + bool suppressProgress; + KoProgressProxy* fileProgressProxy; + QList assistants; KisGridConfig gridConfig; 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->setFileProgressProxy(); document->setUrl(m_url); bool ok = document->openFile(); - if (progressProxy) { - document->setProgressProxy(0); - delete progressProxy; - } + document->clearFileProgressProxy(); 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 QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(m_url.toLocalFile()); if (mime.isValid()) { mimeType = mime.name().toLocal8Bit(); m_bAutoDetectedMime = true; } } const bool ret = openFile(); if (ret) { emit document->completed(); } else { emit document->canceled(QString()); } return ret; } // 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(); } } void setImageAndInitIdleWatcher(KisImageSP _image) { image = _image; imageIdleWatcher.setTrackedImage(image); if (image) { imageIdleConnection.reset( new KisSignalAutoConnection( &imageIdleWatcher, SIGNAL(startedIdleMode()), image.data(), SLOT(explicitRegenerateLevelOfDetail()))); } } }; KisDocument::KisDocument() : d(new Private(this)) { d->undoStack = new UndoStack(this); d->undoStack->setParent(this); d->isEmpty = true; d->filterManager = new KisImportExportManager(this); d->filterManager->setProgresUpdater(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( KSharedConfig::openConfig(), "Undo"); d->undoStack->setUndoLimit(cfgGrp.readEntry("UndoLimit", 1000)); d->firstMod = QDateTime::currentDateTime(); d->lastMod = QDateTime::currentDateTime(); connect(d->undoStack, SIGNAL(indexChanged(int)), this, SLOT(slotUndoStackIndexChanged(int))); // preload the krita resources KisResourceServerProvider::instance(); init(); undoStack()->setUndoLimit(KisConfig().undoStackLimit()); setBackupFile(KisConfig().backupFile()); } 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 QUrl &_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). // QUrl 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. // dbgUI << "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() { dbgUI << "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()) { Q_ASSERT(url().isLocalFile()); KBackup::backupFile(url().toLocalFile(), d->backupPath); } qApp->processEvents(); bool ret = false; bool suppressErrorDialog = false; + KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK; - // create the main progress monitoring object for loading, this can - // contain subtasks for filtering and loading - d->progressUpdater = new KoProgressUpdater(d->progressProxy, KoProgressUpdater::Unthreaded); - d->progressUpdater->start(100, i18n("Saving Document")); - d->filterManager->setProgresUpdater(d->progressUpdater); + setFileProgressUpdater(i18n("Saving Document")); if (!isNativeFormat(outputMimeType)) { dbgUI << "Saving to format" << outputMimeType << "in" << localFilePath(); - // Not native format : save using export filter - d->filterManager->setProgresUpdater(d->progressUpdater); - KisImportExportFilter::ConversionStatus status = d->filterManager->exportDocument(localFilePath(), outputMimeType); + + status = d->filterManager->exportDocument(localFilePath(), outputMimeType); ret = status == KisImportExportFilter::OK; suppressErrorDialog = (status == KisImportExportFilter::UserCancelled || status == KisImportExportFilter::BadConversionGraph); dbgFile << "Export status was" << status; } else { // Native format => normal save Q_ASSERT(!localFilePath().isEmpty()); ret = saveNativeFormat(localFilePath()); } if (ret) { - QPointer updater = d->progressUpdater->startSubtask(1, "clear undo stack"); - updater->setProgress(0); - d->undoStack->setClean(); - updater->setProgress(100); - + if (!d->suppressProgress) { + QPointer updater = d->progressUpdater->startSubtask(1, "clear undo stack"); + updater->setProgress(0); + d->undoStack->setClean(); + updater->setProgress(100); + } else { + d->undoStack->setClean(); + } removeAutoSaveFiles(); // Restart the autosave timer // (we don't want to autosave again 2 seconds after a real save) setAutoSave(d->autoSaveDelay); } - - delete d->progressUpdater; - d->filterManager->setProgresUpdater(0); - d->progressUpdater = 0; + clearFileProgressUpdater(); QApplication::restoreOverrideCursor(); if (!ret) { if (!suppressErrorDialog) { + + if (errorMessage().isEmpty()) { + setErrorMessage(KisImportExportFilter::conversionStatusString(status)); + } + if (errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", localFilePath())); } 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); } 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 +bool KisDocument::fileBatchMode() const { return d->filterManager->getBatchMode(); } -void KisDocument::setSaveInBatchMode(const bool batchMode) +void KisDocument::setFileBatchMode(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); Q_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(); //dbgUI <<"Saving to store"; KoStore::Backend backend = KoStore::Auto; if (d->specialOutputFlag == SaveAsDirectoryStore) { backend = KoStore::Directory; dbgUI << "Saving as uncompressed XML, using directory store."; } else if (d->specialOutputFlag == SaveAsFlatXML) { dbgUI << "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; } dbgUI << "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); if (!isAutosaving()) { KisAsyncActionFeedback f(i18n("Saving document..."), 0); return f.runAction(std::bind(&KisDocument::saveNativeFormatCalligra, this, store)); } return saveNativeFormatCalligra(store); } bool KisDocument::saveNativeFormatCalligra(KoStore *store) { dbgUI << "Saving root"; if (store->open("root")) { KoStoreDevice dev(store); if (!saveToStream(&dev) || !store->close()) { dbgUI << "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 (!isAutosaving()) { 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; } dbgUI << "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()) warnUI << "wrote " << nwritten << "- expected" << s.size(); return nwritten == (int)s.size(); } QString KisDocument::checkImageMimeTypes(const QString &mimeType, const QUrl &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/x-gimp-brush" << "image/x-gimp-brush-animated" << "image/jp2"; if (!imageMimeTypes.contains(mimeType)) return mimeType; QFile f(url.toLocalFile()); if (!f.open(QIODevice::ReadOnly)) { warnKrita << "Could not open file to check the mimetype" << url; } QByteArray ba = f.read(qMin(f.size(), (qint64)512)); // should be enough for images QMimeDatabase db; QMimeType mime = db.mimeTypeForData(ba); f.close(); if (!mime.isValid()) { 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) { dbgUI << "Saving document to store" << _path; _store->pushDirectory(); // Use the path as the internal url if (_path.startsWith(STORE_PROTOCOL)) setUrl(QUrl(_path)); else // ugly hack to pass a relative URI setUrl(QUrl(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(); dbgUI << "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); return QPixmap::fromImage(d->image->convertToQImage(newSize, 0)); } 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 QMimeDatabase db; QMimeType mime = db.mimeTypeForName(nativeFormatMimeType()); if (!mime.isValid()) { qFatal("It seems your installation is broken/incomplete because we failed to load the native mimetype \"%s\".", nativeFormatMimeType().constData()); } const QString extension = QLatin1Char('.') + mime.preferredSuffix(); 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-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").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-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #endif } else { QFileInfo fi(path); QString dir = fi.absolutePath(); QString filename = fi.fileName(); retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension); } return retval; } void KisDocument::setDisregardAutosaveFailure(bool disregardFailure) { d->disregardAutosaveFailure = disregardFailure; } bool KisDocument::importDocument(const QUrl &_url) { bool ret; dbgUI << "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) { dbgUI << "success, resetting url"; resetURL(); setTitleModified(); } d->isImporting = false; return ret; } bool KisDocument::openUrl(const QUrl &_url, KisDocument::OpenUrlFlags flags) { if (!_url.isLocalFile()) { qDebug() << "not a local file" << _url; return false; } dbgUI << "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; } QUrl 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)) { //dbgUI <<"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 { if( !(flags & OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES) ) { KisPart::instance()->addRecentURLToAllMainWindows(_url); } if (ret) { // Detect readonly local-files; remote files are assumed to be writable QFileInfo fi(url.toLocalFile()); setReadWrite(fi.isWritable()); } } return ret; } bool KisDocument::openFile() { //dbgUI <<"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(); QUrl u = QUrl::fromLocalFile(localFilePath()); QString typeName = mimeType(); if (typeName.isEmpty()) { QMimeDatabase db; typeName = db.mimeTypeForFile(u.path()).name(); } // for images, always check content. typeName = checkImageMimeTypes(typeName, u); //dbgUI << "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(); QMimeDatabase db; QMimeType mime = db.mimeTypeForName(typeName); const QStringList patterns = mime.isValid() ? mime.globPatterns() : 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 = db.mimeTypeForFile(path, QMimeDatabase::MatchExtension).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; dbgUI << "loading" << u.fileName() << ", using directory store for" << localFilePath() << "; typeName=" << typeName; } dbgUI << localFilePath() << "type:" << typeName; QString importedFile = localFilePath(); - // create the main progress monitoring object for loading, this can - // contain subtasks for filtering and loading - d->progressUpdater = new KoProgressUpdater(d->progressProxy, KoProgressUpdater::Unthreaded); - d->progressUpdater->start(100, i18n("Opening Document")); - d->filterManager->setProgresUpdater(d->progressUpdater); + setFileProgressUpdater(i18n("Opening Document")); 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; - } + QString msg = KisImportExportFilter::conversionStatusString(status); 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->filterManager->setProgresUpdater(0); - d->progressUpdater = 0; - return false; + clearFileProgressUpdater(); + return false; } d->isEmpty = false; dbgUI << "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(); } - QPointer updater = d->progressUpdater->startSubtask(1, "clear undo stack"); - updater->setProgress(0); - undoStack()->clear(); - updater->setProgress(100); - - delete d->progressUpdater; - d->filterManager->setProgresUpdater(0); - d->progressUpdater = 0; + if (!d->suppressProgress) { + QPointer updater = d->progressUpdater->startSubtask(1, "clear undo stack"); + updater->setProgress(0); + undoStack()->clear(); + updater->setProgress(100); + clearFileProgressUpdater(); + } else { + undoStack()->clear(); + } 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) { //dbgUI <<"Trying to open" << filename; if (!store->open(filename)) { warnUI << "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) { errUI << "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; } dbgUI << "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); dbgUI << 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 { errUI << "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 { //dbgUI <<"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 { dbgKrita << "couldn't open " << url; } _store->pushDirectory(); // Store as document URL if (url.startsWith(STORE_PROTOCOL)) { setUrl(QUrl::fromUserInput(url)); } else { setUrl(QUrl(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 (mod) { updateEditingTime(false); } if (isAutosaving()) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { errKrita << "Can't set a read-only document to 'modified' !" << endl; return; } //dbgUI<<" url:" << url.path(); //dbgUI<<" mod="<docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod))); d->firstMod = now; } else if (firstModDelta > 60 || forceStoreElapsed) { d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta)); d->firstMod = now; } d->lastMod = now; } QString KisDocument::prettyPathOrUrl() const { QString _url(url().toDisplayString()); #ifdef Q_OS_WIN if (url().isLocalFile()) { _url = QDir::toNativeSeparators(_url); } #endif return _url; } // Get caption from document info (title(), in about page) QString KisDocument::caption() const { QString c; if (documentInfo()) { c = documentInfo()->aboutInfo("title"); } const QString _url(url().fileName()); if (!c.isEmpty() && !_url.isEmpty()) { c = QString("%1 - %2").arg(c).arg(_url); } else if (c.isEmpty()) { c = _url; // Fall back to document URL } return c; } void KisDocument::setTitleModified() { emit titleModified(caption(), isModified()); } bool KisDocument::completeLoading(KoStore* store) { if (!d->image) { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("Unknown error.")); } else { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); } return false; } d->kraLoader->loadKeyframes(store, url().url(), isStoredExtern()); 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) { d->kraSaver->saveKeyframes(store, url().url(), isStoredExtern()); 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("krita", 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) { Q_UNUSED(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->setImageAndInitIdleWatcher(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; //dbgUI<<"="<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); } } 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::clearUndoHistory() { d->undoStack->clear(); } KisGridConfig KisDocument::gridConfig() const { return d->gridConfig; } void KisDocument::setGridConfig(const KisGridConfig &config) { d->gridConfig = config; } const KisGuidesConfig& KisDocument::guidesConfig() const { return d->guidesConfig; } void KisDocument::setGuidesConfig(const KisGuidesConfig &data) { if (d->guidesConfig == data) return; d->guidesConfig = data; emit sigGuidesConfigChanged(d->guidesConfig); } bool KisDocument::isEmpty() const { return d->isEmpty; } void KisDocument::setEmpty() { d->isEmpty = true; } // static int KisDocument::defaultAutoSave() { return 300; } void KisDocument::resetURL() { setUrl(QUrl()); setLocalFilePath(QString()); } int KisDocument::pageCount() const { return 1; } KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } QUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( d->document->isReadWrite() && d->document->isModified()) { Q_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 QUrl &kurl ) { if (!kurl.isValid()) { errKrita << "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 = QUrl(); d->m_originalFilePath.clear(); } return result; } bool KisDocument::save() { d->m_saveOk = false; if ( d->m_file.isEmpty() ) { // document was created empty d->prepareSaving(); } updateEditingTime(true); - 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->setFileProgressProxy(); d->document->setUrl(url()); bool ok = d->document->saveFile(); - if (progressProxy) { - d->document->setProgressProxy(0); - delete progressProxy; - } + d->document->clearFileProgressProxy(); if (ok) { return saveToUrl(); } else { emit canceled(QString()); } return false; } bool KisDocument::waitSaveComplete() { return d->m_saveOk; } void KisDocument::setUrl(const QUrl &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 = QUrl(); d->m_originalFilePath.clear(); return true; // Nothing to do } return false; } bool KisDocument::openUrlInternal(const QUrl &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(); } return false; } 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; KisImageSP 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(QUrl::fromLocalFile(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; Q_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; Q_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(); } } +void KisDocument::setFileProgressUpdater(const QString &text) +{ + d->suppressProgress = d->filterManager->getBatchMode(); + + if (!d->suppressProgress) { + d->progressUpdater = new KoProgressUpdater(d->progressProxy, KoProgressUpdater::Unthreaded); + d->progressUpdater->start(100, text); + d->filterManager->setProgresUpdater(d->progressUpdater); + + connect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); + connect(KisPart::instance()->currentMainwindow(), SIGNAL(sigProgressCanceled()), this, SIGNAL(sigProgressCanceled())); + } +} + +void KisDocument::clearFileProgressUpdater() +{ + if (!d->suppressProgress && d->progressUpdater) { + disconnect(KisPart::instance()->currentMainwindow(), SIGNAL(sigProgressCanceled()), this, SIGNAL(sigProgressCanceled())); + disconnect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); + delete d->progressUpdater; + d->filterManager->setProgresUpdater(0); + d->progressUpdater = 0; + } +} + +void KisDocument::setFileProgressProxy() +{ + if (!d->progressProxy && !d->filterManager->getBatchMode()) { + d->fileProgressProxy = progressProxy(); + } else { + d->fileProgressProxy = 0; + } +} + +void KisDocument::clearFileProgressProxy() +{ + if (d->fileProgressProxy) { + setProgressProxy(0); + delete d->fileProgressProxy; + d->fileProgressProxy = 0; + } +} + 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->setImageAndInitIdleWatcher(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); } diff --git a/libs/ui/KisDocument.h b/libs/ui/KisDocument.h index 3bfd506824..d42efa0d8a 100644 --- a/libs/ui/KisDocument.h +++ b/libs/ui/KisDocument.h @@ -1,821 +1,848 @@ /* 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. */ #ifndef KISDOCUMENT_H #define KISDOCUMENT_H #include #include #include #include #include #include #include #include #include #include #include "kritaui_export.h" class QString; class KUndo2Command; class KoUnit; class KoColor; class KoColorSpace; class KoShapeBasedDocumentBase; class KoShapeLayer; class KoStore; class KoOdfReadStore; class KoDocumentInfo; class KoProgressUpdater; class KoProgressProxy; class KoDocumentInfoDlg; class KisUndoStore; class KisPaintingAssistant; class KisPart; class KisGridConfig; class KisGuidesConfig; class QDomDocument; class KisPart; #define KIS_MIME_TYPE "application/x-krita" /** * The %Calligra document class * * This class provides some functionality each %Calligra document should have. * * @short The %Calligra document class */ class KRITAUI_EXPORT KisDocument : public QObject, public KoDocumentBase { Q_OBJECT Q_PROPERTY(bool backupFile READ backupFile WRITE setBackupFile) Q_PROPERTY(int pageCount READ pageCount) protected: explicit KisDocument(); public: enum OpenUrlFlags { OPEN_URL_FLAG_NONE = 1 << 0, OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES = 1 << 1, }; /** * Destructor. * * The destructor does not delete any attached KisView objects and it does not * delete the attached widget as returned by widget(). */ virtual ~KisDocument(); /** * @brief reload Reloads the document from the original url * @return the result of loading the document */ bool reload(); /** * @brief openUrl Open an URL * @param url The URL to open * @param flags Control specific behavior * @return success status */ bool openUrl(const QUrl &url, OpenUrlFlags flags = OPEN_URL_FLAG_NONE); /** * Opens the document given by @p url, without storing the URL * in the KisDocument. * Call this instead of openUrl() to implement KisMainWindow's * File --> Import feature. * * @note This will call openUrl(). To differentiate this from an ordinary * Open operation (in any reimplementation of openUrl() or openFile()) * call isImporting(). */ bool importDocument(const QUrl &url); /** * Saves the document as @p url without changing the state of the * KisDocument (URL, modified flag etc.). Call this instead of * KisParts::ReadWritePart::saveAs() to implement KisMainWindow's * File --> Export feature. * * @note This will call KisDocument::saveAs(). To differentiate this * from an ordinary Save operation (in any reimplementation of * saveFile()) call isExporting(). */ bool exportDocument(const QUrl &url); /** * @brief Sets whether the document can be edited or is read only. * * This recursively applied to all child documents and * KisView::updateReadWrite is called for every attached * view. */ void setReadWrite(bool readwrite = true); /** * To be preferred when a document exists. It is fast when calling * it multiple times since it caches the result that readNativeFormatMimeType() * delivers. * This comes from the X-KDE-NativeMimeType key in the .desktop file. */ QByteArray nativeFormatMimeType() const { return KIS_MIME_TYPE; } /** * Returns the OASIS OpenDocument mimetype of the document, if supported * This comes from the X-KDE-NativeOasisMimeType key in the * desktop file * * @return the oasis mimetype or, if it hasn't one, the nativeformatmimetype. */ virtual QByteArray nativeOasisMimeType() const { return ""; } /// Checks whether a given mimetype can be handled natively. bool isNativeFormat(const QByteArray& mimetype) const; /// Returns a list of the mimetypes considered "native", i.e. which can /// be saved by KisDocument without a filter, in *addition* to the main one QStringList extraNativeMimeTypes() const { return QStringList() << KIS_MIME_TYPE; } /// Enum values used by specialOutputFlag - note that it's a bitfield for supportedSpecialFormats enum { /*SaveAsCalligra1dot1 = 1,*/ // old and removed SaveAsDirectoryStore = 2, SaveAsFlatXML = 4, SaveEncrypted = 8 // bitfield! next value is 16 }; /** * Return the set of SupportedSpecialFormats that the application wants to * offer in the "Save" file dialog. */ virtual int supportedSpecialFormats() const; /** * Returns the actual mimetype of the document */ QByteArray mimeType() const; /** * @brief Sets the mime type for the document. * * When choosing "save as" this is also the mime type * selected by default. */ void setMimeType(const QByteArray & mimeType); /** * @brief Set the format in which the document should be saved. * * This is called on loading, and in "save as", so you shouldn't * have to call it. * * @param mimeType the mime type (format) to use. * @param specialOutputFlag is for "save as older version" etc. */ void setOutputMimeType(const QByteArray & mimeType, int specialOutputFlag = 0); QByteArray outputMimeType() const; int specialOutputFlag() const; /** * Returns true if this document was the result of opening a foreign * file format and if the user hasn't yet saved the document (in any * format). * * Used by KisMainWindow to warn the user when s/he lazily presses * CTRL+S to save in the same foreign format, putting all his/her * formatting at risk (normally an export confirmation only comes up * with Save As). * * @param exporting specifies whether this is the setting for a * File --> Export or File --> Save/Save As operation. */ bool confirmNonNativeSave(const bool exporting) const; void setConfirmNonNativeSave(const bool exporting, const bool on); /** - * @return true if saving/exporting should inhibit the option dialog + * @return true if file operations should inhibit the option dialog */ - bool saveInBatchMode() const; + bool fileBatchMode() const; /** - * @param batchMode if true, do not show the option dialog when saving or exporting. + * @param batchMode if true, do not show the option dialog for file operations. */ - void setSaveInBatchMode(const bool batchMode); + void setFileBatchMode(const bool batchMode); /** * Sets the error message to be shown to the user (use i18n()!) * when loading or saving fails. * If you asked the user about something and they chose "Cancel", * set the message to the magic string "USER_CANCELED", to skip the error dialog. */ void setErrorMessage(const QString& errMsg); /** * Return the last error message. Usually KisDocument takes care of * showing it; this method is mostly provided for non-interactive use. */ QString errorMessage() const; /** * Show the last error message in a message box. * The dialog box will mention a loading problem. * openUrl/openFile takes care of doing it, but not loadNativeFormat itself, * so this is often called after loadNativeFormat returned false. */ void showLoadingErrorDialog(); /** * @brief Generates a preview picture of the document * @note The preview is used in the File Dialog and also to create the Thumbnail */ QPixmap generatePreview(const QSize& size); /** * Tells the document that its title has been modified, either because * the modified status changes (this is done by setModified() ) or * because the URL or the document-info's title changed. */ void setTitleModified(); /** * @return true if the document is empty. */ virtual bool isEmpty() const; /** * @brief Sets the document to empty. * * Used after loading a template * (which is not empty, but not the user's input). * * @see isEmpty() */ void setEmpty(); /** * @brief Loads a document from a store. * * You should never have to reimplement. * * @param store The store to load from * @param url An internal url, like tar:/1/2 */ bool loadFromStore(KoStore *store, const QString& url); /// Unused virtual bool loadOdf(KoOdfReadStore & odfStore); /// Unused virtual bool saveOdf(SavingContext &documentContext); /** * @brief Saves a sub-document to a store. * * You should not have to reimplement this. */ virtual bool saveToStore(KoStore *store, const QString& path); /** * Reimplement this method to load the contents of your Calligra document, * from the XML document. This is for the pre-Oasis file format (maindoc.xml). */ virtual bool loadXML(const KoXmlDocument & doc, KoStore *store); /** * Reimplement this to save the contents of the %Calligra document into * a QDomDocument. The framework takes care of saving it to the store. */ QDomDocument saveXML(); /** * Return a correctly created QDomDocument for this KisDocument, * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element. * @param tagName the name of the tag for the root element * @param version the DTD version (usually the application's version). */ QDomDocument createDomDocument(const QString& tagName, const QString& version) const; /** * Return a correctly created QDomDocument for an old (1.3-style) %Calligra document, * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element. * This static method can be used e.g. by filters. * @param appName the app's instance name, e.g. words, kspread, kpresenter etc. * @param tagName the name of the tag for the root element, e.g. DOC for words/kpresenter. * @param version the DTD version (usually the application's version). */ static QDomDocument createDomDocument(const QString& appName, const QString& tagName, const QString& version); /** * The first thing to do in loadOasis is get hold of the office:body tag, then its child. * If the child isn't the expected one, the error message can indicate what it is instead. * This method returns a translated name for the type of document, * e.g. i18n("Word Processing") for office:text. */ static QString tagNameToDocumentType(const QString& localName); /** * Loads a document in the native format from a given URL. * Reimplement if your native format isn't XML. * * @param file the file to load - usually KReadOnlyPart::m_file or the result of a filter */ bool loadNativeFormat(const QString & file); /** * Saves the document in native format, to a given file * You should never have to reimplement. * Made public for writing templates. */ bool saveNativeFormat(const QString & file); /** * Saves the document in the native format to the given store. */ bool saveNativeFormatCalligra(KoStore *store); /** * Activate/deactivate/configure the autosave feature. * @param delay in seconds, 0 to disable */ void setAutoSave(int delay); /** * Checks whether the document is currently in the process of autosaving */ bool isAutosaving() const; /** * Set whether the next openUrl call should check for an auto-saved file * and offer to open it. This is usually true, but can be turned off * (e.g. for the preview module). This only checks for names auto-saved * files, unnamed auto-saved files are only checked on KisApplication startup. */ void setCheckAutoSaveFile(bool b); /** * Set whether the next openUrl call should show error message boxes in case * of errors. This is usually the case, but e.g. not when generating thumbnail * previews. */ void setAutoErrorHandlingEnabled(bool b); /** * Checks whether error message boxes should be shown. */ bool isAutoErrorHandlingEnabled() const; /** * Retrieve the default value for autosave in seconds. * Called by the applications to use the correct default in their config */ static int defaultAutoSave(); /** * @return the information concerning this document. * @see KoDocumentInfo */ KoDocumentInfo *documentInfo() const; /** * @return the object to report progress to. * * This is only not zero if loading or saving is in progress. * * One can add more KoUpdaters to it to make the progress reporting more * accurate. If no active progress reporter is present, 0 is returned. **/ KoProgressUpdater *progressUpdater() const; /** * Set a custom progress proxy to use to report loading * progress to. */ void setProgressProxy(KoProgressProxy *progressProxy); KoProgressProxy* progressProxy() const; /** * Return true if url() is a real filename, false if url() is * an internal url in the store, like "tar:/..." */ virtual bool isStoredExtern() const; /** * @return the page layout associated with this document (margins, pageSize, etc). * Override this if you want to provide different sized pages. * * @see KoPageLayout */ KoPageLayout pageLayout(int pageNumber = 0) const; void setPageLayout(const KoPageLayout &pageLayout); /** * Performs a cleanup of unneeded backup files */ void removeAutoSaveFiles(); void setBackupFile(bool _b); bool backupFile()const; /** * Returns true if this document or any of its internal child documents are modified. */ bool isModified() const; /** * Returns true during loading (openUrl can be asynchronous) */ bool isLoading() const; /** * Sets the backup path of the document */ void setBackupPath(const QString & _path); /** * @return path to the backup document */ QString backupPath()const; /** * @return caption of the document * * Caption is of the form "[title] - [url]", * built out of the document info (title) and pretty-printed * document URL. * If the title is not present, only the URL it returned. */ QString caption() const; /** * Sets the document URL to empty URL * KParts doesn't allow this, but %Calligra apps have e.g. templates * After using loadNativeFormat on a template, one wants * to set the url to QUrl() */ void resetURL(); /** * Set when you want an external embedded document to be stored internally */ void setStoreInternal(bool i); /** * @return true when external embedded documents are stored internally */ bool storeInternal() const; bool hasExternURL() const; /** * @internal (public for KisMainWindow) */ void setMimeTypeAfterLoading(const QString& mimeType); /** * @return returns the number of pages in the document. */ virtual int pageCount() const; /** * Returns the unit used to display all measures/distances. */ KoUnit unit() const; /** * Sets the unit used to display all measures/distances. */ void setUnit(const KoUnit &unit); /** * Save the unit to the settings writer * * @param settingsWriter */ bool loadNativeFormatFromByteArray(QByteArray &data); KisGridConfig gridConfig() const; void setGridConfig(const KisGridConfig &config); /// returns the guides data for this document. const KisGuidesConfig& guidesConfig() const; void setGuidesConfig(const KisGuidesConfig &data); void clearUndoHistory(); /** * Sets the modified flag on the document. This means that it has * to be saved or not before deleting it. */ void setModified(bool _mod); void updateEditingTime(bool forceStoreElapsed); /** * Initialize an empty document using default values */ void initEmpty(); /** * Returns the global undo stack */ KUndo2Stack *undoStack(); + public Q_SLOTS: /** * Adds a command to the undo stack and executes it by calling the redo() function. * @param command command to add to the undo stack */ void addCommand(KUndo2Command *command); /** * Begins recording of a macro command. At the end endMacro needs to be called. * @param text command description */ void beginMacro(const KUndo2MagicString &text); /** * Ends the recording of a macro command. */ void endMacro(); Q_SIGNALS: /** * This signal is emitted when the unit is changed by setUnit(). * It is common to connect views to it, in order to change the displayed units * (e.g. in the rulers) */ void unitChanged(const KoUnit &unit); /** * Progress info while loading or saving. The value is in percents (i.e. a number between 0 and 100) * Your KisDocument-derived class should emit the signal now and then during load/save. * KisMainWindow will take care of displaying a progress bar automatically. */ void sigProgress(int value); + /** + * Progress cancel button pressed + * This is emitted by KisDocument + */ + void sigProgressCanceled(); + /** * Emitted e.g. at the beginning of a save operation * This is emitted by KisDocument and used by KisView to display a statusbar message */ void statusBarMessage(const QString& text); /** * Emitted e.g. at the end of a save operation * This is emitted by KisDocument and used by KisView to clear the statusbar message */ void clearStatusBarMessage(); /** * Emitted when the document is modified */ void modified(bool); void titleModified(const QString &caption, bool isModified); void sigLoadingFinished(); void sigSavingFinished(); void sigGuidesConfigChanged(const KisGuidesConfig &config); private: friend class KisPart; /** * Generate a name for the document. */ QString newObjectName(); QString autoSaveFile(const QString & path) const; void setDisregardAutosaveFailure(bool disregardFailure); /** * Loads a document * * Applies a filter if necessary, and calls loadNativeFormat in any case * You should not have to reimplement, except for very special cases. * * NOTE: this method also creates a new KisView instance! * * This method is called from the KReadOnlyPart::openUrl method. */ bool openFile(); /** * Saves a document * * Applies a filter if necessary, and calls saveNativeFormat in any case * You should not have to reimplement, except for very special cases. */ bool saveFile(); /** * Overload this function if you have to load additional files * from a store. This function is called after loadXML() * and after loadChildren() have been called. */ bool completeLoading(KoStore *store); /** * If you want to write additional files to a store, * then you must do it here. * In the implementation, you should prepend the document * url (using url().url()) before the filename, so that everything is kept relative * to this document. For instance it will produce urls such as * tar:/1/pictures/picture0.png, if the doc url is tar:/1 * But do this ONLY if the document is not stored extern (see isStoredExtern() ). * If it is, then the pictures should be saved to tar:/pictures. */ bool completeSaving(KoStore *store); /** @internal */ void setModified(); /** * Returns whether or not the current openUrl() or openFile() call is * actually an import operation (like File --> Import). * This is for informational purposes only. */ bool isImporting() const; /** * Returns whether or not the current saveFile() call is actually an export * operation (like File --> Export). * If this function returns true during saveFile() and you are changing * some sort of state, you _must_ restore it before the end of saveFile(); * otherwise, File --> Export will not work properly. */ bool isExporting() const; public: QString localFilePath() const; void setLocalFilePath( const QString &localFilePath ); KoDocumentInfoDlg* createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const; bool isReadWrite() const; QUrl url() const; void setUrl(const QUrl &url); bool closeUrl(bool promptToSave = true); bool saveAs( const QUrl &url ); public Q_SLOTS: bool save(); bool waitSaveComplete(); Q_SIGNALS: void completed(); void canceled(const QString &); private Q_SLOTS: void setImageModified(); void slotAutoSave(); /// Called by the undo stack when undo or redo is called void slotUndoStackIndexChanged(int idx); protected: bool oldLoadAndParse(KoStore *store, const QString& filename, KoXmlDocument& doc); public: /** * Create a new image that has this document as a parent and * replace the current image with this image. */ bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, const QString &imageDescription, const double imageResolution); /** * Create a new image that has this document as a parent and * replace the current image with this image. */ bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &imageDescription, const double imageResolution); /** * Create a new image that has this document as a parent and * replace the current image with this image. */ KisImageWSP newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * colorspace); KisImageWSP image() const; /** * Makes an otherwise empty document ready for import/export */ void prepareForImport(); + /** + * Adds progressproxy for file operations + */ + void setFileProgressProxy(); + + /** + * Clears progressproxy for file operations + */ + void clearFileProgressProxy(); + + /** + * Adds progressupdater for file operations + */ + void setFileProgressUpdater(const QString &text); + + /** + * Clears progressupdater for file operations + */ + void clearFileProgressUpdater(); + /** * Set the current image to the specified image and turn undo on. */ void setCurrentImage(KisImageWSP image); KisUndoStore* createUndoStore(); /** * The shape controller matches internal krita image layers with * the flake shape hierarchy. */ KoShapeBasedDocumentBase * shapeController() const; KoShapeLayer* shapeForNode(KisNodeSP layer) const; /** * @return a list of all layers that are active in all current views */ vKisNodeSP activeNodes() const; /** * set the list of nodes that were marked as currently active */ void setPreActivatedNode(KisNodeSP activatedNode); /** * @return the node that was set as active during loading */ KisNodeSP preActivatedNode() const; /** *@return a list of all the assistants in all current views */ QList assistants(); /** * @return a list of assistants loaded from a document */ QList preLoadedAssistants(); private: void init(); bool saveToStream(QIODevice *dev); QString checkImageMimeTypes(const QString &mimeType, const QUrl &url) const; bool loadNativeFormatFromStoreInternal(KoStore *store); bool savePreview(KoStore *store); QString prettyPathOrUrl() const; bool saveToUrl(); bool openUrlInternal(const QUrl &url); class Private; Private *const d; }; Q_DECLARE_METATYPE(KisDocument*) #endif diff --git a/plugins/impex/psd/psd_saver.h b/libs/ui/KisImageBuilderResult.h similarity index 67% copy from plugins/impex/psd/psd_saver.h copy to libs/ui/KisImageBuilderResult.h index bc81bbb304..bbdab8e5d7 100644 --- a/plugins/impex/psd/psd_saver.h +++ b/libs/ui/KisImageBuilderResult.h @@ -1,76 +1,44 @@ /* - * Copyright (c) 2009 Boudewijn Rempt + * Copyright (c) 2016 Laszlo Fazekas * * 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. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ -#ifndef _PSD_CONVERTER_H_ -#define _PSD_CONVERTER_H_ -#include - -#include - -#include - -#include "kis_types.h" -class KisDocument; +#ifndef KIS_IMAGEBUILDER_RESULT_H +#define KIS_IMAGEBUILDER_RESULT_H /** * Image import/export plugins can use these results to report about success or failure. */ enum KisImageBuilder_Result { KisImageBuilder_RESULT_FAILURE = -400, KisImageBuilder_RESULT_NOT_EXIST = -300, KisImageBuilder_RESULT_NOT_LOCAL = -200, KisImageBuilder_RESULT_BAD_FETCH = -100, KisImageBuilder_RESULT_INVALID_ARG = -50, KisImageBuilder_RESULT_OK = 0, KisImageBuilder_RESULT_PROGRESS = 1, + KisImageBuilder_RESULT_CANCEL = 50, KisImageBuilder_RESULT_EMPTY = 100, KisImageBuilder_RESULT_BUSY = 150, KisImageBuilder_RESULT_NO_URI = 200, KisImageBuilder_RESULT_UNSUPPORTED = 300, KisImageBuilder_RESULT_INTR = 400, KisImageBuilder_RESULT_PATH = 500, KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 - }; - -class PSDSaver : public QObject { - - Q_OBJECT - -public: - - PSDSaver(KisDocument *doc); - virtual ~PSDSaver(); - -public: - - KisImageBuilder_Result buildFile(const QUrl &uri); - - KisImageWSP image(); - -public Q_SLOTS: - - virtual void cancel(); - -private: - - KisImageWSP m_image; - KisDocument *m_doc; - bool m_stop; }; #endif diff --git a/libs/ui/KisImportExportFilter.cpp b/libs/ui/KisImportExportFilter.cpp index c3a11600ff..c1ebc95c2f 100644 --- a/libs/ui/KisImportExportFilter.cpp +++ b/libs/ui/KisImportExportFilter.cpp @@ -1,74 +1,147 @@ /* This file is part of the KDE libraries Copyright (C) 2001 Werner Trobin 2002 Werner Trobin 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 "KisImportExportFilter.h" #include #include #include #include #include #include "KisImportExportManager.h" #include "KoUpdater.h" +#include class Q_DECL_HIDDEN KisImportExportFilter::Private { public: QPointer updater; Private() : updater(0) {} }; KisImportExportFilter::KisImportExportFilter(QObject *parent) : QObject(parent) , m_chain(0) , d(new Private) { } KisImportExportFilter::~KisImportExportFilter() { Q_ASSERT(d->updater); if (d->updater) d->updater->setProgress(100); delete d; } +QString KisImportExportFilter::conversionStatusString(ConversionStatus status) +{ + QString msg; + switch (status) { + case OK: break; + + case FilterCreationError: + msg = i18n("Could not create the filter plugin"); break; + + case CreationError: + msg = i18n("Could not create the output document"); break; + + case FileNotFound: + msg = i18n("File not found"); break; + + case StorageCreationError: + msg = i18n("Cannot create storage"); break; + + case BadMimeType: + msg = i18n("Bad MIME type"); break; + + case EmbeddedDocError: + msg = i18n("Error in embedded document"); break; + + case WrongFormat: + msg = i18n("Format not recognized"); break; + + case NotImplemented: + msg = i18n("Not implemented"); break; + + case ParsingError: + msg = i18n("Parsing error"); break; + + case PasswordProtected: + msg = i18n("Document is password protected"); break; + + case InvalidFormat: + msg = i18n("Invalid file format"); break; + + case InternalError: + case UnexpectedEOF: + case UnexpectedOpcode: + case StupidError: // ?? what is this ?? + case UsageError: + msg = i18n("Internal error"); break; + + case OutOfMemory: + msg = i18n("Out of memory"); break; + + case FilterEntryNull: + msg = i18n("Empty Filter Plugin"); break; + + case NoDocumentCreated: + msg = i18n("Trying to load into the wrong kind of document"); break; + + case DownloadFailed: + msg = i18n("Failed to download remote file"); break; + + case ProgressCancelled: + msg = i18n("Cancelled by user"); break; + + case UserCancelled: + case BadConversionGraph: + // intentionally we do not prompt the error message here + break; + + default: msg = i18n("Unknown error"); break; + } + return msg; +} + void KisImportExportFilter::setUpdater(const QPointer& updater) { Q_ASSERT(updater); if (d->updater && !updater) { disconnect(this, SLOT(slotProgress(int))); } else if (!d->updater && updater) { connect(this, SIGNAL(sigProgress(int)), SLOT(slotProgress(int))); } d->updater = updater; } void KisImportExportFilter::slotProgress(int value) { Q_ASSERT(d->updater); if (d->updater) { d->updater->setValue(value); } } + #include diff --git a/libs/ui/KisImportExportFilter.h b/libs/ui/KisImportExportFilter.h index e364a36caf..475491e748 100644 --- a/libs/ui/KisImportExportFilter.h +++ b/libs/ui/KisImportExportFilter.h @@ -1,133 +1,140 @@ /* This file is part of the Calligra libraries Copyright (C) 2001 Werner Trobin 2002 Werner Trobin 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. */ #ifndef KIS_IMPORT_EXPORT_FILTER_H #define KIS_IMPORT_EXPORT_FILTER_H #include #include #include +#include #include "kritaui_export.h" class KisFilterChain; class KoUpdater; /** * @brief The base class for import and export filters. * * Derive your filter class from this base class and implement * the @ref convert() method. Don't forget to specify the Q_OBJECT * macro in your class even if you don't use signals or slots. * This is needed as filters are created on the fly. * The m_chain member allows access to the @ref KisFilterChain * which invokes the filter to query for input/output. * * @note Take care: The m_chain pointer is invalid while the constructor * runs due to the implementation -- @em don't use it in the constructor. * After the constructor, when running the @ref convert() method it's * guaranteed to be valid, so no need to check against 0. * * @note If the code is compiled in debug mode, setting CALLIGRA_DEBUG_FILTERS * environment variable to any value disables deletion of temporary files while * importing/exporting. This is useful for testing purposes. * * @author Werner Trobin * @todo the class has no constructor and therefore cannot initialize its private class */ class KRITAUI_EXPORT KisImportExportFilter : public QObject { Q_OBJECT friend class KisFilterEntry; // needed for the filter chain pointer :( friend class KisFilterChain; public: /** * This enum is used to signal the return state of your filter. * Return OK in @ref convert() in case everything worked as expected. * Feel free to add some more error conditions @em before the last item * if it's needed. */ enum ConversionStatus { OK, StupidError, UsageError, CreationError, FileNotFound, StorageCreationError, BadMimeType, BadConversionGraph, EmbeddedDocError, WrongFormat, NotImplemented, ParsingError, InternalError, UnexpectedEOF, UnexpectedOpcode, UserCancelled, OutOfMemory, PasswordProtected, InvalidFormat, FilterEntryNull, NoDocumentCreated, DownloadFailed, FilterCreationError, + ProgressCancelled, JustInCaseSomeBrokenCompilerUsesLessThanAByte = 255 }; virtual ~KisImportExportFilter(); /** * The filter chain calls this method to perform the actual conversion. * The passed mimetypes should be a pair of those you specified in your * .desktop file. * You @em have to implement this method to make the filter work. * * @param from The mimetype of the source file/document * @param to The mimetype of the destination file/document * @return The error status, see the @ref #ConversionStatus enum. * KisImportExportFilter::OK means that everything is alright. */ virtual ConversionStatus convert(const QByteArray& from, const QByteArray& to) = 0; /** * Set the updater to which the filter will report progress. * Every emit of the sigProgress signal is reported to the updater. */ void setUpdater(const QPointer& updater); + /** + * Get the text version of the status value + */ + static QString conversionStatusString(ConversionStatus status); + Q_SIGNALS: /** * Emit this signal with a value in the range of 1...100 to have some * progress feedback for the user in the statusbar of the application. * * @param value The actual progress state. Should always remain in * the range 1..100. */ void sigProgress(int value); protected: /** * This is the constructor your filter has to call, obviously. */ KisImportExportFilter(QObject *parent = 0); /** * Use this pointer to access all information about input/output * during the conversion. @em Don't use it in the constructor - * it's invalid while constructing the object! */ KisFilterChain *m_chain; private: KisImportExportFilter(const KisImportExportFilter& rhs); KisImportExportFilter& operator=(const KisImportExportFilter& rhs); class Private; Private *const d; private Q_SLOTS: void slotProgress(int value); }; #endif diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp index 9c2c12e61b..5d97333332 100644 --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -1,2360 +1,2406 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port 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" #include // qt includes #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 #ifdef HAVE_KIO #include #endif #include #include #include #include #include #include #include #include #include #include #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoDocumentInfoDlg.h" #include "KoDocumentInfo.h" #include "KoFileDialog.h" #include #include #include #include #include #include "KoToolDocker.h" #include #include #include #include #include "KisView.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPrintJob.h" #include "KisPart.h" #include "KisApplication.h" #include "kis_action.h" #include "kis_canvas_controller.h" #include "kis_canvas2.h" #include "KisViewManager.h" #include "KisDocument.h" #include "dialogs/kis_dlg_preferences.h" #include "kis_config_notifier.h" #include "kis_canvas_resource_provider.h" #include "kis_node.h" #include "kis_image.h" #include "kis_group_layer.h" #include #include "kis_paintop_box.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "dialogs/kis_about_application.h" #include "kis_mainwindow_observer.h" #include "kis_action_manager.h" #include "thememanager.h" #include "kis_resource_server_provider.h" #ifdef HAVE_OPENGL #include "kis_animation_importer.h" #include "dialogs/kis_dlg_import_image_sequence.h" #include "kis_animation_exporter.h" #endif #include "kis_icon_utils.h" #include #include #include "kis_signal_compressor_with_param.h" #include "dialogs/kis_delayed_save_dialog.h" class ToolDockerFactory : public KoDockFactoryBase { public: ToolDockerFactory() : KoDockFactoryBase() { } QString id() const { return "sharedtooldocker"; } QDockWidget* createDockWidget() { KoToolDocker* dockWidget = new KoToolDocker(); dockWidget->setTabEnabled(false); return dockWidget; } DockPosition defaultDockPosition() const { return DockRight; } }; class Q_DECL_HIDDEN KisMainWindow::Private { public: Private(KisMainWindow *parent) : q(parent) , viewManager(0) , firstTime(true) , windowSizeDirty(false) , readOnly(false) , isImporting(false) , isExporting(false) , noCleanup(false) , showDocumentInfo(0) , saveAction(0) , saveActionAs(0) , printAction(0) , printActionPreview(0) , exportPdf(0) , closeAll(0) // , reloadFile(0) , importFile(0) , exportFile(0) , undo(0) , redo(0) , newWindow(0) , close(0) , mdiCascade(0) , mdiTile(0) , mdiNextWindow(0) , mdiPreviousWindow(0) , toggleDockers(0) , toggleDockerTitleBars(0) , dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent)) , windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent)) , documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent)) , helpMenu(0) , brushesAndStuff(0) , recentFiles(0) , toolOptionsDocker(0) , deferredClosingEvent(0) , themeManager(0) , mdiArea(new QMdiArea(parent)) , activeSubWindow(0) , windowMapper(new QSignalMapper(parent)) , documentMapper(new QSignalMapper(parent)) , lastExportSpecialOutputFlag(0) { } ~Private() { qDeleteAll(toolbarList); } KisMainWindow *q; KisViewManager *viewManager; QPointer activeView; QPointer progress; + QPointer progressCancel; QMutex progressMutex; QList toolbarList; bool firstTime; bool windowSizeDirty; bool readOnly; bool isImporting; bool isExporting; bool noCleanup; KisAction *showDocumentInfo; KisAction *saveAction; KisAction *saveActionAs; KisAction *printAction; KisAction *printActionPreview; KisAction *exportPdf; #ifdef HAVE_OPENGL KisAction *importAnimation; KisAction *exportAnimation; #endif KisAction *closeAll; // KisAction *reloadFile; KisAction *importFile; KisAction *exportFile; KisAction *undo; KisAction *redo; KisAction *newWindow; KisAction *close; KisAction *mdiCascade; KisAction *mdiTile; KisAction *mdiNextWindow; KisAction *mdiPreviousWindow; KisAction *toggleDockers; KisAction *toggleDockerTitleBars; KisAction *expandingSpacers[2]; KActionMenu *dockWidgetMenu; KActionMenu *windowMenu; KActionMenu *documentMenu; KHelpMenu *helpMenu; KToolBar *brushesAndStuff; KRecentFilesAction *recentFiles; QUrl lastExportUrl; QMap dockWidgetsMap; QMap dockWidgetVisibilityMap; QByteArray dockerStateBeforeHiding; KoToolDocker *toolOptionsDocker; QCloseEvent *deferredClosingEvent; Digikam::ThemeManager *themeManager; QMdiArea *mdiArea; QMdiSubWindow *activeSubWindow; QSignalMapper *windowMapper; QSignalMapper *documentMapper; QByteArray lastExportedFormat; int lastExportSpecialOutputFlag; QScopedPointer > tabSwitchCompressor; KisActionManager * actionManager() { return viewManager->actionManager(); } QTabBar* findTabBarHACK() { QObjectList objects = mdiArea->children(); Q_FOREACH (QObject *object, objects) { QTabBar *bar = qobject_cast(object); if (bar) { return bar; } } return 0; } }; KisMainWindow::KisMainWindow() : KXmlGuiWindow() , d(new Private(this)) { KisConfig cfg; d->viewManager = new KisViewManager(this, actionCollection()); KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this); setAcceptDrops(true); setStandardToolBarMenuEnabled(true); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); setDockNestingEnabled(true); qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events #ifdef Q_OS_MAC setUnifiedTitleAndToolBarOnMac(true); #endif connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts())); connect(this, SIGNAL(documentSaved()), d->viewManager, SLOT(slotDocumentSaved())); connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons())); connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu())); connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged())); actionCollection()->addAssociatedWidget(this); QMetaObject::invokeMethod(this, "initializeGeometry", Qt::QueuedConnection); KoToolBoxFactory toolBoxFactory; QDockWidget *toolbox = createDockWidget(&toolBoxFactory); toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); if (cfg.toolOptionsInDocker()) { ToolDockerFactory toolDockerFactory; d->toolOptionsDocker = qobject_cast(createDockWidget(&toolDockerFactory)); } QMap dockwidgetActions; dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction(); Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) { KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker); QDockWidget *dw = createDockWidget(factory); dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction(); } Q_FOREACH (QString title, dockwidgetActions.keys()) { d->dockWidgetMenu->addAction(dockwidgetActions[title]); } Q_FOREACH (QDockWidget *wdg, dockWidgets()) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { wdg->setVisible(true); } } Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) { observer->setObservedCanvas(0); KisMainwindowObserver* mainwindowObserver = dynamic_cast(observer); if (mainwindowObserver) { mainwindowObserver->setMainWindow(d->viewManager); } } d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setTabPosition(QTabWidget::North); d->mdiArea->setTabsClosable(true); setCentralWidget(d->mdiArea); connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated())); connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*))); createActions(); setAutoSaveSettings("krita", false); KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), viewManager()); subWindowActivated(); updateWindowMenu(); if (isHelpMenuEnabled() && !d->helpMenu) { d->helpMenu = new KHelpMenu(this, "Dummy Text That Is Not Used In Frameworks 5", false); // The difference between using KActionCollection->addAction() is that // these actions do not get tied to the MainWindow. What does this all do? KActionCollection *actions = d->viewManager->actionCollection(); QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication())); } // KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves QAction *helpAction = actionCollection()->action("help_contents"); helpAction->disconnect(); connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual())); #if 0 //check for colliding shortcuts QSet existingShortcuts; Q_FOREACH (QAction* action, actionCollection()->actions()) { if(action->shortcut() == QKeySequence(0)) { continue; } dbgKrita << "shortcut " << action->text() << " " << action->shortcut(); Q_ASSERT(!existingShortcuts.contains(action->shortcut())); existingShortcuts.insert(action->shortcut()); } #endif configChanged(); // If we have customized the toolbars, load that first setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita/krita.rc")); QString doc; QStringList allFiles = KoResourcePaths::findAllResources("data", "krita/krita.rc"); // We need at least one krita.rc file! if (allFiles.size() == 0) { m_errorMessage = i18n("Krita cannot find the configuration file! Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } setXMLFile(findMostRecentXMLFile(allFiles, doc)); guiFactory()->addClient(this); // Create and plug toolbar list for Settings menu QList toolbarList; Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) { KToolBar * toolBar = ::qobject_cast(it); if (toolBar) { if (toolBar->objectName() == "BrushesAndStuff") { toolBar->setEnabled(false); } KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!toolBar->isHidden()); toolbarList.append(act); } else warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } plugActionList("toolbarlist", toolbarList); setToolbarList(toolbarList); applyToolBarLayout(); d->viewManager->updateGUI(); d->viewManager->updateIcons(); QTimer::singleShot(1000, this, SLOT(checkSanity())); { using namespace std::placeholders; // For _1 placeholder std::function callback( std::bind(&KisMainWindow::switchTab, this, _1)); d->tabSwitchCompressor.reset( new KisSignalCompressorWithParam(500, callback, KisSignalCompressor::FIRST_INACTIVE)); } } void KisMainWindow::setNoCleanup(bool noCleanup) { d->noCleanup = noCleanup; } KisMainWindow::~KisMainWindow() { // Q_FOREACH (QAction *ac, actionCollection()->actions()) { // QAction *action = qobject_cast(ac); // if (action) { // dbgKrita << "", "").replace("", "") // << "iconText=" << action->iconText().replace("&", "&") // << "shortcut=" << action->shortcut(QAction::ActiveShortcut).toString() // << "defaultShortcut=" << action->shortcut(QAction::DefaultShortcut).toString() // << "isCheckable=" << QString((action->isChecked() ? "true" : "false")) // << "statusTip=" << action->statusTip() // << "/>" ; // } // else { // dbgKrita << "Got a QAction:" << ac->objectName(); // } // } // The doc and view might still exist (this is the case when closing the window) KisPart::instance()->removeMainWindow(this); if (d->noCleanup) return; delete d->viewManager; delete d; } void KisMainWindow::addView(KisView *view) { if (d->activeView == view) return; if (d->activeView) { d->activeView->disconnect(this); } showView(view); updateCaption(); emit restoringDone(); if (d->activeView) { connect(d->activeView, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified(QString,bool))); } } void KisMainWindow::showView(KisView *imageView) { if (imageView && activeView() != imageView) { // XXX: find a better way to initialize this! imageView->setViewManager(d->viewManager); imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager()); imageView->slotLoadingFinished(); QMdiSubWindow *subwin = d->mdiArea->addSubWindow(imageView); subwin->setAttribute(Qt::WA_DeleteOnClose, true); connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu())); KisConfig cfg; subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setWindowIcon(qApp->windowIcon()); if (d->mdiArea->subWindowList().size() == 1) { imageView->showMaximized(); } else { imageView->show(); } setActiveView(imageView); updateWindowMenu(); updateCaption(); } } void KisMainWindow::slotPreferences() { if (KisDlgPreferences::editPreferences()) { KisConfigNotifier::instance()->notifyConfigChanged(); // XXX: should this be changed for the views in other windows as well? Q_FOREACH (QPointer koview, KisPart::instance()->views()) { KisViewManager *view = qobject_cast(koview); if (view) { // Update the settings for all nodes -- they don't query // KisConfig directly because they need the settings during // compositing, and they don't connect to the config notifier // because nodes are not QObjects (because only one base class // can be a QObject). KisNode* node = dynamic_cast(view->image()->rootLayer().data()); node->updateSettings(); } } d->viewManager->showHideScrollbars(); } } void KisMainWindow::slotThemeChanged() { // save theme changes instantly KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); // reload action icons! Q_FOREACH (QAction *action, actionCollection()->actions()) { KisIconUtils::updateIcon(action); } emit themeChanged(); } void KisMainWindow::updateReloadFileAction(KisDocument *doc) { Q_UNUSED(doc); // d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KisMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KisMainWindow::addRecentURL(const QUrl &url) { dbgUI << "KisMainWindow::addRecentURL url=" << url.toDisplayString(); // Add entry to recent documents list // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp"); for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) if (path.contains(*it)) ok = false; // it's in the tmp resource #ifdef HAVE_KIO if (ok) { KRecentDocument::add(path); } #endif } #ifdef HAVE_KIO else { KRecentDocument::add(url.url(QUrl::StripTrailingSlash), true); } #endif if (ok) { d->recentFiles->addUrl(url); } saveRecentFiles(); } } void KisMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = KSharedConfig::openConfig(); d->recentFiles->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start Q_FOREACH (KMainWindow* window, KMainWindow::memberList()) static_cast(window)->reloadRecentFileList(); } void KisMainWindow::reloadRecentFileList() { d->recentFiles->loadEntries( KSharedConfig::openConfig()->group("RecentFiles")); } void KisMainWindow::updateCaption() { if (!d->mdiArea->activeSubWindow()) { updateCaption(QString(), false); } else { QString caption( d->activeView->document()->caption() ); if (d->readOnly) { caption += ' ' + i18n("(write protected)"); } d->activeView->setWindowTitle(caption); updateCaption(caption, d->activeView->document()->isModified()); if (!d->activeView->document()->url().fileName().isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", d->activeView->document()->url().fileName())); else d->saveAction->setToolTip(i18n("Save")); } } void KisMainWindow::updateCaption(const QString & caption, bool mod) { dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")"; #ifdef CALLIGRA_ALPHA setCaption(QString("ALPHA %1: %2").arg(CALLIGRA_ALPHA).arg(caption), mod); return; #endif #ifdef CALLIGRA_BETA setCaption(QString("BETA %1: %2").arg(CALLIGRA_BETA).arg(caption), mod); return; #endif #ifdef CALLIGRA_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(CALLIGRA_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KisView *KisMainWindow::activeView() const { if (d->activeView) { return d->activeView; } return 0; } bool KisMainWindow::openDocument(const QUrl &url) { if (!QFile(url.toLocalFile()).exists()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url())); d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url); } bool KisMainWindow::openDocumentInternal(const QUrl &url, KisDocument *newdoc) { if (!url.isLocalFile()) { qDebug() << "KisMainWindow::openDocumentInternal. Not a local file:" << url; return false; } if (!newdoc) { newdoc = KisPart::instance()->createDocument(); } d->firstTime = true; connect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); connect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); bool openRet = (!isImporting()) ? newdoc->openUrl(url) : newdoc->importDocument(url); if (!openRet) { delete newdoc; return false; } KisPart::instance()->addDocument(newdoc); updateReloadFileAction(newdoc); if (!QFileInfo(url.toLocalFile()).isWritable()) { setReadWrite(false); } return true; } void KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document) { KisView *view = KisPart::instance()->createView(document, resourceManager(), actionCollection(), this); addView(view); emit guiLoadingFinished(); } QStringList KisMainWindow::showOpenFileDialog() { KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument"); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KIS_MIME_TYPE, KisImportExportManager::Import, KisDocumentEntry::extraNativeMimeTypes())); QStringList filters = dialog.nameFilters(); filters << i18n("All files (*.*)"); dialog.setNameFilters(filters); dialog.setHideNameFilterDetailsOption(); dialog.setCaption(isImporting() ? i18n("Import Images") : i18n("Open Images")); return dialog.filenames(); } // Separate from openDocument to handle async loading (remote URLs) void KisMainWindow::slotLoadCompleted() { KisDocument *newdoc = qobject_cast(sender()); addViewAndNotifyLoadingCompleted(newdoc); disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); emit loadCompleted(); } void KisMainWindow::slotLoadCanceled(const QString & errMsg) { dbgUI << "KisMainWindow::slotLoadCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); // ... can't delete the document, it's the one who emitted the signal... KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); } void KisMainWindow::slotSaveCanceled(const QString &errMsg) { dbgUI << "KisMainWindow::slotSaveCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); slotSaveCompleted(); } void KisMainWindow::slotSaveCompleted() { dbgUI << "KisMainWindow::slotSaveCompleted"; KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); if (d->deferredClosingEvent) { KXmlGuiWindow::closeEvent(d->deferredClosingEvent); } } bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool silent, int specialOutputFlag) { if (!document) { return true; } KisDelayedSaveDialog dlg(document->image(), this); dlg.blockIfImageIsBusy(); bool reset_url; if (document->url().isEmpty()) { reset_url = true; saveas = true; } else { reset_url = false; } connect(document, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(document, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); QUrl oldURL = document->url(); QString oldFile = document->localFilePath(); QByteArray _native_format = document->nativeFormatMimeType(); QByteArray oldOutputFormat = document->outputMimeType(); int oldSpecialOutputFlag = document->specialOutputFlag(); QUrl suggestedURL = document->url(); QStringList mimeFilter; QMimeDatabase db; QMimeType mime = db.mimeTypeForName(_native_format); if (specialOutputFlag) { mimeFilter = mime.globPatterns(); } else { mimeFilter = KisImportExportManager::mimeFilter(_native_format, KisImportExportManager::Export, document->extraNativeMimeTypes()); } if (!mimeFilter.contains(oldOutputFormat) && !isExporting()) { dbgUI << "KisMainWindow::saveDocument no export filter for" << oldOutputFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = suggestedURL.fileName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name int c = suggestedFilename.lastIndexOf('.'); const QString ext = mime.preferredSuffix(); if (!ext.isEmpty()) { if (c < 0) suggestedFilename += ext; else suggestedFilename = suggestedFilename.left(c) + ext; } else { // current filename extension wrong anyway if (c > 0) { // this assumes that a . signifies an extension, not just a . suggestedFilename = suggestedFilename.left(c); } } suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename); suggestedURL.setPath(suggestedURL.path() + suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (document->url().isEmpty() || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("untitled")); if (isExporting() && !d->lastExportUrl.isEmpty()) { dialog.setDefaultDir(d->lastExportUrl.toLocalFile(), true); } else { dialog.setDefaultDir(suggestedURL.toLocalFile(), true); } // Default to all supported file types if user is exporting, otherwise use Krita default dialog.setMimeTypeFilters(mimeFilter, isExporting() ? "" : KIS_MIME_TYPE); QUrl newURL = QUrl::fromUserInput(dialog.filename()); if (newURL.isLocalFile()) { QString fn = newURL.toLocalFile(); if (QFileInfo(fn).completeSuffix().isEmpty()) { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(_native_format); fn.append(mime.preferredSuffix()); newURL = QUrl::fromLocalFile(fn); } } if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) { QString fn = newURL.toLocalFile(); QFileInfo info(fn); document->documentInfo()->setAboutInfo("title", info.baseName()); } QByteArray outputFormat = _native_format; if (!specialOutputFlag) { QMimeType mime = db.mimeTypeForUrl(newURL); QString outputFormatString = mime.name(); outputFormat = outputFormatString.toLatin1(); } if (!isExporting()) justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType()) && (specialOutputFlag == oldSpecialOutputFlag); else justChangingFilterOptions = (newURL == d->lastExportUrl) && (outputFormat == d->lastExportedFormat) && (specialOutputFlag == d->lastExportSpecialOutputFlag); bool bOk = true; if (newURL.isEmpty()) { bOk = false; } // adjust URL before doing checks on whether the file exists. if (specialOutputFlag) { QString fileName = newURL.fileName(); if ( specialOutputFlag== KisDocument::SaveAsDirectoryStore) { //dbgKrita << "save to directory: " << newURL.url(); } else if (specialOutputFlag == KisDocument::SaveEncrypted) { int dot = fileName.lastIndexOf('.'); dbgKrita << dot; QString ext = mime.preferredSuffix(); if (!ext.isEmpty()) { if (dot < 0) fileName += ext; else fileName = fileName.left(dot) + ext; } else { // current filename extension wrong anyway if (dot > 0) fileName = fileName.left(dot); } newURL = newURL.adjusted(QUrl::RemoveFilename); newURL.setPath(newURL.path() + fileName); } } if (bOk) { bool wantToSave = true; // don't change this line unless you know what you're doing :) if (!justChangingFilterOptions || document->confirmNonNativeSave(isExporting())) { if (!document->isNativeFormat(outputFormat)) wantToSave = true; } if (wantToSave) { // // Note: // If the user is stupid enough to Export to the current URL, // we do _not_ change this operation into a Save As. Reasons // follow: // // 1. A check like "isExporting() && oldURL == newURL" // doesn't _always_ work on case-insensitive filesystems // and inconsistent behaviour is bad. // 2. It is probably not a good idea to change document->mimeType // and friends because the next time the user File/Save's, // (not Save As) they won't be expecting that they are // using their File/Export settings // // As a bad side-effect of this, the modified flag will not // be updated and it is possible that what is currently on // their screen is not what is stored on disk (through loss // of formatting). But if you are dumb enough to change // mimetype but not the filename, then arguably, _you_ are // the "bug" :) // // - Clarence // document->setOutputMimeType(outputFormat, specialOutputFlag); if (!isExporting()) { // Save As ret = document->saveAs(newURL); if (ret) { dbgUI << "Successful Save As!"; addRecentURL(newURL); setReadWrite(true); } else { dbgUI << "Failed Save As!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } } else { // Export ret = document->exportDocument(newURL); if (ret) { // a few file dialog convenience things d->lastExportUrl = newURL; d->lastExportedFormat = outputFormat; d->lastExportSpecialOutputFlag = specialOutputFlag; } // always restore output format document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } if (silent) // don't let the document change the window caption document->setTitleModified(); } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving bool needConfirm = document->confirmNonNativeSave(false) && !document->isNativeFormat(oldOutputFormat); if (!needConfirm || (needConfirm && exportConfirmation(oldOutputFormat /* not so old :) */)) ) { // be sure document has the correct outputMimeType! if (isExporting() || document->isModified()) { ret = document->save(); } if (!ret) { dbgUI << "Failed Save!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } else ret = false; } if (!ret && reset_url) document->resetURL(); //clean the suggested filename as the save dialog was rejected updateReloadFileAction(document); updateCaption(); return ret; } bool KisMainWindow::exportConfirmation(const QByteArray &/*outputFormat*/) { return true; } void KisMainWindow::undo() { if (activeView()) { activeView()->undoAction()->trigger(); d->undo->setText(activeView()->undoAction()->text()); } } void KisMainWindow::redo() { if (activeView()) { activeView()->redoAction()->trigger(); d->redo->setText(activeView()->redoAction()->text()); } } void KisMainWindow::closeEvent(QCloseEvent *e) { d->mdiArea->closeAllSubWindows(); KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); cfg.writeEntry("ko_geometry", saveGeometry().toBase64()); cfg.writeEntry("ko_windowstate", saveState().toBase64()); { KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); } if(d->activeView && d->activeView->document() && d->activeView->document()->isLoading()) { e->setAccepted(false); return; } QList childrenList = d->mdiArea->subWindowList(); if (childrenList.isEmpty()) { d->deferredClosingEvent = e; if (!d->dockerStateBeforeHiding.isEmpty()) { restoreState(d->dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); if (d->noCleanup) return; Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { KisView *view = dynamic_cast(subwin); if (view) { KisPart::instance()->removeView(view); } } if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency Q_FOREACH (QDockWidget* dockWidget, d->dockWidgetsMap) dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } else { e->setAccepted(false); } } void KisMainWindow::saveWindowSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); if (d->windowSizeDirty ) { dbgUI << "KisMainWindow::saveWindowSettings"; KConfigGroup group = config->group("MainWindow"); KWindowConfig::saveWindowSize(windowHandle(), group); config->sync(); d->windowSizeDirty = false; } if (!d->activeView || d->activeView->document()) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); // Save collapsable state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); } } } KSharedConfig::openConfig()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KisMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KXmlGuiWindow::resizeEvent(e); } void KisMainWindow::setActiveView(KisView* view) { d->activeView = view; updateCaption(); actionCollection()->action("edit_undo")->setText(activeView()->undoAction()->text()); actionCollection()->action("edit_redo")->setText(activeView()->redoAction()->text()); d->viewManager->setCurrentView(view); } void KisMainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisMainWindow::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { openDocument(url); } } } void KisMainWindow::dragMoveEvent(QDragMoveEvent * event) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) { qWarning() << "WARNING!!! Cannot find QTabBar in the main window! Looks like Qt has changed behavior. Drag & Drop between multiple tabs might not work properly (tabs will not switch automatically)!"; } if (tabBar && tabBar->isVisible()) { QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos())); if (tabBar->rect().contains(pos)) { const int tabIndex = tabBar->tabAt(pos); if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) { d->tabSwitchCompressor->start(tabIndex); } } else if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } } void KisMainWindow::dragLeaveEvent(QDragLeaveEvent * /*event*/) { if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } void KisMainWindow::switchTab(int index) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar) return; tabBar->setCurrentIndex(index); } void KisMainWindow::slotFileNew() { KisPart::instance()->showStartUpWidget(this, true /*Always show widget*/); } void KisMainWindow::slotFileOpen() { QStringList urls = showOpenFileDialog(); if (urls.isEmpty()) return; Q_FOREACH (const QString& url, urls) { if (!url.isEmpty()) { bool res = openDocument(QUrl::fromLocalFile(url)); if (!res) { warnKrita << "Loading" << url << "failed"; } } } } void KisMainWindow::slotFileOpenRecent(const QUrl &url) { (void) openDocument(QUrl(url)); } void KisMainWindow::slotFileSave() { if (saveDocument(d->activeView->document())) emit documentSaved(); } void KisMainWindow::slotFileSaveAs() { if (saveDocument(d->activeView->document(), true)) emit documentSaved(); } KoCanvasResourceManager *KisMainWindow::resourceManager() const { return d->viewManager->resourceProvider()->resourceManager(); } int KisMainWindow::viewCount() const { return d->mdiArea->subWindowList().size(); } bool KisMainWindow::restoreWorkspace(const QByteArray &state) { QByteArray oldState = saveState(); const bool showTitlebars = KisConfig().showDockerTitleBars(); // needed because otherwise the layout isn't correctly restored in some situations Q_FOREACH (QDockWidget *dock, dockWidgets()) { dock->hide(); dock->titleBarWidget()->setVisible(showTitlebars); } bool success = KXmlGuiWindow::restoreState(state); if (!success) { KXmlGuiWindow::restoreState(oldState); Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating()); } } return false; } Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(showTitlebars || (dock->isFloating() && isCollapsed)); } } return success; } KisViewManager *KisMainWindow::viewManager() const { return d->viewManager; } void KisMainWindow::slotDocumentInfo() { if (!d->activeView->document()) return; KoDocumentInfo *docInfo = d->activeView->document()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { d->activeView->document()->setModified(false); } else { d->activeView->document()->setModified(true); } d->activeView->document()->setTitleModified(); } delete dlg; } bool KisMainWindow::slotFileCloseAll() { Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { if (subwin) { if(!subwin->close()) return false; } } updateCaption(); return true; } void KisMainWindow::slotFileQuit() { if(!slotFileCloseAll()) return; close(); Q_FOREACH (QPointer mainWin, KisPart::instance()->mainWindows()) { if (mainWin != this) { if(!mainWin->slotFileCloseAll()) return; mainWin->close(); } } } void KisMainWindow::slotFilePrint() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = activeView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) { printJob->printer().setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Point); printJob->printer().setPaperSize(QSizeF(activeView()->image()->width() / (72.0 * activeView()->image()->xRes()), activeView()->image()->height()/ (72.0 * activeView()->image()->yRes())), QPrinter::Inch); printJob->startPrinting(KisPrintJob::DeleteWhenDone); } else { delete printJob; } delete printDialog; } void KisMainWindow::slotFilePrintPreview() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KisPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting())); preview->exec(); delete preview; } KisPrintJob* KisMainWindow::exportToPdf(const QString &pdfFileName) { if (!activeView()) return 0; KoPageLayout pageLayout; pageLayout = activeView()->pageLayout(); return exportToPdf(pageLayout, pdfFileName); } KisPrintJob* KisMainWindow::exportToPdf(KoPageLayout pageLayout, QString pdfFileName) { if (!activeView()) return 0; if (!activeView()->document()) return 0; if (pdfFileName.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString defaultDir = group.readEntry("SavePdfDialog"); if (defaultDir.isEmpty()) defaultDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); QUrl startUrl = QUrl::fromLocalFile(defaultDir); KisDocument* pDoc = d->activeView->document(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.fileName(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl = startUrl.adjusted(QUrl::RemoveFilename); startUrl.setPath(startUrl.path() + fileName ); } QPointer layoutDlg(new KoPageLayoutDialog(this, pageLayout)); layoutDlg->setWindowModality(Qt::WindowModal); if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) { delete layoutDlg; return 0; } pageLayout = layoutDlg->pageLayout(); delete layoutDlg; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("Export as PDF")); dialog.setDefaultDir(startUrl.toLocalFile()); dialog.setMimeTypeFilters(QStringList() << "application/pdf"); QUrl url = QUrl::fromUserInput(dialog.filename()); pdfFileName = url.toLocalFile(); if (pdfFileName.isEmpty()) return 0; } KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return 0; if (isHidden()) { printJob->setProperty("noprogressdialog", true); } applyDefaultSettings(printJob->printer()); // TODO for remote files we have to first save locally and then upload. printJob->printer().setOutputFileName(pdfFileName); printJob->printer().setDocName(pdfFileName); printJob->printer().setColorMode(QPrinter::Color); if (pageLayout.format == KoPageFormat::CustomSize) { printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter); } else { printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format)); } printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter); switch (pageLayout.orientation) { case KoPageFormat::Portrait: printJob->printer().setOrientation(QPrinter::Portrait); break; case KoPageFormat::Landscape: printJob->printer().setOrientation(QPrinter::Landscape); break; } //before printing check if the printer can handle printing if (!printJob->canPrint()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Cannot export to the specified file")); } printJob->startPrinting(KisPrintJob::DeleteWhenDone); return printJob; } void KisMainWindow::importAnimation() { #ifdef HAVE_OPENGL if (!activeView()) return; KisDocument *document = activeView()->document(); if (!document) return; KisDlgImportImageSequence dlg(this, document); if (dlg.exec() == QDialog::Accepted) { QStringList files = dlg.files(); int firstFrame = dlg.firstFrame(); int step = dlg.step(); - KisAnimationImporter importer(document->image()); - importer.import(files, firstFrame, step); + document->setFileProgressProxy(); + document->setFileProgressUpdater(i18n("Import frames")); + KisAnimationImporter importer(document); + KisImportExportFilter::ConversionStatus status = importer.import(files, firstFrame, step); + document->clearFileProgressUpdater(); + document->clearFileProgressProxy(); + if (status != KisImportExportFilter::OK && status != KisImportExportFilter::InternalError) { + QString msg = KisImportExportFilter::conversionStatusString(status); + + if (!msg.isEmpty()) + QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg)); + } activeView()->canvasBase()->refetchDataFromImage(); } #endif } void KisMainWindow::exportAnimation() { #ifdef HAVE_OPENGL if (!activeView()) return; KisDocument *document = activeView()->document(); if (!document) return; + document->setFileProgressProxy(); + document->setFileProgressUpdater(i18n("Export frames")); KisAnimationExporterUI exporter(this); - exporter.exportSequence(document); + KisImportExportFilter::ConversionStatus status = exporter.exportSequence(document); + document->clearFileProgressUpdater(); + document->clearFileProgressProxy(); + if (status != KisImportExportFilter::OK && status != KisImportExportFilter::InternalError) { + QString msg = KisImportExportFilter::conversionStatusString(status); + + if (!msg.isEmpty()) + QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish export animation:\n%1", msg)); + } activeView()->canvasBase()->refetchDataFromImage(); #endif } void KisMainWindow::slotConfigureKeys() { KisPart::instance()->configureShortcuts(); emit keyBindingsChanged(); } void KisMainWindow::slotConfigureToolbars() { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); applyToolBarLayout(); } void KisMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(KSharedConfig::openConfig()->group("krita")); KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); applyToolBarLayout(); } void KisMainWindow::slotToolbarToggled(bool toggle) { //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) { bar->show(); } else { bar->hide(); } if (d->activeView && d->activeView->document()) { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); } } else warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } void KisMainWindow::viewFullscreen(bool fullScreen) { KisConfig cfg; cfg.setFullscreenMode(fullScreen); if (fullScreen) { setWindowState(windowState() | Qt::WindowFullScreen); // set } else { setWindowState(windowState() & ~Qt::WindowFullScreen); // reset } } void KisMainWindow::slotProgress(int value) { qApp->processEvents(); if (!d->progressMutex.tryLock()) return; dbgUI << "KisMainWindow::slotProgress" << value; if (value <= -1 || value >= 100) { if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; + + disconnect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled())); + statusBar()->removeWidget(d->progressCancel); + delete d->progressCancel; + d->progressCancel = 0; } d->firstTime = true; d->progressMutex.unlock(); return; } if (d->firstTime || !d->progress) { // The statusbar might not even be created yet. // So check for that first, and create it if necessary QStatusBar *bar = findChild(); if (!bar) { statusBar()->show(); QApplication::sendPostedEvents(this, QEvent::ChildAdded); } if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; + + disconnect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled())); + statusBar()->removeWidget(d->progressCancel); + delete d->progressCancel; + d->progress = 0; } + d->progressCancel = new QToolButton(statusBar()); + d->progressCancel->setMaximumHeight(statusBar()->fontMetrics().height()); + d->progressCancel->setIcon(KisIconUtils::loadIcon("process-stop")); + statusBar()->addPermanentWidget(d->progressCancel); + d->progress = new QProgressBar(statusBar()); d->progress->setMaximumHeight(statusBar()->fontMetrics().height()); d->progress->setRange(0, 100); statusBar()->addPermanentWidget(d->progress); + + connect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled())); + d->progress->show(); + d->progressCancel->show(); d->firstTime = false; } if (!d->progress.isNull()) { d->progress->setValue(value); } qApp->processEvents(); d->progressMutex.unlock(); } +void KisMainWindow::slotProgressCanceled() +{ + emit sigProgressCanceled(); +} + void KisMainWindow::setMaxRecentItems(uint _number) { d->recentFiles->setMaxItems(_number); } void KisMainWindow::slotReloadFile() { KisDocument* document = d->activeView->document(); if (!document || document->url().isEmpty()) return; if (document->isModified()) { bool ok = QMessageBox::question(this, i18nc("@title:window", "Krita"), i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes; if (!ok) return; } QUrl url = document->url(); saveWindowSettings(); if (!document->reload()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document")); } return; } void KisMainWindow::slotImportFile() { dbgUI << "slotImportFile()"; d->isImporting = true; slotFileOpen(); d->isImporting = false; } void KisMainWindow::slotExportFile() { dbgUI << "slotExportFile()"; d->isExporting = true; slotFileSaveAs(); d->isExporting = false; } bool KisMainWindow::isImporting() const { return d->isImporting; } bool KisMainWindow::isExporting() const { return d->isExporting; } QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) { warnKrita << "Could not create docker for" << factory->id(); return 0; } KoDockWidgetTitleBar *titleBar = dynamic_cast(dockWidget->titleBarWidget()); // Check if the dock widget is supposed to be collapsable if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } titleBar->setFont(KoDockRegistry::dockFont()); dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } KConfigGroup group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; addDockWidget(side, dockWidget); if (!visible) { dockWidget->hide(); } bool collapsed = factory->defaultCollapsed(); bool locked = false; group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); locked = group.readEntry("Locked", locked); //dbgKrita << "docker" << factory->id() << dockWidget << "collapsed" << collapsed << "locked" << locked << "titlebar" << titleBar; if (titleBar && collapsed) titleBar->setCollapsed(true); if (titleBar && locked) titleBar->setLocked(true); d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[factory->id()]; } #ifdef Q_OS_MAC dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(KoDockRegistry::dockFont()); connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts())); return dockWidget; } void KisMainWindow::forceDockTabFonts() { Q_FOREACH (QObject *child, children()) { if (child->inherits("QTabBar")) { ((QTabBar *)child)->setFont(KoDockRegistry::dockFont()); } } } QList KisMainWindow::dockWidgets() const { return d->dockWidgetsMap.values(); } QDockWidget* KisMainWindow::dockWidget(const QString &id) { if (!d->dockWidgetsMap.contains(id)) return 0; return d->dockWidgetsMap[id]; } QList KisMainWindow::canvasObservers() const { QList observers; Q_FOREACH (QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } else { warnKrita << docker << "is not a canvas observer"; } } return observers; } void KisMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->dockerStateBeforeHiding = saveState(); Q_FOREACH (QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->dockerStateBeforeHiding); } } void KisMainWindow::setToolbarList(QList toolbarList) { qDeleteAll(d->toolbarList); d->toolbarList = toolbarList; } void KisMainWindow::slotDocumentTitleModified(const QString &caption, bool mod) { updateCaption(caption, mod); updateReloadFileAction(d->activeView ? d->activeView->document() : 0); } void KisMainWindow::subWindowActivated() { bool enabled = (activeKisView() != 0); d->mdiCascade->setEnabled(enabled); d->mdiNextWindow->setEnabled(enabled); d->mdiPreviousWindow->setEnabled(enabled); d->mdiTile->setEnabled(enabled); d->close->setEnabled(enabled); d->closeAll->setEnabled(enabled); setActiveSubWindow(d->mdiArea->activeSubWindow()); Q_FOREACH (QToolBar *tb, toolBars()) { if (tb->objectName() == "BrushesAndStuff") { tb->setEnabled(enabled); } } updateCaption(); d->actionManager()->updateGUI(); } void KisMainWindow::updateWindowMenu() { QMenu *menu = d->windowMenu->menu(); menu->clear(); menu->addAction(d->newWindow); menu->addAction(d->documentMenu); QMenu *docMenu = d->documentMenu->menu(); docMenu->clear(); Q_FOREACH (QPointer doc, KisPart::instance()->documents()) { if (doc) { QString title = doc->url().toDisplayString(); if (title.isEmpty()) title = doc->image()->objectName(); QAction *action = docMenu->addAction(title); action->setIcon(qApp->windowIcon()); connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map())); d->documentMapper->setMapping(action, doc); } } menu->addSeparator(); menu->addAction(d->close); menu->addAction(d->closeAll); if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) { menu->addSeparator(); menu->addAction(d->mdiTile); menu->addAction(d->mdiCascade); } menu->addSeparator(); menu->addAction(d->mdiNextWindow); menu->addAction(d->mdiPreviousWindow); menu->addSeparator(); QList windows = d->mdiArea->subWindowList(); for (int i = 0; i < windows.size(); ++i) { QPointerchild = qobject_cast(windows.at(i)->widget()); if (child) { QString text; if (i < 9) { text = i18n("&%1 %2", i + 1, child->document()->url().toDisplayString()); } else { text = i18n("%1 %2", i + 1, child->document()->url().toDisplayString()); } QAction *action = menu->addAction(text); action->setIcon(qApp->windowIcon()); action->setCheckable(true); action->setChecked(child == activeKisView()); connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map())); d->windowMapper->setMapping(action, windows.at(i)); } } updateCaption(); } void KisMainWindow::setActiveSubWindow(QWidget *window) { if (!window) return; QMdiSubWindow *subwin = qobject_cast(window); //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow; if (subwin && subwin != d->activeSubWindow) { KisView *view = qobject_cast(subwin->widget()); //dbgKrita << "\t" << view << activeView(); if (view && view != activeView()) { setActiveView(view); } d->activeSubWindow = subwin; } updateWindowMenu(); d->actionManager()->updateGUI(); } void KisMainWindow::configChanged() { KisConfig cfg; QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView); d->mdiArea->setViewMode(viewMode); Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); } KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark")); d->actionManager()->updateGUI(); QBrush brush(cfg.getMDIBackgroundColor()); d->mdiArea->setBackground(brush); QString backgroundImage = cfg.getMDIBackgroundImage(); if (backgroundImage != "") { QImage image(backgroundImage); QBrush brush(image); d->mdiArea->setBackground(brush); } d->mdiArea->update(); } void KisMainWindow::newView(QObject *document) { KisDocument *doc = qobject_cast(document); KisView *view = KisPart::instance()->createView(doc, resourceManager(), actionCollection(), this); addView(view); d->actionManager()->updateGUI(); } void KisMainWindow::newWindow() { KisPart::instance()->createMainWindow()->show(); } void KisMainWindow::closeCurrentWindow() { d->mdiArea->currentSubWindow()->close(); d->actionManager()->updateGUI(); } void KisMainWindow::checkSanity() { // print error if the lcms engine is not available if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) { // need to wait 1 event since exiting here would not work. m_errorMessage = i18n("The Calligra LittleCMS color management plugin is not installed. Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); if (rserver->resources().isEmpty()) { m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } } void KisMainWindow::showErrorAndDie() { QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage); if (m_dieOnError) { exit(10); } } void KisMainWindow::showAboutApplication() { KisAboutApplication dlg(this); dlg.exec(); } QPointerKisMainWindow::activeKisView() { if (!d->mdiArea) return 0; QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow(); //dbgKrita << "activeKisView" << activeSubWindow; if (!activeSubWindow) return 0; return qobject_cast(activeSubWindow->widget()); } void KisMainWindow::newOptionWidgets(const QList > &optionWidgetList) { Q_FOREACH (QWidget *w, optionWidgetList) { #ifdef Q_OS_MAC w->setAttribute(Qt::WA_MacSmallSize, true); #endif w->setFont(KoDockRegistry::dockFont()); } if (d->toolOptionsDocker) { d->toolOptionsDocker->setOptionWidgets(optionWidgetList); } else { d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList); } } void KisMainWindow::applyDefaultSettings(QPrinter &printer) { if (!d->activeView) return; QString title = d->activeView->document()->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { title = d->activeView->document()->url().fileName(); // strip off the native extension (I don't want foobar.kwd.ps when printing into a file) QMimeDatabase db; QMimeType mime = db.mimeTypeForName(d->activeView->document()->outputMimeType()); if (mime.isValid()) { QString extension = mime.preferredSuffix(); if (title.endsWith(extension)) title.chop(extension.length()); } } if (title.isEmpty()) { // #139905 title = i18n("%1 unsaved document (%2)", qApp->applicationDisplayName(), QLocale().toString(QDate::currentDate(), QLocale::ShortFormat)); } printer.setDocName(title); } void KisMainWindow::createActions() { KisActionManager *actionManager = d->actionManager(); actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew())); actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen())); actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit())); actionManager->createStandardAction(KStandardAction::KeyBindings, this, SLOT(slotConfigureKeys())); actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars())); actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool))); d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection()); connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles())); KSharedConfigPtr configPtr = KSharedConfig::openConfig(); d->recentFiles->loadEntries(configPtr->group("RecentFiles")); d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave())); d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs())); d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE); d->printAction = actionManager->createStandardAction(KStandardAction::Print, this, SLOT(slotFilePrint())); d->printAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->printActionPreview = actionManager->createStandardAction(KStandardAction::PrintPreview, this, SLOT(slotFilePrintPreview())); d->printActionPreview->setActivationFlags(KisAction::ACTIVE_IMAGE); d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo())); d->undo ->setActivationFlags(KisAction::ACTIVE_IMAGE); d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo())); d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE); d->exportPdf = actionManager->createAction("file_export_pdf"); connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); #ifdef HAVE_OPENGL d->importAnimation = actionManager->createAction("file_import_animation"); connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation())); d->exportAnimation = actionManager->createAction("file_export_animation"); connect(d->exportAnimation, SIGNAL(triggered()), this, SLOT(exportAnimation())); #endif d->closeAll = actionManager->createAction("file_close_all"); connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll())); // d->reloadFile = actionManager->createAction("file_reload_file"); // d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED); // connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->importFile = actionManager->createAction("file_import_file"); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = actionManager->createAction("file_export_file"); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = actionManager->createAction("file_documentinfo"); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this)); d->themeManager->registerThemeActions(actionCollection()); connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); d->toggleDockers = actionManager->createAction("view_toggledockers"); d->toggleDockers->setChecked(true); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars"); { KisConfig cfg; d->toggleDockerTitleBars->setChecked(cfg.showDockerTitleBars()); } connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool))); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); actionCollection()->addAction("window", d->windowMenu); d->mdiCascade = actionManager->createAction("windows_cascade"); connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows())); d->mdiTile = actionManager->createAction("windows_tile"); connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows())); d->mdiNextWindow = actionManager->createAction("windows_next"); connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow())); d->mdiPreviousWindow = actionManager->createAction("windows_previous"); connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow())); d->newWindow = actionManager->createAction("view_newwindow"); connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow())); d->close = actionManager->createAction("file_close"); connect(d->close, SIGNAL(triggered()), SLOT(closeCurrentWindow())); actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences())); for (int i = 0; i < 2; i++) { d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer")); d->expandingSpacers[i]->setDefaultWidget(new QWidget(this)); d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]); } } void KisMainWindow::applyToolBarLayout() { const bool isPlastiqueStyle = style()->objectName() == "plastique"; Q_FOREACH (KToolBar *toolBar, toolBars()) { toolBar->layout()->setSpacing(4); if (isPlastiqueStyle) { toolBar->setContentsMargins(0, 0, 0, 2); } } } void KisMainWindow::initializeGeometry() { // if the user didn's specify the geometry on the command line (does anyone do that still?), // we first figure out some good default size and restore the x,y position. See bug 285804Z. KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray())); if (!restoreGeometry(geom)) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->availableGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) { desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum)); } quint32 x = desk.x(); quint32 y = desk.y(); quint32 w = 0; quint32 h = 0; // Default size -- maximize on small screens, something useful on big screens const int deskWidth = desk.width(); if (deskWidth > 1024) { // a nice width, and slightly less than total available // height to componensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } restoreWorkspace(QByteArray::fromBase64(cfg.readEntry("ko_windowstate", QByteArray()))); } void KisMainWindow::showManual() { QDesktopServices::openUrl(QUrl("https://docs.krita.org")); } void KisMainWindow::showDockerTitleBars(bool show) { Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(show || (dock->isFloating() && isCollapsed)); } } KisConfig cfg; cfg.setShowDockerTitleBars(show); } void KisMainWindow::moveEvent(QMoveEvent *e) { if (qApp->desktop()->screenNumber(this) != qApp->desktop()->screenNumber(e->oldPos())) { KisConfigNotifier::instance()->notifyConfigChanged(); } } #include #include #include diff --git a/libs/ui/KisMainWindow.h b/libs/ui/KisMainWindow.h index de4f4563ae..b3ea04e1a8 100644 --- a/libs/ui/KisMainWindow.h +++ b/libs/ui/KisMainWindow.h @@ -1,459 +1,463 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2004 David Faure 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. */ #ifndef KIS_MAIN_WINDOW_H #define KIS_MAIN_WINDOW_H #include "kritaui_export.h" #include #include #include #include #include #include #include "KisView.h" class QCloseEvent; class QMoveEvent; struct KoPageLayout; class KoCanvasResourceManager; class KisDocument; class KisView; class KisPrintJob; class KoDockFactoryBase; class QDockWidget; class KisView; class KisViewManager; /** * @brief Main window for Krita * * This class is used to represent a main window within a Krita session. Each * main window contains a menubar and some toolbars, and potentially several * views of several canvases. * */ class KRITAUI_EXPORT KisMainWindow : public KXmlGuiWindow, public KoCanvasSupervisor { Q_OBJECT public: /** * Constructor. * * Initializes a Calligra main window (with its basic GUI etc.). */ explicit KisMainWindow(); /** * Destructor. */ virtual ~KisMainWindow(); /** * Update caption from document info - call when document info * (title in the about page) changes. */ void updateCaption(); // If noCleanup is set, KisMainWindow will not delete the root document // or part manager on destruction. void setNoCleanup(bool noCleanup); /** * @brief showView shows the given view. Override this if you want to show * the view in a different way than by making it the central widget, for instance * as an QMdiSubWindow */ virtual void showView(KisView *view); /** * @returns the currently active view */ KisView *activeView() const; /** * Sets the maximum number of recent documents entries. */ void setMaxRecentItems(uint _number); /** * The document opened a URL -> store into recent documents list. */ void addRecentURL(const QUrl &url); /** * Load the desired document and show it. * @param url the URL to open * * @return TRUE on success. */ bool openDocument(const QUrl &url); void setReadWrite(bool readwrite); /// Return the list of dock widgets belonging to this main window. QList dockWidgets() const; QDockWidget* dockWidget(const QString &id); QList canvasObservers() const; KoCanvasResourceManager *resourceManager() const; int viewCount() const; /** * A wrapper around restoreState * @param state the saved state * @return TRUE on success */ bool restoreWorkspace(const QByteArray &state); KisViewManager *viewManager() const; void addViewAndNotifyLoadingCompleted(KisDocument *document); QStringList showOpenFileDialog(); Q_SIGNALS: /** * This signal is emitted if the document has been saved successfully. */ void documentSaved(); /// This signal is emitted when this windows has finished loading of a /// document. The document may be opened in another window in the end. /// In this case, the signal means there is no link between the window /// and the document anymore. void loadCompleted(); /// This signal is emitted right after the docker states have been succefully restored from config void restoringDone(); /// This signal is emitted when the color theme changes void themeChanged(); /// This signal is emitted when the shortcut key configuration has changed void keyBindingsChanged(); void guiLoadingFinished(); + /// This signal is emitted when the user clicked on the progressbar cancel + void sigProgressCanceled(); + public Q_SLOTS: /** * Slot for opening a new document. * * If the current document is empty, the new document replaces it. * If not, a new mainwindow will be opened for showing the document. */ void slotFileNew(); /** * Slot for opening a saved file. * * If the current document is empty, the opened document replaces it. * If not a new mainwindow will be opened for showing the opened file. */ void slotFileOpen(); /** * Slot for opening a file among the recently opened files. * * If the current document is empty, the opened document replaces it. * If not a new mainwindow will be opened for showing the opened file. */ void slotFileOpenRecent(const QUrl &); /** * @brief slotPreferences open the preferences dialog */ void slotPreferences(); /** * Saves the current document with the current name. */ void slotFileSave(); KisPrintJob* exportToPdf(const QString &pdfFileName = QString()); void slotProgress(int value); /** * Saves the document, asking for a filename if necessary. * * @param saveas if set to TRUE the user is always prompted for a filename * * @param silent if set to TRUE rootDocument()->setTitleModified will not be called. * * @param specialOutputFlag set to enums defined in KisDocument if save to special output format * * @return TRUE on success, false on error or cancel * (don't display anything in this case, the error dialog box is also implemented here * but restore the original URL in slotFileSaveAs) */ bool saveDocument(KisDocument *document, bool saveas = false, bool silent = false, int specialOutputFlag = 0); /** * Update the option widgets to the argument ones, removing the currently set widgets. */ void newOptionWidgets(const QList > & optionWidgetList); private Q_SLOTS: /** * Save the list of recent files. */ void saveRecentFiles(); void slotLoadCompleted(); void slotLoadCanceled(const QString &); void slotSaveCompleted(); void slotSaveCanceled(const QString &); void forceDockTabFonts(); + void slotProgressCanceled(); /** * @internal */ void slotDocumentTitleModified(const QString &caption, bool mod); /** * Prints the actual document. */ void slotFilePrint(); /** * Saves the current document with a new name. */ void slotFileSaveAs(); void slotFilePrintPreview(); KisPrintJob* exportToPdf(KoPageLayout pageLayout, QString pdfFileName = QString()); void importAnimation(); void exportAnimation(); /** * Show a dialog with author and document information. */ void slotDocumentInfo(); /** * Closes all open documents. */ bool slotFileCloseAll(); /** * @brief showAboutApplication show the about box */ virtual void showAboutApplication(); /** * Closes the mainwindow. */ void slotFileQuit(); /** * Configure key bindings. */ void slotConfigureKeys(); /** * Configure toolbars. */ void slotConfigureToolbars(); /** * Post toolbar config. * (Plug action lists back in, etc.) */ void slotNewToolbarConfig(); /** * Shows or hides a toolbar */ void slotToolbarToggled(bool toggle); /** * Toggle full screen on/off. */ void viewFullscreen(bool fullScreen); /** * Toggle docker titlebars on/off. */ void showDockerTitleBars(bool show); /** * Reload file */ void slotReloadFile(); /** * File --> Import * * This will call slotFileOpen(). To differentiate this from an ordinary * call to slotFileOpen() call @ref isImporting(). */ void slotImportFile(); /** * File --> Export * * This will call slotFileSaveAs(). To differentiate this from an ordinary * call to slotFileSaveAs() call @ref isExporting(). */ void slotExportFile(); /** * Hide the dockers */ void toggleDockersVisibility(bool visible); /** * Handle theme changes from theme manager */ void slotThemeChanged(); void undo(); void redo(); void subWindowActivated(); void updateWindowMenu(); void setActiveSubWindow(QWidget *window); void configChanged(); void newView(QObject *document); void newWindow(); void closeCurrentWindow(); void checkSanity(); /// Quits Krita with error message from m_errorMessage. void showErrorAndDie(); protected: void closeEvent(QCloseEvent * e); void resizeEvent(QResizeEvent * e); /// Set the active view, this will update the undo/redo actions virtual void setActiveView(KisView *view); // QWidget overrides virtual void dragEnterEvent(QDragEnterEvent * event); virtual void dropEvent(QDropEvent * event); virtual void dragMoveEvent(QDragMoveEvent * event); virtual void dragLeaveEvent(QDragLeaveEvent * event); void setToolbarList(QList toolbarList); private: /** * Add a the given view to the list of views of this mainwindow. * This is a private implementation. For public usage please use * newView() and addViewAndNotifyLoadingCompleted(). */ void addView(KisView *view); friend class KisApplication; /** * Returns the dockwidget specified by the @p factory. If the dock widget doesn't exist yet it's created. * Add a "view_palette_action_menu" action to your view menu if you want to use closable dock widgets. * @param factory the factory used to create the dock widget if needed * @return the dock widget specified by @p factory (may be 0) */ QDockWidget* createDockWidget(KoDockFactoryBase* factory); bool openDocumentInternal(const QUrl &url, KisDocument *newdoc = 0); /** * Returns whether or not the current slotFileSave[As]() or saveDocument() * call is actually an export operation (like File --> Export). * * If this is true, you must call KisDocument::export() instead of * KisDocument::save() or KisDocument::saveAs(), in any reimplementation of * saveDocument(). */ bool isExporting() const; /** * Returns whether or not the current slotFileOpen() or openDocument() * call is actually an import operation (like File --> Import). * * If this is true, you must call KisDocument::import() instead of * KisDocument::openUrl(), in any reimplementation of openDocument() or * openDocumentInternal(). */ bool isImporting() const; /** * Reloads the recent documents list. */ void reloadRecentFileList(); /** * Updates the window caption based on the document info and path. */ void updateCaption(const QString & caption, bool mod); void updateReloadFileAction(KisDocument *doc); void saveWindowSettings(); QPointeractiveKisView(); void applyDefaultSettings(QPrinter &printer); bool exportConfirmation(const QByteArray &outputFormat); void createActions(); void applyToolBarLayout(); protected: void moveEvent(QMoveEvent *e); private Q_SLOTS: void initializeGeometry(); void showManual(); void switchTab(int index); private: class Private; Private * const d; QString m_errorMessage; bool m_dieOnError; }; #endif diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp index 0c6464c342..1b09a2a19d 100644 --- a/libs/ui/KisViewManager.cpp +++ b/libs/ui/KisViewManager.cpp @@ -1,1231 +1,1231 @@ /* * This file is part of KimageShop^WKrayon^WKrita * * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 1999 Carsten Pfeiffer * 2002 Patrick Julien * 2003-2011 Boudewijn Rempt * 2004 Clarence Dang * 2011 José Luis Vergara * * 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 #include "KisViewManager.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 #include "input/kis_input_manager.h" #include "canvas/kis_canvas2.h" #include "canvas/kis_canvas_controller.h" #include "canvas/kis_grid_manager.h" #include "dialogs/kis_dlg_blacklist_cleanup.h" #include "input/kis_input_profile_manager.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_canvas_controls_manager.h" #include "kis_canvas_resource_provider.h" #include "kis_composite_progress_proxy.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_control_frame.h" #include "kis_coordinates_converter.h" #include #include "KisDocument.h" #include "kis_favorite_resource_manager.h" #include "kis_filter_manager.h" #include "kis_group_layer.h" #include #include "kis_image_manager.h" #include #include "kis_mainwindow_observer.h" #include "kis_mask_manager.h" #include "kis_mimedata.h" #include "kis_mirror_manager.h" #include "kis_node_commands_adapter.h" #include "kis_node.h" #include "kis_node_manager.h" #include "kis_painting_assistants_manager.h" #include #include "kis_paintop_box.h" #include #include "KisPart.h" #include "KisPrintJob.h" #include "kis_progress_widget.h" #include "kis_resource_server_provider.h" #include "kis_selection.h" #include "kis_selection_manager.h" #include "kis_shape_controller.h" #include "kis_shape_layer.h" #include #include "kis_statusbar.h" #include #include #include "kis_tooltip_manager.h" #include #include "KisView.h" #include "kis_zoom_manager.h" #include "kra/kis_kra_loader.h" #include "widgets/kis_floating_message.h" #include "kis_signal_auto_connection.h" #include "kis_icon_utils.h" #include "kis_guides_manager.h" class BlockingUserInputEventFilter : public QObject { bool eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched); if(dynamic_cast(event) || dynamic_cast(event) || dynamic_cast(event)) { return true; } else { return false; } } }; class KisViewManager::KisViewManagerPrivate { public: KisViewManagerPrivate(KisViewManager *_q, QWidget *_q_parent) : filterManager(_q) , createTemplate(0) , saveIncremental(0) , saveIncrementalBackup(0) , openResourcesDirectory(0) , rotateCanvasRight(0) , rotateCanvasLeft(0) , resetCanvasRotation(0) , wrapAroundAction(0) , levelOfDetailAction(0) , showRulersAction(0) , rulersTrackMouseAction(0) , zoomTo100pct(0) , zoomIn(0) , zoomOut(0) , selectionManager(_q) , statusBar(_q) , controlFrame(_q, _q_parent) , nodeManager(_q) , imageManager(_q) , gridManager(_q) , canvasControlsManager(_q) , paintingAssistantsManager(_q) , actionManager(_q) , mainWindow(0) , showFloatingMessage(true) , currentImageView(0) , canvasResourceProvider(_q) , canvasResourceManager() , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q) , mirrorManager(_q) , inputManager(_q) , actionAuthor(0) { } public: KisFilterManager filterManager; KisAction *createTemplate; KisAction *createCopy; KisAction *saveIncremental; KisAction *saveIncrementalBackup; KisAction *openResourcesDirectory; KisAction *rotateCanvasRight; KisAction *rotateCanvasLeft; KisAction *resetCanvasRotation; KisAction *wrapAroundAction; KisAction *levelOfDetailAction; KisAction *showRulersAction; KisAction *rulersTrackMouseAction; KisAction *zoomTo100pct; KisAction *zoomIn; KisAction *zoomOut; KisSelectionManager selectionManager; KisGuidesManager guidesManager; KisStatusBar statusBar; KisControlFrame controlFrame; KisNodeManager nodeManager; KisImageManager imageManager; KisGridManager gridManager; KisCanvasControlsManager canvasControlsManager; KisPaintingAssistantsManager paintingAssistantsManager; BlockingUserInputEventFilter blockingEventFilter; KisActionManager actionManager; QMainWindow* mainWindow; QPointer savedFloatingMessage; bool showFloatingMessage; QPointer currentImageView; KisCanvasResourceProvider canvasResourceProvider; KoCanvasResourceManager canvasResourceManager; KisSignalCompressor guiUpdateCompressor; KActionCollection *actionCollection; KisMirrorManager mirrorManager; KisInputManager inputManager; KisSignalAutoConnectionsStore viewConnections; KSelectAction *actionAuthor; // Select action for author profile. QByteArray canvasState; }; KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection) : d(new KisViewManagerPrivate(this, parent)) { d->actionCollection = _actionCollection; d->mainWindow = dynamic_cast(parent); d->canvasResourceProvider.setResourceManager(&d->canvasResourceManager); connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout())); createActions(); setupManagers(); // These initialization functions must wait until KisViewManager ctor is complete. d->statusBar.setup(); d->controlFrame.setup(parent); //Check to draw scrollbars after "Canvas only mode" toggle is created. this->showHideScrollbars(); KoCanvasController *dummy = new KoDummyCanvasController(actionCollection()); KoToolManager::instance()->registerToolActions(actionCollection(), dummy); QTimer::singleShot(0, this, SLOT(makeStatusBarVisible())); connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)), d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int))); connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), resourceProvider(), SLOT(slotNodeActivated(KisNodeSP))); connect(resourceProvider()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), d->controlFrame.paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant))); connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*))); connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions())); KisInputProfileManager::instance()->loadProfiles(); KisConfig cfg; d->showFloatingMessage = cfg.showCanvasMessages(); } KisViewManager::~KisViewManager() { KisConfig cfg; if (resourceProvider() && resourceProvider()->currentPreset()) { cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name()); } cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength()); delete d; } KActionCollection *KisViewManager::actionCollection() const { return d->actionCollection; } void KisViewManager::slotViewAdded(KisView *view) { d->inputManager.addTrackedCanvas(view->canvasBase()); if (viewCount() == 0) { d->statusBar.showAllStatusBarItems(); } } void KisViewManager::slotViewRemoved(KisView *view) { d->inputManager.removeTrackedCanvas(view->canvasBase()); if (viewCount() == 0) { d->statusBar.hideAllStatusBarItems(); } } void KisViewManager::setCurrentView(KisView *view) { bool first = true; if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(false); d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor)); first = false; KisDocument* doc = d->currentImageView->document(); if (doc) { doc->disconnect(this); } d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar); d->viewConnections.clear(); } QPointerimageView = qobject_cast(view); if (imageView) { // Wait for the async image to have loaded KisDocument* doc = view->document(); // connect(canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), d->statusBar, SLOT(documentMousePositionChanged(QPointF))); d->currentImageView = imageView; KisCanvasController *canvasController = dynamic_cast(d->currentImageView->canvasController()); d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode())); d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15())); d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15())); d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation())); d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool))); d->wrapAroundAction->setChecked(canvasController->wrapAroundMode()); d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool))); d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode()); d->viewConnections.addUniqueConnection(d->currentImageView->canvasController(), SIGNAL(toolOptionWidgetsChanged(QList >)), mainWindow(), SLOT(newOptionWidgets(QList >))); d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*))); d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool))); d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool))); d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100())); d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn())); d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut())); imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked()); imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked()); showHideScrollbars(); } d->filterManager.setView(imageView); d->selectionManager.setView(imageView); d->guidesManager.setView(imageView); d->nodeManager.setView(imageView); d->imageManager.setView(imageView); d->canvasControlsManager.setView(imageView); d->actionManager.setView(imageView); d->gridManager.setView(imageView); d->statusBar.setView(imageView); d->paintingAssistantsManager.setView(imageView); d->mirrorManager.setView(imageView); if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(true); d->currentImageView->canvasController()->activate(); d->currentImageView->canvasController()->setFocus(); } d->actionManager.updateGUI(); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), resourceProvider(), SLOT(slotImageSizeChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigResolutionChanged(double,double)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(updateGUI())); d->viewConnections.addUniqueConnection( view->zoomManager()->zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); resourceProvider()->slotImageSizeChanged(); resourceProvider()->slotOnScreenResolutionChanged(); // Restore the last used brush preset if (first) { KisConfig cfg; KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QString lastPreset = cfg.readEntry("LastPreset", QString("Basic_tip_default")); KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset); if (!preset) { preset = rserver->resourceByName("Basic_tip_default"); } if (!preset) { preset = rserver->resources().first(); } if (preset) { paintOpBox()->restoreResource(preset.data()); } } } KoZoomController *KisViewManager::zoomController() const { if (d->currentImageView) { return d->currentImageView->zoomController(); } return 0; } KisImageWSP KisViewManager::image() const { if (document()) { return document()->image(); } return 0; } KisCanvasResourceProvider * KisViewManager::resourceProvider() { return &d->canvasResourceProvider; } KisCanvas2 * KisViewManager::canvasBase() const { if (d && d->currentImageView) { return d->currentImageView->canvasBase(); } return 0; } QWidget* KisViewManager::canvas() const { if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) { return d->currentImageView->canvasBase()->canvasWidget(); } return 0; } KisStatusBar * KisViewManager::statusBar() const { return &d->statusBar; } void KisViewManager::addStatusBarItem(QWidget *widget, int stretch, bool permanent) { d->statusBar.addStatusBarItem(widget, stretch, permanent); } void KisViewManager::removeStatusBarItem(QWidget *widget) { d->statusBar.removeStatusBarItem(widget); } KisPaintopBox* KisViewManager::paintOpBox() const { return d->controlFrame.paintopBox(); } KoProgressUpdater* KisViewManager::createProgressUpdater(KoProgressUpdater::Mode mode) { return new KisProgressUpdater(d->statusBar.progress(), document()->progressProxy(), mode); } KisSelectionManager * KisViewManager::selectionManager() { return &d->selectionManager; } KisNodeSP KisViewManager::activeNode() { return d->nodeManager.activeNode(); } KisLayerSP KisViewManager::activeLayer() { return d->nodeManager.activeLayer(); } KisPaintDeviceSP KisViewManager::activeDevice() { return d->nodeManager.activePaintDevice(); } KisZoomManager * KisViewManager::zoomManager() { if (d->currentImageView) { return d->currentImageView->zoomManager(); } return 0; } KisFilterManager * KisViewManager::filterManager() { return &d->filterManager; } KisImageManager * KisViewManager::imageManager() { return &d->imageManager; } KisInputManager* KisViewManager::inputManager() const { return &d->inputManager; } KisSelectionSP KisViewManager::selection() { if (d->currentImageView) { return d->currentImageView->selection(); } return 0; } bool KisViewManager::selectionEditable() { KisLayerSP layer = activeLayer(); if (layer) { KoProperties properties; QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties); if (masks.size() == 1) { return masks[0]->isEditable(); } } // global selection is always editable return true; } KisUndoAdapter * KisViewManager::undoAdapter() { if (!document()) return 0; KisImageWSP image = document()->image(); Q_ASSERT(image); return image->undoAdapter(); } void KisViewManager::createActions() { d->saveIncremental = actionManager()->createAction("save_incremental_version"); connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental())); d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup"); connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup())); connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); d->saveIncremental->setEnabled(false); d->saveIncrementalBackup->setEnabled(false); KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger"); connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger())); d->createTemplate = actionManager()->createAction("create_template"); connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate())); d->createCopy = actionManager()->createAction("create_copy"); connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy())); d->openResourcesDirectory = actionManager()->createAction("open_resources_directory"); connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory())); d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right"); d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left"); d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation"); d->wrapAroundAction = actionManager()->createAction("wrap_around_mode"); d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode"); KisAction *tAction = actionManager()->createAction("showStatusBar"); tAction->setChecked(true); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool))); tAction = actionManager()->createAction("view_show_canvas_only"); tAction->setChecked(false); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool))); //Workaround, by default has the same shortcut as mirrorCanvas KisAction *a = dynamic_cast(actionCollection()->action("format_italic")); if (a) { a->setDefaultShortcut(QKeySequence()); } a = actionManager()->createAction("edit_blacklist_cleanup"); connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup())); KisConfig cfg; d->showRulersAction = actionManager()->createAction("view_ruler"); d->showRulersAction->setChecked(cfg.showRulers()); connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool))); d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse"); d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse()); connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool))); d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct"); d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, ""); d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, ""); d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this); connect(d->actionAuthor, SIGNAL(triggered(const QString &)), this, SLOT(changeAuthorProfile(const QString &))); actionCollection()->addAction("settings_active_author", d->actionAuthor); slotUpdateAuthorProfileActions(); } void KisViewManager::setupManagers() { // Create the managers for filters, selections, layers etc. // XXX: When the currentlayer changes, call updateGUI on all // managers d->filterManager.setup(actionCollection(), actionManager()); d->selectionManager.setup(actionManager()); d->guidesManager.setup(actionManager()); d->nodeManager.setup(actionCollection(), actionManager()); d->imageManager.setup(actionManager()); d->gridManager.setup(actionManager()); d->paintingAssistantsManager.setup(actionManager()); d->canvasControlsManager.setup(actionManager()); d->mirrorManager.setup(actionCollection()); } void KisViewManager::updateGUI() { d->guiUpdateCompressor.start(); } void KisViewManager::slotBlacklistCleanup() { KisDlgBlacklistCleanup dialog; dialog.exec(); } KisNodeManager * KisViewManager::nodeManager() const { return &d->nodeManager; } KisActionManager* KisViewManager::actionManager() const { return &d->actionManager; } KisGridManager * KisViewManager::gridManager() const { return &d->gridManager; } KisGuidesManager * KisViewManager::guidesManager() const { return &d->guidesManager; } KisPaintingAssistantsManager* KisViewManager::paintingAssistantsManager() const { return &d->paintingAssistantsManager; } KisDocument *KisViewManager::document() const { if (d->currentImageView && d->currentImageView->document()) { return d->currentImageView->document(); } return 0; } int KisViewManager::viewCount() const { KisMainWindow *mw = qobject_cast(d->mainWindow); if (mw) { return mw->viewCount(); } return 0; } void KisViewManager::slotCreateTemplate() { if (!document()) return; KisTemplateCreateDia::createTemplate(KisPart::instance()->templatesResourcePath(), ".kra", document(), mainWindow()); } void KisViewManager::slotCreateCopy() { if (!document()) return; KisDocument *doc = KisPart::instance()->createDocument(); QString name = document()->documentInfo()->aboutInfo("name"); if (name.isEmpty()) { name = document()->url().toLocalFile(); } name = i18n("%1 (Copy)", name); doc->documentInfo()->setAboutInfo("title", name); KisImageWSP image = document()->image(); KisImageSP newImage = new KisImage(doc->createUndoStore(), image->width(), image->height(), image->colorSpace(), name); newImage->setRootLayer(dynamic_cast(image->rootLayer()->clone().data())); doc->setCurrentImage(newImage); KisPart::instance()->addDocument(doc); KisMainWindow *mw = qobject_cast(d->mainWindow); mw->addViewAndNotifyLoadingCompleted(doc); } QMainWindow* KisViewManager::qtMainWindow() const { if (d->mainWindow) return d->mainWindow; //Fallback for when we have not yet set the main window. QMainWindow* w = qobject_cast(qApp->activeWindow()); if(w) return w; return mainWindow(); } void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow) { d->mainWindow = newMainWindow; } void KisViewManager::slotDocumentSaved() { d->saveIncremental->setEnabled(true); d->saveIncrementalBackup->setEnabled(true); } void KisViewManager::slotSaveIncremental() { if (!document()) return; bool foundVersion; bool fileAlreadyExists; bool isBackup; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // Find current version filenames // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well // Considering our incremental version and backup scheme, format is filename_001~001.ext QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); foundVersion = matches.at(0).isEmpty() ? false : true; // Ensure compatibility with Save Incremental Backup // If this regex is not kept separate, the entire algorithm needs modification; // It's simpler to just add this. QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regexAux.indexIn(fileName); // Perform the search QStringList matchesAux = regexAux.capturedTexts(); isBackup = matchesAux.at(0).isEmpty() ? false : true; // If the filename has a version, prepare it for incrementation if (foundVersion) { version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "_" } else { // ...else, simply add a version to it so the next loop works QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(fileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(version); extensionPlusVersion.prepend("_"); fileName.replace(regex2, extensionPlusVersion); } // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("_"); if (!letter.isNull()) newVersion.append(letter); if (isBackup) { newVersion.append("~"); } else { newVersion.append("."); } fileName.replace(regex, newVersion); fileAlreadyExists = QFile(fileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } - document()->setSaveInBatchMode(true); + document()->setFileBatchMode(true); document()->saveAs(QUrl::fromUserInput(fileName)); - document()->setSaveInBatchMode(false); + document()->setFileBatchMode(false); if (mainWindow()) { mainWindow()->updateCaption(); } } void KisViewManager::slotSaveIncrementalBackup() { if (!document()) return; bool workingOnBackup; bool fileAlreadyExists; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // First, discover if working on a backup file, or a normal file QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); workingOnBackup = matches.at(0).isEmpty() ? false : true; if (workingOnBackup) { // Try to save incremental version (of backup), use letter for alt versions version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "~" // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); QString backupFileName = document()->localFilePath(); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("~"); if (!letter.isNull()) newVersion.append(letter); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName)); if (mainWindow()) mainWindow()->updateCaption(); } else { // if NOT working on a backup... // Navigate directory searching for latest backup version, ignore letters const quint8 HARDCODED_DIGIT_COUNT = 3; QString baseNewVersion = "000"; QString backupFileName = document()->localFilePath(); QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(backupFileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(baseNewVersion); extensionPlusVersion.prepend("~"); backupFileName.replace(regex2, extensionPlusVersion); // Save version with 1 number higher than the highest version found ignoring letters do { newVersion = baseNewVersion; newVersion.prepend("~"); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { // Prepare the base for new version filename, increment by 1 int intVersion = baseNewVersion.toInt(0); ++intVersion; baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) { baseNewVersion.prepend("0"); } } } while (fileAlreadyExists); // Save both as backup and on current file for interapplication workflow - document()->setSaveInBatchMode(true); + document()->setFileBatchMode(true); QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName)); - document()->setSaveInBatchMode(false); + document()->setFileBatchMode(false); if (mainWindow()) mainWindow()->updateCaption(); } } void KisViewManager::disableControls() { // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel // this is for Bug 250944 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool // see KisToolFreehand::initPaint() and endPaint() d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->installEventFilter(&d->blockingEventFilter); } } void KisViewManager::enableControls() { d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->removeEventFilter(&d->blockingEventFilter); } } void KisViewManager::showStatusBar(bool toggled) { if (d->currentImageView && d->currentImageView->statusBar()) { d->currentImageView->statusBar()->setVisible(toggled); } } void KisViewManager::switchCanvasOnly(bool toggled) { KisConfig cfg; KisMainWindow* main = mainWindow(); if(!main) { dbgUI << "Unable to switch to canvas-only mode, main window not found"; return; } if (toggled) { d->canvasState = qtMainWindow()->saveState(); } if (cfg.hideStatusbarFullscreen()) { if (main->statusBar()) { if (!toggled) { if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->statusBar()->property("wasvisible").toBool()) { main->statusBar()->setVisible(true); } } } else { main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible()); main->statusBar()->setVisible(false); } } } if (cfg.hideDockersFullscreen()) { KisAction* action = qobject_cast(main->actionCollection()->action("view_toggledockers")); action->setCheckable(true); if (action && action->isChecked() == toggled) { action->setChecked(!toggled); } } if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) { if(toggled) { main->setWindowState( main->windowState() | Qt::WindowFullScreen); } else { main->setWindowState( main->windowState() & ~Qt::WindowFullScreen); } } if (cfg.hideMenuFullscreen()) { if (!toggled) { if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->menuBar()->property("wasvisible").toBool()) { main->menuBar()->setVisible(true); } } } else { main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible()); main->menuBar()->setVisible(false); } } if (cfg.hideToolbarFullscreen()) { QList toolBars = main->findChildren(); Q_FOREACH (QToolBar* toolbar, toolBars) { if (!toggled) { if (toolbar->dynamicPropertyNames().contains("wasvisible")) { if (toolbar->property("wasvisible").toBool()) { toolbar->setVisible(true); } } } else { toolbar->setProperty("wasvisible", toolbar->isVisible()); toolbar->setVisible(false); } } } showHideScrollbars(); if (toggled) { // show a fading heads-up display about the shortcut to go back showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.", actionCollection()->action("view_show_canvas_only")->shortcut().toString()), QIcon()); } else { main->restoreState(d->canvasState); } } void KisViewManager::toggleTabletLogger() { d->inputManager.toggleTabletLogger(); } void KisViewManager::openResourcesDirectory() { QString dir = KoResourcePaths::locateLocal("data", "krita"); QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); } void KisViewManager::updateIcons() { if (mainWindow()) { QList dockers = mainWindow()->dockWidgets(); Q_FOREACH (QDockWidget* dock, dockers) { dbgKrita << "name " << dock->objectName(); KoDockWidgetTitleBar* titlebar = dynamic_cast(dock->titleBarWidget()); if (titlebar) { titlebar->updateIcons(); } QObjectList objects; objects.append(dock); while (!objects.isEmpty()) { QObject* object = objects.takeFirst(); objects.append(object->children()); KisIconUtils::updateIconCommon(object); } } } } void KisViewManager::makeStatusBarVisible() { d->mainWindow->statusBar()->setVisible(true); } void KisViewManager::guiUpdateTimeout() { d->nodeManager.updateGUI(); d->selectionManager.updateGUI(); d->filterManager.updateGUI(); if (zoomManager()) { zoomManager()->updateGUI(); } d->gridManager.updateGUI(); d->actionManager.updateGUI(); } void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->currentImageView) return; d->currentImageView->showFloatingMessageImpl(message, icon, timeout, priority, alignment); emit floatingMessageRequested(message, icon.name()); } KisMainWindow *KisViewManager::mainWindow() const { return qobject_cast(d->mainWindow); } void KisViewManager::showHideScrollbars() { if (!d->currentImageView) return; if (!d->currentImageView->canvasController()) return; KisConfig cfg; bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked(); if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } void KisViewManager::slotSaveShowRulersState(bool value) { KisConfig cfg; cfg.setShowRulers(value); } void KisViewManager::slotSaveRulersTrackMouseState(bool value) { KisConfig cfg; cfg.setRulersTrackMouse(value); } void KisViewManager::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisViewManager::changeAuthorProfile(const QString &profileName) { KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); if (profileName.isEmpty()) { appAuthorGroup.writeEntry("active-profile", ""); } else if (profileName == i18nc("choice for author profile", "Anonymous")) { appAuthorGroup.writeEntry("active-profile", "anonymous"); } else { appAuthorGroup.writeEntry("active-profile", profileName); } appAuthorGroup.sync(); Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) { doc->documentInfo()->updateParameters(); } } void KisViewManager::slotUpdateAuthorProfileActions() { Q_ASSERT(d->actionAuthor); if (!d->actionAuthor) { return; } d->actionAuthor->clear(); d->actionAuthor->addAction(i18n("Default Author Profile")); d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous")); KConfigGroup authorGroup(KoGlobal::calligraConfig(), "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); Q_FOREACH (const QString &profile , profiles) { d->actionAuthor->addAction(profile); } KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); QString profileName = appAuthorGroup.readEntry("active-profile", ""); if (profileName == "anonymous") { d->actionAuthor->setCurrentItem(1); } else if (profiles.contains(profileName)) { d->actionAuthor->setCurrentAction(profileName); } else { d->actionAuthor->setCurrentItem(0); } } diff --git a/libs/ui/kis_animation_exporter.cpp b/libs/ui/kis_animation_exporter.cpp index 633f316ef5..30af0a6963 100644 --- a/libs/ui/kis_animation_exporter.cpp +++ b/libs/ui/kis_animation_exporter.cpp @@ -1,216 +1,230 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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_animation_exporter.h" -#include #include #include #include +#include #include "KoFileDialog.h" #include "KisDocument.h" #include "kis_image.h" #include "KisImportExportManager.h" #include "kis_image_animation_interface.h" #include "KisPart.h" #include "kis_paint_layer.h" #include "kis_group_layer.h" #include "kis_time_range.h" #include "kis_painter.h" struct KisAnimationExporterUI::Private { QWidget *parentWidget; - QProgressDialog progressDialog; KisAnimationExporter *exporter; Private(QWidget *parent) : parentWidget(parent), - progressDialog(i18n("Exporting frames..."), i18n("Cancel"), 0, 0), exporter(0) {} }; KisAnimationExporterUI::KisAnimationExporterUI(QWidget *parent) : m_d(new Private(parent)) { - connect(&m_d->progressDialog, SIGNAL(canceled()), this, SLOT(cancel())); } KisAnimationExporterUI::~KisAnimationExporterUI() { if (m_d->exporter) { delete m_d->exporter; } } -void KisAnimationExporterUI::exportSequence(KisDocument *document) +KisImportExportFilter::ConversionStatus KisAnimationExporterUI::exportSequence(KisDocument *document) { KoFileDialog dialog(m_d->parentWidget, KoFileDialog::SaveFile, "krita/exportsequence"); dialog.setCaption(i18n("Export sequence")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export)); QString filename = dialog.filename(); - if (filename.isEmpty()) return; + if (filename.isEmpty()) return KisImportExportFilter::FileNotFound; const KisTimeRange fullClipRange = document->image()->animationInterface()->fullClipRange(); int firstFrame = fullClipRange.start(); int lastFrame = fullClipRange.end(); - m_d->progressDialog.setWindowModality(Qt::ApplicationModal); - m_d->progressDialog.setMinimum(firstFrame); - m_d->progressDialog.setMaximum(lastFrame); - m_d->progressDialog.setValue(firstFrame); - m_d->exporter = new KisAnimationExporter(document, filename, firstFrame, lastFrame); - connect(m_d->exporter, SIGNAL(sigExportProgress(int)), this, SLOT(progress(int))); - - m_d->exporter->startExport(); - m_d->progressDialog.exec(); -} - -void KisAnimationExporterUI::progress(int currentFrame) -{ - m_d->progressDialog.setValue(currentFrame); -} - -void KisAnimationExporterUI::cancel() -{ - if (m_d->exporter) { - m_d->exporter->stopExport(); - } + return m_d->exporter->exportAnimation(); } struct KisAnimationExporter::Private { KisDocument *document; KisImageWSP image; QString filenamePrefix; QString filenameSuffix; int firstFrame; int currentFrame; int lastFrame; QScopedPointer tmpDoc; KisImageWSP tmpImage; KisPaintDeviceSP tmpDevice; bool exporting; + bool batchMode; + KisImportExportFilter::ConversionStatus status; + QMutex mutex; QWaitCondition exportFinished; Private(KisDocument *document, int fromTime, int toTime) : document(document), image(document->image()), firstFrame(fromTime), lastFrame(toTime), tmpDoc(KisPart::instance()->createDocument()), - exporting(false) + exporting(false), + batchMode(false) { tmpDoc->setAutoSave(0); tmpImage = new KisImage(tmpDoc->createUndoStore(), image->bounds().width(), image->bounds().height(), image->colorSpace(), QString()); tmpImage->setResolution(image->xRes(), image->yRes()); tmpDoc->setCurrentImage(tmpImage); KisPaintLayer* paintLayer = new KisPaintLayer(tmpImage, "paint device", 255); tmpImage->addNode(paintLayer, tmpImage->rootLayer(), KisLayerSP(0)); tmpDevice = paintLayer->paintDevice(); } }; KisAnimationExporter::KisAnimationExporter(KisDocument *document, const QString &baseFilename, int fromTime, int toTime) : m_d(new Private(document, fromTime, toTime)) { int baseLength = baseFilename.lastIndexOf("."); if (baseLength > -1) { m_d->filenamePrefix = baseFilename.left(baseLength); m_d->filenameSuffix = baseFilename.right(baseFilename.length() - baseLength); } else { m_d->filenamePrefix = baseFilename; } + m_d->batchMode = document->fileBatchMode(); QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(baseFilename); QString mimefilter = mime.name(); m_d->tmpDoc->setOutputMimeType(mimefilter.toLatin1()); - m_d->tmpDoc->setSaveInBatchMode(true); + m_d->tmpDoc->setFileBatchMode(true); connect(this, SIGNAL(sigFrameReadyToSave()), this, SLOT(frameReadyToSave()), Qt::QueuedConnection); } KisAnimationExporter::~KisAnimationExporter() { } -void KisAnimationExporter::startExport() +KisImportExportFilter::ConversionStatus KisAnimationExporter::exportAnimation() { + + if (!m_d->batchMode) { + emit m_d->document->statusBarMessage(i18n("Export frames")); + emit m_d->document->sigProgress(0); + connect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); + } + m_d->status = KisImportExportFilter::OK; m_d->exporting = true; m_d->currentFrame = m_d->firstFrame; connect(m_d->image->animationInterface(), SIGNAL(sigFrameReady(int)), this, SLOT(frameReadyToCopy(int)), Qt::DirectConnection); - m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); + + QEventLoop loop; + loop.connect(this, SIGNAL(sigFinished()), SLOT(quit())); + loop.exec(); + + return m_d->status; } void KisAnimationExporter::stopExport() { if (!m_d->exporting) return; m_d->exporting = false; disconnect(m_d->image->animationInterface(), 0, this, 0); + + if (!m_d->batchMode) { + disconnect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); + emit m_d->document->sigProgress(100); + emit m_d->document->clearStatusBarMessage(); + } + emit sigFinished(); +} + +void KisAnimationExporter::cancel() +{ + m_d->status = KisImportExportFilter::ProgressCancelled; + stopExport(); } void KisAnimationExporter::frameReadyToCopy(int time) { if (time != m_d->currentFrame) return; QRect rc = m_d->image->bounds(); KisPainter::copyAreaOptimized(rc.topLeft(), m_d->image->projection(), m_d->tmpDevice, rc); emit sigFrameReadyToSave(); } void KisAnimationExporter::frameReadyToSave() { QString frameNumber = QString("%1").arg(m_d->currentFrame, 4, 10, QChar('0')); QString filename = m_d->filenamePrefix + frameNumber + m_d->filenameSuffix; - m_d->tmpDoc->exportDocument(QUrl::fromLocalFile(filename)); - - emit sigExportProgress(m_d->currentFrame); - if (m_d->exporting && m_d->currentFrame < m_d->lastFrame) { - m_d->currentFrame++; - m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); + if (m_d->tmpDoc->exportDocument(QUrl::fromLocalFile(filename))) { + + if (m_d->exporting && m_d->currentFrame < m_d->lastFrame) { + if (!m_d->batchMode) { + emit m_d->document->sigProgress((m_d->currentFrame - m_d->firstFrame) * 100 / + (m_d->lastFrame - m_d->firstFrame)); + } + m_d->currentFrame++; + m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); + return; //continue + } } else { - stopExport(); + //error + m_d->status = KisImportExportFilter::InternalError; } + stopExport(); //finish } #include "kis_animation_exporter.moc" diff --git a/libs/ui/kis_animation_exporter.h b/libs/ui/kis_animation_exporter.h index da1fe58efc..544e65ad35 100644 --- a/libs/ui/kis_animation_exporter.h +++ b/libs/ui/kis_animation_exporter.h @@ -1,73 +1,70 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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_ANIMATION_EXPORTER_H #define KIS_ANIMATION_EXPORTER_H #include "kis_types.h" #include "kritaui_export.h" +#include class KisDocument; class KRITAUI_EXPORT KisAnimationExporterUI : public QObject { Q_OBJECT public: KisAnimationExporterUI(QWidget *parent); ~KisAnimationExporterUI(); - void exportSequence(KisDocument *document); - -private Q_SLOTS: - void progress(int currentFrame); - void cancel(); + KisImportExportFilter::ConversionStatus exportSequence(KisDocument *document); private: struct Private; QScopedPointer m_d; }; class KRITAUI_EXPORT KisAnimationExporter : public QObject { Q_OBJECT public: KisAnimationExporter(KisDocument *document, const QString &baseFilename, int fromTime, int toTime); ~KisAnimationExporter(); - void startExport(); + KisImportExportFilter::ConversionStatus exportAnimation(); void stopExport(); Q_SIGNALS: - void sigExportProgress(int currentFrame); - // Internal, used for getting back to main thread void sigFrameReadyToSave(); + void sigFinished(); private Q_SLOTS: void frameReadyToCopy(int time); void frameReadyToSave(); + void cancel(); private: struct Private; QScopedPointer m_d; }; #endif diff --git a/libs/ui/kis_animation_importer.cpp b/libs/ui/kis_animation_importer.cpp index 1a504726d8..3d618c3a8b 100644 --- a/libs/ui/kis_animation_importer.cpp +++ b/libs/ui/kis_animation_importer.cpp @@ -1,87 +1,132 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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_animation_importer.h" +#include + #include "KoColorSpace.h" #include "KisPart.h" #include "KisDocument.h" #include "kis_image.h" #include "kis_undo_adapter.h" #include "kis_paint_layer.h" #include "kis_group_layer.h" #include "kis_raster_keyframe_channel.h" #include "commands/kis_image_layer_add_command.h" struct KisAnimationImporter::Private { KisImageSP image; + KisDocument *document; + bool stop; }; KisAnimationImporter::KisAnimationImporter(KisImageSP image) : m_d(new Private()) { + m_d->document= 0; m_d->image = image; + m_d->stop = false; +} + +KisAnimationImporter::KisAnimationImporter(KisDocument* document) + : m_d(new Private()) +{ + m_d->document= document; + m_d->image = document->image(); + m_d->stop = false; } KisAnimationImporter::~KisAnimationImporter() {} -bool KisAnimationImporter::import(QStringList files, int firstFrame, int step) +KisImportExportFilter::ConversionStatus KisAnimationImporter::import(QStringList files, int firstFrame, int step) { Q_ASSERT(step > 0); + bool batchMode; + + if (m_d->document) { + batchMode= m_d->document->fileBatchMode(); + + if (!batchMode) { + emit m_d->document->statusBarMessage(i18n("Import frames")); + emit m_d->document->sigProgress(0); + connect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); + } + } else batchMode = false; + m_d->image->lock(); KisUndoAdapter *undo = m_d->image->undoAdapter(); undo->beginMacro(kundo2_i18n("Import animation")); KUndo2Command *undoCommand = new KUndo2Command(kundo2_i18n("Import frames")); QScopedPointer importDoc(KisPart::instance()->createDocument()); + importDoc->setFileBatchMode(true); - bool errors = false; + KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK; int frame = firstFrame; KisRasterKeyframeChannel *contentChannel = 0; Q_FOREACH(QString file, files) { bool successfullyLoaded = importDoc->openUrl(QUrl::fromLocalFile(file), KisDocument::OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES); if (!successfullyLoaded) { - errors = true; + status = KisImportExportFilter::InternalError; + break; + } + + if (m_d->stop) { + status = KisImportExportFilter::ProgressCancelled; break; } if (frame == firstFrame) { const KoColorSpace *cs = importDoc->image()->colorSpace(); KisPaintLayerSP paintLayer = new KisPaintLayer(m_d->image, m_d->image->nextLayerName(), OPACITY_OPAQUE_U8, cs); undo->addCommand(new KisImageLayerAddCommand(m_d->image, paintLayer, m_d->image->rootLayer(), m_d->image->rootLayer()->childCount())); paintLayer->enableAnimation(); contentChannel = qobject_cast(paintLayer->getKeyframeChannel(KisKeyframeChannel::Content.id())); } + if (!batchMode) { + emit m_d->document->sigProgress((frame - firstFrame) * 100 / (step * files.size())); + } contentChannel->addKeyframe(frame, undoCommand); contentChannel->importFrame(frame, importDoc->image()->projection(), undoCommand); frame += step; } + if (!batchMode) { + disconnect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); + emit m_d->document->sigProgress(100); + emit m_d->document->clearStatusBarMessage(); + } undo->endMacro(); m_d->image->unlock(); - return !errors; + return status; +} + +void KisAnimationImporter::cancel() +{ + m_d->stop = true; } diff --git a/libs/ui/kis_animation_importer.h b/libs/ui/kis_animation_importer.h index ad2f5b8285..42d9807118 100644 --- a/libs/ui/kis_animation_importer.h +++ b/libs/ui/kis_animation_importer.h @@ -1,43 +1,48 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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_ANIMATION_IMPORTER_H #define KIS_ANIMATION_IMPORTER_H #include "kis_types.h" #include "kritaui_export.h" +#include class KisDocument; class KisMainWindow; class KRITAUI_EXPORT KisAnimationImporter : public QObject { Q_OBJECT public: KisAnimationImporter(KisImageSP image); + KisAnimationImporter(KisDocument* document); ~KisAnimationImporter(); - bool import(QStringList files, int firstFrame, int step); + KisImportExportFilter::ConversionStatus import(QStringList files, int firstFrame, int step); + +private Q_SLOTS: + void cancel(); private: struct Private; QScopedPointer m_d; }; #endif diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc index a17781892b..90544f297e 100644 --- a/libs/ui/kis_layer_manager.cc +++ b/libs/ui/kis_layer_manager.cc @@ -1,907 +1,907 @@ /* * Copyright (C) 2006 Boudewijn Rempt * * 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_layer_manager.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 #include #include #include #include #include "KisImportExportManager.h" #include "kis_config.h" #include "kis_cursor.h" #include "dialogs/kis_dlg_adj_layer_props.h" #include "dialogs/kis_dlg_adjustment_layer.h" #include "dialogs/kis_dlg_layer_properties.h" #include "dialogs/kis_dlg_generator_layer.h" #include "dialogs/kis_dlg_file_layer.h" #include "dialogs/kis_dlg_layer_style.h" #include "KisDocument.h" #include "kis_filter_manager.h" #include "kis_node_visitor.h" #include "kis_paint_layer.h" #include "commands/kis_image_commands.h" #include "commands/kis_layer_commands.h" #include "commands/kis_node_commands.h" #include "kis_canvas_resource_provider.h" #include "kis_selection_manager.h" #include "kis_statusbar.h" #include "KisViewManager.h" #include "kis_zoom_manager.h" #include "canvas/kis_canvas2.h" #include "widgets/kis_meta_data_merge_strategy_chooser_widget.h" #include "widgets/kis_wdg_generator.h" #include "kis_progress_widget.h" #include "kis_node_commands_adapter.h" #include "kis_node_manager.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisPart.h" #include "kis_signal_compressor_with_param.h" #include "kis_abstract_projection_plane.h" #include "commands_new/kis_set_layer_style_command.h" #include "kis_post_execution_undo_adapter.h" #include "kis_selection_mask.h" class KisSaveGroupVisitor : public KisNodeVisitor { public: KisSaveGroupVisitor(KisImageWSP image, bool saveInvisible, bool saveTopLevelOnly, const QUrl &url, const QString &baseName, const QString &extension, const QString &mimeFilter) : m_image(image) , m_saveInvisible(saveInvisible) , m_saveTopLevelOnly(saveTopLevelOnly) , m_url(url) , m_baseName(baseName) , m_extension(extension) , m_mimeFilter(mimeFilter) { } virtual ~KisSaveGroupVisitor() { } public: bool visit(KisNode* ) { return true; } bool visit(KisPaintLayer *) { return true; } bool visit(KisAdjustmentLayer *) { return true; } bool visit(KisExternalLayer *) { return true; } bool visit(KisCloneLayer *) { return true; } bool visit(KisFilterMask *) { return true; } bool visit(KisTransformMask *) { return true; } bool visit(KisTransparencyMask *) { return true; } bool visit(KisGeneratorLayer * ) { return true; } bool visit(KisSelectionMask* ) { return true; } bool visit(KisGroupLayer *layer) { if (layer == m_image->rootLayer()) { KisLayerSP child = dynamic_cast(layer->firstChild().data()); while (child) { child->accept(*this); child = dynamic_cast(child->nextSibling().data()); } } else if (layer->visible() || m_saveInvisible) { QRect r = m_image->bounds(); KisDocument *d = KisPart::instance()->createDocument(); d->prepareForImport(); KisImageWSP dst = new KisImage(d->createUndoStore(), r.width(), r.height(), m_image->colorSpace(), layer->name()); dst->setResolution(m_image->xRes(), m_image->yRes()); d->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", layer->opacity()); KisPainter gc(paintLayer->paintDevice()); gc.bitBlt(QPoint(0, 0), layer->projection(), r); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->refreshGraph(); d->setOutputMimeType(m_mimeFilter.toLatin1()); - d->setSaveInBatchMode(true); + d->setFileBatchMode(true); QUrl url = m_url; url = url.adjusted(QUrl::RemoveFilename); url.setPath(url.path() + m_baseName + '_' + layer->name().replace(' ', '_') + '.' + m_extension); d->exportDocument(url); if (!m_saveTopLevelOnly) { KisGroupLayerSP child = dynamic_cast(layer->firstChild().data()); while (child) { child->accept(*this); child = dynamic_cast(child->nextSibling().data()); } } delete d; } return true; } private: KisImageWSP m_image; bool m_saveInvisible; bool m_saveTopLevelOnly; QUrl m_url; QString m_baseName; QString m_extension; QString m_mimeFilter; }; KisLayerManager::KisLayerManager(KisViewManager * view) : m_view(view) , m_imageView(0) , m_imageFlatten(0) , m_imageMergeLayer(0) , m_groupLayersSave(0) , m_imageResizeToLayer(0) , m_flattenLayer(0) , m_rasterizeLayer(0) , m_commandsAdapter(new KisNodeCommandsAdapter(m_view)) , m_layerStyle(0) { } KisLayerManager::~KisLayerManager() { delete m_commandsAdapter; } void KisLayerManager::setView(QPointerview) { m_imageView = view; } KisLayerSP KisLayerManager::activeLayer() { if (m_imageView) { return m_imageView->currentLayer(); } return 0; } KisPaintDeviceSP KisLayerManager::activeDevice() { if (activeLayer()) { return activeLayer()->paintDevice(); } return 0; } void KisLayerManager::activateLayer(KisLayerSP layer) { if (m_imageView) { emit sigLayerActivated(layer); layersUpdated(); if (layer) { m_view->resourceProvider()->slotNodeActivated(layer.data()); } } } void KisLayerManager::setup(KisActionManager* actionManager) { m_imageFlatten = actionManager->createAction("flatten_image"); connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage())); m_imageMergeLayer = actionManager->createAction("merge_layer"); connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer())); m_flattenLayer = actionManager->createAction("flatten_layer"); connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer())); m_rasterizeLayer = actionManager->createAction("rasterize_layer"); connect(m_rasterizeLayer, SIGNAL(triggered()), this, SLOT(rasterizeLayer())); m_groupLayersSave = actionManager->createAction("save_groups_as_images"); connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers())); m_imageResizeToLayer = actionManager->createAction("resizeimagetolayer"); connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer())); KisAction *action = actionManager->createAction("trim_to_image"); connect(action, SIGNAL(triggered()), this, SLOT(trimToImage())); m_layerStyle = actionManager->createAction("layer_style"); connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle())); } void KisLayerManager::updateGUI() { KisImageWSP image = m_view->image(); KisLayerSP layer; qint32 nlayers = 0; if (image) { layer = activeLayer(); nlayers = image->nlayers(); } // XXX these should be named layer instead of image m_imageFlatten->setEnabled(nlayers > 1); m_imageMergeLayer->setEnabled(nlayers > 1 && layer && layer->prevSibling()); m_flattenLayer->setEnabled(nlayers > 1 && layer && layer->firstChild()); if (m_view->statusBar()) m_view->statusBar()->setProfile(image); } void KisLayerManager::imageResizeToActiveLayer() { KisLayerSP layer; KisImageWSP image = m_view->image(); if (image && (layer = activeLayer())) { QRect cropRect = layer->projection()->nonDefaultPixelArea(); if (!cropRect.isEmpty()) { image->cropImage(cropRect); } else { m_view->showFloatingMessage( i18nc("floating message in layer manager", "Layer is empty "), QIcon(), 2000, KisFloatingMessage::Low); } } } void KisLayerManager::trimToImage() { KisImageWSP image = m_view->image(); if (image) { image->cropImage(image->bounds()); } } void KisLayerManager::layerProperties() { if (!m_view) return; if (!m_view->document()) return; KisLayerSP layer = activeLayer(); QList selectedNodes = m_view->nodeManager()->selectedNodes(); const bool multipleLayersSelected = selectedNodes.size() > 1; if (!layer) return; KisAdjustmentLayerSP alayer = KisAdjustmentLayerSP(dynamic_cast(layer.data())); KisGeneratorLayerSP glayer = KisGeneratorLayerSP(dynamic_cast(layer.data())); if (alayer && !multipleLayersSelected) { KisPaintDeviceSP dev = alayer->projection(); KisDlgAdjLayerProps dlg(alayer, alayer.data(), dev, m_view, alayer->filter().data(), alayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops"); dlg.resize(dlg.minimumSizeHint()); KisSafeFilterConfigurationSP configBefore(alayer->filter()); KIS_ASSERT_RECOVER_RETURN(configBefore); QString xmlBefore = configBefore->toXML(); if (dlg.exec() == QDialog::Accepted) { alayer->setName(dlg.layerName()); KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(alayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, false); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } else { KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { alayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data())); alayer->setDirty(); } } } else if (glayer && !multipleLayersSelected) { KisDlgGeneratorLayer dlg(glayer->name(), m_view, m_view->mainWindow()); dlg.setCaption(i18n("Fill Layer Properties")); KisSafeFilterConfigurationSP configBefore(glayer->filter()); Q_ASSERT(configBefore); QString xmlBefore = configBefore->toXML(); dlg.setConfiguration(configBefore.data()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { glayer->setName(dlg.layerName()); KisSafeFilterConfigurationSP configAfter(dlg.configuration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(glayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, true); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } } else { // If layer == normal painting layer, vector layer, or group layer QList selectedNodes = m_view->nodeManager()->selectedNodes(); KisDlgLayerProperties *dialog = new KisDlgLayerProperties(selectedNodes, m_view); dialog->resize(dialog->minimumSizeHint()); dialog->setAttribute(Qt::WA_DeleteOnClose); Qt::WindowFlags flags = dialog->windowFlags(); dialog->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog); dialog->show(); } } void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source) { KisImageWSP image = m_view->image(); if (!image) return; KisPaintDeviceSP srcDevice = source->paintDevice() ? source->projection() : source->original(); if (!srcDevice) return; KisPaintDeviceSP clone; if (!(*srcDevice->colorSpace() == *srcDevice->compositionSourceColorSpace())) { clone = new KisPaintDevice(srcDevice->compositionSourceColorSpace()); QRect rc(srcDevice->extent()); KisPainter::copyAreaOptimized(rc.topLeft(), srcDevice, clone, rc); } else { clone = new KisPaintDevice(*srcDevice); } KisLayerSP layer = new KisPaintLayer(image, source->name(), source->opacity(), clone); layer->setCompositeOpId(source->compositeOpId()); KisNodeSP parent = source->parent(); KisNodeSP above = source; while (parent && !parent->allowAsChild(layer)) { above = above->parent(); parent = above ? above->parent() : 0; } m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a Paint Layer")); m_commandsAdapter->addNode(layer, parent, above); m_commandsAdapter->removeNode(source); m_commandsAdapter->endMacro(); } void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above) { Q_ASSERT(activeNode); parent = activeNode; above = parent->lastChild(); while (parent && (!parent->allowAsChild(node) || parent->userLocked())) { above = parent; parent = parent->parent(); } if (!parent) { warnKrita << "KisLayerManager::adjustLayerPosition:" << "No node accepted newly created node"; parent = m_view->image()->root(); above = parent->lastChild(); } } void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisLayerSP layer, bool updateImage) { KisNodeSP parent; KisNodeSP above; adjustLayerPosition(layer, activeNode, parent, above); m_commandsAdapter->addNode(layer, parent, above, updateImage, updateImage); } KisLayerSP KisLayerManager::constructDefaultLayer() { KisImageWSP image = m_view->image(); return new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace()); } KisLayerSP KisLayerManager::addLayer(KisNodeSP activeNode) { KisLayerSP layer = constructDefaultLayer(); addLayerCommon(activeNode, layer, false); return layer; } void KisLayerManager::addGroupLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisGroupLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8), false); } void KisLayerManager::addCloneLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisCloneLayer(activeLayer(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8)); } void KisLayerManager::addShapeLayer(KisNodeSP activeNode) { if (!m_view) return; if (!m_view->document()) return; KisImageWSP image = m_view->image(); KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8); addLayerCommon(activeNode, layer, false); } void KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisSelectionSP selection = m_view->selection(); KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection); image->refreshGraph(); KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original()); KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(), i18n("New Filter Layer"), m_view); dlg.resize(dlg.minimumSizeHint()); // ensure that the device may be free'd by the dialog // when it is not needed anymore previewDevice = 0; if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) { // XXX: add messagebox warning if there's no filter set! m_commandsAdapter->undoLastCommand(); } else { adjl->setName(dlg.layerName()); } } KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name, KisFilterConfiguration * filter, KisSelectionSP selection) { KisImageWSP image = m_view->image(); KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection); addLayerCommon(activeNode, layer); return layer; } void KisLayerManager::addGeneratorLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { KisSelectionSP selection = m_view->selection(); KisFilterConfiguration * generator = dlg.configuration(); QString name = dlg.layerName(); addLayerCommon(activeNode, new KisGeneratorLayer(image, name, generator, selection)); } } void KisLayerManager::rotateLayer(double radians) { if (!m_view->image()) return; KisLayerSP layer = activeLayer(); if (!layer) return; m_view->image()->rotateNode(layer, radians); } void KisLayerManager::shearLayer(double angleX, double angleY) { if (!m_view->image()) return; KisLayerSP layer = activeLayer(); if (!layer) return; m_view->image()->shearNode(layer, angleX, angleY); } void KisLayerManager::flattenImage() { KisImageWSP image = m_view->image(); if (image) { bool doIt = true; if (image->nHiddenLayers() > 0) { int answer = QMessageBox::warning(m_view->mainWindow(), i18nc("@title:window", "Flatten Image"), i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer != QMessageBox::Yes) { doIt = false; } } if (doIt) { image->flatten(); } } } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image) { bool result = false; KisNodeSP prevNode = currentNode->prevSibling(); if (isSelectionMask(currentNode) && prevNode && isSelectionMask(prevNode)) { QList mergedNodes; mergedNodes.append(currentNode); mergedNodes.append(prevNode); image->mergeMultipleLayers(mergedNodes, currentNode); result = true; } return result; } void KisLayerManager::mergeLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; QList selectedNodes = m_view->nodeManager()->selectedNodes(); if (selectedNodes.size() > 1) { image->mergeMultipleLayers(selectedNodes, m_view->activeNode()); } else if (!tryMergeSelectionMasks(m_view->activeNode(), image)) { if (!layer->prevSibling()) return; KisLayer *prevLayer = dynamic_cast(layer->prevSibling().data()); if (!prevLayer) return; if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) { image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop")); } else { const KisMetaData::MergeStrategy* strategy = KisMetaDataMergeStrategyChooserWidget::showDialog(m_view->mainWindow()); if (!strategy) return; image->mergeDown(layer, strategy); } } m_view->updateGUI(); } void KisLayerManager::flattenLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; image->flattenLayer(layer); m_view->updateGUI(); } void KisLayerManager::rasterizeLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity()); KisPainter gc(paintLayer->paintDevice()); QRect rc = layer->projection()->exactBounds(); gc.bitBlt(rc.topLeft(), layer->projection(), rc); m_commandsAdapter->beginMacro(kundo2_i18n("Rasterize Layer")); m_commandsAdapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); int childCount = layer->childCount(); for (int i = 0; i < childCount; i++) { m_commandsAdapter->moveNode(layer->firstChild(), paintLayer, paintLayer->lastChild()); } m_commandsAdapter->removeNode(layer); m_commandsAdapter->endMacro(); updateGUI(); } void KisLayerManager::layersUpdated() { KisLayerSP layer = activeLayer(); if (!layer) return; m_view->updateGUI(); } void KisLayerManager::saveGroupLayers() { QStringList listMimeFilter = KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export); KoDialog dlg; QWidget *page = new QWidget(&dlg); dlg.setMainWidget(page); QBoxLayout *layout = new QVBoxLayout(page); KisUrlRequester *urlRequester = new KisUrlRequester(page); urlRequester->setStartDir(QFileInfo(m_view->document()->url().toLocalFile()).absolutePath()); urlRequester->setMimeTypeFilters(listMimeFilter); urlRequester->setUrl(m_view->document()->url()); layout->addWidget(urlRequester); QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page); chkInvisible->setChecked(false); layout->addWidget(chkInvisible); QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page); chkDepth->setChecked(true); layout->addWidget(chkDepth); if (!dlg.exec()) return; // selectedUrl()( does not return the expuected result. So, build up the QUrl the more complicated way //return m_fileWidget->selectedUrl(); QUrl url = urlRequester->url(); QFileInfo f(url.toLocalFile()); QMimeDatabase db; QMimeType mime = db.mimeTypeForUrl(urlRequester->url()); QString mimefilter = mime.name(); QString extension = db.suffixForFileName(urlRequester->url().toLocalFile()); QString basename = f.baseName(); if (url.isEmpty()) return; KisImageWSP image = m_view->image(); if (!image) return; KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), url, basename, extension, mimefilter); image->rootLayer()->accept(v); } bool KisLayerManager::activeLayerHasSelection() { return (activeLayer()->selection() != 0); } void KisLayerManager::addFileLayer(KisNodeSP activeNode) { QString basePath; QUrl url = m_view->document()->url(); if (url.isLocalFile()) { basePath = QFileInfo(url.toLocalFile()).absolutePath(); } KisImageWSP image = m_view->image(); KisDlgFileLayer dlg(basePath, image->nextLayerName(), m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { QString name = dlg.layerName(); QString fileName = dlg.fileName(); if(fileName.isEmpty()){ QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified")); return; } KisFileLayer::ScalingMethod scalingMethod = dlg.scaleToImageResolution(); addLayerCommon(activeNode, new KisFileLayer(image, basePath, fileName, scalingMethod, name, OPACITY_OPAQUE_U8)); } } void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg) { KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone()); } void KisLayerManager::layerStyle() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; KisPSDLayerStyleSP oldStyle; if (layer->layerStyle()) { oldStyle = layer->layerStyle()->clone(); } else { oldStyle = toQShared(new KisPSDLayerStyle()); } KisDlgLayerStyle dlg(oldStyle->clone(), m_view->resourceProvider()); std::function updateCall(std::bind(updateLayerStyles, layer, &dlg)); SignalToFunctionProxy proxy(updateCall); connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start())); if (dlg.exec() == QDialog::Accepted) { KisPSDLayerStyleSP newStyle = dlg.style(); KUndo2CommandSP command = toQShared( new KisSetLayerStyleCommand(layer, oldStyle, newStyle)); image->postExecutionUndoAdapter()->addCommand(command); } } diff --git a/libs/ui/kis_png_converter.h b/libs/ui/kis_png_converter.h index bbb2bdfd32..2bf277b74e 100644 --- a/libs/ui/kis_png_converter.h +++ b/libs/ui/kis_png_converter.h @@ -1,155 +1,136 @@ /* * Copyright (c) 2005, 2007 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_PNG_CONVERTER_H_ #define _KIS_PNG_CONVERTER_H_ #include #include #include #include #include "kis_types.h" #include "kis_global.h" #include "kis_annotation.h" #include +#include class KoStore; class KisDocument; class KoColorSpace; namespace KisMetaData { class Filter; class Store; } struct KisPNGOptions { KisPNGOptions() : compression(0) , interlace(false) , alpha(true) , exif(true) , iptc(true) , xmp(true) , tryToSaveAsIndexed(true) , saveSRGBProfile(false) , forceSRGB(false) , transparencyFillColor(Qt::white) {} int compression; bool interlace; bool alpha; bool exif; bool iptc; bool xmp; bool tryToSaveAsIndexed; bool saveSRGBProfile; bool forceSRGB; QList filters; QColor transparencyFillColor; }; -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 -}; - /** * This class allows to import/export a PNG from either a file or a QIODevice. */ // XXX_PROGRESS (pass KoUpdater to the png converter) class KRITAUI_EXPORT KisPNGConverter : public QObject { Q_OBJECT public: /** * Initialize the converter. * @param doc the KisDocument related to the image, can be null if you don't have a KisDocument * @param adapter the undo adapter to be used by the image, can be null if you don't want to use an undo adapter */ KisPNGConverter(KisDocument *doc, bool batchMode = false); virtual ~KisPNGConverter(); public: /** * Load an image from an URL. If the image is not on a local drive, the image is first downloaded to a * temporary location. * @param uri the url of the image */ KisImageBuilder_Result buildImage(const QUrl &uri); /** * Load an image from a QIODevice. * @param iod device to access the data */ KisImageBuilder_Result buildImage(QIODevice* iod); /** * Save a layer to a PNG * @param uri the url of the destination file * @param device the paint device to save * @param annotationsStart an iterator on the first annotation * @param annotationsEnd an iterator on the last annotation * @param compression a number between 0 and 9 to specify the compression rate (9 is most compressed) * @param interlace set to true if you want to generate an interlaced png * @param alpha set to true if you want to save the alpha channel */ KisImageBuilder_Result buildFile(const QUrl &uri, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData); KisImageBuilder_Result buildFile(QIODevice*, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData); /** * Retrieve the constructed image */ KisImageWSP image(); static bool saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store* metaData = 0); static bool isColorSpaceSupported(const KoColorSpace *cs); public Q_SLOTS: virtual void cancel(); private: void progress(png_structp png_ptr, png_uint_32 row_number, int pass); private: png_uint_32 m_max_row; KisImageWSP m_image; KisDocument *m_doc; bool m_stop; bool m_batchMode; QString m_path; }; #endif diff --git a/plugins/extensions/dockers/compositiondocker/compositiondocker_dock.cpp b/plugins/extensions/dockers/compositiondocker/compositiondocker_dock.cpp index 9f3c84f4cd..558610dc4f 100644 --- a/plugins/extensions/dockers/compositiondocker/compositiondocker_dock.cpp +++ b/plugins/extensions/dockers/compositiondocker/compositiondocker_dock.cpp @@ -1,298 +1,298 @@ /* * Copyright (c) 2012 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "compositiondocker_dock.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 "compositionmodel.h" CompositionDockerDock::CompositionDockerDock( ) : QDockWidget(i18n("Compositions")), m_canvas(0) { QWidget* widget = new QWidget(this); setupUi(widget); m_model = new CompositionModel(this); compositionView->setModel(m_model); compositionView->installEventFilter(this); deleteButton->setIcon(KisIconUtils::loadIcon("edit-delete")); saveButton->setIcon(KisIconUtils::loadIcon("list-add")); exportButton->setIcon(KisIconUtils::loadIcon("document-export")); deleteButton->setToolTip(i18n("Delete Composition")); saveButton->setToolTip(i18n("New Composition")); exportButton->setToolTip(i18n("Export Composition")); setWidget(widget); connect( compositionView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(activated ( const QModelIndex & ) ) ); compositionView->setContextMenuPolicy(Qt::CustomContextMenu); connect( compositionView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); connect( deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteClicked())); connect( saveButton, SIGNAL(clicked(bool)), this, SLOT(saveClicked())); connect( exportButton, SIGNAL(clicked(bool)), this, SLOT(exportClicked())); saveNameEdit->setPlaceholderText(i18n("Insert Name")); updateAction = new KisAction(i18n("Update Composition"), this); updateAction->setObjectName("update_composition"); connect(updateAction, SIGNAL(triggered()), this, SLOT(updateComposition())); renameAction = new KisAction(i18n("Rename Composition..."), this); renameAction->setObjectName("rename_composition"); connect(renameAction, SIGNAL(triggered()), this, SLOT(renameComposition())); m_actions.append(renameAction); } CompositionDockerDock::~CompositionDockerDock() { } void CompositionDockerDock::setCanvas(KoCanvasBase * canvas) { if (m_canvas && m_canvas->viewManager()) { Q_FOREACH (KisAction *action, m_actions) { m_canvas->viewManager()->actionManager()->takeAction(action); } } unsetCanvas(); setEnabled(canvas != 0); m_canvas = dynamic_cast(canvas); if (m_canvas && m_canvas->viewManager()) { Q_FOREACH (KisAction *action, m_actions) { m_canvas->viewManager()->actionManager()->addAction(action->objectName(), action); } updateModel(); } } void CompositionDockerDock::unsetCanvas() { setEnabled(false); m_canvas = 0; m_model->setCompositions(QList()); } void CompositionDockerDock::activated(const QModelIndex& index) { KisLayerComposition* composition = m_model->compositionFromIndex(index); composition->apply(); } void CompositionDockerDock::deleteClicked() { QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerComposition* composition = m_model->compositionFromIndex(index); m_canvas->viewManager()->image()->removeComposition(composition); updateModel(); } } void CompositionDockerDock::saveClicked() { KisImageWSP image = m_canvas->viewManager()->image(); if (!image) return; // format as 001, 002 ... QString name = saveNameEdit->text(); if (name.isEmpty()) { bool found = false; int i = 1; do { name = QString("%1").arg(i, 3, 10, QChar('0')); found = false; Q_FOREACH (KisLayerComposition* composition, m_canvas->viewManager()->image()->compositions()) { if (composition->name() == name) { found = true; break; } } i++; } while(found && i < 1000); } KisLayerComposition* composition = new KisLayerComposition(image, name); composition->store(); image->addComposition(composition); saveNameEdit->clear(); updateModel(); compositionView->setCurrentIndex(m_model->index(image->compositions().count()-1, 0)); image->setModified(); } void CompositionDockerDock::updateModel() { if (m_model && m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image()) { m_model->setCompositions(m_canvas->viewManager()->image()->compositions()); } } void CompositionDockerDock::exportClicked() { if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image()) { QString path; KoFileDialog dialog(0, KoFileDialog::OpenDirectory, "krita/compositiondockerdock"); dialog.setCaption(i18n("Select a Directory")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); path = dialog.filename(); if (path.isNull()) return; if (!path.endsWith('/')) { path.append('/'); } KisImageWSP image = m_canvas->viewManager()->image(); QString filename = m_canvas->viewManager()->document()->localFilePath(); if (!filename.isEmpty()) { QFileInfo info(filename); path += info.baseName() + '_'; } Q_FOREACH (KisLayerComposition* composition, m_canvas->viewManager()->image()->compositions()) { if (!composition->isExportEnabled()) { continue; } composition->apply(); image->refreshGraph(); image->lock(); #if 0 image->rootLayer()->projection()->convertToQImage(0, 0, 0, image->width(), image->height()).save(path + composition->name() + ".png"); #else QRect r = image->bounds(); KisDocument *d = KisPart::instance()->createDocument(); d->prepareForImport(); KisImageWSP dst = new KisImage(d->createUndoStore(), r.width(), r.height(), image->colorSpace(), composition->name()); dst->setResolution(image->xRes(), image->yRes()); d->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", OPACITY_OPAQUE_U8); KisPainter gc(paintLayer->paintDevice()); gc.bitBlt(QPoint(0, 0), image->rootLayer()->projection(), r); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->refreshGraph(); d->setOutputMimeType("image/png"); - d->setSaveInBatchMode(true); + d->setFileBatchMode(true); d->exportDocument(QUrl::fromLocalFile(path + composition->name() + ".png")); delete d; #endif image->unlock(); } } } bool CompositionDockerDock::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) { // new index will be set after the method is called QTimer::singleShot(0, this, SLOT(activateCurrentIndex())); } return false; } else { return QObject::eventFilter(obj, event); } } void CompositionDockerDock::activateCurrentIndex() { QModelIndex index = compositionView->currentIndex(); if (index.isValid()) { activated(index); } } void CompositionDockerDock::customContextMenuRequested(QPoint pos) { QMenu menu; menu.addAction(updateAction); menu.addAction(renameAction); menu.exec(compositionView->mapToGlobal(pos)); } void CompositionDockerDock::updateComposition() { QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerComposition* composition = m_model->compositionFromIndex(index); composition->store(); m_canvas->image()->setModified(); } } void CompositionDockerDock::renameComposition() { dbgKrita << "rename"; QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerComposition* composition = m_model->compositionFromIndex(index); bool ok; QString name = QInputDialog::getText(this, i18n("Rename Composition"), i18n("New Name:"), QLineEdit::Normal, composition->name(), &ok); if (ok && !name.isEmpty()) { composition->setName(name); m_canvas->image()->setModified(); } } } diff --git a/plugins/impex/csv/csv_loader.cpp b/plugins/impex/csv/csv_loader.cpp index 74dea03a03..040dc8eaf7 100644 --- a/plugins/impex/csv/csv_loader.cpp +++ b/plugins/impex/csv/csv_loader.cpp @@ -1,418 +1,457 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * 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 "csv_loader.h" #include #include #include #include #include -#include +#include #include +#include #include #include #include #include #include #include #include #include #include #include #include "csv_read_line.h" #include "csv_layer_record.h" -CSVLoader::CSVLoader(KisDocument *doc) +CSVLoader::CSVLoader(KisDocument *doc, bool batchMode) : m_image(0) , m_doc(doc) + , m_batchMode(batchMode) , m_stop(false) { } CSVLoader::~CSVLoader() { } KisImageBuilder_Result CSVLoader::decode(const QUrl &uri, const QString &filename) { QString field; int idx; int frame = 0; QString projName; int width = 0; int height = 0; int frameCount = 1; float framerate = 24.0; float pixelRatio = 1.0; int projNameIdx = -1; int widthIdx = -1; int heightIdx = -1; int frameCountIdx = -1; int framerateIdx = -1; int pixelRatioIdx = -1; QVector layers; // open the csv file QFile f(uri.toLocalFile()); if (!f.exists()) return KisImageBuilder_RESULT_NOT_EXIST; if (!f.open(QIODevice::ReadOnly)) return KisImageBuilder_RESULT_NOT_EXIST; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QString path = filename; if (path.right(4).toUpper() == ".CSV") path = path.left(path.size() - 4); //according to the QT docs, the slash is a universal directory separator path.append(".frames/"); KisImageBuilder_Result retval = KisImageBuilder_RESULT_OK; dbgFile << "pos:" << f.pos(); CSVReadLine readLine; QScopedPointer importDoc(KisPart::instance()->createDocument()); importDoc->setAutoSave(0); - importDoc->setSaveInBatchMode(true); + importDoc->setFileBatchMode(true); - QProgressDialog progress(i18n("Importing CSV..."), i18n("Cancel"), - 0, 0, KisPart::instance()->currentMainwindow(), 0); + KisView* setView(0); + if (!m_batchMode) { + //show the statusbar message even if no view + Q_FOREACH (KisView* view, KisPart::instance()->views()) { + if (view && view->document() == m_doc) { + setView= view; + break; + } + } + + if (!setView) { + QStatusBar *sb = KisPart::instance()->currentMainwindow()->statusBar(); + if (sb) { + sb->showMessage(i18n("Loading CSV file...")); + } + } else { + emit m_doc->statusBarMessage(i18n("Loading CSV file...")); + } + emit m_doc->sigProgress(0); + connect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); + } int step = 0; do { qApp->processEvents(); - if (progress.wasCanceled()) { - retval = KisImageBuilder_RESULT_FAILURE; + if (m_stop) { + retval = KisImageBuilder_RESULT_CANCEL; break; } if ((idx = readLine.nextLine(&f)) <= 0) { if ((idx < 0) ||(step < 5)) retval = KisImageBuilder_RESULT_FAILURE; break; } field = readLine.nextField(); //first field of the line if (field.isNull()) continue; //empty row switch (step) { case 0 : //skip first row step = 1; break; case 1 : //scene header names step = 2; for (idx = 0; !field.isNull(); idx++) { if (field == "Project Name") { projNameIdx = idx; } else if (field == "Width") { widthIdx = idx; } else if (field == "Height") { heightIdx = idx; } else if (field == "Frame Count") { frameCountIdx = idx; } else if (field == "Frame Rate") { framerateIdx = idx; } else if (field == "Pixel Aspect Ratio") { pixelRatioIdx = idx; } field= readLine.nextField(); } break; case 2 : //scene header values step= 3; for (idx= 0; !field.isNull(); idx++) { if (idx == projNameIdx) { projName = field; } else if (idx == widthIdx) { width = field.toInt(); } else if (idx == heightIdx) { height = field.toInt(); } else if (idx == frameCountIdx) { frameCount = field.toInt(); if (frameCount < 1) frameCount= 1; } else if (idx == framerateIdx) { framerate = field.toFloat(); } else if (idx == pixelRatioIdx) { pixelRatio = field.toFloat(); } field= readLine.nextField(); } if ((width < 1) || (height < 1)) { retval = KisImageBuilder_RESULT_FAILURE; break; } - progress.setWindowModality(Qt::ApplicationModal); - progress.setMinimumDuration(1000); - progress.setMaximum(frameCount); - progress.setValue(0); retval = createNewImage(width, height, pixelRatio, projName.isNull() ? filename : projName); break; case 3 : //create level headers if (field[0] != '#') break; for (; !(field = readLine.nextField()).isNull(); ) { CSVLayerRecord* layerRecord = new CSVLayerRecord(); layers.append(layerRecord); } readLine.rewind(); field = readLine.nextField(); step = 4; //no break! case 4 : //level header if (field == "#Layers") { //layer name for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->name = field; break; } if (field == "#Density") { //layer opacity for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->density = field.toFloat(); break; } if (field == "#Blending") { //layer blending mode for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->blending = field; break; } if (field == "#Visible") { //layer visibility for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->visible = field.toInt(); break; } if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break; step = 5; //no break! case 5 : //frames - progress.setValue(frame); if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break; for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) { CSVLayerRecord* layer = layers.at(idx); if (layer->last != field) { + if (!m_batchMode) { + emit m_doc->sigProgress((frame * layers.size() + idx) * 100 / + (frameCount * layers.size())); + } retval = setLayer(layer, importDoc.data(), path); layer->last = field; layer->frame = frame; } } frame++; break; } } while (retval == KisImageBuilder_RESULT_OK); //finish the layers if (retval == KisImageBuilder_RESULT_OK) { if (m_image) { KisImageAnimationInterface *animation = m_image->animationInterface(); if (frame > frameCount) frameCount = frame; animation->setFullClipRange(KisTimeRange::fromTime(0,frameCount - 1)); animation->setFramerate((int)framerate); } for (idx = 0; idx < layers.size(); idx++) { CSVLayerRecord* layer = layers.at(idx); //empty layers without any pictures are dropped if ((layer->frame > 0) || !layer->last.isEmpty()) { retval = setLayer(layer, importDoc.data(), path); if (retval != KisImageBuilder_RESULT_OK) break; } } } if (m_image) { //insert the existing layers by the right order for (idx = layers.size() - 1; idx >= 0; idx--) { CSVLayerRecord* layer = layers.at(idx); if (layer->layer) { m_image->addNode(layer->layer, m_image->root()); } } m_image->unlock(); } qDeleteAll(layers); f.close(); + + if (!m_batchMode) { + disconnect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); + emit m_doc->sigProgress(100); + + if (!setView) { + QStatusBar *sb = KisPart::instance()->currentMainwindow()->statusBar(); + if (sb) { + sb->clearMessage(); + } + } else { + emit m_doc->clearStatusBarMessage(); + } + } QApplication::restoreOverrideCursor(); return retval; } QString CSVLoader::convertBlending(const QString &blending) { if (blending == "Color") return COMPOSITE_OVER; if (blending == "Behind") return COMPOSITE_BEHIND; if (blending == "Erase") return COMPOSITE_ERASE; // "Shade" if (blending == "Light") return COMPOSITE_LINEAR_LIGHT; if (blending == "Colorize") return COMPOSITE_COLORIZE; if (blending == "Hue") return COMPOSITE_HUE; if (blending == "Add") return COMPOSITE_ADD; if (blending == "Sub") return COMPOSITE_INVERSE_SUBTRACT; if (blending == "Multiply") return COMPOSITE_MULT; if (blending == "Screen") return COMPOSITE_SCREEN; // "Replace" // "Subtitute" if (blending == "Difference") return COMPOSITE_DIFF; if (blending == "Divide") return COMPOSITE_DIVIDE; if (blending == "Overlay") return COMPOSITE_OVERLAY; if (blending == "Light2") return COMPOSITE_DODGE; if (blending == "Shade2") return COMPOSITE_BURN; if (blending == "HardLight") return COMPOSITE_HARD_LIGHT; if (blending == "SoftLight") return COMPOSITE_SOFT_LIGHT_PHOTOSHOP; if (blending == "GrainExtract") return COMPOSITE_GRAIN_EXTRACT; if (blending == "GrainMerge") return COMPOSITE_GRAIN_MERGE; if (blending == "Sub2") return COMPOSITE_SUBTRACT; if (blending == "Darken") return COMPOSITE_DARKEN; if (blending == "Lighten") return COMPOSITE_LIGHTEN; if (blending == "Saturation") return COMPOSITE_SATURATION; return COMPOSITE_OVER; } KisImageBuilder_Result CSVLoader::setLayer(CSVLayerRecord* layer, KisDocument *importDoc, const QString &path) { bool result = true; if (layer->channel == NULL) { //create a new document layer float opacity = layer->density; if (opacity > 1.0) opacity = 1.0; else if (opacity < 0.0) opacity = 0.0; const KoColorSpace* cs = m_image->colorSpace(); const QString layerName = (layer->name).isEmpty() ? m_image->nextLayerName() : layer->name; KisPaintLayer* paintLayer = new KisPaintLayer(m_image, layerName, (quint8)(opacity * OPACITY_OPAQUE_U8), cs); paintLayer->setCompositeOpId(convertBlending(layer->blending)); paintLayer->setVisible(layer->visible); paintLayer->enableAnimation(); layer->layer = paintLayer; layer->channel = qobject_cast (paintLayer->getKeyframeChannel(KisKeyframeChannel::Content.id())); } layer->channel->addKeyframe(layer->frame); if (!layer->last.isEmpty()) { //png image QString filename = path; filename.append(layer->last); result = importDoc->openUrl(QUrl::fromLocalFile(filename), - KisDocument::OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES); + KisDocument::OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES); if (result) layer->channel->importFrame(layer->frame, importDoc->image()->projection(), NULL); } return (result) ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE; } KisImageBuilder_Result CSVLoader::createNewImage(int width, int height, float ratio, const QString &name) { //the CSV is RGBA 8bits, sRGB if (!m_image) { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace( RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), 0); if (cs) m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, name); if (!m_image) return KisImageBuilder_RESULT_FAILURE; m_image->setResolution(ratio, 1.0); m_image->lock(); } return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result CSVLoader::buildAnimation(const QUrl &uri,const QString &filename) { if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!uri.isLocalFile()) return KisImageBuilder_RESULT_NOT_EXIST; return decode(uri, filename); } KisImageWSP CSVLoader::image() { return m_image; } + +void CSVLoader::cancel() +{ + m_stop = true; +} diff --git a/plugins/impex/csv/csv_loader.h b/plugins/impex/csv/csv_loader.h index ffb0c64026..b527be1531 100644 --- a/plugins/impex/csv/csv_loader.h +++ b/plugins/impex/csv/csv_loader.h @@ -1,76 +1,61 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * 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 CSV_LOADER_H_ #define CSV_LOADER_H_ #include #include #include "kis_image.h" #include "kritaui_export.h" +#include class KisDocument; #include "csv_layer_record.h" -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 - }; - class CSVLoader : public QObject { Q_OBJECT public: - CSVLoader(KisDocument* doc); + CSVLoader(KisDocument* doc, bool batchMode); virtual ~CSVLoader(); KisImageBuilder_Result buildAnimation(const QUrl &, const QString &); KisImageWSP image(); private: KisImageBuilder_Result decode(const QUrl &, const QString &); KisImageBuilder_Result setLayer(CSVLayerRecord* , KisDocument* ,const QString &); KisImageBuilder_Result createNewImage(int, int, float, const QString &); QString convertBlending(const QString &); +private Q_SLOTS: + void cancel(); + private: KisImageWSP m_image; KisDocument* m_doc; + bool m_batchMode; bool m_stop; }; #endif diff --git a/plugins/impex/csv/csv_saver.cpp b/plugins/impex/csv/csv_saver.cpp index 84095ddda0..b8f81d3680 100644 --- a/plugins/impex/csv/csv_saver.cpp +++ b/plugins/impex/csv/csv_saver.cpp @@ -1,449 +1,463 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * 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 "csv_saver.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 "csv_layer_record.h" -CSVSaver::CSVSaver(KisDocument *doc) +CSVSaver::CSVSaver(KisDocument *doc, bool batchMode) : m_image(doc->image()) , m_doc(doc) + , m_batchMode(batchMode) , m_stop(false) { } CSVSaver::~CSVSaver() { } KisImageWSP CSVSaver::image() { return m_image; } KisImageBuilder_Result CSVSaver::encode(const QUrl &uri,const QString &filename) { int idx; int start, end; KisNodeSP node; QByteArray ba; KisKeyframeSP keyframe; QVector layers; KisImageAnimationInterface *animation = m_image->animationInterface(); //open the csv file for writing QFile f(uri.toLocalFile()); if (!f.open(QIODevice::WriteOnly)) { return KisImageBuilder_RESULT_NOT_LOCAL; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //DataStream instead of TextStream for correct line endings QDataStream stream(&f); QString path = filename; if (path.right(4).toUpper() == ".CSV") path = path.left(path.size() - 4); path.append(".frames"); //create directory QDir dir(path); if (!dir.exists()) { dir.mkpath("."); } //according to the QT docs, the slash is a universal directory separator path.append("/"); m_image->lock(); node = m_image->rootLayer()->firstChild(); //TODO: correct handling of the layer tree. //for now, only top level paint layers are saved idx = 0; while (node) { if (node->inherits("KisPaintLayer")) { KisPaintLayer* paintLayer = dynamic_cast(node.data()); CSVLayerRecord* layerRecord = new CSVLayerRecord(); layers.prepend(layerRecord); //reverse order! layerRecord->name = paintLayer->name(); layerRecord->name.replace(QRegExp("[\"\\r\\n]"), "_"); if (layerRecord->name.isEmpty()) layerRecord->name= QString("Unnamed-%1").arg(idx); layerRecord->visible = (paintLayer->visible()) ? 1 : 0; layerRecord->density = (float)(paintLayer->opacity()) / OPACITY_OPAQUE_U8; layerRecord->blending = convertToBlending(paintLayer->compositeOpId()); layerRecord->layer = paintLayer; layerRecord->channel = paintLayer->projection()->keyframeChannel(); layerRecord->last = ""; layerRecord->frame = 0; idx++; } node = node->nextSibling(); } KisTimeRange range = animation->fullClipRange(); start = (range.isValid()) ? range.start() : 0; if (!range.isInfinite()) { end = range.end(); if (end < start) end = start; } else { //undefined length, searching for the last keyframe end = start; for (idx = 0; idx < layers.size(); idx++) { keyframe = layers.at(idx)->channel->lastKeyframe(); if ( (!keyframe.isNull()) && (keyframe->time() > end) ) end = keyframe->time(); } } //create temporary doc for exporting QScopedPointer exportDoc(KisPart::instance()->createDocument()); createTempImage(exportDoc.data()); KisImageBuilder_Result retval= KisImageBuilder_RESULT_OK; - QProgressDialog progress(i18n("Exporting CSV..."), i18n("Cancel"), - start, end, KisPart::instance()->currentMainwindow(), 0); - - progress.setWindowModality(Qt::ApplicationModal); - progress.setMinimumDuration(1000); - progress.setValue(start); - + if (!m_batchMode) { + emit m_doc->statusBarMessage(i18n("Saving CSV file...")); + emit m_doc->sigProgress(0); + connect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); + } int frame = start; int step = 0; do { qApp->processEvents(); - if (progress.wasCanceled()) { - retval = KisImageBuilder_RESULT_FAILURE; + if (m_stop) { + retval = KisImageBuilder_RESULT_CANCEL; break; } switch(step) { case 0 : //first row if (f.write("UTF-8, TVPaint, \"CSV 1.0\"\r\n") < 0) { retval = KisImageBuilder_RESULT_FAILURE; } break; case 1 : //scene header names if (f.write("Project Name, Width, Height, Frame Count, Layer Count, Frame Rate, Pixel Aspect Ratio, Field Mode\r\n") < 0) { retval = KisImageBuilder_RESULT_FAILURE; } break; case 2 : //scene header values ba = QString("\"%1\", ").arg(m_image->objectName()).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, %2, ").arg(m_image->width()).arg(m_image->height()).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, %2, ").arg(end - start + 1).arg(layers.size()).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } //the framerate is an integer here ba = QString("%1, ").arg((double)(animation->framerate()),0,'f',6).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, Progressive\r\n").arg((double)(m_image->xRes() / m_image->yRes()),0,'f',6).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } break; case 3 : //layer header values if (f.write("#Layers") < 0) { //Layers retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", \"%1\"").arg(layers.at(idx)->name).toUtf8(); if (f.write(ba.data()) < 0) break; } break; case 4 : if (f.write("\r\n#Density") < 0) { //Density retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", %1").arg((double)(layers.at(idx)->density), 0, 'f', 6).toUtf8(); if (f.write(ba.data()) < 0) break; } break; case 5 : if (f.write("\r\n#Blending") < 0) { //Blending retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", \"%1\"").arg(layers.at(idx)->blending).toUtf8(); if (f.write(ba.data()) < 0) break; } break; case 6 : if (f.write("\r\n#Visible") < 0) { //Visible retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", %1").arg(layers.at(idx)->visible).toUtf8(); if (f.write(ba.data()) < 0) break; } if (idx < layers.size()) { retval = KisImageBuilder_RESULT_FAILURE; } break; default : //frames - progress.setValue(frame); if (frame > end) { if (f.write("\r\n") < 0) retval = KisImageBuilder_RESULT_FAILURE; step = 8; break; } + ba = QString("\r\n#%1").arg(frame, 5, 10, QChar('0')).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { CSVLayerRecord *layer = layers.at(idx); keyframe = layer->channel->keyframeAt(frame); if (!keyframe.isNull()) { + if (!m_batchMode) { + emit m_doc->sigProgress(((frame - start) * layers.size() + idx) * 100 / + ((end - start) * layers.size())); + } retval = getLayer(layer, exportDoc.data(), keyframe, path, frame, idx); if (retval != KisImageBuilder_RESULT_OK) break; } ba = QString(", \"%1\"").arg(layer->last).toUtf8(); if (f.write(ba.data()) < 0) break; } if (idx < layers.size()) retval = KisImageBuilder_RESULT_FAILURE; frame++; step = 6; //keep step here break; } step++; } while((retval == KisImageBuilder_RESULT_OK) && (step < 8)); m_image->unlock(); qDeleteAll(layers); f.close(); + + if (!m_batchMode) { + disconnect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); + emit m_doc->sigProgress(100); + emit m_doc->clearStatusBarMessage(); + } QApplication::restoreOverrideCursor(); return retval; } QString CSVSaver::convertToBlending(const QString &opid) { if (opid == COMPOSITE_OVER) return "Color"; if (opid == COMPOSITE_BEHIND) return "Behind"; if (opid == COMPOSITE_ERASE) return "Erase"; // "Shade" if (opid == COMPOSITE_LINEAR_LIGHT) return "Light"; if (opid == COMPOSITE_COLORIZE) return "Colorize"; if (opid == COMPOSITE_HUE) return "Hue"; if ((opid == COMPOSITE_ADD) || (opid == COMPOSITE_LINEAR_DODGE)) return "Add"; if (opid == COMPOSITE_INVERSE_SUBTRACT) return "Sub"; if (opid == COMPOSITE_MULT) return "Multiply"; if (opid == COMPOSITE_SCREEN) return "Screen"; // "Replace" // "Subtitute" if (opid == COMPOSITE_DIFF) return "Difference"; if (opid == COMPOSITE_DIVIDE) return "Divide"; if (opid == COMPOSITE_OVERLAY) return "Overlay"; if (opid == COMPOSITE_DODGE) return "Light2"; if (opid == COMPOSITE_BURN) return "Shade2"; if (opid == COMPOSITE_HARD_LIGHT) return "HardLight"; if ((opid == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) || (opid == COMPOSITE_SOFT_LIGHT_SVG)) return "SoftLight"; if (opid == COMPOSITE_GRAIN_EXTRACT) return "GrainExtract"; if (opid == COMPOSITE_GRAIN_MERGE) return "GrainMerge"; if (opid == COMPOSITE_SUBTRACT) return "Sub2"; if (opid == COMPOSITE_DARKEN) return "Darken"; if (opid == COMPOSITE_LIGHTEN) return "Lighten"; if (opid == COMPOSITE_SATURATION) return "Saturation"; return "Color"; } KisImageBuilder_Result CSVSaver::getLayer(CSVLayerRecord* layer, KisDocument* exportDoc, KisKeyframeSP keyframe, const QString &path, int frame, int idx) { //render to the temp layer KisImageWSP image = exportDoc->image(); KisPaintDeviceSP device = image->rootLayer()->firstChild()->projection(); layer->channel->fetchFrame(keyframe, device); QRect bounds = device->exactBounds(); if (bounds.isEmpty()) { layer->last = ""; //empty frame return KisImageBuilder_RESULT_OK; } layer->last = QString("frame%1-%2.png").arg(idx + 1,5,10,QChar('0')).arg(frame,5,10,QChar('0')); QString filename = path; filename.append(layer->last); //save to PNG KisSequentialConstIterator it(device, image->bounds()); const KoColorSpace* cs = device->colorSpace(); bool isThereAlpha = false; do { if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) { isThereAlpha = true; break; } } while (it.nextPixel()); if (!KisPNGConverter::isColorSpaceSupported(cs)) { device = new KisPaintDevice(*device.data()); KUndo2Command *cmd= device->convertTo(KoColorSpaceRegistry::instance()->rgb8()); delete cmd; } KisPNGOptions options; options.alpha = isThereAlpha; options.interlace = false; options.compression = 8; options.tryToSaveAsIndexed = false; options.transparencyFillColor = QColor(0,0,0); options.saveSRGBProfile = true; //TVPaint can use only sRGB options.forceSRGB = false; KisPNGConverter kpc(exportDoc); - return kpc.buildFile( QUrl::fromLocalFile(filename), image->bounds(), - image->xRes(), image->yRes(), device, - image->beginAnnotations(), image->endAnnotations(), - options, (KisMetaData::Store* )0 ); + KisImageBuilder_Result result = kpc.buildFile(QUrl::fromLocalFile(filename), image->bounds(), + image->xRes(), image->yRes(), device, + image->beginAnnotations(), image->endAnnotations(), + options, (KisMetaData::Store* )0 ); + + return result; } void CSVSaver::createTempImage(KisDocument* exportDoc) { exportDoc->setAutoSave(0); exportDoc->setOutputMimeType("image/png"); - exportDoc->setSaveInBatchMode(true); + exportDoc->setFileBatchMode(true); KisImageWSP exportImage = new KisImage(exportDoc->createUndoStore(), m_image->width(), m_image->height(), m_image->colorSpace(), QString()); exportImage->setResolution(m_image->xRes(), m_image->yRes()); exportDoc->setCurrentImage(exportImage); KisPaintLayer* paintLayer = new KisPaintLayer(exportImage, "paint device", OPACITY_OPAQUE_U8); exportImage->addNode(paintLayer, exportImage->rootLayer(), KisLayerSP(0)); } KisImageBuilder_Result CSVSaver::buildAnimation(const QUrl &uri,const QString &filename) { if (!m_image) return KisImageBuilder_RESULT_EMPTY; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!uri.isLocalFile()) return KisImageBuilder_RESULT_NOT_EXIST; return encode(uri, filename); } + +void CSVSaver::cancel() +{ + m_stop = true; +} diff --git a/plugins/impex/csv/csv_saver.h b/plugins/impex/csv/csv_saver.h index b5b92c27ba..6553c3620f 100644 --- a/plugins/impex/csv/csv_saver.h +++ b/plugins/impex/csv/csv_saver.h @@ -1,57 +1,61 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * 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 CSV_SAVER_H_ #define CSV_SAVER_H_ #include #include "kis_types.h" #include "kis_raster_keyframe_channel.h" #include "kis_png_converter.h" /* The KisImageBuilder_Result definitions come from kis_png_converter.h here */ #include "csv_layer_record.h" class KisDocument; class CSVSaver : public QObject { Q_OBJECT public: - CSVSaver(KisDocument* doc); + CSVSaver(KisDocument* doc, bool batchMode); virtual ~CSVSaver(); KisImageBuilder_Result buildAnimation(const QUrl &, const QString &); KisImageWSP image(); private: KisImageBuilder_Result encode(const QUrl &, const QString &); KisImageBuilder_Result getLayer(CSVLayerRecord* , KisDocument* , KisKeyframeSP, const QString &, int, int); void createTempImage(KisDocument* ); QString convertToBlending(const QString &); +private Q_SLOTS: + void cancel(); + private: KisImageWSP m_image; KisDocument* m_doc; + bool m_batchMode; bool m_stop; }; #endif diff --git a/plugins/impex/csv/kis_csv_export.cpp b/plugins/impex/csv/kis_csv_export.cpp index da4c1a7111..3cfab607de 100644 --- a/plugins/impex/csv/kis_csv_export.cpp +++ b/plugins/impex/csv/kis_csv_export.cpp @@ -1,107 +1,111 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * 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_csv_export.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "csv_saver.h" K_PLUGIN_FACTORY_WITH_JSON(KisCSVExportFactory, "krita_csv_export.json", registerPlugin();) bool checkHomogenity(KisNodeSP root) { bool res = true; KisNodeSP child = root->firstChild(); while (child) { if (child->childCount() > 0) { res= false; break; } child = child->nextSibling(); } return res; } KisCSVExport::KisCSVExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisCSVExport::~KisCSVExport() { } KisImportExportFilter::ConversionStatus KisCSVExport::convert(const QByteArray& from, const QByteArray& to) { dbgFile << "CSV export! From:" << from << ", To:" << to << ""; if (from != "application/x-krita") return KisImportExportFilter::NotImplemented; KisDocument* input = m_chain->inputDocument(); QString filename = m_chain->outputFile(); if (!input) return KisImportExportFilter::NoDocumentCreated; if (!checkHomogenity(input->image()->rootLayer())) { if (!m_chain->manager()->getBatchMode()) { QMessageBox::critical(0, i18nc("@title:window", "CSV Export Error"), i18n("Unable to save to the CSV format.\n" "The CSV format not supports layer groups or masked layers.")); } return KisImportExportFilter::InvalidFormat; } qApp->processEvents(); // For vector layers to be updated input->image()->waitForDone(); if (filename.isEmpty()) return KisImportExportFilter::FileNotFound; QUrl url = QUrl::fromLocalFile(filename); - CSVSaver kpc(input); + CSVSaver kpc(input, m_chain->manager()->getBatchMode()); KisImageBuilder_Result res; if ((res = kpc.buildAnimation(url, filename)) == KisImageBuilder_RESULT_OK) { dbgFile <<"success !"; return KisImportExportFilter::OK; } dbgFile <<" Result =" << res; + + if (res == KisImageBuilder_RESULT_CANCEL) + return KisImportExportFilter::ProgressCancelled; + return KisImportExportFilter::InternalError; } #include "kis_csv_export.moc" diff --git a/plugins/impex/csv/kis_csv_import.cpp b/plugins/impex/csv/kis_csv_import.cpp index c6520da566..28477c01cc 100644 --- a/plugins/impex/csv/kis_csv_import.cpp +++ b/plugins/impex/csv/kis_csv_import.cpp @@ -1,98 +1,100 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * 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_csv_import.h" #include #include #include #include #include #include #include #include "csv_loader.h" K_PLUGIN_FACTORY_WITH_JSON(CSVImportFactory, "krita_csv_import.json", registerPlugin();) KisCSVImport::KisCSVImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisCSVImport::~KisCSVImport() { } KisImportExportFilter::ConversionStatus KisCSVImport::convert(const QByteArray&, const QByteArray& to) { dbgFile << "Importing using CSVImport!"; if (to != "application/x-krita") return KisImportExportFilter::BadMimeType; KisDocument * doc = m_chain->outputDocument(); if (!doc) return KisImportExportFilter::NoDocumentCreated; QString filename = m_chain->inputFile(); doc -> prepareForImport(); if (!filename.isEmpty()) { QUrl url = QUrl::fromLocalFile(filename); if (url.isEmpty()) return KisImportExportFilter::FileNotFound; - CSVLoader ib(doc); + CSVLoader ib(doc,m_chain->manager()->getBatchMode()); KisImageBuilder_Result result = ib.buildAnimation(url,filename); switch (result) { case KisImageBuilder_RESULT_UNSUPPORTED: return KisImportExportFilter::NotImplemented; case KisImageBuilder_RESULT_INVALID_ARG: return KisImportExportFilter::BadMimeType; case KisImageBuilder_RESULT_NO_URI: case KisImageBuilder_RESULT_NOT_EXIST: case KisImageBuilder_RESULT_NOT_LOCAL: qDebug() << "ib returned KisImageBuilder_RESULT_NOT_LOCAL"; return KisImportExportFilter::FileNotFound; case KisImageBuilder_RESULT_BAD_FETCH: case KisImageBuilder_RESULT_EMPTY: return KisImportExportFilter::ParsingError; case KisImageBuilder_RESULT_FAILURE: return KisImportExportFilter::InternalError; + case KisImageBuilder_RESULT_CANCEL: + return KisImportExportFilter::ProgressCancelled; case KisImageBuilder_RESULT_OK: doc -> setCurrentImage( ib.image()); return KisImportExportFilter::OK; default: return KisImportExportFilter::StorageCreationError; } } return KisImportExportFilter::StorageCreationError; } #include diff --git a/plugins/impex/exr/exr_converter.h b/plugins/impex/exr/exr_converter.h index f25fe1427a..ed180e581c 100644 --- a/plugins/impex/exr/exr_converter.h +++ b/plugins/impex/exr/exr_converter.h @@ -1,75 +1,56 @@ /* * Copyright (c) 2010 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 _EXR_CONVERTER_H_ #define _EXR_CONVERTER_H_ #include #include #include "kis_types.h" +#include class KisDocument; class QUrl; -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 -}; - class exrConverter : public QObject { Q_OBJECT public: exrConverter(KisDocument *doc, bool showNotifications); virtual ~exrConverter(); public: KisImageBuilder_Result buildImage(const QUrl &uri); KisImageBuilder_Result buildFile(const QUrl &uri, KisPaintLayerSP layer); KisImageBuilder_Result buildFile(const QUrl &uri, KisGroupLayerSP layer); /** * Retrieve the constructed image */ KisImageWSP image(); private: KisImageBuilder_Result decode(const QUrl &uri); public Q_SLOTS: virtual void cancel(); private: struct Private; const QScopedPointer m_d; }; #endif diff --git a/plugins/impex/jp2/jp2_converter.h b/plugins/impex/jp2/jp2_converter.h index 0d6216eeeb..a31f4b86f7 100644 --- a/plugins/impex/jp2/jp2_converter.h +++ b/plugins/impex/jp2/jp2_converter.h @@ -1,87 +1,68 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 _JP2_CONVERTER_H_ #define _JP2_CONVERTER_H_ #include #include #include "kis_types.h" +#include class KisDocument; class QUrl; struct JP2ConvertOptions { int rate; int numberresolution; }; -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 -}; - class jp2Converter : public QObject { Q_OBJECT private: enum { J2K_CFMT = 0, JP2_CFMT = 1, JPT_CFMT = 2 }; public: jp2Converter(KisDocument *doc); virtual ~jp2Converter(); public: KisImageBuilder_Result buildImage(const QUrl &uri); KisImageBuilder_Result buildFile(const QUrl &uri, KisPaintLayerSP layer, const JP2ConvertOptions& options); /** * Retrieve the constructed image */ KisImageWSP getImage(); private: KisImageBuilder_Result decode(const QUrl &uri); public Q_SLOTS: virtual void cancel(); private: int getFileFormat(const QUrl &uri) const; private: KisImageWSP m_image; KisDocument *m_doc; bool m_stop; }; #endif diff --git a/plugins/impex/jpeg/kis_jpeg_converter.h b/plugins/impex/jpeg/kis_jpeg_converter.h index 8911beb4a1..1ad1c8de4b 100644 --- a/plugins/impex/jpeg/kis_jpeg_converter.h +++ b/plugins/impex/jpeg/kis_jpeg_converter.h @@ -1,108 +1,89 @@ /* * Copyright (c) 2005 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_JPEG_CONVERTER_H_ #define _KIS_JPEG_CONVERTER_H_ #include extern "C" { #include } #include #include #include #include "kis_types.h" #include "kis_annotation.h" +#include class KisDocument; class QUrl; namespace KisMetaData { class Filter; } -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 -}; - struct KisJPEGOptions { int quality; bool progressive; bool optimize; int smooth; bool baseLineJPEG; int subsampling; bool exif; bool iptc; bool xmp; QList filters; QColor transparencyFillColor; bool forceSRGB; bool saveProfile; }; namespace KisMetaData { class Store; } class KisJPEGConverter : public QObject { Q_OBJECT public: KisJPEGConverter(KisDocument *doc, bool batchMode = false); virtual ~KisJPEGConverter(); public: KisImageBuilder_Result buildImage(const QUrl &uri); KisImageBuilder_Result buildFile(const QUrl &uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisJPEGOptions options, KisMetaData::Store* metaData); /** Retrieve the constructed image */ KisImageWSP image(); public Q_SLOTS: virtual void cancel(); private: KisImageBuilder_Result decode(const QUrl &uri); private: KisImageWSP m_image; KisDocument *m_doc; bool m_stop; bool m_batchMode; }; #endif diff --git a/plugins/impex/psd/psd_loader.h b/plugins/impex/psd/psd_loader.h index abfa912d8f..580ad84c77 100644 --- a/plugins/impex/psd/psd_loader.h +++ b/plugins/impex/psd/psd_loader.h @@ -1,78 +1,59 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * 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 _PSD_LOADER_H_ #define _PSD_LOADER_H_ #include #include #include #include "kis_types.h" +#include class KisDocument; -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 - }; - class PSDLoader : public QObject { Q_OBJECT public: PSDLoader(KisDocument *doc); virtual ~PSDLoader(); KisImageBuilder_Result buildImage(const QUrl &uri); KisImageWSP image(); public Q_SLOTS: virtual void cancel(); private: KisImageBuilder_Result decode(const QUrl &uri); private: KisImageWSP m_image; KisDocument *m_doc; bool m_stop; }; #endif diff --git a/plugins/impex/psd/psd_saver.h b/plugins/impex/psd/psd_saver.h index bc81bbb304..32b32c91a6 100644 --- a/plugins/impex/psd/psd_saver.h +++ b/plugins/impex/psd/psd_saver.h @@ -1,76 +1,57 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * 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 _PSD_CONVERTER_H_ #define _PSD_CONVERTER_H_ #include #include #include #include "kis_types.h" +#include class KisDocument; -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 - }; - class PSDSaver : public QObject { Q_OBJECT public: PSDSaver(KisDocument *doc); virtual ~PSDSaver(); public: KisImageBuilder_Result buildFile(const QUrl &uri); KisImageWSP image(); public Q_SLOTS: virtual void cancel(); private: KisImageWSP m_image; KisDocument *m_doc; bool m_stop; }; #endif diff --git a/plugins/impex/qml/qml_converter.h b/plugins/impex/qml/qml_converter.h index 1aea3437b6..2392380b3f 100644 --- a/plugins/impex/qml/qml_converter.h +++ b/plugins/impex/qml/qml_converter.h @@ -1,63 +1,44 @@ /* * Copyright (c) 2013 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 _QML_CONVERTER_H_ #define _QML_CONVERTER_H_ #include #include #include #include "kis_types.h" - -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 -}; +#include class QMLConverter : public QObject { Q_OBJECT public: QMLConverter(); virtual ~QMLConverter(); public: KisImageBuilder_Result buildFile(const QUrl &uri, KisImageWSP image); private: void writeString(QTextStream& out, int spacing, const QString& setting, const QString& value); void writeInt(QTextStream& out, int spacing, const QString& setting, int value); }; #endif diff --git a/plugins/impex/tiff/kis_tiff_converter.h b/plugins/impex/tiff/kis_tiff_converter.h index 58794dbdf9..7f58d1a762 100644 --- a/plugins/impex/tiff/kis_tiff_converter.h +++ b/plugins/impex/tiff/kis_tiff_converter.h @@ -1,90 +1,71 @@ /* * Copyright (c) 2005-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_TIFF_CONVERTER_H_ #define _KIS_TIFF_CONVERTER_H_ #include #include #include #include "kis_types.h" #include "kis_global.h" #include "kis_annotation.h" +#include class KisDocument; class QUrl; -/** - * Image import/export plugins can use these results to report about success or failure. - */ -enum KisImageBuilder_Result { - KisImageBuilder_RESULT_FAILURE = -400, - KisImageBuilder_RESULT_NOT_EXIST = -300, - KisImageBuilder_RESULT_NOT_LOCAL = -200, - KisImageBuilder_RESULT_BAD_FETCH = -100, - KisImageBuilder_RESULT_INVALID_ARG = -50, - KisImageBuilder_RESULT_OK = 0, - KisImageBuilder_RESULT_PROGRESS = 1, - KisImageBuilder_RESULT_EMPTY = 100, - KisImageBuilder_RESULT_BUSY = 150, - KisImageBuilder_RESULT_NO_URI = 200, - KisImageBuilder_RESULT_UNSUPPORTED = 300, - KisImageBuilder_RESULT_INTR = 400, - KisImageBuilder_RESULT_PATH = 500, - KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 -}; - struct KisTIFFOptions { quint16 compressionType; quint16 predictor; bool alpha; bool flatten; quint16 jpegQuality; quint16 deflateCompress; quint16 faxMode; quint16 pixarLogCompress; bool saveProfile; }; class KisTIFFConverter : public QObject { Q_OBJECT public: KisTIFFConverter(KisDocument *doc); virtual ~KisTIFFConverter(); public: KisImageBuilder_Result buildImage(const QUrl &uri); KisImageBuilder_Result buildFile(const QUrl &uri, KisImageWSP layer, KisTIFFOptions); /** Retrieve the constructed image */ KisImageWSP image(); public Q_SLOTS: virtual void cancel(); private: KisImageBuilder_Result decode(const QUrl &uri); KisImageBuilder_Result readTIFFDirectory(TIFF* image); private: KisImageWSP m_image; KisDocument *m_doc; bool m_stop; }; #endif diff --git a/plugins/impex/tiff/tests/kis_tiff_test.cpp b/plugins/impex/tiff/tests/kis_tiff_test.cpp index e57887a576..eacb5bc862 100644 --- a/plugins/impex/tiff/tests/kis_tiff_test.cpp +++ b/plugins/impex/tiff/tests/kis_tiff_test.cpp @@ -1,115 +1,115 @@ /* * Copyright (C) 2007 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_tiff_test.h" #include #include #include #include "filestest.h" #include #include #include "kisexiv2/kis_exiv2.h" #ifndef FILES_DATA_DIR #error "FILES_DATA_DIR not set. A directory with the data used for testing the importing of files in krita" #endif void KisTiffTest::testFiles() { // XXX: make the exiv io backends real plugins KisExiv2::initialize(); QStringList excludes; #ifndef CPU_32_BITS excludes << "flower-minisblack-06.tif"; #endif #ifdef HAVE_LCMS2 excludes << "flower-separated-contig-08.tif" << "flower-separated-contig-16.tif" << "flower-separated-planar-08.tif" << "flower-separated-planar-16.tif" << "flower-minisblack-10.tif" << "flower-minisblack-12.tif" << "flower-minisblack-14.tif" << "flower-minisblack-16.tif" << "flower-minisblack-24.tif" << "flower-minisblack-32.tif" << "strike.tif"; #endif excludes << "text.tif" << "ycbcr-cat.tif"; TestUtil::testFiles(QString(FILES_DATA_DIR) + "/sources", excludes); } void KisTiffTest::testRoundTripRGBF16() { // Disabled for now, it's broken because we assumed integers. #if 0 QRect testRect(0,0,1000,1000); QRect fillRect(100,100,100,100); const KoColorSpace *csf16 = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float16BitsColorDepthID.id(), 0); KisDocument *doc0 = qobject_cast(KisPart::instance()->createDocument()); doc0->newImage("test", testRect.width(), testRect.height(), csf16, KoColor(Qt::blue, csf16), QString(), 1.0); QTemporaryFile tmpFile(QDir::tempPath() + QLatin1String("/krita_XXXXXX") + QLatin1String(".tiff")); tmpFile.open(); doc0->setBackupFile(false); doc0->setOutputMimeType("image/tiff"); - doc0->setSaveInBatchMode(true); + doc0->setFileBatchMode(true); doc0->saveAs(QUrl::fromLocalFile(tmpFile.fileName())); KisNodeSP layer0 = doc0->image()->root()->firstChild(); Q_ASSERT(layer0); layer0->paintDevice()->fill(fillRect, KoColor(Qt::red, csf16)); KisDocument *doc1 = qobject_cast(KisPart::instance()->createDocument()); KisImportExportManager manager(doc1); manager.setBatchMode(false); KisImportExportFilter::ConversionStatus status; QString s = manager.importDocument(tmpFile.fileName(), QString(), status); dbgKrita << s; Q_ASSERT(doc1->image()); QImage ref0 = doc0->image()->projection()->convertToQImage(0, testRect); QImage ref1 = doc1->image()->projection()->convertToQImage(0, testRect); QCOMPARE(ref0, ref1); #endif } QTEST_MAIN(KisTiffTest)