File indexing completed on 2024-05-19 04:29:15

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org>
0003    SPDX-FileCopyrightText: 2000-2006 David Faure <faure@kde.org>
0004    SPDX-FileCopyrightText: 2007, 2009 Thomas zander <zander@kde.org>
0005    SPDX-FileCopyrightText: 2010 Benjamin Port <port.benjamin@gmail.com>
0006 
0007    SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "KisMainWindow.h"
0011 
0012 #include <KoConfig.h>
0013 
0014 // qt includes
0015 #include <QApplication>
0016 #include <QByteArray>
0017 #include <QCloseEvent>
0018 #include <QStandardPaths>
0019 #include <QDesktopServices>
0020 #include <QDesktopWidget>
0021 #include <QDialog>
0022 #include <QDockWidget>
0023 #include <QIcon>
0024 #include <QInputDialog>
0025 #include <QLabel>
0026 #include <QLayout>
0027 #include <QMdiArea>
0028 #include <QMdiSubWindow>
0029 #include <QMutex>
0030 #include <QMutexLocker>
0031 #include <QPointer>
0032 #include <QToolButton>
0033 #include <KisSignalMapper.h>
0034 #include <QTabBar>
0035 #include <QMoveEvent>
0036 #include <QUrl>
0037 #include <QMessageBox>
0038 #include <QStatusBar>
0039 #include <QStyleFactory>
0040 #include <QMenu>
0041 #include <QMenuBar>
0042 #include <KisMimeDatabase.h>
0043 #include <QMimeData>
0044 #include <QStackedWidget>
0045 #include <QProxyStyle>
0046 #include <QScreen>
0047 #include <QAction>
0048 #include <QWindow>
0049 #include <QTemporaryDir>
0050 #include <QScrollArea>
0051 #include <kactioncollection.h>
0052 #include <kactionmenu.h>
0053 #include <kis_debug.h>
0054 #include <kedittoolbar.h>
0055 #include <khelpmenu.h>
0056 #include <klocalizedstring.h>
0057 #include <kaboutdata.h>
0058 #include <kis_workspace_resource.h>
0059 #include <input/kis_input_manager.h>
0060 #include "kis_selection_manager.h"
0061 #include "kis_icon_utils.h"
0062 #include <krecentfilesaction.h>
0063 #include "krita_utils.h"
0064 #include <ktoggleaction.h>
0065 #include <ktoolbar.h>
0066 #include <kmainwindow.h>
0067 #include <kxmlguiwindow.h>
0068 #include <kxmlguifactory.h>
0069 #include <kxmlguiclient.h>
0070 #include <kguiitem.h>
0071 #include <kwindowconfig.h>
0072 #include <kacceleratormanager.h>
0073 
0074 #include <KoResourcePaths.h>
0075 #include <KoToolFactoryBase.h>
0076 #include <KoToolRegistry.h>
0077 #include <KoDockFactoryBase.h>
0078 #include <KoDockWidgetTitleBar.h>
0079 #include <KoDocumentInfoDlg.h>
0080 #include <KoDocumentInfo.h>
0081 #include <KoFileDialog.h>
0082 #include <kis_icon.h>
0083 #include <KoToolManager.h>
0084 #include <KoZoomController.h>
0085 #include "KoToolDocker.h"
0086 #include "toolbox/KoToolBoxFactory.h"
0087 #include <KoDockRegistry.h>
0088 #include <KoPluginLoader.h>
0089 #include <KoColorSpaceEngine.h>
0090 #include <KoUpdater.h>
0091 #include <KisResourceModel.h>
0092 #include <KisResourceLoaderRegistry.h>
0093 #include <KisResourceIterator.h>
0094 #include <KisResourceTypes.h>
0095 #include <KisResourceCacheDb.h>
0096 #include <KisStorageModel.h>
0097 #include <KisStorageFilterProxyModel.h>
0098 #include <KisPlaybackEngine.h>
0099 
0100 #ifdef Q_OS_ANDROID
0101 #include <QtAndroid>
0102 #endif
0103 
0104 #include <KisUsageLogger.h>
0105 #include <brushengine/kis_paintop_settings.h>
0106 #include "dialogs/kis_about_application.h"
0107 #include "dialogs/kis_delayed_save_dialog.h"
0108 #include "dialogs/kis_dlg_preferences.h"
0109 #include "kis_action_manager.h"
0110 #include "KisApplication.h"
0111 #include "kis_canvas2.h"
0112 #include "kis_canvas_controller.h"
0113 #include "kis_canvas_resource_provider.h"
0114 #include "kis_clipboard.h"
0115 #include "kis_config.h"
0116 #include "kis_config_notifier.h"
0117 #include "kis_custom_image_widget.h"
0118 #include "animation/KisAnimationRender.h"
0119 #include "animation/KisDlgAnimationRenderer.h"
0120 #include <KisDocument.h>
0121 #include "kis_group_layer.h"
0122 #include "kis_image_from_clipboard_widget.h"
0123 #include "kis_image.h"
0124 #include "kis_image_animation_interface.h"
0125 #include <KisImportExportFilter.h>
0126 #include "KisImportExportManager.h"
0127 #include "kis_mainwindow_observer.h"
0128 #include "kis_node.h"
0129 #include "KisOpenPane.h"
0130 #include "kis_paintop_box.h"
0131 #include "KisPart.h"
0132 #include "KisResourceServerProvider.h"
0133 #include "kis_signal_compressor_with_param.h"
0134 #include "kis_statusbar.h"
0135 #include "KisView.h"
0136 #include "KisViewManager.h"
0137 #include "thememanager.h"
0138 #include "kis_animation_importer.h"
0139 #include "dialogs/kis_dlg_import_image_sequence.h"
0140 #include "animation/KisDlgImportVideoAnimation.h"
0141 #include <KisImageConfigNotifier.h>
0142 #include "KisWindowLayoutManager.h"
0143 #include <KisUndoActionsUpdateManager.h>
0144 #include "KisWelcomePageWidget.h"
0145 #include "KisRecentDocumentsModelWrapper.h"
0146 #include <KritaVersionWrapper.h>
0147 #include "KisCanvasWindow.h"
0148 #include "kis_action.h"
0149 #include <katecommandbar.h>
0150 #include "KisNodeActivationActionCreatorVisitor.h"
0151 #include "KisUiFont.h"
0152 #include <KisResourceUserOperations.h>
0153 #include "KisRecentFilesManager.h"
0154 #include "KisWidgetConnectionUtils.h"
0155 #include "KisToolBarStateModel.h"
0156 #include <config-qmdiarea-always-show-subwindow-title.h>
0157 
0158 #include <mutex>
0159 
0160 class ToolDockerFactory : public KoDockFactoryBase
0161 {
0162 public:
0163     ToolDockerFactory() : KoDockFactoryBase() { }
0164 
0165     QString id() const override {
0166         return "sharedtooldocker";
0167     }
0168 
0169     QDockWidget* createDockWidget() override {
0170         KoToolDocker* dockWidget = new KoToolDocker();
0171         return dockWidget;
0172     }
0173 
0174     DockPosition defaultDockPosition() const override {
0175         return DockRight;
0176     }
0177 };
0178 
0179 class Q_DECL_HIDDEN KisMainWindow::Private
0180 {
0181 public:
0182     Private(KisMainWindow *parent, QUuid id)
0183         : q(parent)
0184         , id(id)
0185         , styleMenu(new KActionMenu(i18nc("@action:inmenu", "Styles"), parent))
0186         , dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent))
0187         , windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent))
0188         , documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent))
0189         , workspaceMenu(new KActionMenu(i18nc("@action:inmenu", "Wor&kspace"), parent))
0190         , welcomePage(new KisWelcomePageWidget(parent))
0191         , widgetStack(new QStackedWidget(parent))
0192         , mdiArea(new QMdiArea(parent))
0193         , windowMapper(new KisSignalMapper(parent))
0194         , documentMapper(new KisSignalMapper(parent))
0195     {
0196         if (id.isNull()) this->id = QUuid::createUuid();
0197 
0198         welcomeScroller = new QScrollArea();
0199         welcomeScroller->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0200         welcomeScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0201         welcomeScroller->setWidget(welcomePage);
0202         welcomeScroller->setWidgetResizable(true);
0203 
0204         widgetStack->addWidget(welcomeScroller);
0205         widgetStack->addWidget(mdiArea);
0206         mdiArea->setTabsMovable(true);
0207         mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder);
0208         mdiArea->setDocumentMode(true);
0209         mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation);
0210 #ifdef HAVE_QMDIAREA_ALWAYS_SHOW_SUBWINDOW_TITLE
0211         mdiArea->setOption(QMdiArea::AlwaysShowSubwindowNameInTitleBar);
0212 #endif /* HAVE_QMDIAREA_ALWAYS_SHOW_SUBWINDOW_TITLE */
0213 
0214         commandBar = new KateCommandBar(parent);
0215     }
0216 
0217     ~Private() {
0218         qDeleteAll(toolbarList);
0219     }
0220 
0221     KisMainWindow *q {nullptr};
0222     QUuid id;
0223 
0224     KisViewManager *viewManager {nullptr};
0225 
0226     QPointer<KisView> activeView;
0227     KisSignalAutoConnectionsStore activeViewConnections;
0228 
0229     QList<QAction *> toolbarList;
0230 
0231     bool firstTime {true};
0232     bool windowSizeDirty {false};
0233 
0234     KisAction *showDocumentInfo {nullptr};
0235     KisAction *saveAction {nullptr};
0236     KisAction *saveActionAs {nullptr};
0237     KisAction *importAnimation {nullptr};
0238     KisAction *importVideoAnimation {nullptr};
0239     KisAction *renderAnimation {nullptr};
0240     KisAction *renderAnimationAgain {nullptr};
0241     KisAction *closeAll {nullptr};
0242     KisAction *importFile {nullptr};
0243     KisAction *exportFile {nullptr};
0244     KisAction *exportFileAdvance {nullptr};
0245     KisAction *undo {nullptr};
0246     KisAction *redo {nullptr};
0247     KisAction *close {nullptr};
0248     KisAction *mdiCascade {nullptr};
0249     KisAction *mdiTile {nullptr};
0250     KisAction *mdiNextWindow {nullptr};
0251     KisAction *mdiPreviousWindow {nullptr};
0252     KisAction *toggleDockers {nullptr};
0253     KisAction *resetConfigurations {nullptr};
0254     KisAction *toggleDockerTitleBars {nullptr};
0255 #ifndef Q_OS_ANDROID
0256     KisAction *toggleDetachCanvas {nullptr};
0257     KisAction *newWindow {nullptr};
0258 #endif
0259     KisAction *fullScreenMode {nullptr};
0260     KisAction *showSessionManager {nullptr};
0261     KisAction *commandBarAction {nullptr};
0262     KisAction *expandingSpacers[2];
0263 
0264     KActionMenu *styleMenu {nullptr};
0265     QActionGroup* styleActions {nullptr};
0266     QMap<QString, QAction*> actionMap;
0267 
0268     KActionMenu *dockWidgetMenu {nullptr};
0269     KActionMenu *windowMenu {nullptr};
0270     KActionMenu *documentMenu {nullptr};
0271     KActionMenu *workspaceMenu {nullptr};
0272 
0273     KisKHelpMenu *helpMenu  {nullptr};
0274 
0275     KRecentFilesAction *recentFiles {nullptr};
0276     KisResourceModel *workspacemodel {nullptr};
0277 
0278     QScopedPointer<KisUndoActionsUpdateManager> undoActionsUpdateManager;
0279 
0280     QString lastExportLocation;
0281 
0282     QMap<QString, QDockWidget *> dockWidgetsMap;
0283     QByteArray dockerStateBeforeHiding;
0284     KoToolDocker *toolOptionsDocker {nullptr};
0285 
0286     QCloseEvent *deferredClosingEvent {nullptr};
0287 
0288     Digikam::ThemeManager *themeManager {nullptr};
0289 
0290     QScrollArea *welcomeScroller {nullptr};
0291     KisWelcomePageWidget *welcomePage {nullptr};
0292 
0293 
0294     QStackedWidget *widgetStack {nullptr};
0295 
0296     QMdiArea *mdiArea {nullptr};
0297     QMdiSubWindow *activeSubWindow  {nullptr};
0298     KisSignalMapper *windowMapper {nullptr};
0299     KisSignalMapper *documentMapper {nullptr};
0300     KisCanvasWindow *canvasWindow {nullptr};
0301 
0302     QByteArray lastExportedFormat;
0303     QScopedPointer<KisSignalCompressorWithParam<int> > tabSwitchCompressor;
0304     QMutex savingEntryMutex;
0305 
0306     KConfigGroup windowStateConfig;
0307 
0308     QUuid workspaceBorrowedBy;
0309 
0310     KateCommandBar *commandBar {nullptr};
0311 
0312     KisActionManager * actionManager() {
0313         return viewManager->actionManager();
0314     }
0315 
0316     QTabBar* findTabBarHACK() {
0317         QObjectList objects = mdiArea->children();
0318         Q_FOREACH (QObject *object, objects) {
0319             QTabBar *bar = qobject_cast<QTabBar*>(object);
0320             if (bar) {
0321                 return bar;
0322             }
0323         }
0324         return 0;
0325     }
0326 };
0327 
0328 class ScopedWidgetDisabler
0329 {
0330     QWidget *widget;
0331 public:
0332     ScopedWidgetDisabler(QWidget *widget_)
0333         : widget(widget_)
0334     {
0335         widget->setEnabled(false);
0336     }
0337 
0338     ~ScopedWidgetDisabler()
0339     {
0340         widget->setEnabled(true);
0341     }
0342 };
0343 
0344 KisMainWindow::KisMainWindow(QUuid uuid)
0345     : KXmlGuiWindow()
0346     , d(new Private(this, uuid))
0347 {
0348     KAcceleratorManager::setNoAccel(this);
0349 
0350     d->workspacemodel = new KisResourceModel(ResourceType::Workspaces, this);
0351     connect(d->workspacemodel, SIGNAL(modelReset()), this, SLOT(updateWindowMenu()));
0352     connect(d->workspacemodel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(updateWindowMenu()));
0353     connect(d->workspacemodel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(updateWindowMenu()));
0354 
0355     d->viewManager = new KisViewManager(this, actionCollection());
0356     KConfigGroup group( KSharedConfig::openConfig(), "theme");
0357     d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this);
0358 
0359     d->windowStateConfig = KSharedConfig::openConfig()->group("MainWindow");
0360 
0361     setStandardToolBarMenuEnabled(true);
0362     setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
0363     setDockNestingEnabled(true);
0364 
0365     qApp->setStartDragDistance(25);     // 25 px is a distance that works well for Tablet and Mouse events
0366 
0367 #ifdef Q_OS_MACOS
0368     setUnifiedTitleAndToolBarOnMac(true);
0369 #endif
0370 
0371     connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts()));
0372     connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons()));
0373     connect(this, SIGNAL(themeChanged()), KoToolManager::instance(), SLOT(themeChanged()));
0374     connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu()));
0375     connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu()));
0376     connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged()));
0377 
0378     actionCollection()->addAssociatedWidget(this);
0379     KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), d->viewManager, false);
0380 
0381     // Load the per-application plugins (Right now, only Python) We do this only once, when the first mainwindow is being created.
0382     KoPluginLoader::instance()->load("Krita/ApplicationPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), qApp, true);
0383 
0384     KoToolBoxFactory toolBoxFactory;
0385     QDockWidget *toolbox = createDockWidget(&toolBoxFactory);
0386 
0387     KisConfig cfg(true);
0388     if (cfg.toolOptionsInDocker()) {
0389         ToolDockerFactory toolDockerFactory;
0390         d->toolOptionsDocker = qobject_cast<KoToolDocker*>(createDockWidget(&toolDockerFactory));
0391         if (d->toolOptionsDocker) {
0392             d->toolOptionsDocker->toggleViewAction()->setEnabled(true);
0393         }
0394     }
0395 
0396     QMap<QString, QAction*> dockwidgetActions;
0397 
0398     if (toolbox) {
0399         dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction();
0400     }
0401     Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) {
0402         KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker);
0403         QDockWidget *dw = createDockWidget(factory);
0404         if (dw) {
0405             dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction();
0406         }
0407     }
0408 
0409     if (d->toolOptionsDocker) {
0410         dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction();
0411     }
0412     connect(KoToolManager::instance(), SIGNAL(toolOptionWidgetsChanged(KoCanvasController*,QList<QPointer<QWidget> >)), this, SLOT(newOptionWidgets(KoCanvasController*,QList<QPointer<QWidget> >)));
0413 
0414     Q_FOREACH (QString title, dockwidgetActions.keys()) {
0415         d->dockWidgetMenu->addAction(dockwidgetActions[title]);
0416     }
0417 
0418 
0419     // Style menu actions
0420     d->styleActions = new QActionGroup(this);
0421     QAction * action;
0422 
0423     QStringList allowableStyles = QStringList() << "macintosh" << "breeze" << "fusion";
0424 
0425     Q_FOREACH (QString styleName, QStyleFactory::keys()) {
0426 #ifdef Q_OS_ANDROID
0427         // disable the style for android platform
0428         if (styleName.toLower().contains("android")) {
0429             continue;
0430         }
0431 #endif
0432         if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) {
0433             if (!allowableStyles.contains(styleName.toLower())) {
0434                 continue;
0435             }
0436         }
0437         action = new QAction(styleName, d->styleActions);
0438         action->setCheckable(true);
0439         d->actionMap.insert(styleName, action);
0440         d->styleMenu->addAction(d->actionMap.value(styleName));
0441     }
0442 
0443 
0444     // select the config value, or the current style if that does not exist
0445     QString styleFromConfig = cfg.widgetStyle().toLower();
0446     QString styleToSelect = styleFromConfig == "" ? style()->objectName().toLower() : styleFromConfig;
0447 
0448     Q_FOREACH (auto key, d->actionMap.keys()) {
0449         if(key.toLower() == styleToSelect) { // does the key match selection
0450             d->actionMap.value(key)->setChecked(true);
0451         }
0452     }
0453 
0454     connect(d->styleActions, SIGNAL(triggered(QAction*)),
0455             this, SLOT(slotUpdateWidgetStyle()));
0456 
0457 
0458     // Load all the actions from the tool plugins
0459     // ToolBoxDocker needs them when at setViewManager()
0460     Q_FOREACH(KoToolFactoryBase *toolFactory, KoToolRegistry::instance()->values()) {
0461         toolFactory->createActions(actionCollection());
0462     }
0463 
0464 
0465     Q_FOREACH (QDockWidget *wdg, dockWidgets()) {
0466         if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) {
0467             wdg->setVisible(true);
0468         }
0469     }
0470 
0471     Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) {
0472         observer->setObservedCanvas(0);
0473         KisMainwindowObserver* mainwindowObserver = dynamic_cast<KisMainwindowObserver*>(observer);
0474         if (mainwindowObserver) {
0475             mainwindowObserver->setViewManager(d->viewManager);
0476         }
0477     }
0478 
0479     d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0480     d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0481     d->mdiArea->setTabPosition(QTabWidget::North);
0482     d->mdiArea->setTabsClosable(true);
0483     d->mdiArea->setAcceptDrops(true);
0484 
0485     themeChanged(); // updates icon styles
0486 
0487     setCentralWidget(d->widgetStack);
0488     d->widgetStack->setCurrentIndex(0);
0489 
0490     connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated()));
0491     connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*)));
0492     connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*)));
0493 
0494     d->canvasWindow = new KisCanvasWindow(this);
0495     actionCollection()->addAssociatedWidget(d->canvasWindow);
0496 
0497     createActions();
0498 
0499     // the welcome screen needs to grab actions...so make sure this line goes after the createAction() so they exist
0500     d->welcomePage->setMainWindow(this);
0501 
0502     d->recentFiles->setRecentFilesModel(&KisRecentDocumentsModelWrapper::instance()->model());
0503 
0504     applyMainWindowSettings(d->windowStateConfig);
0505 
0506     subWindowActivated();
0507     updateWindowMenu();
0508 
0509     if (isHelpMenuEnabled() && !d->helpMenu) {
0510         QGuiApplication *app = qApp;
0511         KAboutData aboutData(KAboutData::applicationData());
0512         aboutData.setOrganizationDomain(app->organizationDomain().toUtf8());
0513 
0514         d->helpMenu = new KisKHelpMenu(this, aboutData, false);
0515 
0516         // workaround-less version:
0517         // d->helpMenu = new KisKHelpMenu(this, QString()/*unused*/, false);
0518 
0519         // The difference between using KisKActionCollection->addAction() is that
0520         // these actions do not get tied to the MainWindow.  What does this all do?
0521         KisKActionCollection *actions = d->viewManager->actionCollection();
0522         QAction *helpContentsAction = d->helpMenu->action(KisKHelpMenu::menuHelpContents);
0523         QAction *whatsThisAction = d->helpMenu->action(KisKHelpMenu::menuWhatsThis);
0524         QAction *reportBugAction = d->helpMenu->action(KisKHelpMenu::menuReportBug);
0525         QAction *switchLanguageAction = d->helpMenu->action(KisKHelpMenu::menuSwitchLanguage);
0526         QAction *aboutAppAction = d->helpMenu->action(KisKHelpMenu::menuAboutApp);
0527         QAction *aboutKdeAction = d->helpMenu->action(KisKHelpMenu::menuAboutKDE);
0528 
0529         if (helpContentsAction) {
0530             actions->addAction(helpContentsAction->objectName(), helpContentsAction);
0531         }
0532         if (whatsThisAction) {
0533             actions->addAction(whatsThisAction->objectName(), whatsThisAction);
0534         }
0535         if (reportBugAction) {
0536             actions->addAction(reportBugAction->objectName(), reportBugAction);
0537         }
0538         if (switchLanguageAction) {
0539             actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
0540         }
0541         if (aboutAppAction) {
0542             actions->addAction(aboutAppAction->objectName(), aboutAppAction);
0543         }
0544         if (aboutKdeAction) {
0545             actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
0546         }
0547 
0548         connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication()));
0549     }
0550 
0551     // KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves
0552     QAction *helpAction = actionCollection()->action("help_contents");
0553     helpAction->disconnect();
0554     connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual()));
0555 
0556 #if 0
0557     //check for colliding shortcuts
0558     QSet<QKeySequence> existingShortcuts;
0559     Q_FOREACH (QAction* action, actionCollection()->actions()) {
0560         if(action->shortcut() == QKeySequence(0)) {
0561             continue;
0562         }
0563         dbgKrita << "shortcut " << action->text() << " " << action->shortcut();
0564         Q_ASSERT(!existingShortcuts.contains(action->shortcut()));
0565         existingShortcuts.insert(action->shortcut());
0566     }
0567 #endif
0568 
0569     configChanged();
0570 
0571     // Make sure the python plugins create their actions in time
0572     KisPart::instance()->notifyMainWindowIsBeingCreated(this);
0573 
0574     // If we have customized the toolbars, load that first
0575     setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita5.xmlgui"));
0576     setXMLFile(":/kxmlgui5/krita5.xmlgui");
0577 
0578     guiFactory()->addClient(this);
0579     connect(guiFactory(), SIGNAL(makingChanges(bool)), SLOT(slotXmlGuiMakingChanges(bool)));
0580 
0581     // Create and plug toolbar list for Settings menu
0582     QList<QAction *> toolbarList;
0583     Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) {
0584         KisToolBar * toolBar = ::qobject_cast<KisToolBar *>(it);
0585         if (toolBar) {
0586             KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this);
0587             actionCollection()->addAction(toolBar->objectName().toUtf8(), act);
0588             act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle())));
0589             connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool)));
0590             act->setChecked(!toolBar->isHidden());
0591             toolbarList.append(act);
0592         } else {
0593             warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!";
0594         }
0595     }
0596 
0597     plugActionList("toolbarlist", toolbarList);
0598     d->toolbarList = toolbarList;
0599 
0600     applyToolBarLayout();
0601 
0602     d->viewManager->updateGUI();
0603     d->viewManager->updateIcons();
0604 
0605     QTimer::singleShot(1000, this, SLOT(checkSanity()));
0606 
0607     {
0608         using namespace std::placeholders; // For _1 placeholder
0609         std::function<void (int)> callback(
0610                     std::bind(&KisMainWindow::switchTab, this, _1));
0611 
0612         d->tabSwitchCompressor.reset(
0613                     new KisSignalCompressorWithParam<int>(500, callback, KisSignalCompressor::FIRST_INACTIVE));
0614     }
0615 
0616 
0617     if (cfg.readEntry("CanvasOnlyActive", false)) {
0618         QString currentWorkspace = cfg.readEntry<QString>("CurrentWorkspace", "Default");
0619         KoResourceServer<KisWorkspaceResource> * rserver = KisResourceServerProvider::instance()->workspaceServer();
0620         KisWorkspaceResourceSP workspace = rserver->resource("", "", currentWorkspace);
0621         if (workspace) {
0622             restoreWorkspace(workspace);
0623         }
0624         cfg.writeEntry("CanvasOnlyActive", false);
0625         menuBar()->setVisible(true);
0626     }
0627 
0628     this->winId(); // Ensures the native window has been created.
0629 
0630 #ifdef Q_OS_ANDROID
0631     // HACK: This prevents the mainWindow from going beyond the screen with no
0632     // way to bring it back. Apparently the size doesn't matter here as long as
0633     // it remains fixed?
0634     setFixedSize(KisApplication::primaryScreen()->availableGeometry().size());
0635 
0636     QScreen *s = QGuiApplication::primaryScreen();
0637     s->setOrientationUpdateMask(Qt::LandscapeOrientation|Qt::InvertedLandscapeOrientation|Qt::PortraitOrientation|Qt::InvertedPortraitOrientation);
0638     connect(s, SIGNAL(orientationChanged(Qt::ScreenOrientation)), this, SLOT(orientationChanged()));
0639 
0640     // When Krita starts, Java side sends an event to set applicationState() to active. But, before
0641     // the event could reach KisApplication's platform integration, it is cleared by KisOpenGLModeProber::probeFormat.
0642     // So, we send it manually when MainWindow shows up.
0643     QAndroidJniObject::callStaticMethod<void>("org/qtproject/qt5/android/QtNative", "setApplicationState", "(I)V", Qt::ApplicationActive);
0644 #endif
0645 
0646     setAcceptDrops(true);
0647     QTabBar *tabBar = d->findTabBarHACK();
0648     if (tabBar) {
0649         tabBar->setElideMode(Qt::ElideRight);
0650         // load customized tab style
0651         customizeTabBar();
0652         tabBar->setExpanding(true);
0653         tabBar->setAcceptDrops(true);
0654         tabBar->setChangeCurrentOnDrag(true);
0655     }
0656 
0657 
0658 }
0659 
0660 KisMainWindow::~KisMainWindow()
0661 {
0662     //    Q_FOREACH (QAction *ac, actionCollection()->actions()) {
0663     //        QAction *action = qobject_cast<QAction*>(ac);
0664     //        if (action) {
0665     //        qDebug() << "<Action"
0666     //                 << "\n\tname=" << action->objectName()
0667     //                 << "\n\ticon=" << action->icon().name()
0668     //                 << "\n\ttext="  << action->text().replace("&", "&amp;")
0669     //                 << "\n\twhatsThis="  << action->whatsThis()
0670     //                 << "\n\ttoolTip="  << action->toolTip().replace("<html>", "").replace("</html>", "")
0671     //                 << "\n\ticonText="  << action->iconText().replace("&", "&amp;")
0672     //                 << "\n\tshortcut="  << action->shortcut().toString()
0673     //                 << "\n\tisCheckable="  << QString((action->isChecked() ? "true" : "false"))
0674     //                 << "\n\tstatusTip=" << action->statusTip()
0675     //                 << "\n/>\n"   ;
0676     //        }
0677     //        else {
0678     //            dbgKrita << "Got a non-qaction:" << ac->objectName();
0679     //        }
0680     //    }
0681 
0682     // The doc and view might still exist (this is the case when closing the window)
0683     KisPart::instance()->removeMainWindow(this);
0684 
0685     delete d->viewManager;
0686     delete d;
0687 
0688 }
0689 
0690 QMenu *KisMainWindow::createPopupMenu()
0691 {
0692     return 0;
0693 }
0694 
0695 QUuid KisMainWindow::id() const {
0696     return d->id;
0697 }
0698 
0699 void KisMainWindow::addView(KisView *view, QMdiSubWindow *subWindow)
0700 {
0701     if (d->activeView == view && !subWindow) return;
0702 
0703     if (d->activeView) {
0704         d->activeView->disconnect(this);
0705     }
0706 
0707     // register the newly created view in the input manager
0708     viewManager()->inputManager()->addTrackedCanvas(view->canvasBase());
0709 
0710     showView(view, subWindow);
0711     emit restoringDone();
0712 
0713 //    QTabBar *tabBar = d->findTabBarHACK();
0714 //    Q_FOREACH(QObject *c, tabBar->children()) {
0715 //        if (QWidget *w = qobject_cast<QWidget*>(c)) {
0716 //            w->setAcceptDrops(true);
0717 //        }
0718 //    }
0719 }
0720 
0721 void KisMainWindow::notifyChildViewDestroyed(KisView *view)
0722 {
0723     /**
0724      * If we are the last view of the window, Qt will not activate another tab
0725      * before destroying tab/window. In this case we should clear all the dangling
0726      * pointers manually by setting the current view to null
0727      */
0728     viewManager()->inputManager()->removeTrackedCanvas(view->canvasBase());
0729     if (view->canvasBase() == viewManager()->canvasBase()) {
0730         viewManager()->setCurrentView(0);
0731     }
0732 }
0733 
0734 
0735 void KisMainWindow::showView(KisView *imageView, QMdiSubWindow *subwin)
0736 {
0737     if (imageView && activeView() != imageView) {
0738         // XXX: find a better way to initialize this!
0739         imageView->setViewManager(d->viewManager);
0740 
0741         imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager());
0742         imageView->slotLoadingFinished();
0743 
0744         if (!subwin) {
0745             QMdiSubWindow *currentSubWin = d->mdiArea->currentSubWindow();
0746             bool shouldMaximize = currentSubWin ? currentSubWin->isMaximized() : true;
0747             subwin = d->mdiArea->addSubWindow(imageView);
0748             if (shouldMaximize) {
0749                 subwin->setWindowState(Qt::WindowMaximized);
0750             }
0751         } else {
0752             subwin->setWidget(imageView);
0753         }
0754         imageView->setSubWindow(subwin);
0755         subwin->setAttribute(Qt::WA_DeleteOnClose, true);
0756         connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu()));
0757 
0758         KisConfig cfg(true);
0759         subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
0760         subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
0761         subwin->setWindowIcon(qApp->windowIcon());
0762 
0763 #ifdef Q_OS_MACOS
0764         connect(subwin, SIGNAL(destroyed()), SLOT(updateSubwindowFlags()));
0765         updateSubwindowFlags();
0766 #endif
0767 
0768         if (d->mdiArea->subWindowList().size() == 1) {
0769             imageView->showMaximized();
0770         }
0771         else {
0772             imageView->show();
0773         }
0774 
0775         /**
0776          * Hack alert!
0777          *
0778          * Here we explicitly request KoToolManager to emit all the tool
0779          * activation signals, to reinitialize the tool options docker.
0780          *
0781          * That is needed due to a design flaw we have in the
0782          * initialization procedure.  The tool in the KoToolManager is
0783          * initialized in KisView::setViewManager() calls, which
0784          * happens early enough. During this call the tool manager
0785          * requests KoCanvasControllerWidget to emit the signal to
0786          * update the widgets in the tool docker. *But* at that moment
0787          * of time the view is not yet connected to the main window,
0788          * because it happens in KisViewManager::setCurrentView a bit
0789          * later. This fact makes the widgets updating signals be lost
0790          * and never reach the tool docker.
0791          *
0792          * So here we just explicitly call the tool activation stub.
0793          */
0794 
0795         KoToolManager::instance()->initializeCurrentToolForCanvas();
0796 
0797         // No, no, no: do not try to call this _before_ the show() has
0798         // been called on the view; only when that has happened is the
0799         // opengl context active, and very bad things happen if we tell
0800         // the dockers to update themselves with a view if the opengl
0801         // context is not active.
0802         setActiveView(imageView);
0803 
0804         updateWindowMenu();
0805     } else {
0806         unsetActiveView();
0807     }
0808 }
0809 
0810 void KisMainWindow::slotPreferences()
0811 {
0812     QScopedPointer<KisDlgPreferences> dlgPreferences(new KisDlgPreferences(this));
0813 
0814     if (dlgPreferences->editPreferences()) {
0815         KisConfigNotifier::instance()->notifyConfigChanged();
0816         KisConfigNotifier::instance()->notifyPixelGridModeChanged();
0817         KisImageConfigNotifier::instance()->notifyConfigChanged();
0818 
0819         // XXX: should this be changed for the views in other windows as well?
0820         Q_FOREACH (QPointer<KisView> koview, KisPart::instance()->views()) {
0821             KisViewManager *view = qobject_cast<KisViewManager*>(koview);
0822             if (view) {
0823                 // Update the settings for all nodes -- they don't query
0824                 // KisConfig directly because they need the settings during
0825                 // compositing, and they don't connect to the config notifier
0826                 // because nodes are not QObjects (because only one base class
0827                 // can be a QObject).
0828                 KisNode* node = dynamic_cast<KisNode*>(view->image()->rootLayer().data());
0829                 if (node) {
0830                     node->updateSettings();
0831                 }
0832             }
0833 
0834         }
0835         updateWindowMenu();
0836 
0837         d->viewManager->showHideScrollbars();
0838     }
0839 }
0840 
0841 void KisMainWindow::updateTheme()
0842 {
0843     // reload action icons!
0844     Q_FOREACH (QAction *action, actionCollection()->actions()) {
0845         KisIconUtils::updateIcon(action);
0846     }
0847     if (d->mdiArea) {
0848         d->mdiArea->setPalette(qApp->palette());
0849         for (int i=0; i<d->mdiArea->subWindowList().size(); i++) {
0850             QMdiSubWindow *window = d->mdiArea->subWindowList().at(i);
0851             if (window) {
0852                 window->setPalette(qApp->palette());
0853                 KisView *view = qobject_cast<KisView*>(window->widget());
0854                 if (view) {
0855                     view->slotThemeChanged(qApp->palette());
0856                 }
0857             }
0858         }
0859     }
0860 
0861     customizeTabBar();
0862 
0863     // Update toolbars
0864     {
0865         Q_FOREACH (KisToolBar* aToolBar, toolBars()) {
0866             QObjectList objects;
0867             objects.append(aToolBar);
0868             while (!objects.isEmpty()) {
0869                 QWidget* widget = qobject_cast<QWidget*>(objects.takeFirst());
0870                 if (widget) {
0871                     objects.append(widget->children());
0872                     widget->setPalette(qApp->palette());
0873                 }
0874             }
0875         }
0876     }
0877 }
0878 
0879 void KisMainWindow::slotThemeChanged()
0880 {
0881     KConfigGroup group(KSharedConfig::openConfig(), "theme");
0882 
0883     if (group.readEntry("Theme", "") == d->themeManager->currentThemeName()) return;
0884 
0885     // save theme changes instantly
0886     group.writeEntry("Theme", d->themeManager->currentThemeName());
0887 
0888     updateTheme();
0889 
0890     // Make the other top level windows update as well
0891     Q_FOREACH (QWidget* topLevelWidget, qApp->topLevelWidgets()) {
0892         if (topLevelWidget == this) {
0893             // Skip if the current top level widget is this window
0894             continue;
0895         }
0896         if (topLevelWidget->isHidden()) {
0897             // Skip unwanted top level widgets like menus, dropdowns, tooltips, etc.
0898             continue;
0899         }
0900         KisMainWindow *topLevelMainWindow = qobject_cast<KisMainWindow*>(topLevelWidget);
0901         if (topLevelMainWindow) {
0902             topLevelMainWindow->updateTheme();
0903             emit topLevelMainWindow->themeChanged();
0904         } else {
0905             QObjectList objects;
0906             objects.append(topLevelWidget);
0907             while (!objects.isEmpty()) {
0908                 QWidget* widget = qobject_cast<QWidget*>(objects.takeFirst());
0909                 if (widget) {
0910                     objects.append(widget->children());
0911                     KisIconUtils::updateIconCommon(widget);
0912                 }
0913             }
0914         }
0915     }
0916 
0917     emit themeChanged();
0918 }
0919 
0920 void KisMainWindow::customizeTabBar()
0921 {
0922     // update MDI area theme
0923     // Tab close button override
0924     // just switch this icon out for all OSs so it is easier to see
0925     QString closeButtonImageUrl;
0926     QString closeButtonHoverColor;
0927     if (KisIconUtils::useDarkIcons()) {
0928         closeButtonImageUrl = QStringLiteral(":/dark_close-tab.svg");
0929         closeButtonHoverColor = QStringLiteral("lightcoral");
0930     } else {
0931         closeButtonImageUrl = QStringLiteral(":/light_close-tab.svg");
0932         closeButtonHoverColor = QStringLiteral("darkred");
0933     }
0934     QString tabStyleSheet = QStringLiteral(R"(
0935             QTabBar::close-button {
0936                 image: url(%1);
0937                 padding-top: 3px;
0938             }
0939             QTabBar::close-button:hover {
0940                 background-color: %2;
0941             }
0942             QTabBar::close-button:pressed {
0943                 background-color: red;
0944             }
0945 
0946             QHeaderView::section {
0947                 padding: 7px;
0948             }
0949 
0950            )")
0951            .arg(closeButtonImageUrl, closeButtonHoverColor);
0952 
0953 
0954     QTabBar* tabBar = d->findTabBarHACK();
0955     if (tabBar) {
0956         tabBar->setStyleSheet(tabStyleSheet);
0957     }
0958 
0959 }
0960 
0961 bool KisMainWindow::canvasDetached() const
0962 {
0963     return centralWidget() != d->widgetStack;
0964 }
0965 
0966 void KisMainWindow::setCanvasDetached(bool detach)
0967 {
0968 #ifdef Q_OS_ANDROID
0969     if (detach) {
0970         QMessageBox::warning(this, i18nc("@title:window", "Krita"),
0971                              "Detach Canvas is unsupported on Android");
0972     }
0973 #else
0974     if (detach == canvasDetached()) return;
0975 
0976     QWidget *outgoingWidget = centralWidget() ? takeCentralWidget() : nullptr;
0977     QWidget *incomingWidget = d->canvasWindow->swapMainWidget(outgoingWidget);
0978 
0979     if (incomingWidget) {
0980         setCentralWidget(incomingWidget);
0981     }
0982 
0983     if (detach) {
0984         d->canvasWindow->show();
0985     } else {
0986         d->canvasWindow->hide();
0987     }
0988     d->toggleDetachCanvas->setChecked(detach);
0989 #endif
0990 }
0991 
0992 QWidget * KisMainWindow::canvasWindow() const
0993 {
0994     return d->canvasWindow;
0995 }
0996 
0997 void KisMainWindow::setReadWrite(bool readwrite)
0998 {
0999     d->saveAction->setEnabled(readwrite);
1000     d->importFile->setEnabled(readwrite);
1001 }
1002 
1003 void KisMainWindow::clearRecentFiles()
1004 {
1005     KisRecentFilesManager::instance()->clear();
1006 }
1007 
1008 void KisMainWindow::removeRecentFile(QString url)
1009 {
1010     KisRecentFilesManager::instance()->remove(QUrl::fromLocalFile(url));
1011 }
1012 
1013 void KisMainWindow::slotUpdateSaveActionTitle(const QString &documentPath)
1014 {
1015     const QString fileName = QFileInfo(documentPath).fileName();
1016 
1017     if (!fileName.isEmpty()) {
1018         d->saveAction->setToolTip(i18n("Save as %1", fileName));
1019     }
1020     else {
1021         d->saveAction->setToolTip(i18n("Save"));
1022     }
1023 }
1024 
1025 KisView *KisMainWindow::activeView() const
1026 {
1027     if (d->activeView) {
1028         return d->activeView;
1029     }
1030     return 0;
1031 }
1032 
1033 bool KisMainWindow::openDocument(const QString &path, OpenFlags flags)
1034 {
1035     ScopedWidgetDisabler disabler(d->welcomePage->dropFrameBorder);
1036     QApplication::processEvents(); // make UI more responsive
1037 
1038     if (!QFile(path).exists()) {
1039         if (!(flags & BatchMode)) {
1040             QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", path));
1041         }
1042         KisRecentFilesManager::instance()->remove(QUrl::fromLocalFile(path)); //remove the file from the recent-opened-file-list
1043         return false;
1044     }
1045     return openDocumentInternal(path, flags);
1046 }
1047 
1048 bool KisMainWindow::openDocumentInternal(const QString &path, OpenFlags flags)
1049 {
1050     if (!QFile(path).exists()) {
1051         qWarning() << "KisMainWindow::openDocumentInternal. Could not open:" << path;
1052         return false;
1053     }
1054 
1055     KisDocument *newdoc = KisPart::instance()->createDocument();
1056 
1057     if (flags & BatchMode) {
1058         newdoc->setFileBatchMode(true);
1059     }
1060 
1061     d->firstTime = true;
1062     connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1063     connect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1064 
1065     KisDocument::OpenFlags openFlags = KisDocument::None;
1066     // XXX: Why this duplication of OpenFlags...
1067     if (flags & RecoveryFile) {
1068         openFlags |= KisDocument::RecoveryFile;
1069     }
1070 
1071     bool openRet = !(flags & Import) ? newdoc->openPath(path, openFlags) : newdoc->importDocument(path);
1072 
1073     if (!openRet) {
1074         delete newdoc;
1075         return false;
1076     }
1077 
1078     KisPart::instance()->addDocument(newdoc);
1079 
1080     // Try to determine whether this was an unnamed autosave
1081     if (flags & RecoveryFile &&
1082             (   path.startsWith(QDir::tempPath())
1083                 || path.startsWith(QDir::homePath())
1084                 ) &&
1085             (      QFileInfo(path).fileName().startsWith(".krita")
1086                    || QFileInfo(path).fileName().startsWith("krita")
1087                    )
1088             )
1089     {
1090         QString path = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1091         if (!QFileInfo(path).exists()) {
1092             path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
1093         }
1094         newdoc->setPath(path + "/" + newdoc->objectName() + ".kra");
1095     }
1096 
1097     return true;
1098 }
1099 
1100 void KisMainWindow::showDocument(KisDocument *document) {
1101     Q_FOREACH(QMdiSubWindow *subwindow, d->mdiArea->subWindowList()) {
1102         KisView *view = qobject_cast<KisView*>(subwindow->widget());
1103         KIS_SAFE_ASSERT_RECOVER_NOOP(view);
1104 
1105         if (view) {
1106             if (view->document() == document) {
1107                 setActiveSubWindow(subwindow);
1108                 return;
1109             }
1110         }
1111     }
1112 
1113     addViewAndNotifyLoadingCompleted(document);
1114 }
1115 
1116 KisView* KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document,
1117                                                          QMdiSubWindow *subWindow)
1118 {
1119     showWelcomeScreen(false); // see workaround in function header
1120 
1121     KisView *view = KisPart::instance()->createView(document, d->viewManager, this);
1122     addView(view, subWindow);
1123 
1124     emit guiLoadingFinished();
1125 
1126     return view;
1127 }
1128 
1129 QStringList KisMainWindow::showOpenFileDialog(bool isImporting)
1130 {
1131     KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument");
1132     dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1133     dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
1134     dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images"));
1135 
1136     QStringList filenames = dialog.filenames();
1137     filenames.sort();
1138 
1139     return filenames;
1140 }
1141 
1142 // Separate from openDocument to handle async loading (remote URLs)
1143 void KisMainWindow::slotLoadCompleted()
1144 {
1145     KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
1146     if (newdoc && newdoc->image()) {
1147         addViewAndNotifyLoadingCompleted(newdoc);
1148 
1149         disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1150         disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1151 
1152         emit loadCompleted();
1153     }
1154 }
1155 
1156 void KisMainWindow::slotLoadCanceled(const QString &errMsg)
1157 {
1158     KisUsageLogger::log(QString("Loading canceled: %1.").arg(errMsg));
1159     KisDocument* doc = qobject_cast<KisDocument*>(sender());
1160     Q_ASSERT(doc);
1161     disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1162     disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1163 }
1164 
1165 void KisMainWindow::slotSaveCanceled(const QString &errMsg)
1166 {
1167     if (!errMsg.isEmpty()) {   // empty when cancelled by user
1168         KisUsageLogger::log(QString("Saving cancelled. Error:").arg(errMsg));
1169         QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
1170     }
1171     else {
1172         KisUsageLogger::log(QString("Saving cancelled by the user."));
1173     }
1174     slotSaveCompleted();
1175 }
1176 
1177 void KisMainWindow::slotSaveCompleted()
1178 {
1179     KisUsageLogger::log(QString("Saving Completed"));
1180     KisDocument* doc = qobject_cast<KisDocument*>(sender());
1181     if (doc) {
1182         disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
1183         disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
1184     }
1185 
1186     if (d->deferredClosingEvent) {
1187         KXmlGuiWindow::closeEvent(d->deferredClosingEvent);
1188     }
1189 }
1190 
1191 bool KisMainWindow::hackIsSaving() const
1192 {
1193     std::unique_lock<QMutex> l(d->savingEntryMutex, std::try_to_lock);
1194     return !l.owns_lock();
1195 }
1196 
1197 bool KisMainWindow::installBundle(const QString &fileName) const
1198 {
1199     QFileInfo from(fileName);
1200     QFileInfo to(QStringLiteral("%1/%2").arg(KoResourcePaths::getAppDataLocation(), from.fileName()));
1201     if (to.exists()) {
1202         QFile::remove(to.canonicalFilePath());
1203     }
1204     return QFile::copy(fileName, to.absoluteFilePath());
1205 }
1206 
1207 QImage KisMainWindow::layoutThumbnail()
1208 {
1209     int size = 256;
1210     qreal scale = qreal(size)/qreal(qMax(geometry().width(), geometry().height()));
1211     QImage layoutThumbnail = QImage(qRound(geometry().width()*scale), qRound(geometry().height()*scale), QImage::Format_ARGB32);
1212     QPainter gc(&layoutThumbnail);
1213     gc.fillRect(0, 0, layoutThumbnail.width(), layoutThumbnail.height(), this->palette().dark());
1214 
1215     Q_FOREACH(const QObject *child, children()) {
1216         if (child->isWidgetType()) {
1217             const QWidget *w = static_cast<const QWidget *>(child);
1218 
1219             if (w->isVisible() && !w->property("_kis_excludeFromLayoutThumbnail").toBool()) {
1220                 QRect wRect = QRectF(w->geometry().x()*scale
1221                                      , w->geometry().y()*scale
1222                                      , w->geometry().width()*scale
1223                                      , w->geometry().height()*scale
1224                                      ).toRect();
1225 
1226                 wRect = wRect.intersected(layoutThumbnail.rect().adjusted(-1, -1, -1, -1));
1227 
1228                 gc.setBrush(this->palette().window());
1229                 if (w == d->widgetStack) {
1230                     gc.setBrush(d->mdiArea->background());
1231                 }
1232                 gc.setPen(this->palette().windowText().color());
1233                 gc.drawRect(wRect);
1234             }
1235         }
1236     }
1237     gc.end();
1238     return layoutThumbnail;
1239 }
1240 
1241 bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool isExporting, bool isAdvancedExporting )
1242 {
1243     if (!document) {
1244         return true;
1245     }
1246 
1247     /**
1248      * Make sure that we cannot enter this method twice!
1249      *
1250      * The lower level functions may call processEvents() so
1251      * double-entry is quite possible to achieve. Here we try to lock
1252      * the mutex, and if it is failed, just cancel saving.
1253      */
1254     std::unique_lock<QMutex> l(d->savingEntryMutex, std::try_to_lock);
1255     if (!l.owns_lock()) return false;
1256 
1257     // no busy wait for saving because it is dangerous!
1258     KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this);
1259     dlg.blockIfImageIsBusy();
1260 
1261     if (dlg.result() == KisDelayedSaveDialog::Rejected) {
1262         return false;
1263     }
1264     else if (dlg.result() == KisDelayedSaveDialog::Ignored) {
1265         QMessageBox::critical(qApp->activeWindow(),
1266                               i18nc("@title:window", "Krita"),
1267                               i18n("You are saving a file while the image is "
1268                                    "still rendering. The saved file may be "
1269                                    "incomplete or corrupted.\n\n"
1270                                    "Please select a location where the original "
1271                                    "file will not be overridden!"));
1272 
1273 
1274         saveas = true;
1275     }
1276 
1277     if (document->isRecovered()) {
1278         saveas = true;
1279     }
1280 
1281     if (document->path().isEmpty()) {
1282         saveas = true;
1283     }
1284 
1285     connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
1286     connect(document, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
1287 
1288     QByteArray nativeFormat = document->nativeFormatMimeType();
1289     QByteArray oldMimeFormat = document->mimeType();
1290 
1291     QUrl suggestedURL = QUrl::fromLocalFile(document->path());
1292 
1293     QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
1294 
1295     mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
1296     if (!mimeFilter.contains(oldMimeFormat)) {
1297         dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat;
1298 
1299         // --- don't setOutputMimeType in case the user cancels the Save As
1300         // dialog and then tries to just plain Save ---
1301 
1302         // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :))
1303         QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1304 
1305         if (!suggestedFilename.isEmpty()) {  // ".kra" looks strange for a name
1306             suggestedFilename = suggestedFilename + "." + KisMimeDatabase::suffixesForMimeType(KIS_MIME_TYPE).first();
1307             suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename);
1308             suggestedURL.setPath(suggestedURL.path() + suggestedFilename);
1309         }
1310 
1311         // force the user to choose outputMimeType
1312         saveas = true;
1313     }
1314 
1315     bool ret = false;
1316 
1317     if (document->path().isEmpty() || isExporting || saveas) {
1318         // if you're just File/Save As'ing to change filter options you
1319         // don't want to be reminded about overwriting files etc.
1320         bool justChangingFilterOptions = false;
1321 
1322         KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveAs");
1323         dialog.setCaption(isExporting ? i18n("Exporting") : i18n("Saving As"));
1324 
1325         //qDebug() << ">>>>>" << isExporting << d->lastExportLocation << d->lastExportedFormat << QString::fromLatin1(document->mimeType());
1326 
1327         if (isExporting && !d->lastExportLocation.isEmpty() && !d->lastExportLocation.contains(QDir::tempPath())) {
1328 
1329             // Use the location where we last exported to, if it's set, as the opening location for the file dialog
1330             QString proposedPath = QFileInfo(d->lastExportLocation).absolutePath();
1331             // If the document doesn't have a filename yet, use the title
1332             QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo("title") :  QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1333             // Use the last mimetype we exported to by default
1334             QString proposedMimeType =  d->lastExportedFormat.isEmpty() ? "" : d->lastExportedFormat;
1335             QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,");
1336 
1337             // Set the default dir: this overrides the one loaded from the config file, since we're exporting and the lastExportLocation is not empty
1338             dialog.setDefaultDir(proposedPath + "/" + proposedFileName + "." + proposedExtension, true);
1339             dialog.setMimeTypeFilters(mimeFilter, proposedMimeType);
1340         }
1341         else {
1342             // Get the last used location for saving
1343             KConfigGroup group =  KSharedConfig::openConfig()->group("File Dialogs");
1344             QString proposedPath = group.readEntry("SaveAs", "");
1345             // if that is empty, get the last used location for loading
1346             if (proposedPath.isEmpty()) {
1347                 proposedPath = group.readEntry("OpenDocument", "");
1348             }
1349             // If that is empty, too, use the Pictures location.
1350             if (proposedPath.isEmpty()) {
1351                 proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1352             }
1353             // But only use that if the suggestedUrl, that is, the document's own url is empty, otherwise
1354             // open the location where the document currently is.
1355             dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(), true);
1356 
1357             // If exporting, default to all supported file types if user is exporting
1358             QByteArray default_mime_type = "";
1359             if (!isExporting) {
1360                 // otherwise use the document's mimetype, or if that is empty, kra, which is the safest.
1361                 default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType();
1362             }
1363             dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type));
1364         }
1365 
1366         QString newFilePath = dialog.filename();
1367 
1368         if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) {
1369             QString fn = newFilePath;
1370             QFileInfo info(fn);
1371             document->documentInfo()->setAboutInfo("title", info.completeBaseName());
1372         }
1373 
1374         QByteArray outputFormat = nativeFormat;
1375 
1376         QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newFilePath, false);
1377         outputFormat = outputFormatString.toLatin1();
1378 
1379 
1380         if (!isExporting) {
1381             justChangingFilterOptions = (newFilePath == document->path()) && (outputFormat == document->mimeType());
1382         }
1383         else {
1384             QString path = QFileInfo(d->lastExportLocation).absolutePath();
1385             QString filename = QFileInfo(document->path()).completeBaseName();
1386             justChangingFilterOptions = (QFileInfo(newFilePath).absolutePath() == path)
1387                     && (QFileInfo(newFilePath).completeBaseName() == filename)
1388                     && (outputFormat == d->lastExportedFormat);
1389         }
1390 
1391         bool bOk = true;
1392         if (newFilePath.isEmpty()) {
1393             bOk = false;
1394         }
1395 
1396         if (bOk) {
1397             bool wantToSave = true;
1398 
1399             // don't change this line unless you know what you're doing :)
1400             if (!justChangingFilterOptions) {
1401                 if (!document->isNativeFormat(outputFormat))
1402                     wantToSave = true;
1403             }
1404 
1405             if (wantToSave) {
1406                 if (!isExporting) {  // Save As
1407                     ret = document->saveAs(newFilePath, outputFormat, true);
1408                     if (ret) {
1409                         dbgUI << "Successful Save As!";
1410                         KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath));
1411                         setReadWrite(true);
1412                     } else {
1413                         dbgUI << "Failed Save As!";
1414 
1415                     }
1416                 }
1417                 else { // Export
1418                     ret = document->exportDocument(newFilePath, outputFormat, isAdvancedExporting);
1419                     if (ret) {
1420                         d->lastExportLocation = newFilePath;
1421                         d->lastExportedFormat = outputFormat;
1422                     }
1423                 }
1424 
1425             }   // if (wantToSave)  {
1426             else
1427                 ret = false;
1428         }   // if (bOk) {
1429         else
1430             ret = false;
1431     } else { // saving
1432         // We cannot "export" into the currently
1433         // opened document. We are not Gimp.
1434         KIS_ASSERT_RECOVER_NOOP(!isExporting);
1435 
1436         // be sure document has the correct outputMimeType!
1437         if (document->isModified()) {
1438             ret = document->save(true, 0);
1439         }
1440 
1441         if (!ret) {
1442             dbgUI << "Failed Save!";
1443         }
1444     }
1445 
1446     return ret;
1447 }
1448 
1449 void KisMainWindow::undo()
1450 {
1451     if (activeView()) {
1452         activeView()->document()->undoStack()->undo();
1453     }
1454 }
1455 
1456 void KisMainWindow::redo()
1457 {
1458     if (activeView()) {
1459         activeView()->document()->undoStack()->redo();
1460     }
1461 }
1462 
1463 void KisMainWindow::closeEvent(QCloseEvent *e)
1464 {
1465     if (hackIsSaving()) {
1466         e->setAccepted(false);
1467         return;
1468     }
1469 
1470     if (!KisPart::instance()->closingSession()) {
1471         QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
1472         if ((action) && (action->isChecked())) {
1473             action->setChecked(false);
1474         }
1475 
1476         // Save session when last window is closed
1477         if (KisPart::instance()->mainwindowCount() == 1) {
1478             bool closeAllowed = KisPart::instance()->closeSession();
1479 
1480             if (!closeAllowed) {
1481                 e->setAccepted(false);
1482                 return;
1483             }
1484         }
1485     }
1486 
1487     d->mdiArea->closeAllSubWindows();
1488 
1489     QList<QMdiSubWindow*> childrenList = d->mdiArea->subWindowList();
1490 
1491     if (childrenList.isEmpty()) {
1492         // TODO: that assignment looks really suspicious, because `e`
1493         // is destroyed right after the exit of this function
1494         d->deferredClosingEvent = e;
1495         d->canvasWindow->close();
1496     } else {
1497         e->setAccepted(false);
1498     }
1499 }
1500 
1501 void KisMainWindow::saveWindowSettings()
1502 {
1503     KSharedConfigPtr config =  KSharedConfig::openConfig();
1504 
1505     if (d->windowSizeDirty ) {
1506         dbgUI << "KisMainWindow::saveWindowSettings";
1507         KConfigGroup group = d->windowStateConfig;
1508         KWindowConfig::saveWindowSize(windowHandle(), group);
1509         config->sync();
1510         d->windowSizeDirty = false;
1511     }
1512 
1513     if (!d->activeView || d->activeView->document()) {
1514 
1515         // Save toolbar position into the config file of the app, under the doc's component name
1516         KConfigGroup group = d->windowStateConfig;
1517         saveMainWindowSettings(group);
1518 
1519         // Save state of dock widgets
1520         for (QMap<QString, QDockWidget*>::const_iterator i = d->dockWidgetsMap.constBegin();
1521              i != d->dockWidgetsMap.constEnd(); ++i) {
1522             if (i.value()->widget()) {
1523                 KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key());
1524                 dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool());
1525                 dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value()));
1526                 dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x());
1527                 dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y());
1528 
1529                 dockGroup.writeEntry("width", (int) i.value()->widget()->width());
1530                 dockGroup.writeEntry("height", (int) i.value()->widget()->height());
1531             }
1532         }
1533 
1534     }
1535 
1536     KSharedConfig::openConfig()->sync();
1537     resetAutoSaveSettings(); // Don't let KisKMainWindow override the good stuff we wrote down
1538 
1539 }
1540 
1541 void KisMainWindow::resizeEvent(QResizeEvent * e)
1542 {
1543     d->windowSizeDirty = true;
1544     KXmlGuiWindow::resizeEvent(e);
1545 }
1546 
1547 
1548 void KisMainWindow::dragMoveEvent(QDragMoveEvent *event)
1549 {
1550     dragMove(event);
1551     event->accept();
1552 }
1553 
1554 void KisMainWindow::dragLeaveEvent(QDragLeaveEvent *event)
1555 {
1556     dragLeave();
1557     event->accept();
1558 }
1559 
1560 bool KisMainWindow::windowsLayoutSavingAllowed() const
1561 {
1562     QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
1563     return !action || !action->isChecked();
1564 }
1565 
1566 void KisMainWindow::showEvent(QShowEvent *event)
1567 {
1568     // we're here because, we need to make sure everything (dockers, toolbars etc) is loaded and ready before
1569     // we can hide it.
1570     if (!event->spontaneous()) {
1571         setMainWindowLayoutForCurrentMainWidget(d->widgetStack->currentIndex(), false);
1572     }
1573     return KXmlGuiWindow::showEvent(event);
1574 }
1575 
1576 void KisMainWindow::setMainWindowLayoutForCurrentMainWidget(int widgetIndex, bool widgetIndexChanged)
1577 {
1578 
1579     if (widgetIndex == 0) {
1580         if (widgetIndexChanged) {
1581             /// save the state of the window which existed up-to now (this is before we stop auto-saving).
1582             ///
1583             /// saving should happen if the call s has come from a real state change, but not from
1584             /// on-startup initialization
1585 
1586             if (d->mdiArea->subWindowList().isEmpty()) {
1587                 /**
1588                  * When closing the latest subwindow we should restore the
1589                  * canvas-only mode to save stuff properly
1590                  */
1591                 saveWindowState(true);
1592             } else {
1593                 saveMainWindowSettings(d->windowStateConfig);
1594             }
1595         }
1596         adjustLayoutForWelcomePage();
1597     }
1598     else {
1599         setAutoSaveSettings(d->windowStateConfig, false);
1600         statusBar()->setVisible(KisConfig(true).showStatusBar());
1601     }
1602 
1603     QList<QAction *> actions = d->dockWidgetMenu->menu()->actions();
1604     actions.append(toolBarMenuAction()->menu()->actions());
1605     for (QAction *action : actions) {
1606         if (action) {
1607             action->setEnabled(widgetIndex);
1608         }
1609     }
1610 }
1611 
1612 void KisMainWindow::adjustLayoutForWelcomePage()
1613 {
1614     // This makes sure we don't save window state when we're in welcome page mode, because all the dockers
1615     // etc are hidden while the user is here.
1616     resetAutoSaveSettings();
1617 
1618     toggleDockersVisibility(false, true);
1619     if (statusBar()) {
1620         statusBar()->hide();
1621     }
1622     QList<QToolBar *> toolbars = findChildren<QToolBar *>();
1623     for (QToolBar *toolbar : toolbars) {
1624         if (toolbar->objectName() == "BrushesAndStuff" || toolbar->objectName() == "editToolBar") {
1625             toolbar->hide();
1626         }
1627     }
1628 }
1629 
1630 void KisMainWindow::setActiveView(KisView* view)
1631 {
1632     d->activeView = view;
1633 
1634     if (d->undoActionsUpdateManager) {
1635         d->undoActionsUpdateManager->setCurrentDocument(view ? view->document() : 0);
1636     }
1637 
1638     d->viewManager->setCurrentView(view);
1639 
1640     d->activeViewConnections.clear();
1641     d->activeViewConnections.addConnection(view->document(),
1642                                            SIGNAL(sigPathChanged(QString)),
1643                                            this, SLOT(slotUpdateSaveActionTitle(QString)));
1644     slotUpdateSaveActionTitle(view->document()->path());
1645 
1646     KisWindowLayoutManager::instance()->activeDocumentChanged(view->document());
1647 
1648     emit activeViewChanged();
1649 }
1650 
1651 void KisMainWindow::unsetActiveView()
1652 {
1653     d->activeViewConnections.clear();
1654     slotUpdateSaveActionTitle(QString());
1655 }
1656 
1657 void KisMainWindow::dragMove(QDragMoveEvent * event)
1658 {
1659     QTabBar *tabBar = d->findTabBarHACK();
1660 
1661     if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) {
1662         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)!";
1663     }
1664 
1665     if (tabBar && tabBar->isVisible()) {
1666         QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos()));
1667         if (tabBar->rect().contains(pos)) {
1668             const int tabIndex = tabBar->tabAt(pos);
1669 
1670             if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) {
1671                 d->tabSwitchCompressor->start(tabIndex);
1672             }
1673         } else if (d->tabSwitchCompressor->isActive()) {
1674             d->tabSwitchCompressor->stop();
1675         }
1676     }
1677 }
1678 
1679 void KisMainWindow::dragLeave()
1680 {
1681     if (d->tabSwitchCompressor->isActive()) {
1682         d->tabSwitchCompressor->stop();
1683     }
1684 }
1685 
1686 
1687 void KisMainWindow::switchTab(int index)
1688 {
1689     QTabBar *tabBar = d->findTabBarHACK();
1690     if (!tabBar) return;
1691 
1692     tabBar->setCurrentIndex(index);
1693 }
1694 
1695 void KisMainWindow::showWelcomeScreen(bool show)
1696 {
1697     const int currentIndex = show ? 0 : 1;
1698     if (d->widgetStack->currentIndex() != currentIndex) {
1699         setUpdatesEnabled(false);
1700         // These have to be done in different sequence to avoid graphical
1701         // layout glitch during the switch.
1702         if (show) {
1703             setMainWindowLayoutForCurrentMainWidget(currentIndex, true);
1704             d->widgetStack->setCurrentIndex(currentIndex);
1705         } else {
1706             d->widgetStack->setCurrentIndex(currentIndex);
1707             setMainWindowLayoutForCurrentMainWidget(currentIndex, true);
1708         }
1709         setUpdatesEnabled(true);
1710     }
1711 }
1712 
1713 void KisMainWindow::slotFileNew()
1714 {
1715     const QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import);
1716 
1717     KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/"));
1718     startupWidget->setWindowModality(Qt::WindowModal);
1719     startupWidget->setWindowTitle(i18n("Create new document"));
1720 
1721 
1722     KisConfig cfg(true);
1723 
1724     int w = cfg.defImageWidth();
1725     int h = cfg.defImageHeight();
1726     const double resolution = cfg.defImageResolution();
1727     const QString colorModel = cfg.defColorModel();
1728     const QString colorDepth = cfg.defaultColorDepth();
1729     const QString colorProfile = cfg.defColorProfile();
1730 
1731 
1732     CustomDocumentWidgetItem item;
1733     item.widget = new KisCustomImageWidget(startupWidget,
1734                                            w,
1735                                            h,
1736                                            resolution,
1737                                            colorModel,
1738                                            colorDepth,
1739                                            colorProfile,
1740                                            i18n("Unnamed"));
1741 
1742     item.icon = "document-new";
1743     item.title = i18n("Custom Document");
1744     startupWidget->addCustomDocumentWidget(item.widget, item.title, "Custom Document", item.icon);
1745 
1746     item.widget = new KisImageFromClipboardWidget(startupWidget,
1747                                             0,
1748                                             0,
1749                                             resolution,
1750                                             colorModel,
1751                                             colorDepth,
1752                                             colorProfile,
1753                                             i18n("Unnamed"));
1754 
1755     item.title = i18n("Create from Clipboard");
1756     item.icon = "tab-new";
1757 
1758     startupWidget->addCustomDocumentWidget(item.widget, item.title, "Create from ClipBoard", item.icon);
1759 
1760     connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*)));
1761     connect(startupWidget, SIGNAL(openTemplate(QUrl)), KisPart::instance(), SLOT(openTemplate(QUrl)));
1762 
1763     startupWidget->exec();
1764     startupWidget->deleteLater();
1765 }
1766 
1767 void KisMainWindow::slotImportFile()
1768 {
1769     dbgUI << "slotImportFile()";
1770     slotFileOpen(true);
1771 }
1772 
1773 
1774 void KisMainWindow::slotFileOpen(bool isImporting)
1775 {
1776     QStringList urls = showOpenFileDialog(isImporting);
1777 
1778     if (urls.isEmpty())
1779         return;
1780 
1781     Q_FOREACH (const QString& url, urls) {
1782 
1783         if (!url.isEmpty()) {
1784             OpenFlags flags = isImporting ? Import : None;
1785             bool res = openDocument(url, flags);
1786             if (!res) {
1787                 warnKrita << "Loading" << url << "failed";
1788             }
1789         }
1790     }
1791 }
1792 
1793 void KisMainWindow::slotFileOpenRecent(const QUrl &url)
1794 {
1795     (void) openDocument(url.toLocalFile(), None);
1796 }
1797 
1798 void KisMainWindow::slotFileSave()
1799 {
1800     if (saveDocument(d->activeView->document(), false, false,false)) {
1801         emit documentSaved();
1802     }
1803 }
1804 
1805 void KisMainWindow::slotFileSaveAs()
1806 {
1807     if (saveDocument(d->activeView->document(), true, false,false)) {
1808         emit documentSaved();
1809     }
1810 }
1811 
1812 void KisMainWindow::slotExportFile()
1813 {
1814     if (saveDocument(d->activeView->document(), true, true,false)) {
1815         emit documentSaved();
1816     }
1817 }
1818 void KisMainWindow::slotExportAdvance()
1819 {
1820     if (saveDocument(d->activeView->document(), true, true,true)) {
1821         emit documentSaved();
1822     }
1823 }
1824 
1825 void KisMainWindow::slotShowSessionManager() {
1826     KisPart::instance()->showSessionManager();
1827 }
1828 
1829 KoCanvasResourceProvider *KisMainWindow::resourceManager() const
1830 {
1831     return d->viewManager->canvasResourceProvider()->resourceManager();
1832 }
1833 
1834 int KisMainWindow::viewCount() const
1835 {
1836     return d->mdiArea->subWindowList().size();
1837 }
1838 
1839 const KConfigGroup &KisMainWindow::windowStateConfig() const
1840 {
1841     return d->windowStateConfig;
1842 }
1843 
1844 void KisMainWindow::saveWindowState(bool restoreNormalState)
1845 {
1846     // We don't need to save welcome page's layout
1847     if (d->widgetStack->currentIndex() == 0) {
1848         // TODO(sh_zam): We should still save position/geometry, right?
1849         return;
1850     }
1851 
1852     if (restoreNormalState) {
1853         QAction *showCanvasOnly = d->viewManager->actionCollection()->action("view_show_canvas_only");
1854 
1855         if (showCanvasOnly && showCanvasOnly->isChecked()) {
1856             showCanvasOnly->setChecked(false);
1857         }
1858 
1859         d->windowStateConfig.writeEntry("ko_geometry", saveGeometry().toBase64());
1860         d->windowStateConfig.writeEntry("State", saveState().toBase64());
1861 
1862         // if the dockers are hidden at this time, save their state.
1863         if (!d->toggleDockers->isChecked()) {
1864             restoreState(d->dockerStateBeforeHiding);
1865         }
1866 
1867         statusBar()->setVisible(true);
1868         menuBar()->setVisible(true);
1869 
1870         saveWindowSettings();
1871 
1872     } else {
1873         saveMainWindowSettings(d->windowStateConfig);
1874     }
1875 
1876 }
1877 
1878 bool KisMainWindow::restoreWorkspaceState(const QByteArray &state)
1879 {
1880     QByteArray oldState = saveState();
1881     const bool showTitlebars = KisConfig(false).showDockerTitleBars();
1882 
1883     // needed because otherwise the layout isn't correctly restored in some situations
1884     Q_FOREACH (QDockWidget *dock, dockWidgets()) {
1885         if (dock) {
1886             dock->setProperty("Locked", false); // Unlock invisible dockers
1887             dock->toggleViewAction()->setEnabled(true);
1888             dock->hide();
1889             if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
1890                 dock->titleBarWidget()->setVisible(showTitlebars);
1891             }
1892         }
1893     }
1894 
1895     bool success = KXmlGuiWindow::restoreState(state);
1896 
1897     if (!success) {
1898         KXmlGuiWindow::restoreState(oldState);
1899         Q_FOREACH (QDockWidget *dock, dockWidgets()) {
1900             if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
1901                 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
1902             }
1903         }
1904         return false;
1905     }
1906     return success;
1907 }
1908 
1909 void KisMainWindow::restoreWorkspace()
1910 {
1911     QString md5 = sender()->property("md5").toString();
1912     KoResourceServer<KisWorkspaceResource> *rserver = KisResourceServerProvider::instance()->workspaceServer();
1913     KoResourceSP resource = rserver->resource(md5, "", "");
1914     if (resource) {
1915         restoreWorkspace(resource);
1916     }
1917     else {
1918         qWarning() << "Could not retrieve resource for" << md5;
1919     }
1920 }
1921 
1922 void KisMainWindow::openCommandBar()
1923 {
1924     QList<KisKActionCollection *> actionCollections;
1925 
1926     auto clients = guiFactory()->clients();
1927     int actionsCount = 0;
1928     for (const KisKXMLGUIClient *c : clients) {
1929         if (!c) {
1930             continue;
1931         }
1932         if (auto collection = c->actionCollection()) {
1933             actionCollections.append(collection);
1934             actionsCount += collection->count();
1935         }
1936     }
1937 
1938     if (activeKisView()) {
1939         KisKActionCollection *layerActionCollection = new KisKActionCollection(0, "layeractions (disposable)");
1940         layerActionCollection->setComponentDisplayName(i18n("Layers/Masks"));
1941         KisNodeActivationActionCreatorVisitor v(layerActionCollection, viewManager()->nodeManager());
1942         activeKisView()->image()->rootLayer()->accept(v);
1943         actionCollections.append(layerActionCollection);
1944         actionsCount += layerActionCollection->count();
1945     }
1946 
1947     d->commandBar->updateBar(actionCollections, actionsCount);
1948 
1949     // The following line is needed to work around input method not working
1950     // on Windows.
1951     // See https://bugs.kde.org/show_bug.cgi?id=395598
1952     // and https://bugs.kde.org/show_bug.cgi?id=438122
1953     d->commandBar->activateWindow();
1954 
1955     // The following line is present in Kate's version and was ported over
1956     // but I am sceptical of its use. I worry that it may subtly cause other
1957     // issues, and since the command bar appears to work fine without it, I
1958     // believe it may be better to leave it out.  -- Alvin
1959     // centralWidget()->setFocusProxy(d->commandBar);
1960 }
1961 
1962 void KisMainWindow::slotStoragesWarning(const QString &/*location*/)
1963 {
1964     QString warning;
1965     if (!checkActiveBundlesAvailable()) {
1966         warning = i18n("You don't have any resource bundles enabled.");
1967     }
1968 
1969     if (!checkPaintOpAvailable()) {
1970         warning += i18n("\nThere are no brush presets available. Please enable a bundle that has presets before continuing.\n");
1971         QMessageBox::critical(this, i18nc("@title:window", "Krita"), warning);
1972 
1973         QAction *action = actionCollection()->action("manage_bundles");
1974         if (action) {
1975             action->trigger();
1976         }
1977     }
1978 
1979     if (!checkActiveBundlesAvailable()) {
1980         QMessageBox::warning(this, i18nc("@title:window", "Krita"), warning + i18n("\nOnly your local resources are available."));
1981     }
1982 
1983 }
1984 
1985 bool KisMainWindow::restoreWorkspace(KoResourceSP res)
1986 {
1987     KisWorkspaceResourceSP workspace = res.dynamicCast<KisWorkspaceResource>();
1988 
1989     bool success = restoreWorkspaceState(workspace->dockerState());
1990 
1991     const bool showTitlebars = KisConfig(false).showDockerTitleBars();
1992     Q_FOREACH (QDockWidget *dock, dockWidgets()) {
1993         if (dock->titleBarWidget()) {
1994             dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
1995         }
1996     }
1997 
1998     if (activeKisView()) {
1999         activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace);
2000     }
2001 
2002     d->viewManager->notifyWorkspaceLoaded();
2003 
2004     return success;
2005 }
2006 
2007 QByteArray KisMainWindow::borrowWorkspace(KisMainWindow *other)
2008 {
2009     QByteArray currentWorkspace = saveState();
2010 
2011     if (!d->workspaceBorrowedBy.isNull()) {
2012         if (other->id() == d->workspaceBorrowedBy) {
2013             // We're swapping our original workspace back
2014             d->workspaceBorrowedBy = QUuid();
2015             return currentWorkspace;
2016         } else {
2017             // Get our original workspace back before swapping with a third window
2018             KisMainWindow *borrower = KisPart::instance()->windowById(d->workspaceBorrowedBy);
2019             if (borrower) {
2020                 QByteArray originalLayout = borrower->borrowWorkspace(this);
2021                 borrower->restoreWorkspaceState(currentWorkspace);
2022 
2023                 d->workspaceBorrowedBy = other->id();
2024                 return originalLayout;
2025             }
2026         }
2027     }
2028 
2029     d->workspaceBorrowedBy = other->id();
2030     return currentWorkspace;
2031 }
2032 
2033 void KisMainWindow::swapWorkspaces(KisMainWindow *a, KisMainWindow *b)
2034 {
2035     QByteArray workspaceA = a->borrowWorkspace(b);
2036     QByteArray workspaceB = b->borrowWorkspace(a);
2037 
2038     a->restoreWorkspaceState(workspaceB);
2039     b->restoreWorkspaceState(workspaceA);
2040 }
2041 
2042 KisViewManager *KisMainWindow::viewManager() const
2043 {
2044     return d->viewManager;
2045 }
2046 
2047 void KisMainWindow::slotDocumentInfo()
2048 {
2049     if (!d->activeView->document())
2050         return;
2051 
2052     KoDocumentInfo *docInfo = d->activeView->document()->documentInfo();
2053 
2054     if (!docInfo)
2055         return;
2056 
2057     KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo);
2058 
2059     if (dlg->exec()) {
2060         if (dlg->isDocumentSaved()) {
2061             d->activeView->document()->setModified(false);
2062         } else {
2063             d->activeView->document()->setModified(true);
2064         }
2065     }
2066 
2067     delete dlg;
2068 }
2069 
2070 bool KisMainWindow::slotFileCloseAll()
2071 {
2072     Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2073         if (subwin) {
2074             if(!subwin->close())
2075                 return false;
2076         }
2077     }
2078 
2079     return true;
2080 }
2081 
2082 void KisMainWindow::slotFileQuit()
2083 {
2084     // Do not close while KisMainWindow has the savingEntryMutex locked, bug409395.
2085     // After the background saving job is initiated, KisDocument blocks closing
2086     // while it saves itself.
2087     if (hackIsSaving()) {
2088         return;
2089     }
2090     KisPart::instance()->closeSession();
2091 }
2092 
2093 void KisMainWindow::importAnimation()
2094 {
2095     if (!activeView()) return;
2096 
2097     KisDocument *document = activeView()->document();
2098     if (!document) return;
2099 
2100     KisDlgImportImageSequence dlg(this, document);
2101 
2102     if (dlg.exec() == QDialog::Accepted) {
2103         QStringList files = dlg.files();
2104         int firstFrame = dlg.firstFrame();
2105         int step = dlg.step();
2106         bool startFrom1 = dlg.startFrom1();
2107         bool autoAddHoldframes = dlg.autoAddHoldframes();
2108 
2109 
2110         KoUpdaterPtr updater =
2111                 !document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0;
2112         KisAnimationImporter importer(document->image(), updater);
2113         int isAscending = dlg.isAscending();
2114         KisImportExportErrorCode status = importer.import(files, firstFrame, step, autoAddHoldframes, startFrom1, isAscending);  // modify here, add a flag
2115 
2116         if (!status.isOk() && !status.isInternalError()) {
2117             QString msg = status.errorMessage();
2118             if (!msg.isEmpty())
2119                 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
2120         }
2121         activeView()->canvasBase()->refetchDataFromImage();
2122     }
2123 }
2124 
2125 void KisMainWindow::importVideoAnimation()
2126 {
2127     KisDocument *document;
2128     KisDlgImportVideoAnimation dlg(this, activeView());
2129 
2130     if (dlg.exec() == QDialog::Accepted) {
2131         const QTemporaryDir outputLocation(QDir::tempPath() + QDir::separator() + "krita" + QDir::separator() + "import_files");
2132         RenderedFrames renderedFrames = dlg.renderFrames(QDir(outputLocation.path()));
2133         dbgFile << "Frames rendered to directory: " << outputLocation.path();
2134         QStringList documentInfoList = dlg.documentInfo();
2135 
2136         if (renderedFrames.isEmpty()) return;
2137         
2138         dbgFile << "Animation Import options: " << documentInfoList;
2139 
2140         int firstFrame = 0;
2141         const int step = documentInfoList[0].toInt();
2142         const int fps = documentInfoList[1].toInt();
2143         const int totalFrames = renderedFrames.framesNeedRelocation() ? (renderedFrames.renderedFrameTargetTimes.last() + 1) : renderedFrames.size() * step;
2144         const QString name = QFileInfo(documentInfoList[3]).fileName();
2145         const bool useCurrentDocument = documentInfoList[4].toInt();
2146         bool useDocumentColorSpace = false;
2147 
2148         if ( useCurrentDocument ) {
2149             document = activeView()->document();
2150 
2151             dbgFile << "Current frames:" << document->image()->animationInterface()->totalLength() << "total frames:" << totalFrames;
2152             if ( document->image()->animationInterface()->totalLength() < totalFrames ) {
2153                 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2154                 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2155             }
2156 
2157         } else {
2158 
2159             const int width = documentInfoList[5].toInt();
2160             const int height = documentInfoList[6].toInt();
2161             const double resolution = documentInfoList[7].toDouble();
2162 
2163             const QString colorModel = documentInfoList[8];
2164             const QString colorDepth = documentInfoList[9];
2165             const QString profile = documentInfoList[10];
2166             useDocumentColorSpace = profile != "Default";
2167 
2168             document = KisPart::instance()->createDocument();
2169             document->setObjectName(name);
2170 
2171             KisPart::instance()->addDocument(document, false);
2172             const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile);
2173             Q_ASSERT(cs);
2174 
2175             QColor qc(Qt::white);
2176             qc.setAlpha(0);
2177             KoColor bgColor(qc, cs);
2178 
2179             if (!document->newImage(name, width, height, cs, bgColor, KisConfig::RASTER_LAYER, 1, "", double(resolution / 72) )) {
2180                 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Failed to create new document. Animation import aborted."));
2181                 return;
2182             }
2183 
2184             document->image()->animationInterface()->setFramerate(fps);
2185             document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2186             document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2187 
2188 
2189             this->showDocument(document);
2190 
2191         }
2192 
2193         KoUpdaterPtr updater =
2194                 !document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0;
2195         KisAnimationImporter importer(document->image(), updater);
2196         KisImportExportErrorCode status = importer.import(renderedFrames.renderedFrameFiles, firstFrame, step, false, false, 0, useDocumentColorSpace, renderedFrames.renderedFrameTargetTimes);
2197 
2198         if (!status.isOk() && !status.isInternalError()) {
2199             QString msg = status.errorMessage();
2200             if (!msg.isEmpty())
2201                 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
2202         }
2203 
2204         activeView()->canvasBase()->refetchDataFromImage();
2205         document->image()->refreshGraph();
2206     }
2207 }
2208 
2209 void KisMainWindow::renderAnimation()
2210 {
2211     if (!activeView()) return;
2212 
2213     KisImageSP image = viewManager()->image();
2214 
2215     if (!image) return;
2216     if (!image->animationInterface()->hasAnimation()) return;
2217 
2218     KisDocument *doc = viewManager()->document();
2219 
2220     KisDlgAnimationRenderer dlgAnimationRenderer(doc, viewManager()->mainWindow());
2221     dlgAnimationRenderer.setCaption(i18n("Render Animation"));
2222     if (dlgAnimationRenderer.exec() == QDialog::Accepted) {
2223         KisAnimationRenderingOptions encoderOptions = dlgAnimationRenderer.getEncoderOptions();
2224         KisAnimationRender::render(doc, viewManager(), encoderOptions);
2225     }
2226 }
2227 
2228 void KisMainWindow::renderAnimationAgain()
2229 {
2230     if (!activeView()) return;
2231 
2232     KisImageSP image = viewManager()->image();
2233 
2234     if (!image) return;
2235     if (!image->animationInterface()->hasAnimation()) return;
2236 
2237     KisDocument *doc = viewManager()->document();
2238 
2239     KisConfig cfg(true);
2240 
2241     KisPropertiesConfigurationSP settings = cfg.exportConfiguration("ANIMATION_EXPORT");
2242 
2243     KisAnimationRenderingOptions encoderOptions;
2244     encoderOptions.fromProperties(settings);
2245 
2246     KisAnimationRender::render(doc, viewManager(), encoderOptions);
2247 }
2248 
2249 void KisMainWindow::slotConfigureToolbars()
2250 {
2251     saveWindowState();
2252     KisKEditToolBar edit(factory(), this);
2253     connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
2254     (void) edit.exec();
2255     applyToolBarLayout();
2256 }
2257 
2258 void KisMainWindow::slotResetConfigurations()
2259 {
2260     KisApplication *kisApp = static_cast<KisApplication*>(qApp);
2261     kisApp->askResetConfig();
2262 }
2263 
2264 void KisMainWindow::slotNewToolbarConfig()
2265 {
2266     applyMainWindowSettings(d->windowStateConfig);
2267 
2268     KisKXMLGUIFactory *factory = guiFactory();
2269     Q_UNUSED(factory);
2270 
2271     // Check if there's an active view
2272     if (!d->activeView)
2273         return;
2274 
2275     plugActionList("toolbarlist", d->toolbarList);
2276     applyToolBarLayout();
2277 }
2278 
2279 void KisMainWindow::slotToolbarToggled(bool toggle)
2280 {
2281     //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true;
2282     // The action (sender) and the toolbar have the same name
2283     KisToolBar * bar = toolBar(sender()->objectName());
2284     if (bar) {
2285         if (toggle) {
2286             bar->show();
2287         }
2288         else {
2289             bar->hide();
2290         }
2291 
2292         if (d->activeView && d->activeView->document()) {
2293             saveWindowState();
2294         }
2295     } else
2296         warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!";
2297 }
2298 
2299 void KisMainWindow::viewFullscreen(bool fullScreen)
2300 {
2301     KisConfig cfg(false);
2302     cfg.setFullscreenMode(fullScreen);
2303 
2304     if (fullScreen) {
2305         setWindowState(windowState() | Qt::WindowFullScreen);   // set
2306     } else {
2307         setWindowState(windowState() & ~Qt::WindowFullScreen);   // reset
2308     }
2309     d->fullScreenMode->setChecked(isFullScreen());
2310 }
2311 
2312 QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory)
2313 {
2314     QDockWidget* dockWidget = 0;
2315     const bool showTitlebars = KisConfig(false).showDockerTitleBars();
2316 
2317     if (!d->dockWidgetsMap.contains(factory->id())) {
2318         dockWidget = factory->createDockWidget();
2319         KAcceleratorManager::setNoAccel(dockWidget);
2320 
2321         // It is quite possible that a dock factory cannot create the dock; don't
2322         // do anything in that case.
2323         if (!dockWidget) {
2324             warnKrita << "Could not create docker for" << factory->id();
2325             return 0;
2326         }
2327 
2328         KoDockWidgetTitleBar *titleBar = dynamic_cast<KoDockWidgetTitleBar*>(dockWidget->titleBarWidget());
2329 
2330         // Check if the dock widget is supposed to be collapsible
2331         if (!dockWidget->titleBarWidget()) {
2332             titleBar = new KoDockWidgetTitleBar(dockWidget);
2333             dockWidget->setTitleBarWidget(titleBar);
2334         }
2335         if (titleBar) {
2336             titleBar->setFont(KisUiFont::dockFont());
2337         }
2338 
2339         if (dockWidget->titleBarWidget() && !dockWidget->titleBarWidget()->inherits("KisUtilityTitleBar")) {
2340             dockWidget->titleBarWidget()->setVisible(KisConfig(true).showDockerTitleBars());
2341         }
2342 
2343         dockWidget->setObjectName(factory->id());
2344         dockWidget->setParent(this);
2345         if (!showTitlebars) {
2346             if (dockWidget->titleBarWidget() && !dockWidget->titleBarWidget()->inherits("KisUtilityTitleBar")) {
2347                 dockWidget->titleBarWidget()->setVisible(false);
2348             }
2349             dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
2350         }
2351         if (dockWidget->widget() && dockWidget->widget()->layout())
2352             dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1);
2353 
2354         Qt::DockWidgetArea side = Qt::RightDockWidgetArea;
2355         bool visible = true;
2356 
2357         switch (factory->defaultDockPosition()) {
2358         case KoDockFactoryBase::DockTornOff:
2359             dockWidget->setFloating(true); // position nicely?
2360             break;
2361         case KoDockFactoryBase::DockTop:
2362             side = Qt::TopDockWidgetArea; break;
2363         case KoDockFactoryBase::DockLeft:
2364             side = Qt::LeftDockWidgetArea; break;
2365         case KoDockFactoryBase::DockBottom:
2366             side = Qt::BottomDockWidgetArea; break;
2367         case KoDockFactoryBase::DockRight:
2368             side = Qt::RightDockWidgetArea; break;
2369         case KoDockFactoryBase::DockMinimized:
2370         default:
2371             side = Qt::RightDockWidgetArea;
2372             visible = false;
2373         }
2374 
2375         KConfigGroup group = d->windowStateConfig.group("DockWidget " + factory->id());
2376         side = static_cast<Qt::DockWidgetArea>(group.readEntry("DockArea", static_cast<int>(side)));
2377         if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea;
2378 
2379         addDockWidget(side, dockWidget);
2380         if (!visible) {
2381             dockWidget->hide();
2382         }
2383 
2384         bool locked = group.readEntry("Locked", false);
2385         if (titleBar && locked) {
2386             titleBar->setLocked(true);
2387         }
2388 
2389         d->dockWidgetsMap.insert(factory->id(), dockWidget);
2390     }
2391     else {
2392         dockWidget = d->dockWidgetsMap[factory->id()];
2393     }
2394 
2395 #ifdef Q_OS_MACOS
2396     dockWidget->setAttribute(Qt::WA_MacSmallSize, true);
2397 #endif
2398     dockWidget->setFont(KisUiFont::dockFont());
2399 
2400     connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts()));
2401 
2402     return dockWidget;
2403 }
2404 
2405 void KisMainWindow::forceDockTabFonts()
2406 {
2407     Q_FOREACH (QObject *child, children()) {
2408         if (child->inherits("QTabBar")) {
2409             ((QTabBar *)child)->setFont(KisUiFont::dockFont());
2410         }
2411     }
2412 }
2413 
2414 void KisMainWindow::slotUpdateWidgetStyle()
2415 {
2416      KisConfig cfg(true);
2417      QString themeFromConfig = cfg.widgetStyle();
2418 
2419      Q_FOREACH (auto key, d->actionMap.keys()) { // find checked style to save to config
2420          if(d->actionMap.value(key)->isChecked()) {
2421             cfg.setWidgetStyle(key);
2422             qApp->setProperty(currentUnderlyingStyleNameProperty, key);
2423             qApp->setStyle(key);
2424          }
2425      }
2426 }
2427 
2428 QList<QDockWidget*> KisMainWindow::dockWidgets() const
2429 {
2430     return d->dockWidgetsMap.values();
2431 }
2432 
2433 QDockWidget* KisMainWindow::dockWidget(const QString &id)
2434 {
2435     if (!d->dockWidgetsMap.contains(id)) return 0;
2436     return d->dockWidgetsMap[id];
2437 }
2438 
2439 QList<KoCanvasObserverBase*> KisMainWindow::canvasObservers() const
2440 {
2441     QList<KoCanvasObserverBase*> observers;
2442 
2443     observers.append(static_cast<KoCanvasObserverBase*>(KisPart::instance()->playbackEngine()));
2444 
2445     Q_FOREACH (QDockWidget *docker, dockWidgets()) {
2446         KoCanvasObserverBase *observer = dynamic_cast<KoCanvasObserverBase*>(docker);
2447         if (observer) {
2448             observers << observer;
2449         }
2450         else {
2451             warnKrita << docker << "is not a canvas observer";
2452         }
2453     }
2454 
2455     return observers;
2456 }
2457 
2458 
2459 void KisMainWindow::toggleDockersVisibility(bool visible, bool onWelcomePage)
2460 {
2461     if (!visible) {
2462         d->dockerStateBeforeHiding = saveState();
2463 
2464         Q_FOREACH (QObject* widget, children()) {
2465             if (widget->inherits("QDockWidget")) {
2466                 QDockWidget* dw = static_cast<QDockWidget*>(widget);
2467                 if (dw->isVisible() && !(onWelcomePage && dw->property("ShowOnWelcomePage").toBool())) {
2468                     dw->hide();
2469                 }
2470             }
2471         }
2472     }
2473     else {
2474         restoreState(d->dockerStateBeforeHiding);
2475     }
2476 }
2477 
2478 void KisMainWindow::subWindowActivated()
2479 {
2480     bool enabled = (activeKisView() != 0);
2481 
2482     d->mdiCascade->setEnabled(enabled);
2483     d->mdiNextWindow->setEnabled(enabled);
2484     d->mdiPreviousWindow->setEnabled(enabled);
2485     d->mdiTile->setEnabled(enabled);
2486     d->close->setEnabled(enabled);
2487     d->closeAll->setEnabled(enabled);
2488 
2489     setActiveSubWindow(d->mdiArea->activeSubWindow());
2490 
2491     /**
2492      * Qt has a weirdness, it has hardcoded shortcuts added to an action
2493      * in the window menu. We need to reset the shortcuts for that menu
2494      * to nothing, otherwise the shortcuts cannot be made configurable.
2495      *
2496      * See: https://bugs.kde.org/show_bug.cgi?id=352205
2497      *      https://bugs.kde.org/show_bug.cgi?id=375524
2498      *      https://bugs.kde.org/show_bug.cgi?id=398729
2499      */
2500     QMdiSubWindow *subWindow = d->mdiArea->currentSubWindow();
2501     if (subWindow) {
2502         QMenu *menu = subWindow->systemMenu();
2503         if (menu && menu->actions().size() == 8) {
2504             Q_FOREACH (QAction *action, menu->actions()) {
2505                 action->setShortcut(QKeySequence());
2506 
2507             }
2508             menu->actions().last()->deleteLater();
2509         }
2510     }
2511 
2512     d->actionManager()->updateGUI();
2513 }
2514 
2515 void KisMainWindow::windowFocused()
2516 {
2517     /**
2518      * Notify selection manager so that it could update selection mask overlay
2519      */
2520     if (viewManager() && viewManager()->selectionManager()) {
2521         viewManager()->selectionManager()->selectionChanged();
2522     }
2523 
2524     KisPart *kisPart = KisPart::instance();
2525     KisWindowLayoutManager *layoutManager = KisWindowLayoutManager::instance();
2526     if (!layoutManager->primaryWorkspaceFollowsFocus()) return;
2527 
2528     QUuid primary = layoutManager->primaryWindowId();
2529     if (primary.isNull()) return;
2530 
2531     if (d->id == primary) {
2532         if (!d->workspaceBorrowedBy.isNull()) {
2533             KisMainWindow *borrower = kisPart->windowById(d->workspaceBorrowedBy);
2534             if (!borrower) return;
2535             swapWorkspaces(this, borrower);
2536         }
2537     } else {
2538         if (d->workspaceBorrowedBy == primary) return;
2539 
2540         KisMainWindow *primaryWindow = kisPart->windowById(primary);
2541         if (!primaryWindow) return;
2542         swapWorkspaces(this, primaryWindow);
2543     }
2544 }
2545 
2546 
2547 void KisMainWindow::updateWindowMenu()
2548 {
2549     QMenu *menu = d->windowMenu->menu();
2550     menu->clear();
2551 
2552 #ifndef Q_OS_ANDROID
2553     menu->addAction(d->newWindow);
2554 #endif
2555     menu->addAction(d->documentMenu);
2556 
2557     QMenu *docMenu = d->documentMenu->menu();
2558     docMenu->clear();
2559 
2560     QFontMetrics fontMetrics = docMenu->fontMetrics();
2561 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
2562     QRect geom = this->geometry();
2563     QPoint p(geom.width() / 2 + geom.left(), geom.height() / 2 + geom.top());
2564     QScreen *screen = qApp->screenAt(p);
2565 
2566     int fileStringWidth = 300;
2567     if (screen) {
2568         fileStringWidth = int(screen->availableGeometry().width() * .40f);
2569     }
2570 #else
2571     int fileStringWidth = int(QApplication::desktop()->screenGeometry(this).width() * .40f);
2572 #endif
2573     Q_FOREACH (QPointer<KisDocument> doc, KisPart::instance()->documents()) {
2574         if (doc) {
2575             QString title = fontMetrics.elidedText(doc->path(), Qt::ElideMiddle, fileStringWidth);
2576             if (title.isEmpty() && doc->image()) {
2577                 title = doc->image()->objectName();
2578             }
2579             QAction *action = docMenu->addAction(title);
2580             action->setIcon(qApp->windowIcon());
2581             connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map()));
2582             d->documentMapper->setMapping(action, doc);
2583         }
2584     }
2585 
2586     menu->addAction(d->workspaceMenu);
2587     QMenu *workspaceMenu = d->workspaceMenu->menu();
2588     workspaceMenu->clear();
2589     KisResourceModel resourceModel(ResourceType::Workspaces);
2590     KisResourceIterator resourceIterator(&resourceModel);
2591     KisMainWindow *m_this = this;
2592 
2593     while (resourceIterator.hasNext()) {
2594         KisResourceItemSP resource = resourceIterator.next();
2595         QAction *action = workspaceMenu->addAction(resource->name());
2596         action->setProperty("md5", QVariant::fromValue<QString>(resource->md5sum()));
2597         connect(action, SIGNAL(triggered()), this, SLOT(restoreWorkspace()));
2598     }
2599     workspaceMenu->addSeparator();
2600     connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&Import Workspace...")),
2601             &QAction::triggered,
2602             this,
2603             [&]()
2604     {
2605         QStringList mimeTypes = KisResourceLoaderRegistry::instance()->mimeTypes(ResourceType::Workspaces);
2606 
2607         KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
2608         dialog.setMimeTypeFilters(mimeTypes);
2609         dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
2610         QString filename = dialog.filename();
2611 
2612         KisResourceUserOperations::importResourceFileWithUserInput(this, "", ResourceType::Workspaces, filename);
2613     });
2614 
2615     connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&New Workspace...")),
2616             &QAction::triggered,
2617             [=]() {
2618         QString name;
2619         name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."),
2620                                                                 i18nc("@label:textbox", "Name:"));
2621         if (name.isEmpty()) {
2622             return;
2623         }
2624 
2625         KisWorkspaceResourceSP workspace(new KisWorkspaceResource(""));
2626         workspace->setDockerState(m_this->saveState());
2627         workspace->setImage(layoutThumbnail());
2628         workspace->setValid(true);
2629 
2630         // this line must happen before we save the workspace to resource folder or other places
2631         // because it mostly just triggers palettes to be saved into the workspace
2632         d->viewManager->canvasResourceProvider()->notifySavingWorkspace(workspace);
2633         workspace->setValid(true);
2634 
2635         workspace->setFilename(name.replace(" ", "_") + workspace->defaultFileExtension());
2636         workspace->setName(name);
2637 
2638         KisResourceUserOperations::addResourceWithUserInput(this, workspace);
2639     });
2640 
2641     // TODO: What to do about delete?
2642     //    workspaceMenu->addAction(i18nc("@action:inmenu", "&Delete Workspace..."));
2643 
2644     menu->addSeparator();
2645     menu->addAction(d->close);
2646     menu->addAction(d->closeAll);
2647     if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2648         menu->addSeparator();
2649         menu->addAction(d->mdiTile);
2650         menu->addAction(d->mdiCascade);
2651     }
2652     menu->addSeparator();
2653     menu->addAction(d->mdiNextWindow);
2654     menu->addAction(d->mdiPreviousWindow);
2655     menu->addSeparator();
2656 
2657     QList<QMdiSubWindow *> windows = d->mdiArea->subWindowList();
2658     for (int i = 0; i < windows.size(); ++i) {
2659         QPointer<KisView>child = qobject_cast<KisView*>(windows.at(i)->widget());
2660         if (child && child->document()) {
2661             QString text;
2662             if (i < 9) {
2663                 text = i18n("&%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2664             }
2665             else {
2666                 text = i18n("%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2667             }
2668 
2669             QAction *action  = menu->addAction(text);
2670             action->setIcon(qApp->windowIcon());
2671             action->setCheckable(true);
2672             action->setChecked(child == activeKisView());
2673             connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map()));
2674             d->windowMapper->setMapping(action, windows.at(i));
2675         }
2676     }
2677 
2678     bool showMdiArea = windows.count( ) > 0;
2679     if (!showMdiArea) {
2680         showWelcomeScreen(true); // see workaround in function in header
2681     }
2682 
2683     // enable/disable the toolbox docker if there are no documents open
2684     Q_FOREACH (QObject* widget, children()) {
2685         if (widget->inherits("QDockWidget")) {
2686             QDockWidget* dw = static_cast<QDockWidget*>(widget);
2687 
2688             if ( dw->objectName() == "ToolBox") {
2689                 dw->setEnabled(showMdiArea);
2690             }
2691         }
2692     }
2693 }
2694 
2695 void KisMainWindow::updateSubwindowFlags()
2696 {
2697     bool onlyOne = false;
2698     if (d->mdiArea->subWindowList().size() == 1 && d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2699         onlyOne = true;
2700     }
2701     Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2702         if (onlyOne) {
2703             subwin->setWindowFlags(subwin->windowFlags() | Qt::FramelessWindowHint);
2704             subwin->showMaximized();
2705         } else {
2706             subwin->setWindowFlags((subwin->windowFlags() | Qt::FramelessWindowHint) ^ Qt::FramelessWindowHint);
2707         }
2708     }
2709 }
2710 
2711 void KisMainWindow::setActiveSubWindow(QWidget *window)
2712 {
2713     if (!window) {
2714         unsetActiveView();
2715         return;
2716     }
2717     QMdiSubWindow *subwin = qobject_cast<QMdiSubWindow *>(window);
2718     //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow;
2719 
2720     if (subwin && subwin != d->activeSubWindow) {
2721         KisView *view = qobject_cast<KisView *>(subwin->widget());
2722         //dbgKrita << "\t" << view << activeView();
2723         if (view && view != activeView()) {
2724             setActiveView(view);
2725         }
2726         d->activeSubWindow = subwin;
2727     }
2728     updateWindowMenu();
2729     d->actionManager()->updateGUI();
2730 }
2731 
2732 void KisMainWindow::configChanged()
2733 {
2734     KisConfig cfg(true);
2735     QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry<int>("mdi_viewmode", (int)QMdiArea::TabbedView);
2736     d->mdiArea->setViewMode(viewMode);
2737     Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2738         subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
2739         subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
2740         if (viewMode == QMdiArea::TabbedView) {
2741             subwin->setWindowState(Qt::WindowMaximized);
2742         }
2743 
2744         /**
2745          * Dirty workaround for a bug in Qt (checked on Qt 5.6.1):
2746          *
2747          * If you make a window "Show on top" and then switch to the tabbed mode
2748          * the window will continue to be painted in its initial "mid-screen"
2749          * position. It will persist here until you explicitly switch to its tab.
2750          */
2751         if (viewMode == QMdiArea::TabbedView) {
2752             Qt::WindowFlags oldFlags = subwin->windowFlags();
2753             Qt::WindowFlags flags = oldFlags;
2754 
2755             flags &= ~Qt::WindowStaysOnTopHint;
2756             flags &= ~Qt::WindowStaysOnBottomHint;
2757 
2758             if (flags != oldFlags) {
2759                 subwin->setWindowFlags(flags);
2760                 subwin->showMaximized();
2761             }
2762         }
2763     }
2764 #ifdef Q_OS_MACOS
2765     updateSubwindowFlags();
2766 #endif
2767 
2768     KConfigGroup group( KSharedConfig::openConfig(), "theme");
2769     d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
2770     d->actionManager()->updateGUI();
2771 
2772     QString s = cfg.getMDIBackgroundColor();
2773     KoColor c = KoColor::fromXML(s);
2774     QBrush brush(c.toQColor());
2775     d->mdiArea->setBackground(brush);
2776 
2777     QString backgroundImage = cfg.getMDIBackgroundImage();
2778     if (backgroundImage != "") {
2779         QImage image(backgroundImage);
2780         QBrush brush(image);
2781         d->mdiArea->setBackground(brush);
2782     }
2783 
2784     d->mdiArea->update();
2785 
2786     qApp->setFont(KisUiFont::normalFont());
2787 
2788     Q_FOREACH (QObject* widget, children()) {
2789         if (widget->inherits("QDockWidget")) {
2790             QDockWidget* dw = static_cast<QDockWidget*>(widget);
2791             dw->setFont(KisUiFont::dockFont());
2792         }
2793     }
2794 }
2795 
2796 KisView* KisMainWindow::newView(QObject *document, QMdiSubWindow *subWindow)
2797 {
2798     KisDocument *doc = qobject_cast<KisDocument*>(document);
2799     KisView *view = addViewAndNotifyLoadingCompleted(doc, subWindow);
2800     d->actionManager()->updateGUI();
2801 
2802     return view;
2803 }
2804 
2805 void KisMainWindow::newWindow()
2806 {
2807 #ifdef Q_OS_ANDROID
2808     // Check if current mainwindow exists, just to be sure.
2809     if (KisPart::instance()->currentMainwindow()) {
2810         QMessageBox::warning(this, i18nc("@title:window", "Krita"),
2811                              "Creating a New Main Window is unsupported on Android");
2812         return;
2813     }
2814 #endif
2815     KisMainWindow *mainWindow = KisPart::instance()->createMainWindow();
2816     mainWindow->initializeGeometry();
2817     mainWindow->show();
2818 }
2819 
2820 void KisMainWindow::closeCurrentWindow()
2821 {
2822     if (d->mdiArea->currentSubWindow()) {
2823         d->mdiArea->currentSubWindow()->close();
2824         d->actionManager()->updateGUI();
2825     }
2826 }
2827 
2828 void KisMainWindow::checkSanity()
2829 {
2830     // print error if the lcms engine is not available
2831     if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) {
2832         // need to wait 1 event since exiting here would not work.
2833         m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now.");
2834         m_dieOnError = true;
2835         QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
2836         return;
2837     }
2838 
2839     slotStoragesWarning();
2840 
2841     // window is created signal (used in Python)
2842     // there must be some asynchronous things happening in the constructor, because the window cannot
2843     // be referenced until after this timeout is done
2844     emit KisPart::instance()->sigMainWindowCreated();
2845 }
2846 
2847 void KisMainWindow::showErrorAndDie()
2848 {
2849     QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Installation error"), m_errorMessage);
2850     if (m_dieOnError) {
2851         exit(10);
2852     }
2853 }
2854 
2855 void KisMainWindow::showAboutApplication()
2856 {
2857     KisAboutApplication dlg(this);
2858     dlg.exec();
2859 }
2860 
2861 QPointer<KisView> KisMainWindow::activeKisView()
2862 {
2863     if (!d->mdiArea) return 0;
2864     QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow();
2865     //dbgKrita << "activeKisView" << activeSubWindow;
2866     if (!activeSubWindow) return 0;
2867     return qobject_cast<KisView*>(activeSubWindow->widget());
2868 }
2869 
2870 void KisMainWindow::newOptionWidgets(KoCanvasController *controller, const QList<QPointer<QWidget> > &optionWidgetList)
2871 {
2872     KIS_ASSERT_RECOVER_NOOP(controller == KoToolManager::instance()->activeCanvasController());
2873     bool isOurOwnView = false;
2874 
2875     Q_FOREACH (QPointer<KisView> view, KisPart::instance()->views()) {
2876         if (view && view->canvasController() == controller) {
2877             isOurOwnView = view->mainWindow() == this;
2878         }
2879     }
2880 
2881     if (!isOurOwnView) return;
2882 
2883     Q_FOREACH (QWidget *w, optionWidgetList) {
2884 #ifdef Q_OS_MACOS
2885         w->setAttribute(Qt::WA_MacSmallSize, true);
2886 #endif
2887         w->setFont(KisUiFont::dockFont());
2888     }
2889 
2890     if (d->toolOptionsDocker) {
2891         d->toolOptionsDocker->setOptionWidgets(optionWidgetList);
2892     }
2893     else {
2894         d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList);
2895     }
2896 }
2897 
2898 void KisMainWindow::createActions()
2899 {
2900     KisActionManager *actionManager = d->actionManager();
2901 
2902 
2903 
2904     actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew()));
2905     actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen()));
2906     actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit()));
2907     actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars()));
2908     d->fullScreenMode = actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool)));
2909 
2910     d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
2911 
2912     d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave()));
2913     d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
2914 
2915     d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs()));
2916     d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE);
2917 
2918     d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo()));
2919     d->undo->setActivationFlags(KisAction::ACTIVE_IMAGE);
2920 
2921     d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo()));
2922     d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE);
2923 
2924     d->undoActionsUpdateManager.reset(new KisUndoActionsUpdateManager(d->undo, d->redo));
2925     d->undoActionsUpdateManager->setCurrentDocument(d->activeView ? d->activeView->document() : 0);
2926 
2927     d->importAnimation  = actionManager->createAction("file_import_animation");
2928     connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation()));
2929 
2930     d->importVideoAnimation = actionManager->createAction("file_import_video_animation");
2931     connect(d->importVideoAnimation, SIGNAL(triggered()), this, SLOT(importVideoAnimation()));
2932     
2933     d->renderAnimation = actionManager->createAction("render_animation");
2934     d->renderAnimation->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
2935     connect( d->renderAnimation, SIGNAL(triggered()), this, SLOT(renderAnimation()));
2936 
2937     d->renderAnimationAgain = actionManager->createAction("render_animation_again");
2938     d->renderAnimationAgain->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
2939     connect( d->renderAnimationAgain, SIGNAL(triggered()), this, SLOT(renderAnimationAgain()));
2940 
2941     d->closeAll = actionManager->createAction("file_close_all");
2942     connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll()));
2943 
2944     d->importFile  = actionManager->createAction("file_import_file");
2945     connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile()));
2946 
2947     d->exportFile  = actionManager->createAction("file_export_file");
2948     connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile()));
2949 
2950     d->exportFileAdvance  = actionManager->createAction("file_export_advanced");
2951     connect(d->exportFileAdvance, SIGNAL(triggered(bool)), this, SLOT(slotExportAdvance()));
2952 
2953     /* The following entry opens the document information dialog.  Since the action is named so it
2954         intends to show data this entry should not have a trailing ellipses (...).  */
2955     d->showDocumentInfo  = actionManager->createAction("file_documentinfo");
2956     connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo()));
2957 
2958     d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this));
2959     d->themeManager->registerThemeActions(actionCollection());
2960     connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged()), Qt::QueuedConnection);
2961     connect(this, SIGNAL(themeChanged()), d->welcomePage, SLOT(slotUpdateThemeColors()), Qt::UniqueConnection);
2962     d->toggleDockers = actionManager->createAction("view_toggledockers");
2963 
2964 
2965     KisConfig(true).showDockers(true);
2966     d->toggleDockers->setChecked(true);
2967     connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool)));
2968 
2969     d->resetConfigurations  = actionManager->createAction("reset_configurations");
2970     connect(d->resetConfigurations, SIGNAL(triggered()), this, SLOT(slotResetConfigurations()));
2971 
2972 #ifndef Q_OS_ANDROID
2973     d->toggleDetachCanvas = actionManager->createAction("view_detached_canvas");
2974     d->toggleDetachCanvas->setChecked(false);
2975     connect(d->toggleDetachCanvas, SIGNAL(toggled(bool)), SLOT(setCanvasDetached(bool)));
2976 #endif
2977     setCanvasDetached(false);
2978 
2979     d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars");
2980     d->toggleDockerTitleBars->setChecked(KisConfig(false).showDockerTitleBars());
2981     connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool)));
2982 
2983     actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu);
2984     actionCollection()->addAction("window", d->windowMenu);
2985 
2986     actionCollection()->addAction("style_menu", d->styleMenu); // for widget styles: breeze, fusion, etc
2987 
2988     d->mdiCascade = actionManager->createAction("windows_cascade");
2989     connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows()));
2990 
2991     d->mdiTile = actionManager->createAction("windows_tile");
2992     connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows()));
2993 
2994     d->mdiNextWindow = actionManager->createAction("windows_next");
2995     connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow()));
2996 
2997     d->mdiPreviousWindow = actionManager->createAction("windows_previous");
2998     connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow()));
2999 
3000 #ifndef Q_OS_ANDROID
3001     d->newWindow = actionManager->createAction("view_newwindow");
3002     connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow()));
3003 #endif
3004 
3005     d->close = actionManager->createStandardAction(KStandardAction::Close, this, SLOT(closeCurrentWindow()));
3006 
3007     d->showSessionManager = actionManager->createAction("file_sessions");
3008     connect(d->showSessionManager, SIGNAL(triggered(bool)), this, SLOT(slotShowSessionManager()));
3009 
3010     d->commandBarAction = actionManager->createAction("command_bar_open");
3011     connect(d->commandBarAction, SIGNAL(triggered(bool)), this, SLOT(openCommandBar()));
3012 
3013     actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences()));
3014 
3015     for (int i = 0; i < 2; i++) {
3016         d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer"));
3017         d->expandingSpacers[i]->setDefaultWidget(new QWidget(this));
3018         d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
3019         actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]);
3020     }
3021 
3022     {
3023         using namespace KisWidgetConnectionUtils;
3024 
3025         QAction *action = actionManager->createAction("lock_toolbars");
3026         connectControl(action, KisToolBar::toolBarStateModel(), "toolBarsLocked");
3027     }
3028 }
3029 
3030 void KisMainWindow::applyToolBarLayout()
3031 {
3032     Q_FOREACH (KisToolBar *toolBar, toolBars()) {
3033         toolBar->layout()->setSpacing(4);
3034         toolBar->setStyleSheet("QToolBar { border: none }"); // has a border in "Fusion" style that people don't like
3035 
3036         // Hide text for buttons with an icon in the toolbar
3037         Q_FOREACH (QAction *ac, toolBar->actions()){
3038             if (ac->icon().pixmap(QSize(1,1)).isNull() == false){
3039                 ac->setPriority(QAction::LowPriority);
3040             } else {
3041                 ac->setIcon(QIcon());
3042             }
3043         }
3044     }
3045 }
3046 
3047 void KisMainWindow::initializeGeometry()
3048 {
3049     // if the user didn's specify the geometry on the command line (does anyone do that still?),
3050     // we first figure out some good default size and restore the x,y position. See bug 285804Z.
3051     KConfigGroup cfg = d->windowStateConfig;
3052     QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray()));
3053     if (!restoreGeometry(geom)) {
3054         const int scnum = QApplication::desktop()->screenNumber(parentWidget());
3055         QRect desk = QGuiApplication::screens().at(scnum)->availableVirtualGeometry();
3056 
3057         quint32 x = desk.x();
3058         quint32 y = desk.y();
3059         quint32 w = 0;
3060         quint32 h = 0;
3061 
3062         // Default size -- maximize on small screens, something useful on big screens
3063         const int deskWidth = desk.width();
3064         if (deskWidth > 1024) {
3065             // a nice width, and slightly less than total available
3066             // height to compensate for the window decs
3067             w = (deskWidth / 3) * 2;
3068             h = (desk.height() / 3) * 2;
3069         }
3070         else {
3071             w = desk.width();
3072             h = desk.height();
3073         }
3074 
3075         x += (desk.width() - w) / 2;
3076         y += (desk.height() - h) / 2;
3077 
3078         move(x,y);
3079         setGeometry(geometry().x(), geometry().y(), w, h);
3080     }
3081     d->fullScreenMode->setChecked(isFullScreen());
3082 }
3083 
3084 void KisMainWindow::showManual()
3085 {
3086     QDesktopServices::openUrl(QUrl("https://docs.krita.org"));
3087 }
3088 
3089 void KisMainWindow::showDockerTitleBars(bool show)
3090 {
3091     Q_FOREACH (QDockWidget *dock, dockWidgets()) {
3092         if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
3093             dock->titleBarWidget()->setVisible(show || dock->isFloating());
3094         }
3095     }
3096 
3097     KisConfig cfg(true);
3098     cfg.setShowDockerTitleBars(show);
3099 }
3100 
3101 void KisMainWindow::slotXmlGuiMakingChanges(bool finished)
3102 {
3103     if (finished) {
3104         subWindowActivated();
3105     }
3106 }
3107 
3108 void KisMainWindow::orientationChanged()
3109 {
3110     QScreen *screen = QGuiApplication::primaryScreen();
3111 
3112     for (QWindow* window: QGuiApplication::topLevelWindows()) {
3113         // Android: we shouldn't transform Window managers independent of its child
3114         if ((window->type() == Qt::Popup)
3115             && (window->flags() & Qt::FramelessWindowHint) == 0
3116             && (window->geometry().topLeft() != QPoint(0, 0))) {
3117             // We are using reversed values. Because geometry returned is not the updated
3118             // one, but the previous one.
3119             int screenHeight = screen->geometry().width();
3120             int screenWidth = screen->geometry().height();
3121 
3122             // scaling
3123             int new_x = (window->position().x() * screenWidth) / screenHeight;
3124             int new_y = (window->position().y() * screenHeight) / screenWidth;
3125 
3126             // window width or height shouldn't change
3127             int winWidth = window->geometry().width();
3128             int winHeight = window->geometry().height();
3129 
3130             // Try best to not let the window go beyond screen.
3131             if (new_x > screenWidth - winWidth) {
3132                 new_x = screenWidth - winWidth;
3133                 if (new_x < 0)
3134                     new_x = 0;
3135             }
3136             if (new_y > screenHeight - winHeight) {
3137                 new_y = screenHeight - winHeight;
3138                 if (new_y < 0)
3139                     new_y = 0;
3140             }
3141 
3142             window->setPosition(QPoint(new_x, new_y));
3143         }
3144     }
3145 }
3146 
3147 bool KisMainWindow::checkActiveBundlesAvailable()
3148 {
3149     KisStorageFilterProxyModel proxy;
3150     proxy.setSourceModel(KisStorageModel::instance());
3151     proxy.setFilter(KisStorageFilterProxyModel::ByStorageType,
3152                     QStringList()
3153                     << KisResourceStorage::storageTypeToUntranslatedString(KisResourceStorage::StorageType::Bundle));
3154 
3155     return (proxy.rowCount() > 0);
3156 }
3157 
3158 bool KisMainWindow::checkPaintOpAvailable()
3159 {
3160     KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
3161     return (rserver->resourceCount() > 0);
3162 }
3163 
3164 #include <moc_KisMainWindow.cpp>