diff --git a/krita/gemini/desktopviewproxy.cpp b/krita/gemini/desktopviewproxy.cpp index 279e6339ee..464b28d949 100644 --- a/krita/gemini/desktopviewproxy.cpp +++ b/krita/gemini/desktopviewproxy.cpp @@ -1,239 +1,242 @@ /* * * 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 #include #include +#include class DesktopViewProxy::Private { public: Private(MainWindow* mainWindow, KisMainWindow* desktopView) : mainWindow(mainWindow) , desktopWindow(desktopView) , isImporting(false) {} MainWindow* mainWindow; KisMainWindow* desktopWindow; bool isImporting; }; DesktopViewProxy::DesktopViewProxy(MainWindow* mainWindow, KisMainWindow* parent) : QObject(parent) , d(new Private(mainWindow, parent)) { Q_ASSERT(parent); // "There MUST be a KisMainWindow assigned, otherwise everything will blow up"); // Hide this one... as it doesn't work at all well and release happens :P QAction* closeAction = d->desktopWindow->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->desktopWindow->actionCollection()->action("file_new"); newAction->disconnect(d->desktopWindow); connect(newAction, SIGNAL(triggered(bool)), this, SLOT(fileNew())); QAction* openAction = d->desktopWindow->actionCollection()->action("file_open"); openAction->disconnect(d->desktopWindow); connect(openAction, SIGNAL(triggered(bool)), this, SLOT(fileOpen())); QAction* saveAction = d->desktopWindow->actionCollection()->action("file_save"); saveAction->disconnect(d->desktopWindow); connect(saveAction, SIGNAL(triggered(bool)), this, SLOT(fileSave())); QAction* saveasAction = d->desktopWindow->actionCollection()->action("file_save_as"); saveasAction->disconnect(d->desktopWindow); connect(saveasAction, SIGNAL(triggered(bool)), this, SLOT(fileSaveAs())); QAction* reloadAction = d->desktopWindow->actionCollection()->action("file_reload_file"); reloadAction->disconnect(d->desktopWindow); connect(reloadAction, SIGNAL(triggered(bool)), this, SLOT(reload())); QAction* loadExistingAsNewAction = d->desktopWindow->actionCollection()->action("file_import_file"); //Hide the "Load existing as new" action. It serves little purpose and currently //does the same as open. We cannot just remove it from the action collection though //since that causes a crash in KisMainWindow. loadExistingAsNewAction->setVisible(false); // Recent files need a touch more work, as they aren't simply an action. KRecentFilesAction* recent = qobject_cast(d->desktopWindow->actionCollection()->action("file_open_recent")); recent->disconnect(d->desktopWindow); connect(recent, SIGNAL(urlSelected(QUrl)), this, SLOT(slotFileOpenRecent(QUrl))); recent->clear(); recent->loadEntries(KGlobal::config()->group("RecentFiles")); connect(d->desktopWindow, SIGNAL(documentSaved()), this, SIGNAL(documentSaved())); - connect(d->desktopWindow, SIGNAL(keyBindingsChanged()), this, SLOT(updateKeyBindings())); + // XXX: Shortcut editor is untested in Gemini since refactoring. + connect(KisActionRegistry::instance(), SIGNAL(shortcutsUpdated()), this, SLOT(updateShortcuts())); + } DesktopViewProxy::~DesktopViewProxy() { delete d; } void DesktopViewProxy::fileNew() { QProcess::startDetached(qApp->applicationFilePath(), QStringList(), QDir::currentPath()); } void DesktopViewProxy::fileOpen() { KisDocumentEntry entry = KisDocumentEntry::queryByMimeType(KIS_MIME_TYPE); KService::Ptr service = entry.service(); const QStringList mimeFilter = KisImportExportManager::mimeFilter(KIS_MIME_TYPE, KisImportExportManager::Import, service->property("X-KDE-ExtraNativeMimeTypes").toStringList()); KoFileDialog dialog(d->desktopWindow, 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->desktopWindow->saveDocument(d->desktopWindow->activeView()->document(), 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->desktopWindow->saveDocument(d->desktopWindow->activeView()->document(), 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 QUrl& url) { QProcess::startDetached(qApp->applicationFilePath(), QStringList() << url.toLocalFile(), QDir::currentPath()); } /** * @brief Override to allow for full-screen support with Canvas-mode * * The basic behaviour of the KisViewManager is to check the KoConfig and * to adjust the main window appropriately. If "hideTitlebar" is set * true, then it switches the window between windowed and full-screen. * To prevent it leaving the full-screen mode, we set the mode to false * * @param toggled */ void DesktopViewProxy::toggleShowJustTheCanvas(bool toggled) { KisViewManager *kisViewManager = qobject_cast(d->desktopWindow->activeView()); if(toggled) { kisViewManager->showJustTheCanvas(toggled); } else { KisConfig cfg; bool fullScreen = d->mainWindow->forceFullScreen(); bool hideTitlebar = cfg.hideTitlebarFullscreen(); if (fullScreen) { cfg.setHideTitlebarFullscreen(false); } kisViewManager->showJustTheCanvas(toggled); if (fullScreen) { cfg.setHideTitlebarFullscreen(hideTitlebar); } } } void DesktopViewProxy::documentChanged() { // Remove existing linking for toggling canvas, in order // to over-ride the window state behaviour QAction* toggleJustTheCanvasAction = d->desktopWindow->actionCollection()->action("view_show_canvas_only"); toggleJustTheCanvasAction->disconnect(d->desktopWindow); connect(toggleJustTheCanvasAction, SIGNAL(toggled(bool)), this, SLOT(toggleShowJustTheCanvas(bool))); } void DesktopViewProxy::updateKeyBindings() { //KisView* view = qobject_cast(d->mainWindow->sketchKisView()); Q_FOREACH (QAction* action, d->desktopWindow->actions()) { QAction* otherAction = d->desktopWindow->action(action->objectName().toLatin1()); if(otherAction) { otherAction->setShortcut(action->shortcut()); } else { // That's ok - there are some actions that are not truly actions and don't have shortcuts, // so we'll just not bother with those, and we're fine with that :) } } } diff --git a/krita/krita.rc b/krita/krita.rc index 5a349efb99..fe8863b8d6 100644 --- a/krita/krita.rc +++ b/krita/krita.rc @@ -1,341 +1,342 @@ &File &Edit &View &Canvas &Image &Rotate &Layer New &Import/Export Import &Convert &Select S&plit Alpha &Rotate &Select Filte&r &Tools Recording Macros Setti&ngs - + + &Help File Brushes and Stuff diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp index 5b8c854ab1..33282e42d0 100644 --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -1,2406 +1,2399 @@ /* 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, KisDocument::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(); 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); 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 b3ea04e1a8..6b8b23db7f 100644 --- a/libs/ui/KisMainWindow.h +++ b/libs/ui/KisMainWindow.h @@ -1,463 +1,458 @@ /* 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/KisPart.cpp b/libs/ui/KisPart.cpp index b2d68a260c..1417625bec 100644 --- a/libs/ui/KisPart.cpp +++ b/libs/ui/KisPart.cpp @@ -1,644 +1,636 @@ /* This file is part of the KDE project * Copyright (C) 1998-1999 Torben Weis * Copyright (C) 2000-2005 David Faure * Copyright (C) 2007-2008 Thorsten Zachmann * Copyright (C) 2010-2012 Boudewijn Rempt * Copyright (C) 2011 Inge Wallin * Copyright (C) 2015 Michael Abrahams * * 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 "KisPart.h" #include "KoProgressProxy.h" #include #include #include #include #include #include #include #include #include "KisApplication.h" #include "KisDocument.h" #include "KisView.h" #include "KisViewManager.h" #include "KisOpenPane.h" #include "KisImportExportManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisView.h" #include "KisDocument.h" #include "kis_config.h" #include "kis_clipboard.h" #include "kis_custom_image_widget.h" #include "kis_image_from_clipboard_widget.h" #include "kis_shape_controller.h" #include "kis_resource_server_provider.h" #ifdef HAVE_OPENGL #include "kis_animation_cache_populator.h" #endif #include "kis_idle_watcher.h" #include "kis_image.h" #include "KisImportExportManager.h" #include "KisDocument.h" #include "KoToolManager.h" #include "kis_color_manager.h" #include "kis_debug.h" #include "kis_action.h" #include "kis_action_registry.h" Q_GLOBAL_STATIC(KisPart, s_instance) class Q_DECL_HIDDEN KisPart::Private { public: Private(KisPart *_part) : part(_part) , idleWatcher(2500) #ifdef HAVE_OPENGL , animationCachePopulator(_part) #endif { } ~Private() { delete canvasItem; } KisPart *part; QList > views; QList > mainWindows; QList > documents; QGraphicsItem *canvasItem{0}; KisOpenPane *startupWidget{0}; KActionCollection *actionCollection{0}; KisIdleWatcher idleWatcher; #ifdef HAVE_OPENGL KisAnimationCachePopulator animationCachePopulator; #endif void loadActions(); }; // Basically, we are going to insert the current UI/MainWindow ActionCollection // into the KisActionRegistry. -void KisPart::Private::loadActions() +void KisPart::loadActions() { - actionCollection = part->currentMainwindow()->viewManager()->actionCollection(); + d->actionCollection = currentMainwindow()->viewManager()->actionCollection(); KisActionRegistry * actionRegistry = KisActionRegistry::instance(); - Q_FOREACH (auto action, actionCollection->actions()) { + Q_FOREACH (auto action, d->actionCollection->actions()) { auto name = action->objectName(); actionRegistry->addAction(action->objectName(), action); } }; KisPart* KisPart::instance() { return s_instance; } KisPart::KisPart() : d(new Private(this)) { // Preload all the resources in the background Q_UNUSED(KoResourceServerProvider::instance()); Q_UNUSED(KisResourceServerProvider::instance()); Q_UNUSED(KisColorManager::instance()); connect(this, SIGNAL(documentOpened(QString)), this, SLOT(updateIdleWatcherConnections())); connect(this, SIGNAL(documentClosed(QString)), this, SLOT(updateIdleWatcherConnections())); connect(KisActionRegistry::instance(), SIGNAL(shortcutsUpdated()), this, SLOT(updateShortcuts())); #ifdef HAVE_OPENGL connect(&d->idleWatcher, SIGNAL(startedIdleMode()), &d->animationCachePopulator, SLOT(slotRequestRegeneration())); d->animationCachePopulator.slotRequestRegeneration(); #endif } KisPart::~KisPart() { while (!d->documents.isEmpty()) { delete d->documents.takeFirst(); } while (!d->views.isEmpty()) { delete d->views.takeFirst(); } while (!d->mainWindows.isEmpty()) { delete d->mainWindows.takeFirst(); } delete d; } void KisPart::updateIdleWatcherConnections() { QVector images; Q_FOREACH (QPointer document, documents()) { images << document->image(); } d->idleWatcher.setTrackedImages(images); } void KisPart::addDocument(KisDocument *document) { //dbgUI << "Adding document to part list" << document; Q_ASSERT(document); if (!d->documents.contains(document)) { d->documents.append(document); emit documentOpened('/'+objectName()); } } QList > KisPart::documents() const { return d->documents; } KisDocument *KisPart::createDocument() const { KisDocument *doc = new KisDocument(); return doc; } int KisPart::documentCount() const { return d->documents.size(); } void KisPart::removeDocument(KisDocument *document) { d->documents.removeAll(document); emit documentClosed('/'+objectName()); document->deleteLater(); } KisMainWindow *KisPart::createMainWindow() { KisMainWindow *mw = new KisMainWindow(); dbgUI <<"mainWindow" << (void*)mw << "added to view" << this; d->mainWindows.append(mw); return mw; } KisView *KisPart::createView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent) { // If creating the canvas fails, record this and disable OpenGL next time KisConfig cfg; KConfigGroup grp( KSharedConfig::openConfig(), "krita/crashprevention"); if (grp.readEntry("CreatingCanvas", false)) { cfg.setUseOpenGL(false); } if (cfg.canvasState() == "OPENGL_FAILED") { cfg.setUseOpenGL(false); } grp.writeEntry("CreatingCanvas", true); grp.sync(); QApplication::setOverrideCursor(Qt::WaitCursor); KisView *view = new KisView(document, resourceManager, actionCollection, parent); QApplication::restoreOverrideCursor(); // Record successful canvas creation grp.writeEntry("CreatingCanvas", false); grp.sync(); addView(view); return view; } void KisPart::addView(KisView *view) { if (!view) return; if (!d->views.contains(view)) { d->views.append(view); } connect(view, SIGNAL(destroyed()), this, SLOT(viewDestroyed())); emit sigViewAdded(view); } void KisPart::removeView(KisView *view) { if (!view) return; emit sigViewRemoved(view); QPointer doc = view->document(); d->views.removeAll(view); if (doc) { bool found = false; Q_FOREACH (QPointer view, d->views) { if (view && view->document() == doc) { found = true; break; } } if (!found) { removeDocument(doc); } } } QList > KisPart::views() const { return d->views; } int KisPart::viewCount(KisDocument *doc) const { if (!doc) { return d->views.count(); } else { int count = 0; Q_FOREACH (QPointer view, d->views) { if (view->document() == doc) { count++; } } return count; } } QGraphicsItem *KisPart::canvasItem(KisDocument *document, bool create) { if (create && !d->canvasItem) { d->canvasItem = createCanvasItem(document); } return d->canvasItem; } QGraphicsItem *KisPart::createCanvasItem(KisDocument *document) { if (!document) return 0; KisView *view = createView(document, 0, 0, 0); QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(); QWidget *canvasController = view->findChild(); proxy->setWidget(canvasController); return proxy; } void KisPart::removeMainWindow(KisMainWindow *mainWindow) { dbgUI <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this; if (mainWindow) { d->mainWindows.removeAll(mainWindow); } } const QList > &KisPart::mainWindows() const { return d->mainWindows; } int KisPart::mainwindowCount() const { return d->mainWindows.count(); } KisMainWindow *KisPart::currentMainwindow() const { QWidget *widget = qApp->activeWindow(); KisMainWindow *mainWindow = qobject_cast(widget); while (!mainWindow && widget) { widget = widget->parentWidget(); mainWindow = qobject_cast(widget); } if (!mainWindow && mainWindows().size() > 0) { mainWindow = mainWindows().first(); } return mainWindow; } KisIdleWatcher* KisPart::idleWatcher() const { return &d->idleWatcher; } #ifdef HAVE_OPENGL KisAnimationCachePopulator* KisPart::cachePopulator() const { return &d->animationCachePopulator; } #endif void KisPart::openExistingFile(const QUrl &url) { Q_ASSERT(url.isLocalFile()); qApp->setOverrideCursor(Qt::BusyCursor); KisDocument *document = createDocument(); if (!document->openUrl(url)) { delete document; return; } if (!document->image()) { delete document; return; } document->setModified(false); addDocument(document); KisMainWindow *mw = 0; if (d->startupWidget) { mw = qobject_cast(d->startupWidget->parent()); } if (!mw) { mw = currentMainwindow(); } mw->addViewAndNotifyLoadingCompleted(document); if (d->startupWidget) { d->startupWidget->setParent(0); d->startupWidget->hide(); } qApp->restoreOverrideCursor(); } -void KisPart::configureShortcuts() -{ - d->loadActions(); - - auto actionRegistry = KisActionRegistry::instance(); - actionRegistry->configureShortcuts(); -} - void KisPart::updateShortcuts() { // Update any non-UI actionCollections. That includes: // - Shortcuts called inside of tools // - Perhaps other things? KoToolManager::instance()->updateToolShortcuts(); // Now update the UI actions. Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) { KActionCollection *ac = mainWindow->actionCollection(); ac->updateShortcuts(); // Loop through mainWindow->actionCollections() to modify tooltips // so that they list shortcuts at the end in parentheses Q_FOREACH ( QAction* action, ac->actions()) { // Remove any existing suffixes from the tooltips. // Note this regexp starts with a space, e.g. " (Ctrl-a)" QString strippedTooltip = action->toolTip().remove(QRegExp("\\s\\(.*\\)")); // Now update the tooltips with the new shortcut info. if(action->shortcut() == QKeySequence(0)) action->setToolTip(strippedTooltip); else action->setToolTip( strippedTooltip + " (" + action->shortcut().toString() + ")"); } } } void KisPart::openTemplate(const QUrl &url) { qApp->setOverrideCursor(Qt::BusyCursor); KisDocument *document = createDocument(); bool ok = document->loadNativeFormat(url.toLocalFile()); document->setModified(false); 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$" ) ); document->setMimeTypeAfterLoading(mimeType); document->resetURL(); document->setEmpty(); } else { document->showLoadingErrorDialog(); document->initEmpty(); } addDocument(document); KisMainWindow *mw = qobject_cast(d->startupWidget->parent()); if (!mw) mw = currentMainwindow(); mw->addViewAndNotifyLoadingCompleted(document); d->startupWidget->setParent(0); d->startupWidget->hide(); qApp->restoreOverrideCursor(); } void KisPart::viewDestroyed() { KisView *view = qobject_cast(sender()); if (view) { removeView(view); } } void KisPart::addRecentURLToAllMainWindows(QUrl url) { // Add to recent actions list in our mainWindows Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) { mainWindow->addRecentURL(url); } } void KisPart::showStartUpWidget(KisMainWindow *mainWindow, bool alwaysShow) { if (!alwaysShow) { KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); QString fullTemplateName = cfgGrp.readPathEntry("AlwaysUseTemplate", QString()); if (!fullTemplateName.isEmpty()) { QUrl url(fullTemplateName); QFileInfo fi(url.toLocalFile()); if (!fi.exists()) { const QString templatesPath = templatesResourcePath(); QString desktopfile = KoResourcePaths::findResource("data", templatesPath + "*/" + fullTemplateName); if (desktopfile.isEmpty()) { desktopfile = KoResourcePaths::findResource("data", templatesPath + fullTemplateName); } if (desktopfile.isEmpty()) { fullTemplateName.clear(); } else { KDesktopFile f(desktopfile); fullTemplateName = QFileInfo(desktopfile).absolutePath() + '/' + f.readUrl(); } } if (!fullTemplateName.isEmpty()) { openTemplate(QUrl::fromLocalFile(fullTemplateName)); return; } } } if (d->startupWidget) { delete d->startupWidget; } const QStringList mimeFilter = KisImportExportManager::mimeFilter(KIS_MIME_TYPE, KisImportExportManager::Import, KisDocument::extraNativeMimeTypes()); d->startupWidget = new KisOpenPane(0, mimeFilter, templatesResourcePath()); d->startupWidget->setWindowModality(Qt::WindowModal); QList widgetList = createCustomDocumentWidgets(d->startupWidget); Q_FOREACH (const CustomDocumentWidgetItem & item, widgetList) { d->startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); connect(item.widget, SIGNAL(documentSelected(KisDocument*)), this, SLOT(startCustomDocument(KisDocument*))); } connect(d->startupWidget, SIGNAL(openExistingFile(const QUrl&)), this, SLOT(openExistingFile(const QUrl&))); connect(d->startupWidget, SIGNAL(openTemplate(const QUrl&)), this, SLOT(openTemplate(const QUrl&))); d->startupWidget->setParent(mainWindow); d->startupWidget->setWindowFlags(Qt::Dialog); d->startupWidget->exec(); } QList KisPart::createCustomDocumentWidgets(QWidget * parent) { KisConfig cfg; int w = cfg.defImageWidth(); int h = cfg.defImageHeight(); const double resolution = cfg.defImageResolution(); const QString colorModel = cfg.defColorModel(); const QString colorDepth = cfg.defaultColorDepth(); const QString colorProfile = cfg.defColorProfile(); QList widgetList; { KisPart::CustomDocumentWidgetItem item; item.widget = new KisCustomImageWidget(parent, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.icon = "application-x-krita"; widgetList << item; } { QSize sz = KisClipboard::instance()->clipSize(); if (sz.isValid() && sz.width() != 0 && sz.height() != 0) { w = sz.width(); h = sz.height(); } KisPart::CustomDocumentWidgetItem item; item.widget = new KisImageFromClipboard(parent, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.title = i18n("Create from Clipboard"); item.icon = "klipper"; widgetList << item; } return widgetList; } QString KisPart::templatesResourcePath() const { return QStringLiteral("krita/templates/"); } void KisPart::startCustomDocument(KisDocument* doc) { addDocument(doc); KisMainWindow *mw = qobject_cast(d->startupWidget->parent()); if (!mw) mw = currentMainwindow(); mw->addViewAndNotifyLoadingCompleted(doc); d->startupWidget->setParent(0); d->startupWidget->hide(); } KisInputManager* KisPart::currentInputManager() { return instance()->currentMainwindow()->viewManager()->inputManager(); } #include #include #include diff --git a/libs/ui/KisPart.h b/libs/ui/KisPart.h index 272a7ff95e..0ca283c1da 100644 --- a/libs/ui/KisPart.h +++ b/libs/ui/KisPart.h @@ -1,316 +1,311 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2005 David Faure Copyright (C) 2007 Thorsten Zachmann Copyright (C) 2010 Boudewijn Rempt Copyright (C) 2015 Michael Abrahams 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_PART_H #define KIS_PART_H #include #include #include #include "kritaui_export.h" #include #include namespace KIO { } class KisDocument; class KisView; class QGraphicsItem; class KisDocument; class KisIdleWatcher; #ifdef HAVE_OPENGL class KisAnimationCachePopulator; #endif /** * KisPart is the Great Deku Tree of Krita. * * It is a singleton class which provides the main entry point to the application. * Krita supports multiple documents, multiple main windows, and multiple * components. KisPart manages these resources and provides them to the rest of * Krita. It manages lists of Actions and shortcuts as well. * * The terminology comes from KParts, which is a system allowing one KDE app * to be run from inside another, like pressing F4 inside dophin to run konsole. * * Needless to say, KisPart hasn't got much to do with KParts anymore. */ class KRITAUI_EXPORT KisPart : public QObject { Q_OBJECT public: static KisPart *instance(); /** * Constructor. * * @param parent may be another KisDocument, or anything else. * Usually passed by KPluginFactory::create. */ explicit KisPart(); /** * Destructor. * * The destructor does not delete any attached KisView objects and it does not * delete the attached widget as returned by widget(). */ ~KisPart(); // ----------------- Document management ----------------- /** * create an empty document. The document is not automatically registered with the part. */ KisDocument *createDocument() const; /** * Add the specified document to the list of documents this KisPart manages. */ void addDocument(KisDocument *document); /** * @return a list of all documents this part manages */ QList > documents() const; /** * @return number of documents this part manages. */ int documentCount() const; void removeDocument(KisDocument *document); // ----------------- MainWindow management ----------------- /** * Create a new main window. */ KisMainWindow *createMainWindow(); /** * Removes a main window from the list of managed windows. * * This is called by the MainWindow after it finishes its shutdown routine. */ void removeMainWindow(KisMainWindow *mainWindow); /** * @return the list of main windows. */ const QList >& mainWindows() const; /** * @return the number of shells for the main window */ int mainwindowCount() const; void addRecentURLToAllMainWindows(QUrl url); /** * @return the currently active main window. */ KisMainWindow *currentMainwindow() const; + /** + * Load actions for currently active main window into KisActionRegistry. + */ + void loadActions(); /** * @return the application-wide KisIdleWatcher. */ KisIdleWatcher *idleWatcher() const; #ifdef HAVE_OPENGL /** * @return the application-wide AnimationCachePopulator. */ KisAnimationCachePopulator *cachePopulator() const; #endif public Q_SLOTS: /** * This slot loads an existing file and deletes the start up widget. * @param url the file to load */ void openExistingFile(const QUrl &url); - /** - * @brief configureShortcuts opens the shortcut configuration dialog. - * @param parent the parent widget for the dialog - * - * After the user closes the dialog, all actioncollections will be updated - * with the new shortcuts. - */ - void configureShortcuts(); - protected Q_SLOTS: /** * This slot loads a template and deletes the start up widget. * @param url the template to load */ void openTemplate(const QUrl &url); private Q_SLOTS: void viewDestroyed(); void startCustomDocument(KisDocument *doc); void updateIdleWatcherConnections(); void updateShortcuts(); Q_SIGNALS: /** * emitted when a new document is opened. */ void documentOpened(const QString &ref); /** * emitted when an old document is closed. */ void documentClosed(const QString &ref); void sigViewAdded(KisView *view); void sigViewRemoved(KisView *view); public: static KisInputManager *currentInputManager(); //------------------ View management ------------------ /** * Create a new view for the document. The view is added to the list of * views, and if the document wasn't known yet, it's registered as well. */ KisView *createView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent); /** * Adds a view to the document. If the part doesn't know yet about * the document, it is registered. * * This calls KisView::updateReadWrite to tell the new view * whether the document is readonly or not. */ void addView(KisView *view); /** * Removes a view of the document. */ void removeView(KisView *view); /** * @return a list of views this document is displayed in */ QList > views() const; /** * @return number of views this document is displayed in */ int viewCount(KisDocument *doc) const; /** * @return a QGraphicsItem canvas displaying this document. There is only one QGraphicsItem canvas that can * be shown by many QGraphicsView subclasses (those should reimplement KoCanvasController * as well). * * @param create if true, a new canvas item is created if there wasn't one. */ QGraphicsItem *canvasItem(KisDocument *document, bool create = true); // ------- Startup/openpane etc --------------- /** * Template resource path used. This is used by the start up widget to show * the correct templates. */ QString templatesResourcePath() const; /** * Creates and shows the start up widget. * @param parent the KisMainWindow used as parent for the widget. * @param alwaysShow always show the widget even if the user has configured it to not show. */ void showStartUpWidget(KisMainWindow *parent, bool alwaysShow = false); protected: /** * Struct used in the list created by createCustomDocumentWidgets() */ struct CustomDocumentWidgetItem { /// Pointer to the custom document widget QWidget *widget; /// title used in the sidebar. If left empty it will be displayed as "Custom Document" QString title; /// icon used in the sidebar. If left empty it will use the unknown icon QString icon; }; /** * This generates widgets for the startup dialog. It populates the dialog * with widgets providing different ways to load new documents. * * (For example, "blank document", "create from clipboard", "open file") * * Each widget returned from this function should follow a certain format. * The returned widget should provide its own button (preferably 'Create') * and implement the logic to implement the document instance correctly. * The widget should use the signal 'documentSelected(KisDocument*)' to * notify that the startup dialog should close and KisPart should load the * new document. * * @see KisPart::showStartUpWidget() * @see KisDocument::createCustomDocumentWidget() * @see KisOpenPane::createCustomDocumentWidget() * * @param parent the parent of the to be created widget. * @return a list of KisDocument::CustomDocumentWidgetItem. */ QList createCustomDocumentWidgets(QWidget *parent); /** * Override this to create a QGraphicsItem that does not rely * on proxying a KoCanvasController. */ QGraphicsItem *createCanvasItem(KisDocument *document); private: Q_DISABLE_COPY(KisPart) class Private; Private *const d; }; #endif diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc index 0680fe64a1..f51aba5469 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -1,1021 +1,1065 @@ /* * preferencesdlg.cc - part of KImageShop * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 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_dlg_preferences.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 "KoID.h" #include #include #include #include #include +#include "kis_action_registry.h" #include "widgets/squeezedcombobox.h" #include "kis_clipboard.h" #include "widgets/kis_cmb_idlist.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "kis_cursor.h" #include "kis_config.h" #include "kis_canvas_resource_provider.h" #include "kis_preference_set_registry.h" #include "kis_color_manager.h" #include "slider_and_spin_box_sync.h" // for the performance update #include #include "input/config/kis_input_configuration_page.h" GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) { KisConfig cfg; m_cmbCursorShape->addItem(i18n("No Cursor")); m_cmbCursorShape->addItem(i18n("Tool Icon")); m_cmbCursorShape->addItem(i18n("Arrow")); m_cmbCursorShape->addItem(i18n("Small Circle")); m_cmbCursorShape->addItem(i18n("Crosshair")); m_cmbCursorShape->addItem(i18n("Triangle Righthanded")); m_cmbCursorShape->addItem(i18n("Triangle Lefthanded")); m_cmbCursorShape->addItem(i18n("Black Pixel")); m_cmbCursorShape->addItem(i18n("White Pixel")); m_cmbOutlineShape->addItem(i18n("No Outline")); m_cmbOutlineShape->addItem(i18n("Circle Outline")); m_cmbOutlineShape->addItem(i18n("Preview Outline")); m_cmbOutlineShape->addItem(i18n("Tilt Outline")); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); chkShowRootLayer->setChecked(cfg.showRootLayer()); int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); m_undoStackSize->setValue(cfg.undoStackLimit()); m_backupFileCheckBox->setChecked(cfg.backupFile()); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); m_hideSplashScreen->setChecked(cfg.hideSplashScreen()); m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); m_mdiColor->setColor(cfg.getMDIBackgroundColor()); m_backgroundimage->setText(cfg.getMDIBackgroundImage()); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); m_chkCompressKra->setChecked(cfg.compressKra()); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt()); connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); } void GeneralTab::setDefault() { KisConfig cfg; m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true)); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true)); chkShowRootLayer->setChecked(cfg.showRootLayer(true)); m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); m_undoStackSize->setValue(cfg.undoStackLimit(true)); m_backupFileCheckBox->setChecked(cfg.backupFile(true)); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_hideSplashScreen->setChecked(cfg.hideSplashScreen(true)); m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView); m_chkRubberBand->setChecked(cfg.useOpenGL(true)); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true)); m_mdiColor->setColor(cfg.getMDIBackgroundColor(true)); m_backgroundimage->setText(cfg.getMDIBackgroundImage(true)); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true)); m_chkCompressKra->setChecked(cfg.compressKra(true)); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true)); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true)); } CursorStyle GeneralTab::cursorStyle() { return (CursorStyle)m_cmbCursorShape->currentIndex(); } OutlineStyle GeneralTab::outlineStyle() { return (OutlineStyle)m_cmbOutlineShape->currentIndex(); } bool GeneralTab::showRootLayer() { return chkShowRootLayer->isChecked(); } int GeneralTab::autoSaveInterval() { //convert to seconds return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value()*60 : 0; } int GeneralTab::undoStackSize() { return m_undoStackSize->value(); } bool GeneralTab::showOutlineWhilePainting() { return m_showOutlinePainting->isChecked(); } bool GeneralTab::hideSplashScreen() { return m_hideSplashScreen->isChecked(); } int GeneralTab::mdiMode() { return m_cmbMDIType->currentIndex(); } int GeneralTab::favoritePresets() { return m_favoritePresetsSpinBox->value(); } bool GeneralTab::showCanvasMessages() { return m_chkCanvasMessages->isChecked(); } bool GeneralTab::compressKra() { return m_chkCompressKra->isChecked(); } bool GeneralTab::toolOptionsInDocker() { return m_radioToolOptionsInDocker->isChecked(); } bool GeneralTab::switchSelectionCtrlAlt() { return m_chkSwitchSelectionCtrlAlt->isChecked(); } void GeneralTab::getBackgroundImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages"); dialog.setCaption(i18n("Select a Background Image")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setImageFilters(); QString fn = dialog.filename(); // dialog box was canceled or somehow no file was selected if (fn.isEmpty()) { return; } QImage image(fn); if (image.isNull()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn)); } else { m_backgroundimage->setText(fn); } } void GeneralTab::clearBackgroundImage() { // clearing the background image text will implicitly make the background color be used m_backgroundimage->setText(""); } +ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name) + : QWidget(parent) +{ + setObjectName(name); + + QGridLayout * l = new QGridLayout(this); + l->setMargin(0); + m_page = new WdgShortcutSettings(this); + l->addWidget(m_page, 0, 0); + + KisPart::instance()->loadActions(); + KisActionRegistry::instance()->setupDialog(m_page); +} + +void ShortcutSettingsTab::setDefault() +{ + m_page->allDefault(); +} + +void ShortcutSettingsTab::saveChanges() +{ + KisActionRegistry::instance()->settingsPageSaved(); +} + +void ShortcutSettingsTab::revertChanges() +{ + m_page->allDefault(); +} + ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); // XXX: Make sure only profiles that fit the specified color model // are shown in the profile combos QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgColorSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile()); connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool))); // XXX: no color management integration on Windows or OSX yet #ifndef HAVE_X11 m_page->chkUseSystemMonitorProfile->setVisible(false); #endif m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace()); m_page->cmbPrintingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbPrintingColorSpace->setCurrent(cfg.printerColorSpace()); m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open")); m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") ); connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile())); refillPrintProfiles(KoID(cfg.printerColorSpace(), "")); //hide printing settings m_page->groupBox2->hide(); QGridLayout *monitorProfileGrid = new QGridLayout(m_page->monitorprofileholder); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1)); monitorProfileGrid->addWidget(lbl, i, 0); m_monitorProfileLabels << lbl; SqueezedComboBox *cmb = new SqueezedComboBox(); monitorProfileGrid->addWidget(cmb, i, 1); m_monitorProfileWidgets << cmb; } refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } if (m_page->cmbPrintProfile->contains(cfg.printerProfile())) m_page->cmbPrintProfile->setCurrentIndex(m_page->cmbPrintProfile->findText(cfg.printerProfile())); m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation()); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization()); m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB); m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR); m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour()); Q_ASSERT(button); if (button) { button->setChecked(true); } m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent()); toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile()); connect(m_page->cmbPrintingColorSpace, SIGNAL(activated(const KoID &)), this, SLOT(refillPrintProfiles(const KoID &))); } void ColorSettingsTab::installProfile() { QStringList mime; mime << "ICM Profile (*.icm)" << "ICC Profile (*.icc)"; KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); dialog.setNameFilters(mime); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { QUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { dbgKrita << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); refillPrintProfiles(KoID(cfg.printerColorSpace(), "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } if (m_page->cmbPrintProfile->contains(cfg.printerProfile())) m_page->cmbPrintProfile->setCurrentIndex(m_page->cmbPrintProfile->findText(cfg.printerProfile())); } void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile) { if (useSystemProfile) { KisConfig cfg; QStringList devices = KisColorManager::instance()->devices(); if (devices.size() == QApplication::desktop()->screenCount()) { for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); QString monitorForScreen = cfg.monitorForScreen(i, devices[i]); Q_FOREACH (const QString &device, devices) { m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device); if (devices[i] == monitorForScreen) { m_monitorProfileWidgets[i]->setCurrentIndex(i); } } } } } else { KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } } void ColorSettingsTab::setDefault() { m_page->cmbWorkingColorSpace->setCurrent("RGBA"); m_page->cmbPrintingColorSpace->setCurrent("CMYK"); refillPrintProfiles(KoID("CMYK", "")); refillMonitorProfiles(KoID("RGBA", "")); KisConfig cfg; m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true)); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true)); m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true)); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true)); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true)); Q_ASSERT(button); if (button) { button->setChecked(true); } } void ColorSettingsTab::refillMonitorProfiles(const KoID & s) { const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s.id()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); } if (!csf) return; QList profileList = KoColorSpaceRegistry::instance()->profilesFor(csf); Q_FOREACH (const KoColorProfile *profile, profileList) { // //dbgKrita << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile(); if (profile->isSuitableForDisplay()) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->addSqueezedItem(profile->name()); } } } for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->setCurrent(csf->defaultProfile()); } } void ColorSettingsTab::refillPrintProfiles(const KoID & s) { const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s.id()); m_page->cmbPrintProfile->clear(); if (!csf) return; QList profileList = KoColorSpaceRegistry::instance()->profilesFor(csf); Q_FOREACH (const KoColorProfile *profile, profileList) { if (profile->isSuitableForPrinting()) m_page->cmbPrintProfile->addSqueezedItem(profile->name()); } m_page->cmbPrintProfile->setCurrent(csf->defaultProfile()); } //--------------------------------------------------------------------------------------------------- void TabletSettingsTab::setDefault() { KisCubicCurve curve; curve.fromString(DEFAULT_CURVE_STRING); m_page->pressureCurve->setCurve(curve); } TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgTabletSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; KisCubicCurve curve; curve.fromString( cfg.pressureTabletCurve() ); m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); m_page->pressureCurve->setCurve(curve); } //--------------------------------------------------------------------------------------------------- #include "kis_image_config.h" #include "kis_acyclic_signal_connector.h" int getTotalRAM() { KisImageConfig cfg; return cfg.totalRAM(); } int PerformanceTab::realTilesRAM() { return intMemoryLimit->value() - intPoolLimit->value(); } PerformanceTab::PerformanceTab(QWidget *parent, const char *name) : WdgPerformanceSettings(parent, name) { KisImageConfig cfg; const int totalRAM = cfg.totalRAM(); lblTotalMemory->setText(i18n("%1 MiB", totalRAM)); sliderMemoryLimit->setSuffix(i18n(" %")); sliderMemoryLimit->setRange(1, 100, 2); sliderMemoryLimit->setSingleStep(0.01); sliderPoolLimit->setSuffix(i18n(" %")); sliderPoolLimit->setRange(0, 20, 2); sliderMemoryLimit->setSingleStep(0.01); sliderUndoLimit->setSuffix(i18n(" %")); sliderUndoLimit->setRange(0, 50, 2); sliderMemoryLimit->setSingleStep(0.01); intMemoryLimit->setMinimumWidth(80); intPoolLimit->setMinimumWidth(80); intUndoLimit->setMinimumWidth(80); SliderAndSpinBoxSync *sync1 = new SliderAndSpinBoxSync(sliderMemoryLimit, intMemoryLimit, getTotalRAM); sync1->slotParentValueChanged(); m_syncs << sync1; SliderAndSpinBoxSync *sync2 = new SliderAndSpinBoxSync(sliderPoolLimit, intPoolLimit, std::bind(&QSpinBox::value, intMemoryLimit)); connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged())); sync2->slotParentValueChanged(); m_syncs << sync2; SliderAndSpinBoxSync *sync3 = new SliderAndSpinBoxSync(sliderUndoLimit, intUndoLimit, std::bind(&PerformanceTab::realTilesRAM, this)); connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged())); sync3->slotParentValueChanged(); m_syncs << sync3; sliderSwapSize->setSuffix(i18n(" GiB")); sliderSwapSize->setRange(1, 64); intSwapSize->setRange(1, 64); KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this); swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)), intSwapSize, SLOT(setValue(int))); swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)), sliderSwapSize, SLOT(setValue(int))); lblSwapFileLocation->setText(cfg.swapDir()); connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir())); load(false); } PerformanceTab::~PerformanceTab() { qDeleteAll(m_syncs); } void PerformanceTab::load(bool requestDefault) { KisImageConfig cfg; sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault)); sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault)); sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault)); chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault)); chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault)); sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024); lblSwapFileLocation->setText(cfg.swapDir(requestDefault)); { KisConfig cfg2; chkOpenGLLogging->setChecked(cfg2.enableOpenGLDebugging(requestDefault)); chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(requestDefault)); } } void PerformanceTab::save() { KisImageConfig cfg; cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value()); cfg.setMemorySoftLimitPercent(sliderUndoLimit->value()); cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value()); cfg.setEnablePerfLog(chkPerformanceLogging->isChecked()); cfg.setEnableProgressReporting(chkProgressReporting->isChecked()); cfg.setMaxSwapSize(sliderSwapSize->value() * 1024); cfg.setSwapDir(lblSwapFileLocation->text()); { KisConfig cfg2; cfg2.setEnableOpenGLDebugging(chkOpenGLLogging->isChecked()); cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked()); } } void PerformanceTab::selectSwapDir() { KisImageConfig cfg; QString swapDir = cfg.swapDir(); swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir); lblSwapFileLocation->setText(swapDir); } //--------------------------------------------------------------------------------------------------- #include "KoColor.h" #include "KoColorPopupAction.h" DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name) : WdgDisplaySettings(parent, name) { KisConfig cfg; #ifdef HAVE_OPENGL if (!KisOpenGL::hasOpenGL()) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableDoubleBuffering->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL()); chkUseTextureBuffer->setEnabled(cfg.useOpenGL()); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer()); chkDisableDoubleBuffering->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableDoubleBuffering->setEnabled(cfg.useOpenGL()); chkDisableDoubleBuffering->setChecked(cfg.disableDoubleBuffering()); chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableVsync->setEnabled(cfg.useOpenGL()); chkDisableVsync->setChecked(cfg.disableVSync()); cmbFilterMode->setEnabled(cfg.useOpenGL()); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode()); } if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { grpOpenGL->setVisible(false); grpOpenGL->setMaximumHeight(0); } #else grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); #endif KoColor c; c.fromQColor(cfg.selectionOverlayMaskColor()); m_selectionOverlayColorAction = new KoColorPopupAction(this); m_selectionOverlayColorAction->setCurrentColor(c); m_selectionOverlayColorAction->setIcon(KisIconUtils::loadIcon("format-stroke-color")); m_selectionOverlayColorAction->setToolTip(i18n("Change the background color of the image")); btnSelectionOverlayColor->setDefaultAction(m_selectionOverlayColorAction); intCheckSize->setValue(cfg.checkSize()); chkMoving->setChecked(cfg.scrollCheckers()); colorChecks1->setColor(cfg.checkersColor1()); colorChecks2->setColor(cfg.checkersColor2()); canvasBorder->setColor(cfg.canvasBorderColor()); hideScrollbars->setChecked(cfg.hideScrollbars()); chkCurveAntialiasing->setChecked(cfg.antialiasCurves()); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline()); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor()); chkHidePopups->setChecked(cfg.hidePopups()); connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool))); } void DisplaySettingsTab::setDefault() { KisConfig cfg; #ifdef HAVE_OPENGL if (!KisOpenGL::hasOpenGL()) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableDoubleBuffering->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL(true)); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true)); chkUseTextureBuffer->setEnabled(true); chkDisableDoubleBuffering->setEnabled(true); chkDisableDoubleBuffering->setChecked(cfg.disableDoubleBuffering(true)); chkDisableVsync->setEnabled(true); chkDisableVsync->setChecked(cfg.disableVSync(true)); cmbFilterMode->setEnabled(true); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true)); } #else grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); #endif chkMoving->setChecked(cfg.scrollCheckers(true)); intCheckSize->setValue(cfg.checkSize(true)); colorChecks1->setColor(cfg.checkersColor1(true)); colorChecks2->setColor(cfg.checkersColor2(true)); canvasBorder->setColor(cfg.canvasBorderColor(true)); hideScrollbars->setChecked(cfg.hideScrollbars(true)); chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true)); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true)); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true)); chkHidePopups->setChecked(cfg.hidePopups(true)); } void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked) { #ifdef HAVE_OPENGL chkUseTextureBuffer->setEnabled(isChecked); chkDisableDoubleBuffering->setEnabled(isChecked); chkDisableVsync->setEnabled(isChecked); cmbFilterMode->setEnabled(isChecked); #else Q_UNUSED(isChecked); #endif } //--------------------------------------------------------------------------------------------------- FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent) { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen()); chkMenu->setChecked(cfg.hideMenuFullscreen()); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen()); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen()); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen()); chkToolbar->setChecked(cfg.hideToolbarFullscreen()); } void FullscreenSettingsTab::setDefault() { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen(true)); chkMenu->setChecked(cfg.hideMenuFullscreen(true)); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true)); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true)); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true)); chkToolbar->setChecked(cfg.hideToolbarFullscreen(true)); } //--------------------------------------------------------------------------------------------------- KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name) : KPageDialog(parent) { Q_UNUSED(name); setWindowTitle(i18n("Preferences")); // QT5TODO: help button needs custom wiring up to whatever help should be shown setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::RestoreDefaults); button(QDialogButtonBox::Ok)->setDefault(true); setFaceType(KPageDialog::List); // General KoVBox *vbox = new KoVBox(); KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General")); page->setObjectName("general"); page->setHeader(i18n("General")); page->setIcon(KisIconUtils::loadIcon("configure")); addPage(page); m_general = new GeneralTab(vbox); + // Shortcuts + vbox = new KoVBox(); + page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts")); + page->setObjectName("shortcuts"); + page->setHeader(i18n("Shortcuts")); + page->setIcon(KisIconUtils::loadIcon("configure-shortcuts")); + addPage(page); + m_shortcutSettings = new ShortcutSettingsTab(vbox); + connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges())); + connect(this, SIGNAL(rejected()), m_shortcutSettings, SLOT(revertChanges())); + + // Canvas input settings + m_inputConfiguration = new KisInputConfigurationPage(); + page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); + page->setHeader(i18n("Canvas Input")); + page->setObjectName("canvasinput"); + page->setIcon(KisIconUtils::loadIcon("applications-system")); + // Display vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Display")); page->setObjectName("display"); page->setHeader(i18n("Display")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display")); addPage(page); m_displaySettings = new DisplaySettingsTab(vbox); // Color vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Color Management")); page->setObjectName("colormanagement"); page->setHeader(i18n("Color")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color")); addPage(page); m_colorSettings = new ColorSettingsTab(vbox); // Performance vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Performance")); page->setObjectName("performance"); page->setHeader(i18n("Performance")); page->setIcon(KisIconUtils::loadIcon("applications-system")); addPage(page); m_performanceSettings = new PerformanceTab(vbox); // Tablet vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Tablet settings")); page->setObjectName("tablet"); page->setHeader(i18n("Tablet")); page->setIcon(KisIconUtils::loadIcon("document-edit")); addPage(page); m_tabletSettings = new TabletSettingsTab(vbox); // full-screen mode vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Canvas-only settings")); page->setObjectName("canvasonly"); page->setHeader(i18n("Canvas-only")); page->setIcon(KisIconUtils::loadIcon("folder-pictures")); addPage(page); m_fullscreenSettings = new FullscreenSettingsTab(vbox); // Author profiles m_authorPage = new KoConfigAuthorPage(); page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" )); page->setObjectName("author"); page->setHeader(i18n("Author")); page->setIcon(KisIconUtils::loadIcon("im-user")); - // input settings - m_inputConfiguration = new KisInputConfigurationPage(); - page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); - page->setHeader(i18n("Canvas Input")); - page->setObjectName("canvasinput"); - page->setIcon(KisIconUtils::loadIcon("applications-system")); - QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults); connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges())); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), m_inputConfiguration, SLOT(setDefaults())); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); Q_FOREACH (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) { KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet(); vbox = new KoVBox(); page = new KPageWidgetItem(vbox, preferenceSet->name()); page->setHeader(preferenceSet->header()); page->setIcon(preferenceSet->icon()); addPage(page); preferenceSet->setParent(vbox); preferenceSet->loadPreferences(); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection); connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection); } connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault())); } KisDlgPreferences::~KisDlgPreferences() { } void KisDlgPreferences::slotDefault() { if (currentPage()->objectName() == "default") { m_general->setDefault(); } + else if (currentPage()->objectName() == "shortcuts") { + m_shortcutSettings->setDefault(); + } else if (currentPage()->objectName() == "display") { m_displaySettings->setDefault(); } else if (currentPage()->objectName() == "colormanagement") { m_colorSettings->setDefault(); } else if (currentPage()->objectName() == "performance") { m_performanceSettings->load(true); } else if (currentPage()->objectName() == "tablet") { m_tabletSettings->setDefault(); } else if (currentPage()->objectName() == "canvasonly") { m_fullscreenSettings->setDefault(); } else if (currentPage()->objectName() == "canvasinput") { m_inputConfiguration->setDefaults(); } } bool KisDlgPreferences::editPreferences() { KisDlgPreferences* dialog; dialog = new KisDlgPreferences(); bool baccept = (dialog->exec() == Accepted); if (baccept) { // General settings KisConfig cfg; cfg.setNewCursorStyle(dialog->m_general->cursorStyle()); cfg.setNewOutlineStyle(dialog->m_general->outlineStyle()); cfg.setShowRootLayer(dialog->m_general->showRootLayer()); cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting()); cfg.setHideSplashScreen(dialog->m_general->hideSplashScreen()); cfg.writeEntry("mdi_viewmode", dialog->m_general->mdiMode()); cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color()); cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text()); cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval()); cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages()); cfg.setCompressKra(dialog->m_general->compressKra()); cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker()); cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt()); KisPart *part = KisPart::instance(); if (part) { Q_FOREACH (QPointer doc, part->documents()) { if (doc) { doc->setAutoSave(dialog->m_general->autoSaveInterval()); doc->setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); doc->undoStack()->setUndoLimit(dialog->m_general->undoStackSize()); } } } cfg.setUndoStackLimit(dialog->m_general->undoStackSize()); cfg.setFavoritePresets(dialog->m_general->favoritePresets()); // Color settings cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) { int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex(); QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString(); cfg.setMonitorForScreen(i, monitorid); } else { cfg.setMonitorProfile(i, dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(), dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); } } cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id()); cfg.setPrinterColorSpace(dialog->m_colorSettings->m_page->cmbPrintingColorSpace->currentItem().id()); cfg.setPrinterProfile(dialog->m_colorSettings->m_page->cmbPrintProfile->itemHighlighted()); cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked()); cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked()); cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId()); cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex()); // Tablet settings cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() ); dialog->m_performanceSettings->save(); #ifdef HAVE_OPENGL if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked()) cfg.setCanvasState("TRY_OPENGL"); cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked()); cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked()); cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex()); cfg.setDisableDoubleBuffering(dialog->m_displaySettings->chkDisableDoubleBuffering->isChecked()); cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked()); #endif cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value()); cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked()); cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color()); cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color()); cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color()); cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked()); cfg.setSelectionOverlayMaskColor(dialog->m_displaySettings->m_selectionOverlayColorAction->currentKoColor().toQColor()); cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked()); cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked()); cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked()); cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked()); cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState()); cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState()); cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState()); cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState()); cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState()); cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState()); dialog->m_authorPage->apply(); } delete dialog; return baccept; } diff --git a/libs/ui/dialogs/kis_dlg_preferences.h b/libs/ui/dialogs/kis_dlg_preferences.h index 5285ec39c7..60f941741a 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.h +++ b/libs/ui/dialogs/kis_dlg_preferences.h @@ -1,285 +1,327 @@ /* * preferencesdlg.h - part of KImageShop^WKrita * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 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 _KIS_DLG_PREFERENCES_H_ #define _KIS_DLG_PREFERENCES_H_ #include #include #include "kis_global.h" #include "ui_wdggeneralsettings.h" #include "ui_wdgdisplaysettings.h" #include "ui_wdgcolorsettings.h" #include "ui_wdgtabletsettings.h" #include "ui_wdgperformancesettings.h" #include "ui_wdgfullscreensettings.h" +#include "KisShortcutsDialog.h" class KoID; class KisInputConfigurationPage; class KoConfigAuthorPage; /** * "General"-tab for preferences dialog */ class WdgGeneralSettings : public QWidget, public Ui::WdgGeneralSettings { Q_OBJECT public: WdgGeneralSettings(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); setupUi(this); chkShowRootLayer->setVisible(false); } }; class GeneralTab : public WdgGeneralSettings { Q_OBJECT public: GeneralTab(QWidget *parent = 0, const char *name = 0); CursorStyle cursorStyle(); OutlineStyle outlineStyle(); bool showRootLayer(); int autoSaveInterval(); void setDefault(); int undoStackSize(); bool showOutlineWhilePainting(); bool hideSplashScreen(); int mdiMode(); int favoritePresets(); bool showCanvasMessages(); bool compressKra(); bool toolOptionsInDocker(); bool switchSelectionCtrlAlt(); private Q_SLOTS: void getBackgroundImage(); void clearBackgroundImage(); }; -//======================= + + +/** + * "Shortcuts" tab for preferences dialog + */ + +class WdgShortcutSettings : public KisShortcutsDialog +{ + Q_OBJECT + +public: + WdgShortcutSettings(QWidget *parent) + : KisShortcutsDialog(KisShortcutsEditor::AllActions, + KisShortcutsEditor::LetterShortcutsAllowed, + parent) + { } +}; + +class ShortcutSettingsTab : public QWidget +{ + Q_OBJECT + +public: + + ShortcutSettingsTab(QWidget *parent = 0, const char *name = 0); + +public: + void setDefault(); + WdgShortcutSettings *m_page; + + +public Q_SLOTS: + void saveChanges(); + void revertChanges(); +}; + + + +/** + * "Color" tab for preferences dialog + */ class WdgColorSettings : public QWidget, public Ui::WdgColorSettings { Q_OBJECT public: WdgColorSettings(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class ColorSettingsTab : public QWidget { Q_OBJECT public: ColorSettingsTab(QWidget *parent = 0, const char *name = 0); private Q_SLOTS: void refillMonitorProfiles(const KoID & s); void refillPrintProfiles(const KoID & s); void installProfile(); void toggleAllowMonitorProfileSelection(bool useSystemProfile); public: void setDefault(); WdgColorSettings *m_page; QButtonGroup m_pasteBehaviourGroup; QList m_monitorProfileLabels; QList m_monitorProfileWidgets; }; //======================= class WdgTabletSettings : public QWidget, public Ui::WdgTabletSettings { Q_OBJECT public: WdgTabletSettings(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class TabletSettingsTab : public QWidget { Q_OBJECT public: TabletSettingsTab(QWidget *parent = 0, const char *name = 0); public: void setDefault(); WdgTabletSettings *m_page; }; //======================= /** * "Performance"-tab for preferences dialog */ class SliderAndSpinBoxSync; class WdgPerformanceSettings : public QWidget, public Ui::WdgPerformanceSettings { Q_OBJECT public: WdgPerformanceSettings(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); setupUi(this); } }; class PerformanceTab : public WdgPerformanceSettings { Q_OBJECT public: PerformanceTab(QWidget *parent = 0, const char *name = 0); ~PerformanceTab(); void load(bool requestDefault); void save(); private Q_SLOTS: void selectSwapDir(); private: int realTilesRAM(); private: QVector m_syncs; }; //======================= class WdgDisplaySettings : public QWidget, public Ui::WdgDisplaySettings { Q_OBJECT public: WdgDisplaySettings(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); setupUi(this); } }; /** * Display settings tab for preferences dialog */ class KoColorPopupAction; class DisplaySettingsTab : public WdgDisplaySettings { Q_OBJECT public: DisplaySettingsTab(QWidget *parent = 0, const char *name = 0); public: void setDefault(); protected Q_SLOTS: void slotUseOpenGLToggled(bool isChecked); public: KoColorPopupAction *m_selectionOverlayColorAction; }; //======================= /** * Full screen settings tab for preferences dialog */ class WdgFullscreenSettingsBase : public QWidget, public Ui::WdgFullscreenSettings { Q_OBJECT public: WdgFullscreenSettingsBase(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class FullscreenSettingsTab : public WdgFullscreenSettingsBase { Q_OBJECT public: FullscreenSettingsTab(QWidget *parent); public: void setDefault(); }; //======================= /** * Preferences dialog of KImageShop^WKrayon^WKrita */ class KisDlgPreferences : public KPageDialog { Q_OBJECT public: static bool editPreferences(); protected: KisDlgPreferences(QWidget *parent = 0, const char *name = 0); ~KisDlgPreferences(); protected: GeneralTab *m_general; + ShortcutSettingsTab *m_shortcutSettings; ColorSettingsTab *m_colorSettings; PerformanceTab *m_performanceSettings; DisplaySettingsTab *m_displaySettings; TabletSettingsTab *m_tabletSettings; FullscreenSettingsTab *m_fullscreenSettings; KisInputConfigurationPage *m_inputConfiguration; KoConfigAuthorPage *m_authorPage; protected Q_SLOTS: void slotDefault(); }; #endif diff --git a/libs/widgetutils/kis_action_registry.cpp b/libs/widgetutils/kis_action_registry.cpp index df765fc1fe..4d00eb0dec 100644 --- a/libs/widgetutils/kis_action_registry.cpp +++ b/libs/widgetutils/kis_action_registry.cpp @@ -1,477 +1,471 @@ /* * Copyright (c) 2015 Michael Abrahams * * 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 3 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 #include #include #include #include #include #include #include #include "kis_debug.h" #include "KoResourcePaths.h" #include "kis_icon_utils.h" #include "kactioncollection.h" #include "kactioncategory.h" #include "kis_action_registry.h" #include "kshortcutschemeshelper_p.h" namespace { /** * We associate several pieces of information with each shortcut. The first * piece of information is a QDomElement, containing the raw data from the * .action XML file. The second and third are QKeySequences, the first of * which is the default shortcut, the last of which is any custom shortcut. * The last two are the KActionCollection and KActionCategory used to * organize the shortcut editor. */ struct ActionInfoItem { QDomElement xmlData; QKeySequence defaultShortcut; QKeySequence customShortcut; QString collectionName; QString categoryName; }; // Convenience macros to extract text of a child node. QString getChildContent(QDomElement xml, QString node) { return xml.firstChildElement(node).text(); }; ActionInfoItem emptyActionInfo; // Used as default return value QString quietlyTranslate(const QString &s) { if (s.isEmpty()) { return s; } if (i18n(s.toUtf8().constData()).isEmpty()) { dbgAction << "No translation found for" << s; return s; } return i18n(s.toUtf8().constData()); }; QKeySequence preferredShortcut(ActionInfoItem action) { if (action.customShortcut.isEmpty()) { return action.defaultShortcut; } else { return action.customShortcut; } }; }; class Q_DECL_HIDDEN KisActionRegistry::Private { public: Private(KisActionRegistry *_q) : q(_q) {}; // This is the main place containing ActionInfoItems. QMap actionInfoList; void loadActionFiles(); void loadActionCollections(); void loadCustomShortcuts(QString filename = QStringLiteral("kritashortcutsrc")); ActionInfoItem &actionInfo(const QString &name) { if (!actionInfoList.contains(name)) { dbgAction << "Tried to look up info for unknown action" << name; } return actionInfoList[name]; }; KisActionRegistry *q; KActionCollection * defaultActionCollection; QMap actionCollections; }; Q_GLOBAL_STATIC(KisActionRegistry, s_instance); KisActionRegistry *KisActionRegistry::instance() { return s_instance; }; KisActionRegistry::KisActionRegistry() : d(new KisActionRegistry::Private(this)) { d->loadActionFiles(); KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes"); QString schemeName = cg.readEntry("Current Scheme", "Default"); loadShortcutScheme(schemeName); loadCustomShortcuts(); } QKeySequence KisActionRegistry::getCustomShortcut(const QString &name) { return d->actionInfo(name).customShortcut; }; QKeySequence KisActionRegistry::getPreferredShortcut(const QString &name) { return preferredShortcut(d->actionInfo(name)); }; QKeySequence KisActionRegistry::getCategory(const QString &name) { return d->actionInfo(name).categoryName; }; QStringList KisActionRegistry::allActions() { return d->actionInfoList.keys(); }; KActionCollection * KisActionRegistry::getDefaultCollection() { return d->actionCollections.value("Krita"); }; void KisActionRegistry::addAction(const QString &name, QAction *a) { auto info = d->actionInfo(name); KActionCollection *collection = d->actionCollections.value(info.collectionName); if (!collection) { dbgAction << "No collection found for action" << name; return; } if (collection->action(name)) { dbgAction << "duplicate action" << name << "in collection" << collection->componentName(); } else { } collection->addCategorizedAction(name, a, info.categoryName); }; void KisActionRegistry::notifySettingsUpdated() { d->loadCustomShortcuts(); }; void KisActionRegistry::loadCustomShortcuts(const QString &path) { if (path.isEmpty()) { d->loadCustomShortcuts(); } else { d->loadCustomShortcuts(path); } }; void KisActionRegistry::loadShortcutScheme(const QString &schemeName) { // Load scheme file if (schemeName != QStringLiteral("Default")) { QString schemeFileName = KShortcutSchemesHelper::schemeFileLocations().value(schemeName); if (schemeFileName.isEmpty()) { // qDebug() << "No configuration file found for scheme" << schemeName; return; } KConfig schemeConfig(schemeFileName, KConfig::SimpleConfig); applyShortcutScheme(&schemeConfig); } else { // Apply default scheme, updating KisActionRegistry data applyShortcutScheme(); } } QAction * KisActionRegistry::makeQAction(const QString &name, QObject *parent) { QAction * a = new QAction(parent); if (!d->actionInfoList.contains(name)) { dbgAction << "Warning: requested data for unknown action" << name; return a; } propertizeAction(name, a); return a; }; -void KisActionRegistry::configureShortcuts() +void KisActionRegistry::setupDialog(KisShortcutsDialog *dlg) { - KisShortcutsDialog dlg; - for (auto i = d->actionCollections.constBegin(); i != d->actionCollections.constEnd(); i++ ) { - dlg.addCollection(i.value(), i.key()); + dlg->addCollection(i.value(), i.key()); } +} - /* Testing */ - // QStringList mainWindowActions; - // foreach (auto a, ac->actions()) { - // mainWindowActions << a->objectName(); - // } - // dlg.addCollection(ac, "TESTING: XMLGUI-MAINWINDOW"); - - dlg.configure(); // Show the dialog. - +void KisActionRegistry::settingsPageSaved() +{ + // For now, custom shortcuts are dealt with by writing to file and reloading. loadCustomShortcuts(); + // Announce UI should reload current shortcuts. emit shortcutsUpdated(); } void KisActionRegistry::applyShortcutScheme(const KConfigBase *config) { // First, update the things in KisActionRegistry if (config == 0) { // Use default shortcut scheme. Simplest just to reload everything. d->actionInfoList.clear(); d->loadActionFiles(); loadCustomShortcuts(); } else { const auto schemeEntries = config->group(QStringLiteral("Shortcuts")).entryMap(); // Load info item for each shortcut, reset custom shortcuts auto it = schemeEntries.constBegin(); while (it != schemeEntries.end()) { ActionInfoItem &info = d->actionInfo(it.key()); info.defaultShortcut = it.value(); it++; } } } void KisActionRegistry::updateShortcut(const QString &name, QAction *action) { const ActionInfoItem info = d->actionInfo(name); action->setShortcut(preferredShortcut(info)); auto propertizedShortcut = qVariantFromValue(QList() << info.defaultShortcut); action->setProperty("defaultShortcuts", propertizedShortcut); } bool KisActionRegistry::propertizeAction(const QString &name, QAction * a) { const ActionInfoItem info = d->actionInfo(name); QDomElement actionXml = info.xmlData; if (actionXml.text().isEmpty()) { dbgAction << "No XML data found for action" << name; return false; } // i18n requires converting format from QString. auto getChildContent_i18n = [=](QString node){return quietlyTranslate(getChildContent(actionXml, node));}; // Note: the fields in the .action documents marked for translation are determined by extractrc. QString icon = getChildContent(actionXml, "icon"); QString text = getChildContent(actionXml, "text"); QString whatsthis = getChildContent_i18n("whatsThis"); QString toolTip = getChildContent_i18n("toolTip"); QString statusTip = getChildContent_i18n("statusTip"); QString iconText = getChildContent_i18n("iconText"); bool isCheckable = getChildContent(actionXml, "isCheckable") == QString("true"); a->setObjectName(name); // This is helpful, should be added more places in Krita a->setIcon(KisIconUtils::loadIcon(icon.toLatin1())); a->setText(text); a->setObjectName(name); a->setWhatsThis(whatsthis); a->setToolTip(toolTip); a->setStatusTip(statusTip); a->setIconText(iconText); a->setCheckable(isCheckable); updateShortcut(name, a); // TODO: check for colliding shortcuts, either here, or in loading code #if 0 QMap existingShortcuts; Q_FOREACH (QAction* action, actionCollection->actions()) { if(action->shortcut() == QKeySequence(0)) { continue; } if (existingShortcuts.contains(action->shortcut())) { dbgAction << QString("Actions %1 and %2 have the same shortcut: %3") \ .arg(action->text()) \ .arg(existingShortcuts[action->shortcut()]->text()) \ .arg(action->shortcut()); } else { existingShortcuts[action->shortcut()] = action; } } #endif return true; } QString KisActionRegistry::getActionProperty(const QString &name, const QString &property) { ActionInfoItem info = d->actionInfo(name); QDomElement actionXml = info.xmlData; if (actionXml.text().isEmpty()) { dbgAction << "No XML data found for action" << name; return QString(); } return getChildContent(actionXml, property); } void KisActionRegistry::writeCustomShortcuts(KConfigBase *config) const { KConfigGroup cg; if (config == 0) { cg = KConfigGroup(KSharedConfig::openConfig("kritashortcutsrc"), QStringLiteral("Shortcuts")); } else { cg = KConfigGroup(config, QStringLiteral("Shortcuts")); } for (auto it = d->actionInfoList.constBegin(); it != d->actionInfoList.constEnd(); ++it) { QString actionName = it.key(); QString s = it.value().customShortcut.toString(); if (s.isEmpty()) { cg.deleteEntry(actionName, KConfigGroup::Persistent); } else { cg.writeEntry(actionName, s, KConfigGroup::Persistent); } } cg.sync(); } void KisActionRegistry::Private::loadActionFiles() { auto searchType = KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates; QStringList actionDefinitions = KoResourcePaths::findAllResources("kis_actions", "*.action", searchType); // Extract actions all XML .action files. Q_FOREACH (const QString &actionDefinition, actionDefinitions) { QDomDocument doc; QFile f(actionDefinition); f.open(QFile::ReadOnly); doc.setContent(f.readAll()); QDomElement base = doc.documentElement(); // "ActionCollection" outer group QString collectionName = base.attribute("name"); QString version = base.attribute("version"); if (version != "2") { errAction << ".action XML file" << actionDefinition << "has incorrect version; skipping."; continue; } KActionCollection *actionCollection; if (!actionCollections.contains(collectionName)) { actionCollection = new KActionCollection(q, collectionName); actionCollections.insert(collectionName, actionCollection); dbgAction << "Adding a new action collection " << collectionName; } else { actionCollection = actionCollections.value(collectionName); } // Loop over nodes. Each of these corresponds to a // KActionCategory, producing a group of actions in the shortcut dialog. QDomElement actions = base.firstChild().toElement(); while (!actions.isNull()) { // field QDomElement categoryTextNode = actions.firstChild().toElement(); QString categoryName = quietlyTranslate(categoryTextNode.text()); // KActionCategory *category = actionCollection->getCategory(categoryName); // dbgAction << "Using category" << categoryName; // tags QDomElement actionXml = categoryTextNode.nextSiblingElement(); // Loop over individual actions while (!actionXml.isNull()) { if (actionXml.tagName() == "Action") { // Read name from format QString name = actionXml.attribute("name"); // Bad things if (name.isEmpty()) { errAction << "Unnamed action in definitions file " << actionDefinition; } else if (actionInfoList.contains(name)) { // errAction << "NOT COOL: Duplicated action name from xml data: " << name; } else { ActionInfoItem info; info.xmlData = actionXml; info.defaultShortcut = getChildContent(actionXml, "shortcut"); info.customShortcut = QKeySequence(); info.categoryName = categoryName; info.collectionName = collectionName; // dbgAction << "default shortcut for" << name << " - " << info.defaultShortcut; actionInfoList.insert(name,info); } } actionXml = actionXml.nextSiblingElement(); } actions = actions.nextSiblingElement(); } } }; void KisActionRegistry::Private::loadCustomShortcuts(QString filename) { const KConfigGroup localShortcuts(KSharedConfig::openConfig(filename), QStringLiteral("Shortcuts")); if (!localShortcuts.exists()) { return; } for (auto i = actionInfoList.begin(); i != actionInfoList.end(); ++i) { if (localShortcuts.hasKey(i.key())) { QString entry = localShortcuts.readEntry(i.key(), QString()); i.value().customShortcut = QKeySequence(entry); } else { i.value().customShortcut = QKeySequence(); } } }; diff --git a/libs/widgetutils/kis_action_registry.h b/libs/widgetutils/kis_action_registry.h index 9d79d7bbed..5bad45f76e 100644 --- a/libs/widgetutils/kis_action_registry.h +++ b/libs/widgetutils/kis_action_registry.h @@ -1,159 +1,166 @@ /* * Copyright (c) 2015 Michael Abrahams * * 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 3 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 #include #include #include "kritawidgetutils_export.h" class KActionCollection; class QDomElement; class KConfigBase; +class KisShortcutsDialog; /** * KisActionRegistry is intended to manage the global action configuration data * for Krita. The data come from four sources: * - .action files, containing static action configuration data in XML format, * - .rc configuration files, originally from XMLGUI and now in WidgetUtils, * - kritashortcutsrc, containing temporary shortcut configuration, and * - .shortcuts scheme files providing sets of default shortcuts, also from XMLGUI * * This class can be used as a factory by calling makeQAction. It can be used to * add standard properties such as default shortcuts and default tooltip to an * existing action with propertizeAction. If you have a custom action class * which needs to add other properties, you can use propertizeAction to add any * sort of data you wish to the .action configuration file. * * This class is also in charge of displaying the shortcut configuration dialog. * The interplay between this class, KActionCollection, KisShortcutsEditor and * so on can be complex, and is sometimes synchronized by file I/O by reading * and writing the configuration files mentioned above. * * It is a global static. Grab an ::instance(). */ class KRITAWIDGETUTILS_EXPORT KisActionRegistry : public QObject { Q_OBJECT public: static KisActionRegistry *instance(); /** * Get shortcut for an action */ QKeySequence getPreferredShortcut(const QString &name); /** * Get shortcut for an action */ QKeySequence getDefaultShortcut(const QString &name); /** * Get custom shortcut for an action */ QKeySequence getCustomShortcut(const QString &name); /** * Get category name */ QKeySequence getCategory(const QString &name); /** * @return value @p property for an action @p name. * * Allow flexible info structure for KisActions, etc. */ QString getActionProperty(const QString &name, const QString &property); /** * Saves action in a category. Note that this grabs ownership of the action. */ void addAction(const QString &name, QAction *a); /** * Produces a new QAction based on the .action data files. * * N.B. this action will not be saved in the registry. */ QAction * makeQAction(const QString &name, QObject *parent); /** * Fills the standard QAction properties of an action. * * @return true if the action was loaded successfully. */ bool propertizeAction(const QString &name, QAction *a); /** * @return list of actions with data available. */ QStringList allActions(); /** - * Display the shortcut configuration dialog. + * Setup the shortcut configuration widget. */ - void configureShortcuts(); + void setupDialog(KisShortcutsDialog *dlg); + + + /** + * Called when "OK" button is pressed in settings dialog. + */ + void settingsPageSaved(); /** * Reload custom shortcuts from kritashortcutsrc */ void loadCustomShortcuts(const QString &path = QString()); /** * Write custom shortcuts to a specific file */ void writeCustomShortcuts(KConfigBase *config) const; /** * Call after settings are changed. */ void notifySettingsUpdated(); /** * Constructor. Please don't touch! */ KisActionRegistry(); // Undocumented void updateShortcut(const QString &name, QAction *ac); KActionCollection * getDefaultCollection(); void loadShortcutScheme(const QString &schemeName); // If config == 0, reload defaults void applyShortcutScheme(const KConfigBase *config = 0); Q_SIGNALS: void shortcutsUpdated(); private: class Private; Private * const d; }; diff --git a/libs/widgetutils/xmlgui/KisShortcutsDialog.cpp b/libs/widgetutils/xmlgui/KisShortcutsDialog.cpp index f9859fe909..88e4e771fc 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsDialog.cpp +++ b/libs/widgetutils/xmlgui/KisShortcutsDialog.cpp @@ -1,182 +1,147 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe Copyright (C) 1997 Nicolas Hadacek Copyright (C) 1998 Mark Donohoe Copyright (C) 1998 Matthias Ettrich Copyright (C) 1999 Espen Sand Copyright (C) 2001 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Roberto Raggi Copyright (C) 2007 Andreas Hartmetz Copyright (C) 2008 Michael Jansen Copyright (C) 2008 Alexander Dymo Copyright (C) 2009 Chani Armitage 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 "KisShortcutsDialog.h" #include "KisShortcutsDialog_p.h" #include "kshortcutschemeshelper_p.h" #include "kshortcutschemeseditor.h" #include #include #include #include #include #include #include #include #include "kxmlguiclient.h" #include "kxmlguifactory.h" #include "kactioncollection.h" KisShortcutsDialog::KisShortcutsDialog(KisShortcutsEditor::ActionTypes types, KisShortcutsEditor::LetterShortcuts allowLetterShortcuts, QWidget *parent) - : QDialog(parent) + : QWidget(parent) , d(new KisShortcutsDialogPrivate(this)) { d->m_shortcutsEditor = new KisShortcutsEditor(this, types, allowLetterShortcuts); /* Construct & Connect UI */ - setWindowTitle(i18n("Configure Shortcuts")); - setModal(true); - QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(d->m_shortcutsEditor); QHBoxLayout *bottomLayout = new QHBoxLayout; d->m_schemeEditor = new KShortcutSchemesEditor(this); connect(d->m_schemeEditor, SIGNAL(shortcutsSchemeChanged(QString)), this, SLOT(changeShortcutScheme(QString))); bottomLayout->addLayout(d->m_schemeEditor); QPushButton *printButton = new QPushButton; KGuiItem::assign(printButton, KStandardGuiItem::print()); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->addButton(printButton, QDialogButtonBox::ActionRole); - buttonBox->setStandardButtons(QDialogButtonBox::Ok | - QDialogButtonBox::Cancel); - KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), - KStandardGuiItem::ok()); - KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), - KStandardGuiItem::cancel()); bottomLayout->addWidget(buttonBox); mainLayout->addLayout(bottomLayout); connect(printButton, SIGNAL(clicked()), d->m_shortcutsEditor, SLOT(printShortcuts())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(undo())); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); KConfigGroup group(KSharedConfig::openConfig(), "KisShortcutsDialog Settings"); resize(group.readEntry("Dialog Size", sizeHint())); } KisShortcutsDialog::~KisShortcutsDialog() { KConfigGroup group(KSharedConfig::openConfig(), "KisShortcutsDialog Settings"); group.writeEntry("Dialog Size", size()); delete d; } void KisShortcutsDialog::addCollection(KActionCollection *collection, const QString &title) { d->m_shortcutsEditor->addCollection(collection, title); d->m_collections.insert(title, collection); } -QList KisShortcutsDialog::actionCollections() const -{ - return d->m_collections.values(); -} -bool KisShortcutsDialog::configure(bool saveSettings) +void KisShortcutsDialog::save() { - d->m_saveSettings = saveSettings; - if (isModal()) { - int retcode = exec(); - return retcode; - } else { - show(); - return false; - } + d->save(); } -void KisShortcutsDialog::accept() +QList KisShortcutsDialog::actionCollections() const { - if (d->m_saveSettings) { - d->save(); - } - QDialog::accept(); + return d->m_collections.values(); } QSize KisShortcutsDialog::sizeHint() const { return QSize(600, 480); } -int KisShortcutsDialog::configure(KActionCollection *collection, - KisShortcutsEditor::LetterShortcuts allowLetterShortcuts, - QWidget *parent, bool saveSettings) -{ - KisShortcutsDialog dlg(KisShortcutsEditor::AllActions, allowLetterShortcuts, parent); - dlg.d->m_shortcutsEditor->addCollection(collection); - return dlg.configure(saveSettings); -} - void KisShortcutsDialog::allDefault() { d->m_shortcutsEditor->allDefault(); } void KisShortcutsDialog::importConfiguration(const QString &path) { KConfig config(path); d->m_shortcutsEditor->importConfiguration(static_cast(&config), true); } void KisShortcutsDialog::exportConfiguration(const QString &path) const { KConfig config(path); d->m_shortcutsEditor->exportConfiguration(static_cast(&config)); } void KisShortcutsDialog::saveCustomShortcuts(const QString &path) const { KConfig config(path); KConfigGroup cg(&config, QStringLiteral("Shortcuts")); d->m_shortcutsEditor->saveShortcuts(&cg); } void KisShortcutsDialog::loadCustomShortcuts(const QString &path) { KConfig config(path); d->m_shortcutsEditor->importConfiguration(static_cast(&config), false); } #include "moc_KisShortcutsDialog.cpp" diff --git a/libs/widgetutils/xmlgui/KisShortcutsDialog.h b/libs/widgetutils/xmlgui/KisShortcutsDialog.h index 1fa0728741..539cfa25bd 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsDialog.h +++ b/libs/widgetutils/xmlgui/KisShortcutsDialog.h @@ -1,209 +1,180 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Nicolas Hadacek Copyright (C) 2001 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Roberto Raggi Copyright (C) 2007 Andreas Hartmetz Copyright (C) 2008 Michael Jansen Copyright (C) 2015 Michael Abrahams 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 KISSHORTCUTSDIALOG_H #define KISSHORTCUTSDIALOG_H #include #include // Altering this class and some classes it uses was one major impetus behind // forking XmlGui. The first major workaround was to allow // KisPart::configureShortcuts() to pull up the dialog, and to remote the scheme // editor support, since it's incompatible with Krita. // // The files were forked from KF5 XmlGui version 5.12.0 // dialogs/KisShortcutsEditorItem.cpp <- kshortcutseditoritem.cpp // dialogs/KisShortcutEditWidget.cpp <- kshortcuteditwidget.cpp // dialogs/KisShortcutsEditorDelegate.cpp <- kshortcutseditordelegate.cpp // dialogs/KisShortcutsDialog.cpp <- kshortcutsdialog.cpp, , kshortcutsdialog_p.cpp // dialogs/KisShortcutsDialog.h <- kshortcutsdialog.h // dialogs/KisShortcutsDialog_p.h <- kshortcutsdialog_p.h, kshortcutseditor_p.h // forms/KisShortcutsDialog.ui <- kshortcutsdialog.ui // // // Changes that have been done to the files: // * Adapt of includes // * Removing unwanted parts related to schemes // * Renamed KShortcutsDialog/Editor to KisShortcutsDialog/Editor // * Add export macro // * Split apart kshortcutseditor_p // * Copied KShortcutsEditorPrivate::itemFromIndex() implmentation from // KF5 XmlGui's kshortcutseditor.cpp to begin of KisShortcutsEditorItem.cpp /** * @short Dialog for configuration of KActionCollection and KGlobalAccel. * * The KisShortcutsDialog class is used for configuring dictionaries of * key/action associations for KActionCollection and KGlobalAccel. It uses the * KShortcutsEditor widget and offers buttons to set all keys to defaults and * invoke on-line help. * * Several static methods are supplied which provide the most convenient * interface to the dialog. The most common and most encouraged use is with * KActionCollection. * * \code * KisShortcutsDialog::configure( actionCollection() ); * \endcode * * @since 4.3 By default this dialog is modal. If you don't want that, * setModal(false) and then the non-static configure() will show the dialog. If * you want to do anything extra when the dialog is done, connect to okClicked() * and/or cancelClicked(). However, if your extra stuff depends on the changed * settings already being saved, connect to saved() instead to be safe; if you * connect to okClicked() your function might be called before the save happens. * * example: * \code * KisShortcutsDialog dlg; * dlg.addCollection(myActions); * dlg.setModal(false); * connect(&dlg, SIGNAL(saved()), this, SLOT(doExtraStuff())); * dlg.configure(); * \endcode * * \image html kshortcutsdialog.png "KDE Shortcuts Dialog" * * @author Nicolas Hadacek * @author Hamish Rodda (KDE 4 porting) * @author Michael Jansen */ const auto defaultActionTypes = KisShortcutsEditor::WidgetAction \ | KisShortcutsEditor::WindowAction \ | KisShortcutsEditor::ApplicationAction; -class KRITAWIDGETUTILS_EXPORT KisShortcutsDialog : public QDialog +class KRITAWIDGETUTILS_EXPORT KisShortcutsDialog : public QWidget { Q_OBJECT public: /** * Constructs a KisShortcutsDialog. Mostly UI boilerplate. * - * @p parent is parent widget. Set @p allowLetterShortcuts to false if - * unmodified alphanumeric keys ('A', '1', etc.) are not permissible - * shortcuts. + * @param allowLetterShortcuts set to KisShortcutsEditor::LetterShortcutsDisallowed if unmodified alphanumeric + * keys ('A', '1', etc.) are not permissible shortcuts. + * + * @param parent the parent widget to attach to * * There is some legacy support for global (i.e. desktop-wide) shortucts * that should probably be removed. */ explicit KisShortcutsDialog(KisShortcutsEditor::ActionTypes types = defaultActionTypes, KisShortcutsEditor::LetterShortcuts allowLetterShortcuts \ = KisShortcutsEditor::LetterShortcutsAllowed, QWidget *parent = 0); virtual ~KisShortcutsDialog(); /** * Add all actions of the collection to the ones displayed and configured * by the dialog. This is where the business happens. * * @param title the title associated with the collection. */ void addCollection(KActionCollection *, const QString &title = QString()); /** * @return the list of action collections that are available for configuration in the dialog. */ QList actionCollections() const; - /** - * Run the dialog. If @p saveSettings is true, save any modifications to the - * actioncollections with a call to writeSettings(). - * - * - * FIXME should there be a setSaveSettings method? When is this f'n called? - */ - bool configure(bool saveSettings = true); - /** @see QWidget::sizeHint() */ QSize sizeHint() const Q_DECL_OVERRIDE; + /** + * Called when the "OK" button in the main configuration page is pressed. + */ + void save(); /** * Import shortcut scheme file from @p path */ void importConfiguration(const QString &path); /** * Exports shortcut scheme file to @p path */ void exportConfiguration(const QString &path) const; /** * Import custom shortcuts from @p path */ void loadCustomShortcuts(const QString &path); /** * Exports custom shortcuts to @p path */ void saveCustomShortcuts(const QString &path) const; - /** - * Pops up a modal (blocking) dialog for configuring key settings. The new - * shortcut settings will become active if the user presses OK. - * - * @param collection the KActionCollection to configure - * @param allowLetterShortcuts set to KisShortcutsEditor::LetterShortcutsDisallowed if unmodified alphanumeric - * keys ('A', '1', etc.) are not permissible shortcuts. - * @param parent the parent widget to attach to - * @param bSaveSettings if true, the settings will also be saved back - * by calling writeSettings() on the action collections that were added. - * - * @return Accept if the dialog was closed with OK, Reject otherwise. - */ - static int configure(KActionCollection *collection, - KisShortcutsEditor::LetterShortcuts allowLetterShortcuts \ - = KisShortcutsEditor::LetterShortcutsAllowed, - QWidget *parent = 0, - bool bSaveSettings = true); - public Q_SLOTS: - ///@reimp - void accept() Q_DECL_OVERRIDE; void allDefault(); -Q_SIGNALS: - ///emitted after OK is clicked and settings are saved - void saved(); - private: Q_PRIVATE_SLOT(d, void changeShortcutScheme(const QString &)) Q_PRIVATE_SLOT(d, void undo()) class KisShortcutsDialogPrivate; class KisShortcutsDialogPrivate *const d; Q_DISABLE_COPY(KisShortcutsDialog) }; #endif // KISSHORTCUTSDIALOG_H diff --git a/libs/widgetutils/xmlgui/KisShortcutsDialog_p.cpp b/libs/widgetutils/xmlgui/KisShortcutsDialog_p.cpp index 44c2745142..27ab3cf590 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsDialog_p.cpp +++ b/libs/widgetutils/xmlgui/KisShortcutsDialog_p.cpp @@ -1,94 +1,93 @@ /* * Copyright (c) 2015 Michael Abrahams * * 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 3 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 "KisShortcutsDialog_p.h" #include "kshortcutschemeshelper_p.h" #include "kxmlguiclient.h" #include #include "kactioncollection.h" #include "kxmlguifactory.h" #include #include #include #include "kis_action_registry.h" #include #include QKeySequence primarySequence(const QList &sequences) { return sequences.isEmpty() ? QKeySequence() : sequences.at(0); } QKeySequence alternateSequence(const QList &sequences) { return sequences.size() <= 1 ? QKeySequence() : sequences.at(1); } KisShortcutsDialog::KisShortcutsDialogPrivate::KisShortcutsDialogPrivate(KisShortcutsDialog *q) : q(q) { } void KisShortcutsDialog::KisShortcutsDialogPrivate::changeShortcutScheme(const QString &schemeName) { // KTreeWidgetSearchLine is unhappy if the contents of the tree change m_shortcutsEditor->clearSearch(); QString dialogText = i18n("The current shortcut scheme is modified. Save before switching to the new one?"); if (m_shortcutsEditor->isModified() && KMessageBox::questionYesNo( q,dialogText ) == KMessageBox::Yes) { m_shortcutsEditor->save(); } else { m_shortcutsEditor->undo(); } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_shortcutsEditor->clearCollections(); KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes"); cg.writeEntry("Current Scheme", schemeName); KisActionRegistry::instance()->loadShortcutScheme(schemeName); // Update actions themselves, and re-add to dialog box to refresh auto it = m_collections.constBegin(); while (it != m_collections.constEnd()) { it.value()->updateShortcuts(); // TODO: BAD m_shortcutsEditor->addCollection(it.value(), it.key()); it++; } QApplication::restoreOverrideCursor(); } void KisShortcutsDialog::KisShortcutsDialogPrivate::undo() { m_shortcutsEditor->undo(); } void KisShortcutsDialog::KisShortcutsDialogPrivate::save() { m_shortcutsEditor->save(); - emit q->saved(); }; #include "moc_KisShortcutsDialog_p.cpp" diff --git a/libs/widgetutils/xmlgui/KisShortcutsDialog_p.h b/libs/widgetutils/xmlgui/KisShortcutsDialog_p.h index 703a6e3647..600ffe6ab5 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsDialog_p.h +++ b/libs/widgetutils/xmlgui/KisShortcutsDialog_p.h @@ -1,237 +1,236 @@ /* This file is part of the KDE libraries Copyright (C) 2006,2007 Andreas Hartmetz (ahartmetz@gmail.com) Copyright (C) 2008 Michael Jansen Copyright (C) 2008 Alexander Dymo 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 KISSHORTCUTSDIALOG_P_H #define KISSHORTCUTSDIALOG_P_H #include "KisShortcutsEditor.h" #include "kkeysequencewidget.h" #include "KisShortcutsDialog.h" #include #include #include #include #include #include #include #include #include class QLabel; class QTreeWidget; class QTreeWidgetItem; class QRadioButton; class QAction; class KActionCollection; class QPushButton; class QComboBox; class KisShortcutsDialog; class KShortcutSchemesEditor; class QAction; enum ColumnDesignation { Name = 0, LocalPrimary, LocalAlternate, Id }; // XXX: Hmm enum MyRoles { ShortcutRole = Qt::UserRole, DefaultShortcutRole, ObjectRole }; /** * Type used for QTreeWidgetItems * * @internal */ enum ItemTypes { NonActionItem = 0, ActionItem = 1 }; // Return the first item of the list, if it exists QKeySequence primarySequence(const QList &sequences); // Return the second item of the list, if it exists QKeySequence alternateSequence(const QList &sequences); class KisShortcutsDialog::KisShortcutsDialogPrivate { public: KisShortcutsDialogPrivate(KisShortcutsDialog *q); void changeShortcutScheme(const QString &scheme); void undo(); void save(); QHash m_collections; KisShortcutsDialog *q; KisShortcutsEditor *m_shortcutsEditor; - bool m_saveSettings{false}; KShortcutSchemesEditor *m_schemeEditor{0}; }; /** * Mixes the KShortcutWidget into the treeview used by KisShortcutsEditor. When * selecting an shortcut it changes the display from "CTRL-W" to the Widget. * * @bug That delegate uses KExtendableItemDelegate. That means a cell can be * expanded. When selected a cell is replaced by a KisShortcutsEditor. When * painting the widget KExtendableItemDelegate reparents the widget to the * viewport of the itemview it belongs to. The widget is destroyed when the user * selects another shortcut or explicitly issues a contractItem event. But when * the user clears the model the delegate misses that event and doesn't delete * the KShortcutseditor. And remains as a visible artefact in your treeview. * Additionally when closing your application you get an assertion failure from * KExtendableItemDelegate. * * @internal */ class KisShortcutsEditorDelegate : public KExtendableItemDelegate { Q_OBJECT public: KisShortcutsEditorDelegate(QTreeWidget *parent, bool allowLetterShortcuts); //reimplemented to have some extra height QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; /** * Set a list of action collections to check against for conflicting * shortcuts. * * @see KKeySequenceWidget::setCheckActionCollections */ void setCheckActionCollections(const QList checkActionCollections); bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; private: mutable QPersistentModelIndex m_editingIndex; bool m_allowLetterShortcuts; QWidget *m_editor; //! List of actionCollections to check for conflicts. QList m_checkActionCollections; Q_SIGNALS: void shortcutChanged(QVariant, const QModelIndex &); public Q_SLOTS: void hiddenBySearchLine(QTreeWidgetItem *, bool); private Q_SLOTS: void itemActivated(QModelIndex index); /** * When the user collapses a hole subtree of shortcuts then remove eventually * extended items. Else we get that artefact bug. See above. */ void itemCollapsed(QModelIndex index); /** * If the user allowed stealing a shortcut we want to be able to undo * that. */ void stealShortcut(const QKeySequence &seq, QAction *action); void keySequenceChanged(const QKeySequence &); }; /** * Edit a shortcut. This widget is displayed when a user clicks on a shortcut * from the list. It contains radio buttons choosing between default and custom * shortcuts, and a button to configure the custom shortcut. * * @see KisShortcutsEditorDeligate::itemActivated * @see KisShortcutWidget.cpp * * @internal */ class ShortcutEditWidget : public QWidget { Q_OBJECT public: ShortcutEditWidget(QWidget *viewport, const QKeySequence &defaultSeq, const QKeySequence &activeSeq, bool allowLetterShortcuts); //! @see KKeySequenceWidget::setCheckActionCollections() void setCheckActionCollections(const QList checkActionCollections); //@{ //! @see KKeySequenceWidget::checkAgainstStandardShortcuts() KKeySequenceWidget::ShortcutTypes checkForConflictsAgainst() const; void setCheckForConflictsAgainst(KKeySequenceWidget::ShortcutTypes); //@} //@{ //! @see KKeySequenceWidget::checkAgainstStandardShortcuts() bool multiKeyShortcutsAllowed() const; void setMultiKeyShortcutsAllowed(bool); //@} //! @see KKeySequenceWidget::setComponentName void setComponentName(const QString componentName); void setAction(QObject *action); void paintEvent(QPaintEvent *pe) Q_DECL_OVERRIDE; Q_SIGNALS: //! Emitted when the key sequence is changed. void keySequenceChanged(const QKeySequence &); //! @see KKeySequenceWidget::stealShortcut() void stealShortcut(const QKeySequence &seq, QAction *action); public Q_SLOTS: //! Set the displayed sequences void setKeySequence(const QKeySequence &activeSeq); private Q_SLOTS: void defaultToggled(bool); void setCustom(const QKeySequence &); private: QLabel *m_defaultLabel; QKeySequence m_defaultKeySequence; QRadioButton *m_defaultRadio; QRadioButton *m_customRadio; KKeySequenceWidget *m_customEditor; bool m_isUpdating; QObject *m_action; }; #endif /* KISSHORTCUTSDIALOG_P_H */ diff --git a/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp b/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp index c5f1019593..1943603836 100644 --- a/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp +++ b/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp @@ -1,239 +1,237 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Alexander Dymo 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 "kshortcutschemeseditor.h" #include "KisShortcutsDialog_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisShortcutsDialog.h" #include "kshortcutschemeshelper_p.h" #include "kactioncollection.h" #include "kxmlguiclient.h" #include "KoResourcePaths.h" KShortcutSchemesEditor::KShortcutSchemesEditor(KisShortcutsDialog *parent) : QHBoxLayout(parent) , m_dialog(parent) { KConfigGroup group(KSharedConfig::openConfig(), "Shortcut Schemes"); QStringList schemes; schemes << QStringLiteral("Default"); auto schemeFileLocations = KShortcutSchemesHelper::schemeFileLocations(); schemes << schemeFileLocations.keys(); const QString currentScheme = group.readEntry("Current Scheme", "Default"); setMargin(0); QLabel *schemesLabel = new QLabel(i18n("Shortcut Schemes:"), m_dialog); addWidget(schemesLabel); m_schemesList = new QComboBox(m_dialog); m_schemesList->setEditable(false); m_schemesList->addItems(schemes); m_schemesList->setCurrentIndex(m_schemesList->findText(currentScheme)); schemesLabel->setBuddy(m_schemesList); addWidget(m_schemesList); m_newScheme = new QPushButton(i18n("New...")); addWidget(m_newScheme); m_deleteScheme = new QPushButton(i18n("Delete")); addWidget(m_deleteScheme); - QPushButton *moreActions = new QPushButton(i18n("More Actions")); + QPushButton *moreActions = new QPushButton(i18n("Save/Load")); addWidget(moreActions); QMenu *moreActionsMenu = new QMenu(m_dialog); // moreActionsMenu->addAction(i18n("Save as Scheme Defaults"), // this, SLOT(saveAsDefaultsForScheme())); moreActionsMenu->addAction(i18n("Save Custom Shortcuts"), this, SLOT(saveCustomShortcuts())); moreActionsMenu->addAction(i18n("Load Custom Shortcuts"), this, SLOT(loadCustomShortcuts())); moreActionsMenu->addAction(i18n("Export Scheme..."), this, SLOT(exportShortcutsScheme())); moreActionsMenu->addAction(i18n("Import Scheme..."), this, SLOT(importShortcutsScheme())); - moreActionsMenu->addAction(i18n("Restore Defaults"), - m_dialog, SLOT(allDefault())); moreActions->setMenu(moreActionsMenu); addStretch(1); connect(m_schemesList, SIGNAL(activated(QString)), this, SIGNAL(shortcutsSchemeChanged(QString))); connect(m_newScheme, SIGNAL(clicked()), this, SLOT(newScheme())); connect(m_deleteScheme, SIGNAL(clicked()), this, SLOT(deleteScheme())); updateDeleteButton(); } void KShortcutSchemesEditor::newScheme() { bool ok; const QString newName = QInputDialog::getText(m_dialog, i18n("Name for New Scheme"), i18n("Name for new scheme:"), QLineEdit::Normal, i18n("New Scheme"), &ok); if (!ok) { return; } if (m_schemesList->findText(newName) != -1) { KMessageBox::sorry(m_dialog, i18n("A scheme with this name already exists.")); return; } const QString newSchemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(newName); QFile schemeFile(newSchemeFileName); if (!schemeFile.open(QFile::WriteOnly | QFile::Truncate)) { return; } schemeFile.close(); m_dialog->exportConfiguration(newSchemeFileName); m_schemesList->addItem(newName); m_schemesList->setCurrentIndex(m_schemesList->findText(newName)); m_schemeFileLocations.insert(newName, newSchemeFileName); updateDeleteButton(); emit shortcutsSchemeChanged(newName); } void KShortcutSchemesEditor::deleteScheme() { if (KMessageBox::questionYesNo(m_dialog, i18n("Do you really want to delete the scheme %1?\n\ Note that this will not remove any system wide shortcut schemes.", currentScheme())) == KMessageBox::No) { return; } //delete the scheme for the app itself QFile::remove(KShortcutSchemesHelper::shortcutSchemeFileName(currentScheme())); m_schemesList->removeItem(m_schemesList->findText(currentScheme())); updateDeleteButton(); emit shortcutsSchemeChanged(currentScheme()); } QString KShortcutSchemesEditor::currentScheme() { return m_schemesList->currentText(); } void KShortcutSchemesEditor::exportShortcutsScheme() { //ask user about dir QFileDialog dlg(m_dialog, i18n("Export Shortcuts"), KoResourcePaths::saveLocation("kis_shortcuts"), i18n("Shortcuts (*.shortcuts)")); dlg.setDefaultSuffix(QStringLiteral(".shortcuts")); dlg.setAcceptMode(QFileDialog::AcceptSave); dlg.exec(); auto path = dlg.selectedFiles().first(); // Parent, caption, dir, filter if (path.isEmpty()) { return; } m_dialog->exportConfiguration(path); } void KShortcutSchemesEditor::saveCustomShortcuts() { //ask user about dir QFileDialog dlg(m_dialog, i18n("Save Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); dlg.setDefaultSuffix(QStringLiteral(".shortcuts")); dlg.setAcceptMode(QFileDialog::AcceptSave); dlg.exec(); auto path = dlg.selectedFiles().first(); if (path.isEmpty()) { return; } m_dialog->saveCustomShortcuts(path); } void KShortcutSchemesEditor::loadCustomShortcuts() { auto path = QFileDialog::getOpenFileName(m_dialog, i18n("Import Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); if (path.isEmpty()) { return; } // auto ar = KisActionRegistry::instance(); // ar->loadCustomShortcuts(path); m_dialog->loadCustomShortcuts(path); } void KShortcutSchemesEditor::importShortcutsScheme() { //ask user about dir QString path = QFileDialog::getOpenFileName(m_dialog, i18n("Import Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); if (path.isEmpty()) { return; } m_dialog->importConfiguration(path); } #if 0 // XXX: Not implemented void KShortcutSchemesEditor::saveAsDefaultsForScheme() { foreach (KActionCollection *collection, m_dialog->actionCollections()) { KShortcutSchemesHelper::exportActionCollection(collection, currentScheme()); } } #endif void KShortcutSchemesEditor::updateDeleteButton() { m_deleteScheme->setEnabled(m_schemesList->count() >= 1); }