diff --git a/krita/gemini/MainWindow.cpp b/krita/gemini/MainWindow.cpp index cb0bd196957..7f541d043fa 100644 --- a/krita/gemini/MainWindow.cpp +++ b/krita/gemini/MainWindow.cpp @@ -1,708 +1,710 @@ /* 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 "opengl/kis_opengl.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 "filter/kis_filter.h" #include "filter/kis_filter_registry.h" #include "kis_paintop.h" #include "kis_paintop_registry.h" #include #include #include #include #include #include #include #include #include "sketch/SketchDeclarativeView.h" #include "sketch/RecentFileManager.h" #include "sketch/DocumentManager.h" #include "sketch/KisSketchPart.h" #include "sketch/QmlGlobalEngine.h" #include "sketch/Settings.h" #ifdef Q_OS_WIN // qml requires correct-case to path, even in Windows #include "sketch/pathconverter.h" // Slate mode/docked detection stuff #include #define SM_CONVERTIBLESLATEMODE 0x2003 #define SM_SYSTEMDOCKED 0x2004 #endif #ifdef HAVE_STEAMWORKS #include "steam/kritasteam.h" #endif class MainWindow::Private { public: Private(MainWindow* qq) : q(qq) , allowClose(true) , sketchView(0) , desktopView(0) , currentView(0) , desktopCursorStyle(CURSOR_STYLE_OUTLINE) , slateMode(false) , docked(false) , sketchKisView(0) , desktopViewProxy(0) , forceDesktop(false) , forceSketch(false) , wasMaximized(true) , 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 #ifdef HAVE_STEAMWORKS // Big Picture Mode should force full-screen behaviour in Sketch mode slateMode = KritaSteamClient::instance()->isInBigPictureMode(); #endif centerer = new QTimer(q); centerer->setInterval(10); centerer->setSingleShot(true); connect(centerer, SIGNAL(timeout()), q, SLOT(adjustZoomOnDocumentChangedAndStuff())); } MainWindow* q; bool allowClose; SketchDeclarativeView* sketchView; KoMainWindow* desktopView; QObject* currentView; enumCursorStyle desktopCursorStyle; bool slateMode; bool docked; QString currentSketchPage; KisView2* sketchKisView; DesktopViewProxy* desktopViewProxy; bool forceDesktop; bool forceSketch; bool wasMaximized; bool temporaryFile; ViewModeSynchronisationObject* syncObject; QTimer* centerer; KAction* toDesktop; KAction* toSketch; QToolButton* switcher; KAction* crashTest; void initSketchView(QObject* parent) { sketchView = new SketchDeclarativeView(); QmlGlobalEngine::instance()->setEngine(sketchView->engine()); sketchView->engine()->rootContext()->setContextProperty("mainWindow", parent); QString importPath = KGlobal::dirs()->findDirs("lib", "calligra/imports").value(0); QString mainqml = KGlobal::dirs()->findResource("data", "kritagemini/kritagemini.qml"); #ifdef Q_OS_WIN importPath = WindowsTools::correctPathForCase(importPath); mainqml = WindowsTools::correctPathForCase(mainqml); #endif sketchView->engine()->addImportPath(importPath); Q_ASSERT(QFile::exists(mainqml)); if (!QFile::exists(mainqml)) { QMessageBox::warning(0, "No QML found", mainqml + " doesn't exist."); } QFileInfo fi(mainqml); sketchView->setSource(QUrl::fromLocalFile(fi.canonicalFilePath())); sketchView->setResizeMode( QDeclarativeView::SizeRootObjectToView ); toDesktop = new KAction(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); // Uncomment these lines to enable a crash when the combination Ctrl+Shift+D is pressed /* crashTest = new KAction(q); crashTest->setEnabled(true); crashTest->setText(tr("Sample crash")); crashTest->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D); q->addAction(crashTest); connect(crashTest, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(debugTestCrash())); */ } void initDesktopView() { // Tell the iconloader about share/apps/calligra/icons KIconLoader::global()->addAppDir("calligra"); // 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"); } desktopView = new KoMainWindow(KIS_MIME_TYPE, KisFactory2::componentData()); toSketch = new KAction(desktopView); toSketch->setEnabled(false); toSketch->setText(tr("Switch to Sketch")); toSketch->setIcon(QIcon::fromTheme("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())); desktopView->actionCollection()->addAction("SwitchToSketchView", toSketch); switcher = new QToolButton(); switcher->setEnabled(false); switcher->setText(tr("Switch to Sketch")); switcher->setIcon(QIcon::fromTheme("system-reboot")); switcher->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); //connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchDesktopForced())); connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchToSketch())); desktopView->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, desktopView); + 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(KIcon("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.cursorStyle() != CURSOR_STYLE_NO_CURSOR) d->desktopCursorStyle = cfg.cursorStyle(); cfg.setCursorStyle(CURSOR_STYLE_NO_CURSOR); cfg.setUseOpenGL(true); 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(); d->wasMaximized = true; 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() { KUrl url(DocumentManager::instance()->settingsManager()->currentFile()); QString fileName = url.fileName(); if(url.protocol() == "temp") fileName = i18n("Untitled"); setWindowTitle(QString("%1 - %2").arg(fileName).arg(i18n("Krita Gemini"))); } void MainWindow::debugTestCrash() { QMessageBox msg(this); msg.setText("Debug Test Crash"); msg.setModal(true); msg.show(); MainWindow* thisWillCrash; thisWillCrash->debugTestCrash(); } 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() { QTime timer; timer.start(); if (d->toSketch) { d->toSketch->setEnabled(false); d->switcher->setEnabled(false); } d->syncObject = new ViewModeSynchronisationObject; KisView2* view = 0; KisConfig cfg; if (d->desktopView && centralWidget() == d->desktopView) { d->desktopCursorStyle = cfg.cursorStyle(); view = qobject_cast(d->desktopView->rootView()); //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->desktopView->setParent(0); d->wasMaximized = isMaximized(); } setCentralWidget(d->sketchView); emit switchedToSketch(); if (d->slateMode) { showFullScreen(); if (d->syncObject->initialized) QTimer::singleShot(50, this, SLOT(sketchChange())); } else QTimer::singleShot(50, this, SLOT(sketchChange())); //qDebug() << "milliseconds to switch to sketch:" << timer.elapsed(); } void MainWindow::sketchChange() { if (centralWidget() != d->sketchView || !d->syncObject) return; if (d->desktopView) { if (!d->sketchKisView || !d->sketchView->canvasWidget()) { QTimer::singleShot(100, this, SLOT(sketchChange())); return; } qApp->processEvents(); KisView2* view = qobject_cast(d->desktopView->rootView()); //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.setCursorStyle(CURSOR_STYLE_NO_CURSOR); } if (d->toDesktop) { qApp->processEvents(); d->toDesktop->setEnabled(true); } } void MainWindow::switchToDesktop(bool justLoaded) { QTime timer; timer.start(); if (d->toDesktop) d->toDesktop->setEnabled(false); ViewModeSynchronisationObject* syncObject = new ViewModeSynchronisationObject; KisView2* view = 0; if (d->desktopView) { view = qobject_cast(d->desktopView->rootView()); } //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->desktopView); } if (d->wasMaximized) showMaximized(); else showNormal(); 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.setCursorStyle(d->desktopCursorStyle); } if (d->toSketch && !justLoaded) { qApp->processEvents(); d->toSketch->setEnabled(true); d->switcher->setEnabled(true); } //qDebug() << "milliseconds to switch to desktop:" << timer.elapsed(); } void MainWindow::adjustZoomOnDocumentChangedAndStuff() { if (d->desktopView && centralWidget() == d->desktopView) { KisView2* view = qobject_cast(d->desktopView->rootView()); // We have to set the focus on the view here, otherwise the toolmanager is unaware of which // canvas should be handled. d->desktopView->rootView()->setFocus(); QPoint center = view->rect().center(); view->canvasControllerWidget()->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->canvasControllerWidget()->zoomRelativeToPoint(center, 0.9); qApp->processEvents(); d->toDesktop->setEnabled(true); } } void MainWindow::documentChanged() { if (d->desktopView) { d->desktopView->setNoCleanup(true); d->desktopView->deleteLater(); d->desktopView = 0; } d->initDesktopView(); d->desktopView->setRootDocument(DocumentManager::instance()->document(), DocumentManager::instance()->part(), false); qApp->processEvents(); KisView2* view = qobject_cast(d->desktopView->rootView()); view->setQtMainWindow(d->desktopView); connect(view, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start())); if (d->sketchKisView) d->sketchKisView->setQtMainWindow(this); 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; } 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(); } QObject* MainWindow::sketchKisView() const { return d->sketchKisView; } void MainWindow::setSketchKisView(QObject* newView) { if (d->sketchKisView) d->sketchKisView->disconnect(this); if (d->sketchKisView != newView) { d->sketchKisView = qobject_cast(newView); connect(d->sketchKisView, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start())); d->centerer->start(); emit sketchKisViewChanged(); } } void MainWindow::minimize() { setWindowState(windowState() ^ Qt::WindowMinimized); } void MainWindow::closeWindow() { d->desktopView->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(); DocumentManager::instance()->part()->deleteLater(); qApp->processEvents(); QApplication::instance()->quit(); } bool MainWindow::Private::queryClose() { desktopView->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 = KMessageBox::warningYesNoCancel(q, i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard()); switch (res) { case KMessageBox::Yes : { if (DocumentManager::instance()->isTemporaryFile() && !desktopViewProxy->fileSaveAs()) return false; if (!DocumentManager::instance()->save()) return false; break; } case KMessageBox::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 KMessageBox::Cancel : return false; } } return true; } void MainWindow::closeEvent(QCloseEvent* event) { if (centralWidget() == d->desktopView) { if (DocumentManager::instance()->document()->isLoading()) { event->ignore(); return; } d->allowClose = d->queryClose(); } if (d->allowClose) { if (d->desktopView) { d->desktopView->setNoCleanup(true); d->desktopView->close(); } event->accept(); } else { event->ignore(); emit closeRequested(); } } MainWindow::~MainWindow() { delete d; KisConfig cfg; cfg.setCursorStyle(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(); } //qDebug() << "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; //qDebug() << "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()); } #include "MainWindow.moc" diff --git a/krita/gemini/MainWindow.h b/krita/gemini/MainWindow.h index 1eb9c41e3e7..25d8732283e 100644 --- a/krita/gemini/MainWindow.h +++ b/krita/gemini/MainWindow.h @@ -1,95 +1,96 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * 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. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class KisCanvasResourceProvider; class MainWindow : public QMainWindow { Q_OBJECT Q_PROPERTY(bool allowClose READ allowClose WRITE setAllowClose) Q_PROPERTY(bool slateMode READ slateMode NOTIFY slateModeChanged) Q_PROPERTY(QString applicationName READ applicationName CONSTANT) Q_PROPERTY(QString currentSketchPage READ currentSketchPage WRITE setCurrentSketchPage NOTIFY currentSketchPageChanged) Q_PROPERTY(bool temporaryFile READ temporaryFile WRITE setTemporaryFile NOTIFY temporaryFileChanged) Q_PROPERTY(QObject* sketchKisView READ sketchKisView WRITE setSketchKisView NOTIFY sketchKisViewChanged) public: explicit MainWindow(QStringList fileNames, QWidget* parent = 0, Qt::WindowFlags flags = 0); virtual ~MainWindow(); bool allowClose() const; void setAllowClose(bool allow); bool slateMode() const; QString applicationName() const { return QLatin1String("KRITA GEMINI"); } QString currentSketchPage() const; void setCurrentSketchPage(QString newPage); bool temporaryFile() const; void setTemporaryFile(bool newValue); QObject* sketchKisView() const; void setSketchKisView(QObject* newView); virtual void closeEvent(QCloseEvent* event); public Q_SLOTS: void minimize(); void closeWindow(); void switchToSketch(); void switchToDesktop(bool justLoaded = false); void documentChanged(); void resetWindowTitle(); Q_SIGNALS: void closeRequested(); void switchedToSketch(); void slateModeChanged(); void currentSketchPageChanged(); void temporaryFileChanged(); void sketchKisViewChanged(); + void documentSaved(); private Q_SLOTS: void debugTestCrash(); void switchDesktopForced(); void switchSketchForced(); void adjustZoomOnDocumentChangedAndStuff(); void sketchChange(); private: void cloneResources(KisCanvasResourceProvider *from, KisCanvasResourceProvider *to); class Private; Private * const d; #ifdef Q_OS_WIN bool winEvent(MSG * message, long * result); #endif }; #endif // MAINWINDOW_H diff --git a/krita/gemini/desktopviewproxy.cpp b/krita/gemini/desktopviewproxy.cpp index a91ff5c8030..6b1e95f0cda 100644 --- a/krita/gemini/desktopviewproxy.cpp +++ b/krita/gemini/desktopviewproxy.cpp @@ -1,172 +1,175 @@ /* * * Copyright 2013 Dan Leinir Turthra Jensen * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * */ #include "desktopviewproxy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "MainWindow.h" #include #include #include #include class DesktopViewProxy::Private { public: Private(MainWindow* mainWindow, KoMainWindow* desktopView) : mainWindow(mainWindow) , desktopView(desktopView) , isImporting(false) {} MainWindow* mainWindow; KoMainWindow* desktopView; bool isImporting; }; DesktopViewProxy::DesktopViewProxy(MainWindow* mainWindow, KoMainWindow* parent) : QObject(parent) , d(new Private(mainWindow, parent)) { Q_ASSERT(parent); // "There MUST be a KoMainWindow assigned, otherwise everything will blow up"); // Hide this one... as it doesn't work at all well and release happens :P QAction* closeAction = d->desktopView->actionCollection()->action("file_close"); closeAction->setVisible(false); // Concept is simple - simply steal all the actions we require to work differently, and reconnect them to local functions QAction* newAction = d->desktopView->actionCollection()->action("file_new"); newAction->disconnect(d->desktopView); connect(newAction, SIGNAL(triggered(bool)), this, SLOT(fileNew())); QAction* openAction = d->desktopView->actionCollection()->action("file_open"); openAction->disconnect(d->desktopView); connect(openAction, SIGNAL(triggered(bool)), this, SLOT(fileOpen())); QAction* saveAction = d->desktopView->actionCollection()->action("file_save"); saveAction->disconnect(d->desktopView); connect(saveAction, SIGNAL(triggered(bool)), this, SLOT(fileSave())); QAction* saveasAction = d->desktopView->actionCollection()->action("file_save_as"); saveasAction->disconnect(d->desktopView); connect(saveasAction, SIGNAL(triggered(bool)), this, SLOT(fileSaveAs())); QAction* reloadAction = d->desktopView->actionCollection()->action("file_reload_file"); reloadAction->disconnect(d->desktopView); connect(reloadAction, SIGNAL(triggered(bool)), this, SLOT(reload())); QAction* loadExistingAsNewAction = d->desktopView->actionCollection()->action("file_import_file"); loadExistingAsNewAction->disconnect(d->desktopView); connect(loadExistingAsNewAction, SIGNAL(triggered(bool)), this, SLOT(loadExistingAsNew())); // Recent files need a touch more work, as they aren't simply an action. KRecentFilesAction* recent = qobject_cast(d->desktopView->actionCollection()->action("file_open_recent")); recent->disconnect(d->desktopView); connect(recent, SIGNAL(urlSelected(KUrl)), this, SLOT(slotFileOpenRecent(KUrl))); recent->clear(); recent->loadEntries(KGlobal::config()->group("RecentFiles")); } DesktopViewProxy::~DesktopViewProxy() { delete d; } void DesktopViewProxy::fileNew() { QProcess::startDetached(qApp->applicationFilePath(), QStringList(), QDir::currentPath()); } void DesktopViewProxy::fileOpen() { KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(KIS_MIME_TYPE); KService::Ptr service = entry.service(); const QStringList mimeFilter = KoFilterManager::mimeFilter(KIS_MIME_TYPE, KoFilterManager::Import, service->property("X-KDE-ExtraNativeMimeTypes").toStringList()); KoFileDialog dialog(d->desktopView, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Document")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setMimeTypeFilters(mimeFilter); QString filename = dialog.url(); if (filename.isEmpty()) return; DocumentManager::instance()->recentFileManager()->addRecent(filename); QProcess::startDetached(qApp->applicationFilePath(), QStringList() << filename, QDir::currentPath()); } void DesktopViewProxy::fileSave() { if(DocumentManager::instance()->isTemporaryFile()) { if(d->desktopView->saveDocument(true)) { DocumentManager::instance()->recentFileManager()->addRecent(DocumentManager::instance()->document()->url().toLocalFile()); DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); DocumentManager::instance()->setTemporaryFile(false); + emit documentSaved(); } } else { DocumentManager::instance()->save(); + emit documentSaved(); } } bool DesktopViewProxy::fileSaveAs() { if(d->desktopView->saveDocument(true)) { DocumentManager::instance()->recentFileManager()->addRecent(DocumentManager::instance()->document()->url().toLocalFile()); DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); DocumentManager::instance()->setTemporaryFile(false); + emit documentSaved(); return true; } DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); return false; } void DesktopViewProxy::reload() { DocumentManager::instance()->reload(); } void DesktopViewProxy::loadExistingAsNew() { d->isImporting = true; fileOpen(); d->isImporting = false; } void DesktopViewProxy::slotFileOpenRecent(const KUrl& url) { QProcess::startDetached(qApp->applicationFilePath(), QStringList() << url.toLocalFile(), QDir::currentPath()); } #include "desktopviewproxy.moc" diff --git a/krita/gemini/desktopviewproxy.h b/krita/gemini/desktopviewproxy.h index 2d9738479a8..33a689a8fa6 100644 --- a/krita/gemini/desktopviewproxy.h +++ b/krita/gemini/desktopviewproxy.h @@ -1,52 +1,55 @@ /* * * Copyright 2013 Dan Leinir Turthra Jensen * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * */ #ifndef DESKTOPVIEWPROXY_H #define DESKTOPVIEWPROXY_H #include class KUrl; class KoMainWindow; class MainWindow; class DesktopViewProxy : public QObject { Q_OBJECT public: explicit DesktopViewProxy(MainWindow* mainWindow, KoMainWindow* parent = 0); virtual ~DesktopViewProxy(); public Q_SLOTS: void fileNew(); void fileOpen(); void fileSave(); bool fileSaveAs(); void reload(); void loadExistingAsNew(); void slotFileOpenRecent(const KUrl &url); +Q_SIGNALS: + void documentSaved(); + private: class Private; Private* d; }; #endif // DESKTOPVIEWPROXY_H diff --git a/krita/ui/kis_view2.cpp b/krita/ui/kis_view2.cpp index 096c71b64ae..1259f19716b 100644 --- a/krita/ui/kis_view2.cpp +++ b/krita/ui/kis_view2.cpp @@ -1,1551 +1,1557 @@ /* * 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 "kis_view2.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 #include #include #include #include #include #include "kis_composite_progress_proxy.h" #include #include "canvas/kis_canvas2.h" #include "canvas/kis_canvas_controller.h" #include "canvas/kis_grid_manager.h" #include "canvas/kis_perspective_grid_manager.h" #include "dialogs/kis_dlg_preferences.h" #include "dialogs/kis_dlg_blacklist_cleanup.h" #include "kis_canvas_resource_provider.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_control_frame.h" #include "kis_coordinates_converter.h" #include "kis_doc2.h" #include "kis_factory2.h" #include "kis_filter_manager.h" #include "kis_group_layer.h" #include "kis_image_manager.h" #include "kis_mask_manager.h" #include "kis_mimedata.h" #include "kis_node.h" #include "kis_node_manager.h" #include "kis_painting_assistants_decoration.h" #include #include "kis_paintop_box.h" #include "kis_print_job.h" #include "kis_progress_widget.h" #include "kis_resource_server_provider.h" #include "kis_selection.h" #include "kis_selection_manager.h" #include "kis_shape_layer.h" #include "kis_shape_controller.h" #include "kis_statusbar.h" #include "kis_zoom_manager.h" #include "kra/kis_kra_loader.h" #include "widgets/kis_floating_message.h" #include #include "kis_node_commands_adapter.h" #include #include "ko_favorite_resource_manager.h" #include "kis_action_manager.h" #include "input/kis_input_profile_manager.h" #include "kis_canvas_controls_manager.h" #include "krita/gemini/ViewModeSwitchEvent.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 KisView2::KisView2Private { public: KisView2Private() : canvas(0) , doc(0) , viewConverter(0) , canvasController(0) , resourceProvider(0) , filterManager(0) , statusBar(0) , selectionManager(0) , controlFrame(0) , nodeManager(0) , zoomManager(0) , imageManager(0) , gridManager(0) , perspectiveGridManager(0) , paintingAssistantsDecoration(0) , actionManager(0) , mainWindow(0) { } ~KisView2Private() { if (canvasController) { KoToolManager::instance()->removeCanvasController(canvasController); } delete resourceProvider; delete canvasController; delete canvas; delete filterManager; delete selectionManager; delete nodeManager; delete zoomManager; delete imageManager; delete gridManager; delete perspectiveGridManager; delete paintingAssistantsDecoration; delete viewConverter; delete statusBar; delete actionManager; delete canvasControlsManager; } public: KisCanvas2 *canvas; KisDoc2 *doc; KisCoordinatesConverter *viewConverter; KisCanvasController *canvasController; KisCanvasResourceProvider *resourceProvider; KisFilterManager *filterManager; KisStatusBar *statusBar; KAction *totalRefresh; KAction *mirrorCanvas; KAction *createTemplate; KAction *saveIncremental; KAction *saveIncrementalBackup; KAction *openResourcesDirectory; KisSelectionManager *selectionManager; KisControlFrame *controlFrame; KisNodeManager *nodeManager; KisZoomManager *zoomManager; KisImageManager *imageManager; KisGridManager *gridManager; KisCanvasControlsManager *canvasControlsManager; KisPerspectiveGridManager * perspectiveGridManager; KisPaintingAssistantsDecoration *paintingAssistantsDecoration; BlockingUserInputEventFilter blockingEventFilter; KisFlipbook *flipbook; KisActionManager* actionManager; QMainWindow* mainWindow; }; KisView2::KisView2(KoPart *part, KisDoc2 * doc, QWidget * parent) : KoView(part, doc, parent), m_d(new KisView2Private()) { Q_ASSERT(doc); Q_ASSERT(doc->image()); setXMLFile(QString("%1.rc").arg(qAppName())); KisConfig cfg; setFocusPolicy(Qt::NoFocus); if (mainWindow()) { mainWindow()->setDockNestingEnabled(true); actionCollection()->addAction(KStandardAction::KeyBindings, "keybindings", mainWindow()->guiFactory(), SLOT(configureShortcuts())); } m_d->doc = doc; m_d->viewConverter = new KisCoordinatesConverter(); KisCanvasController *canvasController = new KisCanvasController(this, actionCollection()); canvasController->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); canvasController->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); canvasController->setDrawShadow(false); canvasController->setCanvasMode(KoCanvasController::Infinite); canvasController->setVastScrolling(cfg.vastScrolling()); m_d->canvasController = canvasController; m_d->resourceProvider = new KisCanvasResourceProvider(this); m_d->resourceProvider->resetDisplayProfile(QApplication::desktop()->screenNumber(this)); KConfigGroup grp(KGlobal::config(), "krita/crashprevention"); if (grp.readEntry("CreatingCanvas", false)) { cfg.setUseOpenGL(false); } if (cfg.canvasState() == "OPENGL_FAILED") { cfg.setUseOpenGL(false); } grp.writeEntry("CreatingCanvas", true); grp.sync(); m_d->canvas = new KisCanvas2(m_d->viewConverter, this, doc->shapeController()); grp.writeEntry("CreatingCanvas", false); grp.sync(); connect(m_d->resourceProvider, SIGNAL(sigDisplayProfileChanged(const KoColorProfile*)), m_d->canvas, SLOT(slotSetDisplayProfile(const KoColorProfile*))); m_d->canvasController->setCanvas(m_d->canvas); m_d->resourceProvider->setResourceManager(m_d->canvas->resourceManager()); createManagers(); createActions(); m_d->controlFrame = new KisControlFrame(this); Q_ASSERT(m_d->canvasController); KoToolManager::instance()->addController(m_d->canvasController); KoToolManager::instance()->registerTools(actionCollection(), m_d->canvasController); // krita/krita.rc must also be modified to add actions to the menu entries m_d->saveIncremental = new KAction(i18n("Save Incremental &Version"), this); m_d->saveIncremental->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_S)); actionCollection()->addAction("save_incremental_version", m_d->saveIncremental); connect(m_d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental())); m_d->saveIncrementalBackup = new KAction(i18n("Save Incremental Backup"), this); m_d->saveIncrementalBackup->setShortcut(Qt::Key_F4); actionCollection()->addAction("save_incremental_backup", m_d->saveIncrementalBackup); connect(m_d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup())); - connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); + connect(qtMainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); if (m_d->doc->localFilePath().isNull()) { m_d->saveIncremental->setEnabled(false); m_d->saveIncrementalBackup->setEnabled(false); } m_d->totalRefresh = new KAction(i18n("Total Refresh"), this); actionCollection()->addAction("total_refresh", m_d->totalRefresh); m_d->totalRefresh->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_R)); connect(m_d->totalRefresh, SIGNAL(triggered()), this, SLOT(slotTotalRefresh())); KAction *tabletDebugger = new KAction(i18n("Toggle Tablet Debugger"), this); actionCollection()->addAction("tablet_debugger", tabletDebugger ); tabletDebugger->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_T)); connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger())); m_d->createTemplate = new KAction( i18n( "&Create Template From Image..." ), this); actionCollection()->addAction("createTemplate", m_d->createTemplate); connect(m_d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate())); m_d->mirrorCanvas = new KToggleAction(i18n("Mirror View"), this); m_d->mirrorCanvas->setChecked(false); actionCollection()->addAction("mirror_canvas", m_d->mirrorCanvas); m_d->mirrorCanvas->setShortcut(QKeySequence(Qt::Key_M)); connect(m_d->mirrorCanvas, SIGNAL(toggled(bool)),m_d->canvasController, SLOT(mirrorCanvas(bool))); m_d->openResourcesDirectory = new KAction(i18n("Open Resources Folder"), this); m_d->openResourcesDirectory->setToolTip(i18n("Opens a file browser at the location Krita saves resources such as brushes to.")); m_d->openResourcesDirectory->setWhatsThis(i18n("Opens a file browser at the location Krita saves resources such as brushes to.")); actionCollection()->addAction("open_resources_directory", m_d->openResourcesDirectory); connect(m_d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory())); KAction *rotateCanvasRight = new KAction(i18n("Rotate Canvas Right"), this); actionCollection()->addAction("rotate_canvas_right", rotateCanvasRight); rotateCanvasRight->setShortcut(QKeySequence("Ctrl+]")); connect(rotateCanvasRight, SIGNAL(triggered()),m_d->canvasController, SLOT(rotateCanvasRight15())); KAction *rotateCanvasLeft = new KAction(i18n("Rotate Canvas Left"), this); actionCollection()->addAction("rotate_canvas_left", rotateCanvasLeft); rotateCanvasLeft->setShortcut(QKeySequence("Ctrl+[")); connect(rotateCanvasLeft, SIGNAL(triggered()),m_d->canvasController, SLOT(rotateCanvasLeft15())); KAction *resetCanvasTransformations = new KAction(i18n("Reset Canvas Transformations"), this); actionCollection()->addAction("reset_canvas_transformations", resetCanvasTransformations); resetCanvasTransformations->setShortcut(QKeySequence("Ctrl+'")); connect(resetCanvasTransformations, SIGNAL(triggered()),m_d->canvasController, SLOT(resetCanvasTransformations())); KToggleAction *wrapAroundAction = new KToggleAction(i18n("Wrap Around Mode"), this); actionCollection()->addAction("wrap_around_mode", wrapAroundAction); wrapAroundAction->setShortcut(QKeySequence(Qt::Key_W)); connect(wrapAroundAction, SIGNAL(toggled(bool)), m_d->canvasController, SLOT(slotToggleWrapAroundMode(bool))); KToggleAction *tAction = new KToggleAction(i18n("Show Status Bar"), this); tAction->setCheckedState(KGuiItem(i18n("Hide Status Bar"))); tAction->setChecked(true); tAction->setToolTip(i18n("Shows or hides the status bar")); actionCollection()->addAction("showStatusBar", tAction); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool))); tAction = new KToggleAction(i18n("Show Canvas Only"), this); tAction->setCheckedState(KGuiItem(i18n("Return to Window"))); tAction->setToolTip(i18n("Shows just the canvas or the whole window")); QList shortcuts; shortcuts << QKeySequence(Qt::Key_Tab); tAction->setShortcuts(shortcuts); tAction->setChecked(false); actionCollection()->addAction("view_show_just_the_canvas", tAction); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showJustTheCanvas(bool))); //Workaround, by default has the same shortcut as mirrorCanvas KAction* action = dynamic_cast(actionCollection()->action("format_italic")); if (action) { action->setShortcut(QKeySequence(), KAction::DefaultShortcut); action->setShortcut(QKeySequence(), KAction::ActiveShortcut); } //Workaround, by default has the same shortcut as hide/show dockers if (mainWindow()) { connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); action = dynamic_cast(mainWindow()->actionCollection()->action("view_toggledockers")); if (action) { action->setShortcut(QKeySequence(), KAction::DefaultShortcut); action->setShortcut(QKeySequence(), KAction::ActiveShortcut); } KoToolBoxFactory toolBoxFactory(m_d->canvasController); mainWindow()->createDockWidget(&toolBoxFactory); connect(canvasController, SIGNAL(toolOptionWidgetsChanged(QList)), mainWindow()->dockerManager(), SLOT(newOptionWidgets(QList))); mainWindow()->dockerManager()->setIcons(false); } m_d->statusBar = new KisStatusBar(this); connect(m_d->canvasController->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), m_d->statusBar, SLOT(documentMousePositionChanged(QPointF))); connect(m_d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), m_d->resourceProvider, SLOT(slotNodeActivated(KisNodeSP))); connect(m_d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), m_d->controlFrame->paintopBox(), SLOT(slotCurrentNodeChanged(KisNodeSP))); connect(m_d->resourceProvider->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), m_d->controlFrame->paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant))); connect(m_d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), m_d->doc->image(), SLOT(requestStrokeEnd())); connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)), m_d->controlFrame->paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), m_d->controlFrame->paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int))); // 25 px is a distance that works well for Tablet and Mouse events qApp->setStartDragDistance(25); loadPlugins(); // Wait for the async image to have loaded connect(m_d->doc, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished())); if (!m_d->doc->isLoading() || m_d->doc->image()) { slotLoadingFinished(); } connect(m_d->canvasController, SIGNAL(documentSizeChanged()), m_d->zoomManager, SLOT(slotScrollAreaSizeChanged())); setAcceptDrops(true); KConfigGroup group(KGlobal::config(), "krita/shortcuts"); foreach(KActionCollection *collection, KActionCollection::allCollections()) { collection->setConfigGroup("krita/shortcuts"); collection->readSettings(&group); } KisInputProfileManager::instance()->loadProfiles(); #if 0 //check for colliding shortcuts QSet existingShortcuts; foreach(QAction* action, actionCollection()->actions()) { if(action->shortcut() == QKeySequence(0)) { continue; } dbgUI << "shortcut " << action->text() << " " << action->shortcut(); Q_ASSERT(!existingShortcuts.contains(action->shortcut())); existingShortcuts.insert(action->shortcut()); } #endif KoResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); KisPaintOpPreset *preset = rserver->resourceByName("Basic_tip_default"); if (!preset) { if (rserver->resources().isEmpty()) { KMessageBox::error(this, i18n("Krita cannot find any brush presets and will close now. Please check your installation.", i18n("Critical Error"))); exit(0); } preset = rserver->resources().first(); } if (preset) { paintOpBox()->resourceSelected(preset); } } KisView2::~KisView2() { if (m_d->filterManager->isStrokeRunning()) { m_d->filterManager->cancel(); } { KConfigGroup group(KGlobal::config(), "krita/shortcuts"); foreach(KActionCollection *collection, KActionCollection::allCollections()) { collection->setConfigGroup("krita/shortcuts"); collection->writeSettings(&group); } } delete m_d; } void KisView2::dragEnterEvent(QDragEnterEvent *event) { dbgUI << "KisView2::dragEnterEvent"; // Only accept drag if we're not busy, particularly as we may // be showing a progress bar and calling qApp->processEvents(). if (event->mimeData()->hasImage() || event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node")) { event->accept(); } else { event->ignore(); } } void KisView2::dropEvent(QDropEvent *event) { KisImageSP kisimage = image(); Q_ASSERT(kisimage); QPoint cursorPos = canvasBase()->coordinatesConverter()->widgetToImage(event->pos()).toPoint(); QRect imageBounds = kisimage->bounds(); QPoint pasteCenter; bool forceRecenter; if (event->keyboardModifiers() & Qt::ShiftModifier && imageBounds.contains(cursorPos)) { pasteCenter = cursorPos; forceRecenter = true; } else { pasteCenter = imageBounds.center(); forceRecenter = false; } if (event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasImage()) { KisShapeController *kritaShapeController = dynamic_cast(m_d->doc->shapeController()); KisNodeSP node = KisMimeData::loadNode(event->mimeData(), imageBounds, pasteCenter, forceRecenter, kisimage, kritaShapeController); if (node) { KisNodeCommandsAdapter adapter(this); if (!m_d->nodeManager->activeLayer()) { adapter.addNode(node, kisimage->rootLayer() , 0); } else { adapter.addNode(node, m_d->nodeManager->activeLayer()->parent(), m_d->nodeManager->activeLayer()); } } } else if (event->mimeData()->hasUrls()) { QList urls = event->mimeData()->urls(); if (urls.length() > 0) { KMenu popup; popup.setObjectName("drop_popup"); QAction *insertAsNewLayer = new KAction(i18n("Insert as New Layer"), &popup); QAction *insertManyLayers = new KAction(i18n("Insert Many Layers"), &popup); QAction *openInNewDocument = new KAction(i18n("Open in New Document"), &popup); QAction *openManyDocuments = new KAction(i18n("Open Many Documents"), &popup); QAction *replaceCurrentDocument = new KAction(i18n("Replace Current Document"), &popup); QAction *cancel = new KAction(i18n("Cancel"), &popup); popup.addAction(insertAsNewLayer); popup.addAction(openInNewDocument); popup.addAction(replaceCurrentDocument); popup.addAction(insertManyLayers); popup.addAction(openManyDocuments); insertAsNewLayer->setEnabled(image() && urls.count() == 1); openInNewDocument->setEnabled(urls.count() == 1); replaceCurrentDocument->setEnabled(image() && urls.count() == 1); insertManyLayers->setEnabled(image() && urls.count() > 1); openManyDocuments->setEnabled(urls.count() > 1); popup.addSeparator(); popup.addAction(cancel); QAction *action = popup.exec(QCursor::pos()); if (action != 0 && action != cancel) { foreach(const QUrl &url, urls) { if (action == insertAsNewLayer || action == insertManyLayers) { m_d->imageManager->importImage(KUrl(url)); activateWindow(); } else if (action == replaceCurrentDocument) { if (m_d->doc->isModified()) { m_d->doc->save(); } if (mainWindow() != 0) { /** * NOTE: this is effectively deferred self-destruction */ connect(mainWindow(), SIGNAL(loadCompleted()), mainWindow(), SLOT(close())); mainWindow()->openDocument(url); } } else { Q_ASSERT(action == openInNewDocument || action == openManyDocuments); if (mainWindow()) { mainWindow()->openDocument(url); } } } } } } } bool KisView2::event( QEvent* event ) { switch(static_cast(event->type())) { case ViewModeSwitchEvent::AboutToSwitchViewModeEvent: { ViewModeSynchronisationObject* syncObject = static_cast(event)->synchronisationObject(); KisCanvasResourceProvider* provider = resourceProvider(); syncObject->backgroundColor = provider->bgColor(); syncObject->foregroundColor = provider->fgColor(); syncObject->exposure = provider->HDRExposure(); syncObject->gamma = provider->HDRGamma(); syncObject->compositeOp = provider->currentCompositeOp(); syncObject->pattern = provider->currentPattern(); syncObject->gradient = provider->currentGradient(); syncObject->node = provider->currentNode(); syncObject->paintOp = provider->currentPreset(); syncObject->opacity = provider->opacity(); syncObject->globalAlphaLock = provider->globalAlphaLock(); syncObject->documentOffset = canvasControllerWidget()->scrollBarValue() - pos(); syncObject->zoomLevel = zoomController()->zoomAction()->effectiveZoom(); syncObject->rotationAngle = canvasBase()->rotationAngle(); syncObject->activeToolId = KoToolManager::instance()->activeToolId(); syncObject->gridData = &document()->gridData(); syncObject->initialized = true; QMainWindow* mainWindow = qobject_cast(qApp->activeWindow()); if(mainWindow) { QList dockWidgets = mainWindow->findChildren(); foreach(QDockWidget* widget, dockWidgets) { if (widget->isFloating()) { widget->hide(); } } } return true; } case ViewModeSwitchEvent::SwitchedToDesktopModeEvent: { ViewModeSynchronisationObject* syncObject = static_cast(event)->synchronisationObject(); canvasControllerWidget()->setFocus(); qApp->processEvents(); if(syncObject->initialized) { KisCanvasResourceProvider* provider = resourceProvider(); provider->setPaintOpPreset(syncObject->paintOp); qApp->processEvents(); KoToolManager::instance()->switchToolRequested(syncObject->activeToolId); qApp->processEvents(); KisPaintOpPresetSP preset = canvasBase()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); preset->settings()->setProperty("CompositeOp", syncObject->compositeOp); if(preset->settings()->hasProperty("OpacityValue")) preset->settings()->setProperty("OpacityValue", syncObject->opacity); provider->setPaintOpPreset(preset); provider->setBGColor(syncObject->backgroundColor); provider->setFGColor(syncObject->foregroundColor); provider->setHDRExposure(syncObject->exposure); provider->setHDRGamma(syncObject->gamma); provider->slotPatternActivated(syncObject->pattern); provider->slotGradientActivated(syncObject->gradient); provider->slotNodeActivated(syncObject->node); provider->setOpacity(syncObject->opacity); provider->setGlobalAlphaLock(syncObject->globalAlphaLock); provider->setCurrentCompositeOp(syncObject->compositeOp); document()->gridData().setGrid(syncObject->gridData->gridX(), syncObject->gridData->gridY()); document()->gridData().setGridColor(syncObject->gridData->gridColor()); document()->gridData().setPaintGridInBackground(syncObject->gridData->paintGridInBackground()); document()->gridData().setShowGrid(syncObject->gridData->showGrid()); document()->gridData().setSnapToGrid(syncObject->gridData->snapToGrid()); actionCollection()->action("zoom_in")->trigger(); qApp->processEvents(); QMainWindow* mainWindow = qobject_cast(qApp->activeWindow()); if(mainWindow) { QList dockWidgets = mainWindow->findChildren(); foreach(QDockWidget* widget, dockWidgets) { if (widget->isFloating()) { widget->show(); } } } zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, syncObject->zoomLevel); canvasControllerWidget()->rotateCanvas(syncObject->rotationAngle - canvasBase()->rotationAngle()); QPoint newOffset = syncObject->documentOffset + pos(); qApp->processEvents(); canvasControllerWidget()->setScrollBarValue(newOffset); } return true; } default: break; } return QWidget::event( event ); } KoZoomController *KisView2::zoomController() const { return m_d->zoomManager->zoomController(); } KisImageWSP KisView2::image() const { if (m_d && m_d->doc) { return m_d->doc->image(); } return 0; } KisCanvasResourceProvider * KisView2::resourceProvider() { return m_d->resourceProvider; } KisCanvas2 * KisView2::canvasBase() const { return m_d->canvas; } QWidget* KisView2::canvas() const { return m_d->canvas->canvasWidget(); } KisStatusBar * KisView2::statusBar() const { return m_d->statusBar; } KisPaintopBox* KisView2::paintOpBox() const { return m_d->controlFrame->paintopBox(); } KoProgressUpdater* KisView2::createProgressUpdater(KoProgressUpdater::Mode mode) { return new KisProgressUpdater(m_d->statusBar->progress(), document()->progressProxy(), mode); } KisSelectionManager * KisView2::selectionManager() { return m_d->selectionManager; } KoCanvasController * KisView2::canvasController() { return m_d->canvasController; } KisCanvasController *KisView2::canvasControllerWidget() { return m_d->canvasController; } KisNodeSP KisView2::activeNode() { if (m_d->nodeManager) return m_d->nodeManager->activeNode(); else return 0; } KisLayerSP KisView2::activeLayer() { if (m_d->nodeManager) return m_d->nodeManager->activeLayer(); else return 0; } KisPaintDeviceSP KisView2::activeDevice() { if (m_d->nodeManager) return m_d->nodeManager->activePaintDevice(); else return 0; } KisZoomManager * KisView2::zoomManager() { return m_d->zoomManager; } KisFilterManager * KisView2::filterManager() { return m_d->filterManager; } KisImageManager * KisView2::imageManager() { return m_d->imageManager; } KisSelectionSP KisView2::selection() { KisLayerSP layer = activeLayer(); if (layer) return layer->selection(); // falls through to the global // selection, or 0 in the end return image()->globalSelection(); } bool KisView2::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 * KisView2::undoAdapter() { KisImageWSP image = m_d->doc->image(); Q_ASSERT(image); return image->undoAdapter(); } void KisView2::slotLoadingFinished() { /** * Cold-start of image size/resolution signals */ slotImageResolutionChanged(); if (m_d->statusBar) { m_d->statusBar->imageSizeChanged(); } if (m_d->resourceProvider) { m_d->resourceProvider->slotImageSizeChanged(); } if (m_d->nodeManager) { m_d->nodeManager->nodesUpdated(); } if (m_d->statusBar) { connect(image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), m_d->statusBar, SLOT(updateStatusBarProfileLabel())); connect(image(), SIGNAL(sigProfileChanged(const KoColorProfile*)), m_d->statusBar, SLOT(updateStatusBarProfileLabel())); connect(image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), m_d->statusBar, SLOT(imageSizeChanged())); } connect(image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), m_d->resourceProvider, SLOT(slotImageSizeChanged())); connect(image(), SIGNAL(sigResolutionChanged(double,double)), m_d->resourceProvider, SLOT(slotOnScreenResolutionChanged())); connect(zoomManager()->zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), m_d->resourceProvider, SLOT(slotOnScreenResolutionChanged())); connect(image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), this, SLOT(slotImageSizeChanged(const QPointF&, const QPointF&))); connect(image(), SIGNAL(sigResolutionChanged(double,double)), this, SLOT(slotImageResolutionChanged())); connect(image(), SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(slotNodeChanged())); connect(image()->undoAdapter(), SIGNAL(selectionChanged()), selectionManager(), SLOT(selectionChanged())); /* * WARNING: Currently we access the global progress bar in two ways: * connecting to composite progress proxy (strokes) and creating * progress updaters. The latter way should be deprecated in favour * of displaying the status of the global strokes queue */ //image()->compositeProgressProxy()->addProxy(m_d->statusBar->progress()->progressProxy()); m_d->canvas->initializeImage(); if (m_d->controlFrame) { connect(image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), m_d->controlFrame->paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*))); } if (image()->locked()) { // If this is the first view on the image, the image will have been locked // so unlock it. image()->blockSignals(false); image()->unlock(); } KisNodeSP activeNode = m_d->doc->preActivatedNode(); m_d->doc->setPreActivatedNode(0); // to make sure that we don't keep a reference to a layer the user can later delete. if (!activeNode) { activeNode = image()->rootLayer()->firstChild(); } while (activeNode && !activeNode->inherits("KisLayer")) { activeNode = activeNode->nextSibling(); } if (activeNode) { m_d->nodeManager->slotNonUiActivatedNode(activeNode); } // get the assistants and push them to the manager QList paintingAssistants = m_d->doc->preLoadedAssistants(); foreach (KisPaintingAssistant* assistant, paintingAssistants) { m_d->paintingAssistantsDecoration->addAssistant(assistant); } /** * Dirty hack alert */ if (mainWindow() && m_d->zoomManager && m_d->zoomManager->zoomController()) m_d->zoomManager->zoomController()->setAspectMode(true); if (m_d->viewConverter) m_d->viewConverter->setZoomMode(KoZoomMode::ZOOM_PAGE); if (m_d->paintingAssistantsDecoration){ foreach(KisPaintingAssistant* assist, m_d->doc->preLoadedAssistants()){ m_d->paintingAssistantsDecoration->addAssistant(assist); } m_d->paintingAssistantsDecoration->setVisible(true); } updateGUI(); emit sigLoadingFinished(); } void KisView2::createActions() { actionCollection()->addAction(KStandardAction::Preferences, "preferences", this, SLOT(slotPreferences())); KAction *action = new KAction(i18n("Cleanup removed files..."), this); actionCollection()->addAction("edit_blacklist_cleanup", action); connect(action, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup())); } void KisView2::createManagers() { // Create the managers for filters, selections, layers etc. // XXX: When the currentlayer changes, call updateGUI on all // managers m_d->actionManager = new KisActionManager(this); m_d->filterManager = new KisFilterManager(this, m_d->doc); m_d->filterManager->setup(actionCollection()); m_d->selectionManager = new KisSelectionManager(this, m_d->doc); m_d->selectionManager->setup(actionCollection(), actionManager()); m_d->nodeManager = new KisNodeManager(this, m_d->doc); m_d->nodeManager->setup(actionCollection(), actionManager()); // the following cast is not really safe, but better here than in the zoomManager // best place would be outside kisview too m_d->zoomManager = new KisZoomManager(this, m_d->viewConverter, m_d->canvasController); m_d->zoomManager->setup(actionCollection()); m_d->imageManager = new KisImageManager(this); m_d->imageManager->setup(actionCollection()); m_d->gridManager = new KisGridManager(this); m_d->gridManager->setup(actionCollection()); m_d->canvas->addDecoration(m_d->gridManager); m_d->perspectiveGridManager = new KisPerspectiveGridManager(this); m_d->perspectiveGridManager->setup(actionCollection()); m_d->canvas->addDecoration(m_d->perspectiveGridManager); m_d->paintingAssistantsDecoration = new KisPaintingAssistantsDecoration(this); m_d->paintingAssistantsDecoration->setup(actionCollection()); m_d->canvas->addDecoration(m_d->paintingAssistantsDecoration); m_d->canvasControlsManager = new KisCanvasControlsManager(this); m_d->canvasControlsManager->setup(actionCollection()); } void KisView2::updateGUI() { m_d->nodeManager->updateGUI(); m_d->selectionManager->updateGUI(); m_d->filterManager->updateGUI(); m_d->zoomManager->updateGUI(); m_d->gridManager->updateGUI(); m_d->perspectiveGridManager->updateGUI(); m_d->actionManager->updateGUI(); } void KisView2::slotPreferences() { if (KisDlgPreferences::editPreferences()) { KisConfigNotifier::instance()->notifyConfigChanged(); m_d->resourceProvider->resetDisplayProfile(QApplication::desktop()->screenNumber(this)); // 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 confignotifier // because nodes are not QObjects (because only one base class // can be a QObject). KisNode* node = dynamic_cast(image()->rootLayer().data()); node->updateSettings(); } } void KisView2::slotBlacklistCleanup() { KisDlgBlacklistCleanup dialog; dialog.exec(); } void KisView2::resetImageSizeAndScroll(bool changeCentering, const QPointF oldImageStillPoint, const QPointF newImageStillPoint) { const KisCoordinatesConverter *converter = canvasBase()->coordinatesConverter(); QPointF oldPreferredCenter = m_d->canvasController->preferredCenter(); /** * Calculating the still point in old coordinates depending on the * parameters given */ QPointF oldStillPoint; if (changeCentering) { oldStillPoint = converter->imageToWidget(oldImageStillPoint) + converter->documentOffset(); } else { QSize oldDocumentSize = m_d->canvasController->documentSize(); oldStillPoint = QPointF(0.5 * oldDocumentSize.width(), 0.5 * oldDocumentSize.height()); } /** * Updating the document size */ QSizeF size(image()->width() / image()->xRes(), image()->height() / image()->yRes()); KoZoomController *zc = m_d->zoomManager->zoomController(); zc->setZoom(KoZoomMode::ZOOM_CONSTANT, zc->zoomAction()->effectiveZoom()); zc->setPageSize(size); zc->setDocumentSize(size, true); /** * Calculating the still point in new coordinates depending on the * parameters given */ QPointF newStillPoint; if (changeCentering) { newStillPoint = converter->imageToWidget(newImageStillPoint) + converter->documentOffset(); } else { QSize newDocumentSize = m_d->canvasController->documentSize(); newStillPoint = QPointF(0.5 * newDocumentSize.width(), 0.5 * newDocumentSize.height()); } m_d->canvasController->setPreferredCenter(oldPreferredCenter - oldStillPoint + newStillPoint); } void KisView2::slotImageSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint) { resetImageSizeAndScroll(true, oldStillPoint, newStillPoint); m_d->zoomManager->updateGUI(); } void KisView2::slotImageResolutionChanged() { resetImageSizeAndScroll(false); m_d->zoomManager->updateGUI(); } void KisView2::slotNodeChanged() { updateGUI(); } void KisView2::loadPlugins() { // Load all plugins KService::List offers = KServiceTypeTrader::self()->query(QString::fromLatin1("Krita/ViewPlugin"), QString::fromLatin1("(Type == 'Service') and " "([X-Krita-Version] == 28)")); KService::List::ConstIterator iter; for (iter = offers.constBegin(); iter != offers.constEnd(); ++iter) { KService::Ptr service = *iter; dbgUI << "Load plugin " << service->name(); QString error; KXMLGUIClient* plugin = dynamic_cast(service->createInstance(this, QVariantList(), &error)); if (plugin) { insertChildClient(plugin); } else { errKrita << "Fail to create an instance for " << service->name() << " " << error; } } } KisDoc2 * KisView2::document() const { return m_d->doc; } KoPrintJob * KisView2::createPrintJob() { return new KisPrintJob(image()); } KisNodeManager * KisView2::nodeManager() { return m_d->nodeManager; } KisActionManager* KisView2::actionManager() { return m_d->actionManager; } KisPerspectiveGridManager* KisView2::perspectiveGridManager() { return m_d->perspectiveGridManager; } KisGridManager * KisView2::gridManager() { return m_d->gridManager; } KisPaintingAssistantsDecoration* KisView2::paintingAssistantsDecoration() { return m_d->paintingAssistantsDecoration; } QMainWindow* KisView2::qtMainWindow() { if(m_d->mainWindow) return m_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 KisView2::setQtMainWindow(QMainWindow* newMainWindow) { m_d->mainWindow = newMainWindow; } void KisView2::slotTotalRefresh() { KisConfig cfg; m_d->canvas->resetCanvas(cfg.useOpenGL()); } void KisView2::slotCreateTemplate() { KoTemplateCreateDia::createTemplate("krita_template", ".kra", KisFactory2::componentData(), m_d->doc, this); } void KisView2::slotDocumentSaved() { m_d->saveIncremental->setEnabled(true); m_d->saveIncrementalBackup->setEnabled(true); } void KisView2::slotSaveIncremental() { if (!m_d->doc) return; bool foundVersion; bool fileAlreadyExists; bool isBackup; QString version = "000"; QString newVersion; QString letter; QString fileName = m_d->doc->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 = KIO::NetAccess::exists(fileName, KIO::NetAccess::DestinationSide, this); 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 == "{") { KMessageBox::error(this, "Alternative names exhausted, try manually saving with a higher number", "Couldn't save incremental version"); return; } m_d->doc->setSaveInBatchMode(true); m_d->doc->saveAs(fileName); m_d->doc->setSaveInBatchMode(false); if (mainWindow()) { mainWindow()->updateCaption(); } } void KisView2::slotSaveIncrementalBackup() { if (!m_d->doc) return; bool workingOnBackup; bool fileAlreadyExists; QString version = "000"; QString newVersion; QString letter; QString fileName = m_d->doc->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 = m_d->doc->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 = KIO::NetAccess::exists(backupFileName, KIO::NetAccess::DestinationSide, this); 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 == "{") { KMessageBox::error(this, "Alternative names exhausted, try manually saving with a higher number", "Couldn't save incremental backup"); return; } QFile::copy(fileName, backupFileName); m_d->doc->saveAs(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 = m_d->doc->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 = KIO::NetAccess::exists(backupFileName, KIO::NetAccess::DestinationSide, this); 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 m_d->doc->setSaveInBatchMode(true); QFile::copy(fileName, backupFileName); m_d->doc->saveAs(fileName); m_d->doc->setSaveInBatchMode(false); if (mainWindow()) mainWindow()->updateCaption(); } } void KisView2::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() m_d->controlFrame->paintopBox()->installEventFilter(&m_d->blockingEventFilter); foreach(QObject* child, m_d->controlFrame->paintopBox()->children()) { child->installEventFilter(&m_d->blockingEventFilter); } } void KisView2::enableControls() { m_d->controlFrame->paintopBox()->removeEventFilter(&m_d->blockingEventFilter); foreach(QObject* child, m_d->controlFrame->paintopBox()->children()) { child->removeEventFilter(&m_d->blockingEventFilter); } } void KisView2::showStatusBar(bool toggled) { if (KoView::statusBar()) { KoView::statusBar()->setVisible(toggled); } } #if defined HAVE_OPENGL && defined Q_OS_WIN32 #include #endif void KisView2::showJustTheCanvas(bool toggled) { KisConfig cfg; /** * Workaround for a broken Intel video driver on Windows :( * See bug 330040 */ #if defined HAVE_OPENGL && defined Q_OS_WIN32 if (toggled && cfg.useOpenGL()) { QString renderer((const char*)glGetString(GL_RENDERER)); bool failingDriver = renderer.startsWith("Intel(R) HD Graphics"); if (failingDriver && cfg.hideStatusbarFullscreen() && cfg.hideDockersFullscreen() && cfg.hideTitlebarFullscreen() && cfg.hideMenuFullscreen() && cfg.hideToolbarFullscreen() && cfg.hideScrollbarsFullscreen()) { int result = KMessageBox::warningYesNo(this, "Intel(R) HD Graphics video adapters " "are known to have problems with running " "Krita in pure canvas only mode. At least " "one UI control must be shown to " "workaround it.\n\nShow the scroll bars?", "Failing video adapter", KStandardGuiItem::yes(), KStandardGuiItem::no(), "messagebox_WorkaroundIntelVideoOnWindows"); if (result == KMessageBox::Yes) { cfg.setHideScrollbarsFullscreen(false); } } } #endif /* defined HAVE_OPENGL && defined Q_OS_WIN32 */ KoMainWindow* main = mainWindow(); if(!main) { main = window()->findChildren().value(0); } if(!main) { dbgUI << "Unable to switch to canvas-only mode, main window not found"; return; } if (cfg.hideStatusbarFullscreen()) { if(main->statusBar() && main->statusBar()->isVisible() == toggled) { main->statusBar()->setVisible(!toggled); } } if (cfg.hideDockersFullscreen()) { KToggleAction* action = qobject_cast(main->actionCollection()->action("view_toggledockers")); if (action && action->isChecked() == toggled) { action->setChecked(!toggled); } } if (cfg.hideTitlebarFullscreen()) { if(toggled) { window()->setWindowState( window()->windowState() | Qt::WindowFullScreen); } else { window()->setWindowState( window()->windowState() & ~Qt::WindowFullScreen); } } if (cfg.hideMenuFullscreen()) { if (main->menuBar()->isVisible() == toggled) { main->menuBar()->setVisible(!toggled); } } if (cfg.hideToolbarFullscreen()) { QList toolBars = main->findChildren(); foreach(QToolBar* toolbar, toolBars) { if (toolbar->isVisible() == toggled) { toolbar->setVisible(!toggled); } } } if (cfg.hideScrollbarsFullscreen()) { if (toggled) { dynamic_cast(canvasController())->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); dynamic_cast(canvasController())->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { dynamic_cast(canvasController())->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); dynamic_cast(canvasController())->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } if (toggled) { // show a fading heads-up display about the shortcut to go back KisFloatingMessage *floatingMessage = new KisFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.", actionCollection()->action("view_show_just_the_canvas")->shortcut().toString()), this); floatingMessage->showMessage(); } } void KisView2::toggleTabletLogger() { m_d->canvas->toggleTabletLogger(); } void KisView2::openResourcesDirectory() { QString dir = KStandardDirs::locateLocal("data", "krita"); QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); } void KisView2::showFloatingMessage(const QString message, const QIcon& icon) { // Yes, the @return is correct. But only for widget based KDE apps, not QML based ones if (mainWindow()) { KisFloatingMessage *floatingMessage = new KisFloatingMessage(message, mainWindow()->centralWidget()); floatingMessage->setShowOverParent(true); floatingMessage->setIcon(icon); floatingMessage->showMessage(); } #if QT_VERSION >= 0x040700 emit floatingMessageRequested(message, icon.name()); #endif } #include "kis_view2.moc"