Krita Source Code Documentation
Loading...
Searching...
No Matches
KisMainWindow.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org>
3 SPDX-FileCopyrightText: 2000-2006 David Faure <faure@kde.org>
4 SPDX-FileCopyrightText: 2007, 2009 Thomas zander <zander@kde.org>
5 SPDX-FileCopyrightText: 2010 Benjamin Port <port.benjamin@gmail.com>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "KisMainWindow.h"
11
12#include <KoConfig.h>
13
14// qt includes
15#include <QApplication>
16#include <QByteArray>
17#include <QCloseEvent>
18#include <QStandardPaths>
19#include <QDesktopServices>
20#include <QScreen>
21#include <QDialog>
22#include <QDockWidget>
23#include <QIcon>
24#include <QInputDialog>
25#include <QLayout>
26#include <QMdiArea>
27#include <QMdiSubWindow>
28#include <QMutex>
29#include <QMutexLocker>
30#include <QPointer>
31#include <KisSignalMapper.h>
32#include <QTabBar>
33#include <QMoveEvent>
34#include <QUrl>
35#include <QMessageBox>
36#include <QStatusBar>
37#include <QStyleFactory>
38#include <QMenu>
39#include <QMenuBar>
40#include <KisMimeDatabase.h>
41#include <QMimeData>
42#include <QStackedWidget>
43#include <QProxyStyle>
44#include <QScreen>
45#include <QAction>
46#include <QWindow>
47#include <QTemporaryDir>
48#include <QScrollArea>
49#include <QActionGroup>
50
51#include <kactioncollection.h>
52#include <kactionmenu.h>
53#include <kis_debug.h>
54#include <kedittoolbar.h>
55#include <khelpmenu.h>
56#include <klocalizedstring.h>
57#include <kaboutdata.h>
61#include "kis_icon_utils.h"
62#include <krecentfilesaction.h>
63#include "krita_utils.h"
64#include <ktoggleaction.h>
65#include <ktoolbar.h>
66#include <kmainwindow.h>
67#include <kxmlguiwindow.h>
68#include <kxmlguifactory.h>
69#include <kxmlguiclient.h>
70#include <kguiitem.h>
71#include <kwindowconfig.h>
72#include <kacceleratormanager.h>
73
74#include <KoResourcePaths.h>
75#include <KoToolFactoryBase.h>
76#include <KoToolRegistry.h>
77#include <KoDockFactoryBase.h>
80#include <KoDocumentInfoDlg.h>
81#include <KoDocumentInfo.h>
82#include <KoFileDialog.h>
83#include <kis_icon.h>
84#include <KoToolManager.h>
85#include "KoToolDocker.h"
87#include <KoDockRegistry.h>
88#include <KoPluginLoader.h>
89#include <KoColorSpaceEngine.h>
90#include <KoUpdater.h>
91#include <KisResourceModel.h>
93#include <KisResourceIterator.h>
94#include <KisResourceTypes.h>
95#include <KisResourceCacheDb.h>
96#include <KisStorageModel.h>
98#include <KisPlaybackEngine.h>
99
100#ifdef Q_OS_ANDROID
101#include <QtAndroid>
102#include <KisAndroidUtils.h>
103#endif
104
105#include <KisUsageLogger.h>
110#include "kis_action_manager.h"
111#include "KisApplication.h"
112#include "kis_canvas2.h"
115#include "kis_clipboard.h"
116#include "kis_config.h"
117#include "kis_config_notifier.h"
121#include <KisDocument.h>
122#include "kis_group_layer.h"
124#include "kis_image.h"
129#include "kis_node.h"
130#include "KisOpenPane.h"
131#include "kis_paintop_box.h"
132#include "KisPart.h"
135#include "kis_statusbar.h"
136#include "KisView.h"
137#include "KisViewManager.h"
138#include "thememanager.h"
145#include "KisWelcomePageWidget.h"
147#include <KritaVersionWrapper.h>
148#include "KisCanvasWindow.h"
149#include "kis_action.h"
150#include <katecommandbar.h>
152#include "KisUiFont.h"
156#include "KisToolBarStateModel.h"
157#include <config-qmdiarea-always-show-subwindow-title.h>
158
159#include <mutex>
160
162{
163public:
165
166 QString id() const override {
167 return "sharedtooldocker";
168 }
169
170 QDockWidget* createDockWidget() override {
171 KoToolDocker* dockWidget = new KoToolDocker();
172 return dockWidget;
173 }
174
176 return DockRight;
177 }
178};
179
180class Q_DECL_HIDDEN KisMainWindow::Private
181{
182public:
183 Private(KisMainWindow *parent, QUuid id)
184 : q(parent)
185 , id(id)
186 , styleMenu(new KActionMenu(i18nc("@action:inmenu", "Styles"), parent))
187 , dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent))
188 , windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent))
189 , documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent))
190 , workspaceMenu(new KActionMenu(i18nc("@action:inmenu", "Wor&kspace"), parent))
191 , welcomePage(new KisWelcomePageWidget(parent))
192 , widgetStack(new QStackedWidget(parent))
193 , mdiArea(new QMdiArea(parent))
194 , windowMapper(new KisSignalMapper(parent))
195 , documentMapper(new KisSignalMapper(parent))
196 {
197 if (id.isNull()) this->id = QUuid::createUuid();
198
199 welcomeScroller = new QScrollArea();
200 welcomeScroller->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
201 welcomeScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
202 welcomeScroller->setWidget(welcomePage);
203 welcomeScroller->setWidgetResizable(true);
204
205 widgetStack->addWidget(welcomeScroller);
206 widgetStack->addWidget(mdiArea);
207 mdiArea->setTabsMovable(true);
208 mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder);
209 mdiArea->setDocumentMode(true);
210 mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation);
211#ifdef HAVE_QMDIAREA_ALWAYS_SHOW_SUBWINDOW_TITLE
212 mdiArea->setOption(QMdiArea::AlwaysShowSubwindowNameInTitleBar);
213#endif /* HAVE_QMDIAREA_ALWAYS_SHOW_SUBWINDOW_TITLE */
214
215 commandBar = new KateCommandBar(parent);
216 }
217
219 qDeleteAll(toolbarList);
220 }
221
222 KisMainWindow *q {nullptr};
223 QUuid id;
224
225 KisViewManager *viewManager {nullptr};
226
229
231
232 bool firstTime {true};
233 bool windowSizeDirty {false};
234
235 KisAction *showDocumentInfo {nullptr};
236 KisAction *saveAction {nullptr};
237 KisAction *saveActionAs {nullptr};
238 KisAction *importAnimation {nullptr};
239 KisAction *importVideoAnimation {nullptr};
240 KisAction *renderAnimation {nullptr};
241 KisAction *renderAnimationAgain {nullptr};
242 KisAction *closeAll {nullptr};
243 KisAction *importFile {nullptr};
244 KisAction *exportFile {nullptr};
245 KisAction *exportFileAdvance {nullptr};
246 KisAction *undo {nullptr};
247 KisAction *redo {nullptr};
248 KisAction *close {nullptr};
249 KisAction *mdiCascade {nullptr};
250 KisAction *mdiTile {nullptr};
251 KisAction *mdiNextWindow {nullptr};
252 KisAction *mdiPreviousWindow {nullptr};
253 KisAction *toggleDockers {nullptr};
254 KisAction *resetConfigurations {nullptr};
255 KisAction *toggleDockerTitleBars {nullptr};
256#ifndef Q_OS_ANDROID
257 KisAction *toggleDetachCanvas {nullptr};
258 KisAction *newWindow {nullptr};
259#endif
260 KisAction *fullScreenMode {nullptr};
261 KisAction *showSessionManager {nullptr};
262 KisAction *commandBarAction {nullptr};
263 KisAction *expandingSpacers[2];
264
265 KActionMenu *styleMenu {nullptr};
266 QActionGroup* styleActions {nullptr};
267 QMap<QString, QAction*> actionMap;
268
269 KActionMenu *dockWidgetMenu {nullptr};
270 KActionMenu *windowMenu {nullptr};
271 KActionMenu *documentMenu {nullptr};
272 KActionMenu *workspaceMenu {nullptr};
273
274 KisKHelpMenu *helpMenu {nullptr};
275
276 KRecentFilesAction *recentFiles {nullptr};
277 KisResourceModel *workspacemodel {nullptr};
278
279 QScopedPointer<KisUndoActionsUpdateManager> undoActionsUpdateManager;
280
282
283 QMap<QString, QDockWidget *> dockWidgetsMap;
285 KoToolDocker *toolOptionsDocker {nullptr};
286
287 QCloseEvent *deferredClosingEvent {nullptr};
288#ifndef Q_OS_HAIKU
289 Digikam::ThemeManager *themeManager {nullptr};
290#endif
291 QScrollArea *welcomeScroller {nullptr};
292 KisWelcomePageWidget *welcomePage {nullptr};
293
294
295 QStackedWidget *widgetStack {nullptr};
296
297 QMdiArea *mdiArea {nullptr};
298 QMdiSubWindow *activeSubWindow {nullptr};
299 KisSignalMapper *windowMapper {nullptr};
300 KisSignalMapper *documentMapper {nullptr};
301 KisCanvasWindow *canvasWindow {nullptr};
302
304 QScopedPointer<KisSignalCompressorWithParam<int> > tabSwitchCompressor;
306
307 KConfigGroup windowStateConfig;
308
310
311 KateCommandBar *commandBar {nullptr};
312
314 return viewManager->actionManager();
315 }
316
317 QTabBar* findTabBarHACK() {
318 QObjectList objects = mdiArea->children();
319 Q_FOREACH (QObject *object, objects) {
320 QTabBar *bar = qobject_cast<QTabBar*>(object);
321 if (bar) {
322 return bar;
323 }
324 }
325 return 0;
326 }
327};
328
330{
331 QWidget *widget;
332public:
333 ScopedWidgetDisabler(QWidget *widget_)
334 : widget(widget_)
335 {
336 widget->setEnabled(false);
337 }
338
340 {
341 widget->setEnabled(true);
342 }
343};
344
346 : KXmlGuiWindow()
347 , d(new Private(this, uuid))
348{
349 KAcceleratorManager::setNoAccel(this);
350
351 d->workspacemodel = new KisResourceModel(ResourceType::Workspaces, this);
352 connect(d->workspacemodel, SIGNAL(modelReset()), this, SLOT(updateWindowMenu()));
353 connect(d->workspacemodel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(updateWindowMenu()));
354 connect(d->workspacemodel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(updateWindowMenu()));
355
356 d->viewManager = new KisViewManager(this, actionCollection());
357 KConfigGroup group( KSharedConfig::openConfig(), "theme");
358#ifndef Q_OS_HAIKU
359 d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this);
360#endif
361 d->windowStateConfig = KSharedConfig::openConfig()->group("MainWindow");
362
364 setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
365 setDockNestingEnabled(true);
366#ifdef Q_OS_ANDROID
367 // On Android, the animations for docks and menus are not only slow, but
368 // also can cause crashes in some circumstances. Turning them off feels
369 // better and avoids those.
370 setAnimated(false);
371#endif
372
373 qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events
374
375 connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts()));
376 connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons()));
377 connect(this, SIGNAL(themeChanged()), KoToolManager::instance(), SLOT(themeChanged()));
378 connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu()));
379 connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu()));
380 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged()));
381#ifdef Q_OS_ANDROID
382 connect(this, &KisMainWindow::sigFullscreenOnShow, this, &KisMainWindow::viewFullscreen, Qt::QueuedConnection);
383#endif
384
386 KoPluginLoader::instance()->load("Krita/ViewPlugin", KoPluginLoader::PluginsConfig(), d->viewManager, false);
387
388 // Load the per-application plugins (Right now, only Python) We do this only once, when the first mainwindow is being created.
389 KoPluginLoader::instance()->load("Krita/ApplicationPlugin", KoPluginLoader::PluginsConfig(), qApp, true);
390
391 KoToolBoxFactory toolBoxFactory;
392 QDockWidget *toolbox = createDockWidget(&toolBoxFactory);
393
394 KisConfig cfg(true);
395 if (cfg.toolOptionsInDocker()) {
396 ToolDockerFactory toolDockerFactory;
397 d->toolOptionsDocker = qobject_cast<KoToolDocker*>(createDockWidget(&toolDockerFactory));
398 if (d->toolOptionsDocker) {
399 d->toolOptionsDocker->toggleViewAction()->setEnabled(true);
400 }
401 }
402
403 QMap<QString, QAction*> dockwidgetActions;
404
405 if (toolbox) {
406 dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction();
407 }
408 Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) {
410 QDockWidget *dw = createDockWidget(factory);
411 if (dw) {
412 dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction();
413 }
414 }
415
416 if (d->toolOptionsDocker) {
417 dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction();
418 }
420
421 Q_FOREACH (QString title, dockwidgetActions.keys()) {
422 d->dockWidgetMenu->addAction(dockwidgetActions[title]);
423 }
424
425
426 // Style menu actions
427 d->styleActions = new QActionGroup(this);
428 QAction * action;
429
430#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
431 QStringList allowableStyles = QStringList() << "macintosh" << "breeze" << "fusion";
432#else
433 QStringList allowableStyles = QStringList() << "macos" << "breeze" << "fusion";
434#endif
435
436 Q_FOREACH (QString styleName, QStyleFactory::keys()) {
437#ifdef Q_OS_ANDROID
438 // disable the style for android platform
439 if (styleName.toLower().contains("android")) {
440 continue;
441 }
442#endif
443 if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) {
444 if (!allowableStyles.contains(styleName.toLower())) {
445 continue;
446 }
447 }
448 action = new QAction(styleName, d->styleActions);
449 action->setCheckable(true);
450 d->actionMap.insert(styleName, action);
451 d->styleMenu->addAction(d->actionMap.value(styleName));
452 }
453
454
455 // select the config value, or the current style if that does not exist
456 QString styleFromConfig = cfg.widgetStyle().toLower();
457 QString styleToSelect = styleFromConfig == "" ? style()->objectName().toLower() : styleFromConfig;
458
459 Q_FOREACH (auto key, d->actionMap.keys()) {
460 if(key.toLower() == styleToSelect) { // does the key match selection
461 d->actionMap.value(key)->setChecked(true);
462 }
463 }
464
465 connect(d->styleActions, SIGNAL(triggered(QAction*)),
466 this, SLOT(slotUpdateWidgetStyle()));
467
468
469 // Load all the actions from the tool plugins
470 // ToolBoxDocker needs them when at setViewManager()
471 Q_FOREACH(KoToolFactoryBase *toolFactory, KoToolRegistry::instance()->values()) {
472 toolFactory->createActions(actionCollection());
473 }
474
475
476 Q_FOREACH (QDockWidget *wdg, dockWidgets()) {
477 if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) {
478 wdg->setVisible(true);
479 }
480 }
481
482 Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) {
483 observer->setObservedCanvas(0);
484 KisMainwindowObserver* mainwindowObserver = dynamic_cast<KisMainwindowObserver*>(observer);
485 if (mainwindowObserver) {
486 mainwindowObserver->setViewManager(d->viewManager);
487 }
488 }
489
490 d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
491 d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
492 d->mdiArea->setTabPosition(QTabWidget::North);
493 d->mdiArea->setTabsClosable(true);
494 d->mdiArea->setAcceptDrops(true);
495
496 themeChanged(); // updates icon styles
497
498 setCentralWidget(d->widgetStack);
499 d->widgetStack->setCurrentIndex(0);
500
501 connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated()));
502 connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*)));
503 connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*)));
504
505 d->canvasWindow = new KisCanvasWindow(this);
506 actionCollection()->addAssociatedWidget(d->canvasWindow);
507
509
510 // the welcome screen needs to grab actions...so make sure this line goes after the createAction() so they exist
511 d->welcomePage->setMainWindow(this);
512
513 d->recentFiles->setRecentFilesModel(&KisRecentDocumentsModelWrapper::instance()->model());
514
517
518 if (isHelpMenuEnabled() && !d->helpMenu) {
519 d->helpMenu = new KisKHelpMenu(this, KAboutData::applicationData(), false);
520
521 // workaround-less version:
522 // d->helpMenu = new KisKHelpMenu(this, QString()/*unused*/, false);
523
524 // The difference between using KisKActionCollection->addAction() is that
525 // these actions do not get tied to the MainWindow. What does this all do?
526 KisKActionCollection *actions = d->viewManager->actionCollection();
527 QAction *helpContentsAction = d->helpMenu->action(KisKHelpMenu::menuHelpContents);
528 QAction *whatsThisAction = d->helpMenu->action(KisKHelpMenu::menuWhatsThis);
529 QAction *reportBugAction = d->helpMenu->action(KisKHelpMenu::menuReportBug);
530 QAction *switchLanguageAction = d->helpMenu->action(KisKHelpMenu::menuSwitchLanguage);
531 QAction *aboutAppAction = d->helpMenu->action(KisKHelpMenu::menuAboutApp);
532 QAction *aboutKdeAction = d->helpMenu->action(KisKHelpMenu::menuAboutKDE);
533
534 if (helpContentsAction) {
535 actions->addAction(helpContentsAction->objectName(), helpContentsAction);
536 }
537 if (whatsThisAction) {
538 actions->addAction(whatsThisAction->objectName(), whatsThisAction);
539 }
540 if (reportBugAction) {
541 actions->addAction(reportBugAction->objectName(), reportBugAction);
542 }
543 if (switchLanguageAction) {
544 actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
545 }
546 if (aboutAppAction) {
547 actions->addAction(aboutAppAction->objectName(), aboutAppAction);
548 }
549 if (aboutKdeAction) {
550 actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
551 }
552
553 connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication()));
554 }
555
556 // KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves
557 QAction *helpAction = actionCollection()->action("help_contents");
558 helpAction->disconnect();
559 connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual()));
560
561#if 0
562 //check for colliding shortcuts
563 QSet<QKeySequence> existingShortcuts;
564 Q_FOREACH (QAction* action, actionCollection()->actions()) {
565 if(action->shortcut() == QKeySequence(0)) {
566 continue;
567 }
568 dbgKrita << "shortcut " << action->text() << " " << action->shortcut();
569 Q_ASSERT(!existingShortcuts.contains(action->shortcut()));
570 existingShortcuts.insert(action->shortcut());
571 }
572#endif
573
575
576 // Make sure the python plugins create their actions in time
578
579 // If we have customized the toolbars, load that first
580 setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita5.xmlgui"));
581 setXMLFile(":/kxmlgui5/krita5.xmlgui");
582
583 guiFactory()->addClient(this);
585 connect(guiFactory(), SIGNAL(makingChanges(bool)), SLOT(slotXmlGuiMakingChanges(bool)));
586
587 // Create and plug toolbar list for Settings menu
589 Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) {
590 KisToolBar * toolBar = ::qobject_cast<KisToolBar *>(it);
591 if (toolBar) {
592 KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this);
593 actionCollection()->addAction(toolBar->objectName().toUtf8(), act);
594 act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle())));
595 connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool)));
596 act->setChecked(!toolBar->isHidden());
597 toolbarList.append(act);
598 } else {
599 warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!";
600 }
601 }
602
603 plugActionList("toolbarlist", toolbarList);
604 d->toolbarList = toolbarList;
605
607
608 d->viewManager->updateGUI();
609 d->viewManager->updateIcons();
610
611 QTimer::singleShot(1000, Qt::CoarseTimer, this, SLOT(checkSanity()));
612
613 {
614 using namespace std::placeholders; // For _1 placeholder
615 std::function<void (int)> callback(
616 std::bind(&KisMainWindow::switchTab, this, _1));
617
618 d->tabSwitchCompressor.reset(
620 }
621
622
623 if (cfg.readEntry("CanvasOnlyActive", false)) {
624 QString currentWorkspace = cfg.readEntry<QString>("CurrentWorkspace", "Default");
626 KisWorkspaceResourceSP workspace = rserver->resource("", "", currentWorkspace);
627 if (workspace) {
628 restoreWorkspace(workspace);
629 }
630 cfg.writeEntry("CanvasOnlyActive", false);
631 menuBar()->setVisible(true);
632 }
633
634 this->winId(); // Ensures the native window has been created.
635
636#ifdef Q_OS_ANDROID
637 // HACK: This prevents the mainWindow from going beyond the screen with no
638 // way to bring it back. Apparently the size doesn't matter here as long as
639 // it remains fixed?
640 setFixedSize(KisApplication::primaryScreen()->availableGeometry().size());
641
642 QScreen *s = QGuiApplication::primaryScreen();
643 s->setOrientationUpdateMask(Qt::LandscapeOrientation|Qt::InvertedLandscapeOrientation|Qt::PortraitOrientation|Qt::InvertedPortraitOrientation);
644 connect(s, SIGNAL(orientationChanged(Qt::ScreenOrientation)), this, SLOT(orientationChanged()));
645
646 // When Krita starts, Java side sends an event to set applicationState() to active. But, before
647 // the event could reach KisApplication's platform integration, it is cleared by KisOpenGLModeProber::probeFormat.
648 // So, we send it manually when MainWindow shows up.
649 QAndroidJniObject::callStaticMethod<void>("org/qtproject/qt5/android/QtNative", "setApplicationState", "(I)V", Qt::ApplicationActive);
650#endif
651
652 setAcceptDrops(true);
653 QTabBar *tabBar = d->findTabBarHACK();
654 if (tabBar) {
655 tabBar->setElideMode(Qt::ElideRight);
656 // load customized tab style
658 tabBar->setExpanding(true);
659 tabBar->setAcceptDrops(true);
660 tabBar->setChangeCurrentOnDrag(true);
661 }
662
663 applyMainWindowSettings(d->windowStateConfig);
664}
665
667{
668 // Q_FOREACH (QAction *ac, actionCollection()->actions()) {
669 // QAction *action = qobject_cast<QAction*>(ac);
670 // if (action) {
671 // qDebug() << "<Action"
672 // << "\n\tname=" << action->objectName()
673 // << "\n\ticon=" << action->icon().name()
674 // << "\n\ttext=" << action->text().replace("&", "&amp;")
675 // << "\n\twhatsThis=" << action->whatsThis()
676 // << "\n\ttoolTip=" << action->toolTip().replace("<html>", "").replace("</html>", "")
677 // << "\n\ticonText=" << action->iconText().replace("&", "&amp;")
678 // << "\n\tshortcut=" << action->shortcut().toString()
679 // << "\n\tisCheckable=" << QString((action->isChecked() ? "true" : "false"))
680 // << "\n\tstatusTip=" << action->statusTip()
681 // << "\n/>\n" ;
682 // }
683 // else {
684 // dbgKrita << "Got a non-qaction:" << ac->objectName();
685 // }
686 // }
687
688 // The doc and view might still exist (this is the case when closing the window)
690
691 delete d->viewManager;
692 delete d;
693
694}
695
697{
698 return 0;
699}
700
701QUuid KisMainWindow::id() const {
702 return d->id;
703}
704
705void KisMainWindow::addView(KisView *view, QMdiSubWindow *subWindow)
706{
707 if (d->activeView == view && !subWindow) return;
708
709 if (d->activeView) {
710 d->activeView->disconnect(this);
711 }
712
713 // register the newly created view in the input manager
715
716 showView(view, subWindow);
717 Q_EMIT restoringDone();
718
719// QTabBar *tabBar = d->findTabBarHACK();
720// Q_FOREACH(QObject *c, tabBar->children()) {
721// if (QWidget *w = qobject_cast<QWidget*>(c)) {
722// w->setAcceptDrops(true);
723// }
724// }
725}
726
728{
735 if (view->canvasBase() == viewManager()->canvasBase()) {
737 }
738}
739
740
741void KisMainWindow::showView(KisView *imageView, QMdiSubWindow *subwin)
742{
743 if (imageView && activeView() != imageView) {
744 // XXX: find a better way to initialize this!
745 imageView->setViewManager(d->viewManager);
746
747 imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager());
748 imageView->slotLoadingFinished();
749
750 if (!subwin) {
751 QMdiSubWindow *currentSubWin = d->mdiArea->currentSubWindow();
752 bool shouldMaximize = currentSubWin ? currentSubWin->isMaximized() : true;
753 subwin = d->mdiArea->addSubWindow(imageView);
754 if (shouldMaximize) {
755 subwin->setWindowState(Qt::WindowMaximized);
756 }
757 } else {
758 subwin->setWidget(imageView);
759 }
760 imageView->setSubWindow(subwin);
761 subwin->setAttribute(Qt::WA_DeleteOnClose, true);
762 connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu()));
763
764 KisConfig cfg(true);
765 subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
766 subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
767 subwin->setWindowIcon(qApp->windowIcon());
768
769#ifdef Q_OS_MACOS
770 connect(subwin, SIGNAL(destroyed()), SLOT(updateSubwindowFlags()));
772#endif
773
774 if (d->mdiArea->subWindowList().size() == 1) {
775 imageView->showMaximized();
776 }
777 else {
778 imageView->show();
779 }
780
802
803 // No, no, no: do not try to call this _before_ the show() has
804 // been called on the view; only when that has happened is the
805 // opengl context active, and very bad things happen if we tell
806 // the dockers to update themselves with a view if the opengl
807 // context is not active.
808 setActiveView(imageView);
809
811 } else {
813 }
814}
815
817{
818 QScopedPointer<KisDlgPreferences> dlgPreferences(new KisDlgPreferences(this));
819
820 if (dlgPreferences->editPreferences()) {
824
825 // XXX: should this be changed for the views in other windows as well?
826 Q_FOREACH (QPointer<KisView> koview, KisPart::instance()->views()) {
827 KisViewManager *view = qobject_cast<KisViewManager*>(koview);
828 if (view) {
829 // Update the settings for all nodes -- they don't query
830 // KisConfig directly because they need the settings during
831 // compositing, and they don't connect to the config notifier
832 // because nodes are not QObjects (because only one base class
833 // can be a QObject).
834 KisNode* node = dynamic_cast<KisNode*>(view->image()->rootLayer().data());
835 if (node) {
836 node->updateSettings();
837 }
838 }
839
840 }
842
843 d->viewManager->showHideScrollbars();
844 }
845}
846
848{
849 // reload action icons!
850 Q_FOREACH (QAction *action, actionCollection()->actions()) {
852 }
853 if (d->mdiArea) {
854 d->mdiArea->setPalette(qApp->palette());
855 for (int i=0; i<d->mdiArea->subWindowList().size(); i++) {
856 QMdiSubWindow *window = d->mdiArea->subWindowList().at(i);
857 if (window) {
858 window->setPalette(qApp->palette());
859 KisView *view = qobject_cast<KisView*>(window->widget());
860 if (view) {
861 view->slotThemeChanged(qApp->palette());
862 }
863 }
864 }
865 }
866
868
869 // Update toolbars
870 {
871 Q_FOREACH (KisToolBar* aToolBar, toolBars()) {
872 QObjectList objects;
873 objects.append(aToolBar);
874 while (!objects.isEmpty()) {
875 QWidget* widget = qobject_cast<QWidget*>(objects.takeFirst());
876 if (widget) {
877 objects.append(widget->children());
878 widget->setPalette(qApp->palette());
879 }
880 }
881 }
882 }
883}
884
886{
887 KConfigGroup group(KSharedConfig::openConfig(), "theme");
888#ifndef Q_OS_HAIKU
889 if (group.readEntry("Theme", "") == d->themeManager->currentThemeName()) return;
890
891 // save theme changes instantly
892 group.writeEntry("Theme", d->themeManager->currentThemeName());
893#endif
894 updateTheme();
895
896 // Make the other top level windows update as well
897 Q_FOREACH (QWidget* topLevelWidget, qApp->topLevelWidgets()) {
898 if (topLevelWidget == this) {
899 // Skip if the current top level widget is this window
900 continue;
901 }
902 if (topLevelWidget->isHidden()) {
903 // Skip unwanted top level widgets like menus, dropdowns, tooltips, etc.
904 continue;
905 }
906 KisMainWindow *topLevelMainWindow = qobject_cast<KisMainWindow*>(topLevelWidget);
907 if (topLevelMainWindow) {
908 topLevelMainWindow->updateTheme();
909 Q_EMIT topLevelMainWindow->themeChanged();
910 } else {
911 QObjectList objects;
912 objects.append(topLevelWidget);
913 while (!objects.isEmpty()) {
914 QWidget* widget = qobject_cast<QWidget*>(objects.takeFirst());
915 if (widget) {
916 objects.append(widget->children());
918 }
919 }
920 }
921 }
922
924
925 Q_EMIT themeChanged();
926}
927
929{
930 // update MDI area theme
931 // Tab close button override
932 // just switch this icon out for all OSs so it is easier to see
933 QString closeButtonImageUrl;
934 QString closeButtonHoverColor;
936 closeButtonImageUrl = QStringLiteral(":/dark_close-tab.svg");
937 closeButtonHoverColor = QStringLiteral("lightcoral");
938 } else {
939 closeButtonImageUrl = QStringLiteral(":/light_close-tab.svg");
940 closeButtonHoverColor = QStringLiteral("darkred");
941 }
942 QString tabStyleSheet = QStringLiteral(R"(
943 QTabBar::close-button {
944 image: url(%1);
945 padding-top: 3px;
946 }
947 QTabBar::close-button:hover {
948 background-color: %2;
949 }
950 QTabBar::close-button:pressed {
951 background-color: red;
952 }
953
954 QHeaderView::section {
955 padding: 7px;
956 }
957
958 )")
959 .arg(closeButtonImageUrl, closeButtonHoverColor);
960
961
962 QTabBar* tabBar = d->findTabBarHACK();
963 if (tabBar) {
964 tabBar->setStyleSheet(tabStyleSheet);
965 }
966
967}
968
970{
971 return centralWidget() != d->widgetStack;
972}
973
975{
976#ifdef Q_OS_ANDROID
977 if (detach) {
978 QMessageBox::warning(this, i18nc("@title:window", "Krita"),
979 "Detach Canvas is unsupported on Android");
980 }
981#else
982 if (detach == canvasDetached()) return;
983
984 QWidget *outgoingWidget = centralWidget() ? takeCentralWidget() : nullptr;
985 QWidget *incomingWidget = d->canvasWindow->swapMainWidget(outgoingWidget);
986
987 if (incomingWidget) {
988 setCentralWidget(incomingWidget);
989 }
990
991 if (detach) {
992 d->canvasWindow->show();
993 } else {
994 d->canvasWindow->hide();
995 }
996 d->toggleDetachCanvas->setChecked(detach);
997#endif
998}
999
1000QWidget * KisMainWindow::canvasWindow() const
1001{
1002 return d->canvasWindow;
1003}
1004
1009
1011{
1012 KisRecentFilesManager::instance()->remove(QUrl::fromLocalFile(url));
1013}
1014
1015void KisMainWindow::slotUpdateSaveActionTitle(const QString &documentPath)
1016{
1017 const QString fileName = QFileInfo(documentPath).fileName();
1018
1019 if (!fileName.isEmpty()) {
1020 d->saveAction->setToolTip(i18n("Save as %1", fileName));
1021 }
1022 else {
1023 d->saveAction->setToolTip(i18n("Save"));
1024 }
1025}
1026
1028{
1029 Q_UNUSED(readWrite);
1030 d->actionManager()->updateGUI();
1031}
1032
1034{
1035 if (d->activeView) {
1036 return d->activeView;
1037 }
1038 return 0;
1039}
1040
1041bool KisMainWindow::openDocument(const QString &path, OpenFlags flags)
1042{
1043 ScopedWidgetDisabler disabler(d->welcomePage->dropFrameBorder);
1044 QApplication::processEvents(); // make UI more responsive
1045
1046 if (!QFile(path).exists()) {
1047 if (!(flags & BatchMode)) {
1048 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", path));
1049 }
1050 KisRecentFilesManager::instance()->remove(QUrl::fromLocalFile(path)); //remove the file from the recent-opened-file-list
1051 return false;
1052 }
1053 return openDocumentInternal(path, flags);
1054}
1055
1056bool KisMainWindow::openDocumentInternal(const QString &path, OpenFlags flags)
1057{
1058 if (!QFile(path).exists()) {
1059 qWarning() << "KisMainWindow::openDocumentInternal. Could not open:" << path;
1060 return false;
1061 }
1062
1064
1065 if (flags & BatchMode) {
1066 newdoc->setFileBatchMode(true);
1067 }
1068
1069 d->firstTime = true;
1070 connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1071 connect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1072
1073 KisDocument::OpenFlags openFlags = KisDocument::None;
1074 // XXX: Why this duplication of OpenFlags...
1075 if (flags & RecoveryFile) {
1076 openFlags |= KisDocument::RecoveryFile;
1077 }
1078
1079 bool openRet = !(flags & Import) ? newdoc->openPath(path, openFlags) : newdoc->importDocument(path);
1080
1081 if (!openRet) {
1082 delete newdoc;
1083 return false;
1084 }
1085
1086 KisPart::instance()->addDocument(newdoc);
1087
1088 // Try to determine whether this was an unnamed autosave
1089 if (flags & RecoveryFile &&
1090 ( path.startsWith(QDir::tempPath())
1091 || path.startsWith(QDir::homePath())
1092 ) &&
1093 ( QFileInfo(path).fileName().startsWith(".krita")
1094 || QFileInfo(path).fileName().startsWith("krita")
1095 )
1096 )
1097 {
1098 QString path = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1099 if (!QFileInfo(path).exists()) {
1100 path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
1101 }
1102 newdoc->setPath(path + "/" + newdoc->objectName() + ".kra");
1103 }
1104
1105 return true;
1106}
1107
1109 Q_FOREACH(QMdiSubWindow *subwindow, d->mdiArea->subWindowList()) {
1110 KisView *view = qobject_cast<KisView*>(subwindow->widget());
1112
1113 if (view) {
1114 if (view->document() == document) {
1115 setActiveSubWindow(subwindow);
1116 return;
1117 }
1118 }
1119 }
1120
1122}
1123
1125 QMdiSubWindow *subWindow)
1126{
1127 showWelcomeScreen(false); // see workaround in function header
1128
1129 KisView *view = KisPart::instance()->createView(document, d->viewManager, this);
1130 addView(view, subWindow);
1131
1132 Q_EMIT guiLoadingFinished();
1133
1134#ifdef Q_OS_ANDROID
1135 // HACK: When opening documents on Android, the main window sometimes fails
1136 // to update until the application is shunted to the background and brought
1137 // back or the menu bar is fiddled with. Flickering the window fixes this.
1138 // Having a docker that uses QML somehow also fixes this, so this hack is
1139 // gone again in 5.3 with the introduction of the text properties docker.
1140 // UNHACK: But not on Xiaomi devices! There flickering the window makes it
1141 // sorta exit fullscreen, adding black bars at the top and bottom. Just
1142 // opening a document seems to work fine on these devices though, it delays
1143 // for a moment then displays fine, so just skip this there I guess.
1145 QTimer::singleShot(0, this, [this] {
1146 hide();
1147 QTimer::singleShot(0, this, [this] {
1148 show();
1149 });
1150 });
1151 }
1152#endif
1153
1154 return view;
1155}
1156
1158{
1159 KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument");
1160 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1162 dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images"));
1163
1164 QStringList filenames = dialog.filenames();
1165 filenames.sort();
1166
1167 return filenames;
1168}
1169
1170// Separate from openDocument to handle async loading (remote URLs)
1172{
1173 KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
1174 if (newdoc && newdoc->image()) {
1176
1177 disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1178 disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1179
1180 Q_EMIT loadCompleted();
1181 }
1182}
1183
1184void KisMainWindow::slotLoadCanceled(const QString &errMsg)
1185{
1186 KisUsageLogger::log(QString("Loading canceled: %1.").arg(errMsg));
1187 KisDocument* doc = qobject_cast<KisDocument*>(sender());
1188 Q_ASSERT(doc);
1189 disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1190 disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1191}
1192
1193void KisMainWindow::slotSaveCanceled(const QString &errMsg)
1194{
1195 if (!errMsg.isEmpty()) { // empty when cancelled by user
1196 KisUsageLogger::log(QString("Saving cancelled. Error:").arg(errMsg));
1197 QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
1198 }
1199 else {
1200 KisUsageLogger::log(QString("Saving cancelled by the user."));
1201 }
1203}
1204
1206{
1207 KisUsageLogger::log(QString("Saving Completed"));
1208 KisDocument* doc = qobject_cast<KisDocument*>(sender());
1209 if (doc) {
1210 disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
1211 disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
1212 }
1213
1214 if (d->deferredClosingEvent) {
1215 KXmlGuiWindow::closeEvent(d->deferredClosingEvent);
1216 }
1217}
1218
1220{
1221 std::unique_lock<QMutex> l(d->savingEntryMutex, std::try_to_lock);
1222 return !l.owns_lock();
1223}
1224
1225bool KisMainWindow::installBundle(const QString &fileName) const
1226{
1227 QFileInfo from(fileName);
1228 QFileInfo to(QStringLiteral("%1/%2").arg(KoResourcePaths::getAppDataLocation(), from.fileName()));
1229 if (to.exists()) {
1230 QFile::remove(to.canonicalFilePath());
1231 }
1232 return QFile::copy(fileName, to.absoluteFilePath());
1233}
1234
1236{
1237 int size = 256;
1238 qreal scale = qreal(size)/qreal(qMax(geometry().width(), geometry().height()));
1239 QImage layoutThumbnail = QImage(qRound(geometry().width()*scale), qRound(geometry().height()*scale), QImage::Format_ARGB32);
1240 QPainter gc(&layoutThumbnail);
1241 gc.fillRect(0, 0, layoutThumbnail.width(), layoutThumbnail.height(), this->palette().dark());
1242
1243 Q_FOREACH(const QObject *child, children()) {
1244 if (child->isWidgetType()) {
1245 const QWidget *w = static_cast<const QWidget *>(child);
1246
1247 if (w->isVisible() && !w->property("_kis_excludeFromLayoutThumbnail").toBool()) {
1248 QRect wRect = QRectF(w->geometry().x()*scale
1249 , w->geometry().y()*scale
1250 , w->geometry().width()*scale
1251 , w->geometry().height()*scale
1252 ).toRect();
1253
1254 wRect = wRect.intersected(layoutThumbnail.rect().adjusted(-1, -1, -1, -1));
1255
1256 gc.setBrush(this->palette().window());
1257 if (w == d->widgetStack) {
1258 gc.setBrush(d->mdiArea->background());
1259 }
1260 gc.setPen(this->palette().windowText().color());
1261 gc.drawRect(wRect);
1262 }
1263 }
1264 }
1265 gc.end();
1266 return layoutThumbnail;
1267}
1268
1269bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool isExporting, bool isAdvancedExporting )
1270{
1271 if (!document) {
1272 return true;
1273 }
1274
1282 std::unique_lock<QMutex> l(d->savingEntryMutex, std::try_to_lock);
1283 if (!l.owns_lock()) return false;
1284
1285 // no busy wait for saving because it is dangerous!
1286 KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this);
1287 dlg.blockIfImageIsBusy();
1288
1289 if (dlg.result() == KisDelayedSaveDialog::Rejected) {
1290 return false;
1291 }
1292 else if (dlg.result() == KisDelayedSaveDialog::Ignored) {
1293 QMessageBox::critical(qApp->activeWindow(),
1294 i18nc("@title:window", "Krita"),
1295 i18n("You are saving a file while the image is "
1296 "still rendering. The saved file may be "
1297 "incomplete or corrupted.\n\n"
1298 "Please select a location where the original "
1299 "file will not be overridden!"));
1300
1301
1302 saveas = true;
1303 }
1304
1305 if (document->isRecovered()) {
1306 saveas = true;
1307 }
1308
1309 if (document->path().isEmpty()) {
1310 saveas = true;
1311 }
1312
1313 connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
1314 connect(document, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
1315
1316 QByteArray nativeFormat = document->nativeFormatMimeType();
1317 QByteArray oldMimeFormat = document->mimeType();
1318
1319 QUrl suggestedURL = QUrl::fromLocalFile(document->path());
1320
1322
1324 if (!mimeFilter.contains(oldMimeFormat)) {
1325 dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat;
1326
1327 // --- don't setOutputMimeType in case the user cancels the Save As
1328 // dialog and then tries to just plain Save ---
1329
1330 // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :))
1331 QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1332
1333 if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name
1334 suggestedFilename = suggestedFilename + "." + KisMimeDatabase::suffixesForMimeType(KIS_MIME_TYPE).first();
1335 suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename);
1336 suggestedURL.setPath(suggestedURL.path() + suggestedFilename);
1337 }
1338
1339 // force the user to choose outputMimeType
1340 saveas = true;
1341 }
1342
1343 bool ret = false;
1344
1345 if (document->path().isEmpty() || isExporting || saveas) {
1346 // if you're just File/Save As'ing to change filter options you
1347 // don't want to be reminded about overwriting files etc.
1348 bool justChangingFilterOptions = false;
1349
1350 KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveAs");
1351 dialog.setCaption(isExporting ? i18n("Exporting") : i18n("Saving As"));
1352
1353 //qDebug() << ">>>>>" << isExporting << d->lastExportLocation << d->lastExportedFormat << QString::fromLatin1(document->mimeType());
1354
1355 if (isExporting && !d->lastExportLocation.isEmpty() && !d->lastExportLocation.contains(QDir::tempPath())) {
1356
1357 // Use the location where we last exported to, if it's set, as the opening location for the file dialog
1358 QString proposedPath = QFileInfo(d->lastExportLocation).absolutePath();
1359 // If the document doesn't have a filename yet, use the title
1360 QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo("title") : QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1361 // Use the last mimetype we exported to by default
1362 QString proposedMimeType = d->lastExportedFormat.isEmpty() ? "" : d->lastExportedFormat;
1363 QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,");
1364
1365 // Set the default dir: this overrides the one loaded from the config file, since we're exporting and the lastExportLocation is not empty
1366 dialog.setDefaultDir(proposedPath + "/" + proposedFileName + "." + proposedExtension, true);
1367 dialog.setMimeTypeFilters(mimeFilter, proposedMimeType);
1368 }
1369 else {
1370 //This section only runs when the user haven't exported yet during the session, behavior set in the File Handling, Default MimeType setting.
1371 KisConfig cfg(true);
1372 QByteArray default_mime_type = cfg.exportMimeType(false).toUtf8();
1373 QString proposedMimeType = QString::fromLatin1(default_mime_type);
1374
1375 // Get the last used location for saving
1376 KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
1377 QString proposedPath = group.readEntry("SaveAs", "");
1378 // if that is empty, get the last used location for loading
1379 if (proposedPath.isEmpty()) {
1380 proposedPath = group.readEntry("OpenDocument", "");
1381 }
1382 // If that is empty, too, use the Pictures location.
1383 if (proposedPath.isEmpty()) {
1384 proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1385 }
1386 // But only use that if the suggestedUrl, that is, the document's own url is empty, otherwise
1387 // open the location where the document currently is.
1388
1389 //For if the user picked All Files Supported as the default, where there would not be an extension
1390 if(default_mime_type == "all/mime"){
1391 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : proposedPath + "/" + QFileInfo(suggestedURL.toLocalFile()).completeBaseName(), true);
1392 }
1393
1394 if (default_mime_type != "all/mime" && !default_mime_type.isEmpty()) {
1395 QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,");
1396 //This line is responsible for setting filename, which also manipulates filters.
1397 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : proposedPath + "/" + QFileInfo(suggestedURL.toLocalFile()).completeBaseName() + "." + proposedExtension, true);
1398 }
1399
1400 if (!isExporting) {
1401 // If Saving, use the document's mimetype, or if that is empty, kra, which is the safest.
1402 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(), true);
1403 default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType();
1404 }
1405 dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type));
1406 }
1407
1408 QString newFilePath = dialog.filename();
1409
1410 bool bOk = true;
1411 if (newFilePath.isEmpty()) {
1412 bOk = false;
1413 }
1414
1415 if (bOk) {
1416 if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) {
1417 QString fn = newFilePath;
1418 QFileInfo info(fn);
1419 document->documentInfo()->setAboutInfo("title", info.completeBaseName());
1420 }
1421
1422 QByteArray outputFormat = nativeFormat;
1423
1424 QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newFilePath, false);
1425 outputFormat = outputFormatString.toLatin1();
1426
1427
1428 if (!isExporting) {
1429 justChangingFilterOptions = (newFilePath == document->path()) && (outputFormat == document->mimeType());
1430 }
1431 else {
1432 QString path = QFileInfo(d->lastExportLocation).absolutePath();
1433 QString filename = QFileInfo(document->path()).completeBaseName();
1434 justChangingFilterOptions = (QFileInfo(newFilePath).absolutePath() == path)
1435 && (QFileInfo(newFilePath).completeBaseName() == filename)
1436 && (outputFormat == d->lastExportedFormat);
1437 }
1438
1439 bool wantToSave = true;
1440
1441 // don't change this line unless you know what you're doing :)
1442 if (!justChangingFilterOptions) {
1443 if (!document->isNativeFormat(outputFormat))
1444 wantToSave = true;
1445 }
1446
1447 if (wantToSave) {
1448 if (!isExporting) { // Save As
1449 ret = document->saveAs(newFilePath, outputFormat, true);
1450 if (ret) {
1451 dbgUI << "Successful Save As!";
1452 KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath));
1453 } else {
1454 dbgUI << "Failed Save As!";
1455
1456 }
1457 }
1458 else { // Export
1459 ret = document->exportDocument(newFilePath, outputFormat, isAdvancedExporting, true);
1460 if (ret) {
1461 d->lastExportLocation = newFilePath;
1462 d->lastExportedFormat = outputFormat;
1463 }
1464 }
1465
1466 } // if (wantToSave) {
1467 else
1468 ret = false;
1469 } // if (bOk) {
1470 else
1471 ret = false;
1472 } else { // saving
1473 // We cannot "export" into the currently
1474 // opened document. We are not Gimp.
1475 KIS_ASSERT_RECOVER_NOOP(!isExporting);
1476
1477 // be sure document has the correct outputMimeType!
1478 if (document->isModified()) {
1479 ret = document->save(true, 0);
1480 }
1481
1482 if (!ret) {
1483 dbgUI << "Failed Save!";
1484 }
1485 }
1486
1487 return ret;
1488}
1489
1491{
1492 if (activeView()) {
1493 activeView()->document()->undoStack()->undo();
1494 }
1495}
1496
1498{
1499 if (activeView()) {
1500 activeView()->document()->undoStack()->redo();
1501 }
1502}
1503
1504void KisMainWindow::closeEvent(QCloseEvent *e)
1505{
1506 if (hackIsSaving()) {
1507 e->setAccepted(false);
1508 return;
1509 }
1510
1511 if (!KisPart::instance()->closingSession()) {
1512 QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
1513 if ((action) && (action->isChecked())) {
1514 action->setChecked(false);
1515 }
1516
1517 // Save session when last window is closed
1518 if (KisPart::instance()->mainwindowCount() == 1) {
1519 bool closeAllowed = KisPart::instance()->closeSession();
1520
1521 if (!closeAllowed) {
1522 e->setAccepted(false);
1523 return;
1524 }
1525 }
1526 }
1527
1528 d->mdiArea->closeAllSubWindows();
1529
1530 QList<QMdiSubWindow*> childrenList = d->mdiArea->subWindowList();
1531
1532 if (childrenList.isEmpty()) {
1533 // TODO: that assignment looks really suspicious, because `e`
1534 // is destroyed right after the exit of this function
1535 d->deferredClosingEvent = e;
1536 d->canvasWindow->close();
1537 } else {
1538 e->setAccepted(false);
1539 }
1540}
1541
1543{
1544 KSharedConfigPtr config = KSharedConfig::openConfig();
1545
1546 if (d->windowSizeDirty ) {
1547 dbgUI << "KisMainWindow::saveWindowSettings";
1548 KConfigGroup group = d->windowStateConfig;
1549 KWindowConfig::saveWindowSize(windowHandle(), group);
1550 config->sync();
1551 d->windowSizeDirty = false;
1552 }
1553
1554 if (!d->activeView || d->activeView->document()) {
1555
1556 // Save toolbar position into the config file of the app, under the doc's component name
1557 KConfigGroup group = d->windowStateConfig;
1559
1560 // Save state of dock widgets
1561 for (QMap<QString, QDockWidget*>::const_iterator i = d->dockWidgetsMap.constBegin();
1562 i != d->dockWidgetsMap.constEnd(); ++i) {
1563 if (i.value()->widget()) {
1564 KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key());
1565 dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool());
1566 dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value()));
1567 dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x());
1568 dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y());
1569
1570 dockGroup.writeEntry("width", (int) i.value()->widget()->width());
1571 dockGroup.writeEntry("height", (int) i.value()->widget()->height());
1572 }
1573 }
1574
1575 }
1576
1577 KSharedConfig::openConfig()->sync();
1578 resetAutoSaveSettings(); // Don't let KisKMainWindow override the good stuff we wrote down
1579
1580}
1581
1582void KisMainWindow::resizeEvent(QResizeEvent * e)
1583{
1584 d->windowSizeDirty = true;
1585 KXmlGuiWindow::resizeEvent(e);
1586}
1587
1588
1589void KisMainWindow::dragMoveEvent(QDragMoveEvent *event)
1590{
1591 dragMove(event);
1592 event->accept();
1593}
1594
1595void KisMainWindow::dragLeaveEvent(QDragLeaveEvent *event)
1596{
1597 dragLeave();
1598 event->accept();
1599}
1600
1602{
1603 QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
1604 return !action || !action->isChecked();
1605}
1606
1607void KisMainWindow::showEvent(QShowEvent *event)
1608{
1609 // we're here because, we need to make sure everything (dockers, toolbars etc) is loaded and ready before
1610 // we can hide it.
1611 if (!event->spontaneous()) {
1612 setMainWindowLayoutForCurrentMainWidget(d->widgetStack->currentIndex(), false);
1613 }
1614#ifdef Q_OS_ANDROID
1615 Q_EMIT sigFullscreenOnShow(true); // Android defaults to fullscreen.
1616#endif
1617 return KXmlGuiWindow::showEvent(event);
1618}
1619
1620void KisMainWindow::setMainWindowLayoutForCurrentMainWidget(int widgetIndex, bool widgetIndexChanged)
1621{
1622
1623 if (widgetIndex == 0) {
1624 if (widgetIndexChanged) {
1629
1630 if (d->mdiArea->subWindowList().isEmpty()) {
1635 saveWindowState(true);
1636 } else {
1637 saveMainWindowSettings(d->windowStateConfig);
1638 }
1639 }
1641 }
1642 else {
1643 setAutoSaveSettings(d->windowStateConfig, false);
1644 statusBar()->setVisible(KisConfig(true).showStatusBar());
1645 }
1646
1647 QList<QAction *> actions = d->dockWidgetMenu->menu()->actions();
1648 actions.append(toolBarMenuAction()->menu()->actions());
1649 for (QAction *action : actions) {
1650 if (action) {
1651 action->setEnabled(widgetIndex);
1652 }
1653 }
1654}
1655
1657{
1658 // This makes sure we don't save window state when we're in welcome page mode, because all the dockers
1659 // etc are hidden while the user is here.
1661
1662 toggleDockersVisibility(false, true);
1663 if (statusBar()) {
1664 statusBar()->hide();
1665 }
1666 QList<QToolBar *> toolbars = findChildren<QToolBar *>();
1667 for (QToolBar *toolbar : toolbars) {
1668 if (toolbar->objectName() != "mainToolBar") {
1669 toolbar->hide();
1670 }
1671 }
1672}
1673
1675{
1676 d->activeView = view;
1677
1678 if (d->undoActionsUpdateManager) {
1679 d->undoActionsUpdateManager->setCurrentDocument(view ? view->document() : 0);
1680 }
1681
1682 d->viewManager->setCurrentView(view);
1683
1684 d->activeViewConnections.clear();
1685 d->activeViewConnections.addConnection(view->document(),
1686 SIGNAL(sigPathChanged(QString)),
1687 this, SLOT(slotUpdateSaveActionTitle(QString)));
1688 slotUpdateSaveActionTitle(view->document()->path());
1689 d->activeViewConnections.addConnection(view->document(),
1690 SIGNAL(sigReadWriteChanged(bool)),
1691 this, SLOT(slotUpdateReadWriteMode(bool)));
1692 slotUpdateReadWriteMode(view->document()->isReadWrite());
1693
1695
1696 Q_EMIT activeViewChanged();
1697}
1698
1700{
1701 d->activeViewConnections.clear();
1702 slotUpdateSaveActionTitle(QString());
1704}
1705
1706void KisMainWindow::dragMove(QDragMoveEvent * event)
1707{
1708 QTabBar *tabBar = d->findTabBarHACK();
1709
1710 if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) {
1711 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)!";
1712 }
1713
1714 if (tabBar && tabBar->isVisible()) {
1715 QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos()));
1716 if (tabBar->rect().contains(pos)) {
1717 const int tabIndex = tabBar->tabAt(pos);
1718
1719 if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) {
1720 d->tabSwitchCompressor->start(tabIndex);
1721 }
1722 } else if (d->tabSwitchCompressor->isActive()) {
1723 d->tabSwitchCompressor->stop();
1724 }
1725 }
1726}
1727
1729{
1730 if (d->tabSwitchCompressor->isActive()) {
1731 d->tabSwitchCompressor->stop();
1732 }
1733}
1734
1735
1737{
1738 QTabBar *tabBar = d->findTabBarHACK();
1739 if (!tabBar) return;
1740
1741 tabBar->setCurrentIndex(index);
1742}
1743
1745{
1746 const int currentIndex = show ? 0 : 1;
1747 if (d->widgetStack->currentIndex() != currentIndex) {
1748 setUpdatesEnabled(false);
1749 // These have to be done in different sequence to avoid graphical
1750 // layout glitch during the switch.
1751 if (show) {
1752 setMainWindowLayoutForCurrentMainWidget(currentIndex, true);
1753 d->widgetStack->setCurrentIndex(currentIndex);
1754 } else {
1755 d->widgetStack->setCurrentIndex(currentIndex);
1756 setMainWindowLayoutForCurrentMainWidget(currentIndex, true);
1757 }
1758 setUpdatesEnabled(true);
1759 }
1760}
1761
1763{
1765
1766 KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/"));
1767 startupWidget->setWindowModality(Qt::WindowModal);
1768 startupWidget->setWindowTitle(i18n("Create new document"));
1769
1770
1771 KisConfig cfg(true);
1772
1773 int w = cfg.defImageWidth();
1774 int h = cfg.defImageHeight();
1775 const double resolution = cfg.defImageResolution();
1776 const QString colorModel = cfg.defColorModel();
1777 const QString colorDepth = cfg.defaultColorDepth();
1778 const QString colorProfile = cfg.defColorProfile();
1779
1780
1782 item.widget = new KisCustomImageWidget(startupWidget,
1783 w,
1784 h,
1785 resolution,
1786 colorModel,
1787 colorDepth,
1788 colorProfile,
1789 i18n("Unnamed"));
1790
1791 item.icon = "document-new";
1792 item.title = i18n("Custom Document");
1793 startupWidget->addCustomDocumentWidget(item.widget, item.title, "Custom Document", item.icon);
1794
1795 item.widget = new KisImageFromClipboardWidget(startupWidget,
1796 0,
1797 0,
1798 resolution,
1799 colorModel,
1800 colorDepth,
1801 colorProfile,
1802 i18n("Unnamed"));
1803
1804 item.title = i18n("Create from Clipboard");
1805 item.icon = "tab-new";
1806
1807 startupWidget->addCustomDocumentWidget(item.widget, item.title, "Create from ClipBoard", item.icon);
1808
1809 connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*)));
1810 connect(startupWidget, SIGNAL(openTemplate(QUrl)), KisPart::instance(), SLOT(openTemplate(QUrl)));
1811
1812 startupWidget->exec();
1813 startupWidget->deleteLater();
1814}
1815
1817{
1818 dbgUI << "slotImportFile()";
1819 slotFileOpen(true);
1820}
1821
1822
1823void KisMainWindow::slotFileOpen(bool isImporting)
1824{
1825 QStringList urls = showOpenFileDialog(isImporting);
1826
1827 if (urls.isEmpty())
1828 return;
1829
1830 Q_FOREACH (const QString& url, urls) {
1831
1832 if (!url.isEmpty()) {
1833 OpenFlags flags = isImporting ? Import : None;
1834 bool res = openDocument(url, flags);
1835 if (!res) {
1836 warnKrita << "Loading" << url << "failed";
1837 }
1838 }
1839 }
1840}
1841
1843{
1844 (void) openDocument(url.toLocalFile(), None);
1845}
1846
1848{
1849 if (saveDocument(d->activeView->document(), false, false,false)) {
1850 Q_EMIT documentSaved();
1851 }
1852}
1853
1855{
1856 if (saveDocument(d->activeView->document(), true, false,false)) {
1857 Q_EMIT documentSaved();
1858 }
1859}
1860
1862{
1863 if (saveDocument(d->activeView->document(), true, true,false)) {
1864 Q_EMIT documentSaved();
1865 }
1866}
1868{
1869 if (saveDocument(d->activeView->document(), true, true,true)) {
1870 Q_EMIT documentSaved();
1871 }
1872}
1873
1877
1879{
1880 return d->viewManager->canvasResourceProvider()->resourceManager();
1881}
1882
1884{
1885 return d->mdiArea->subWindowList().size();
1886}
1887
1888const KConfigGroup &KisMainWindow::windowStateConfig() const
1889{
1890 return d->windowStateConfig;
1891}
1892
1893void KisMainWindow::saveWindowState(bool restoreNormalState)
1894{
1895 // We don't need to save welcome page's layout
1896 if (d->widgetStack->currentIndex() == 0) {
1897 // TODO(sh_zam): We should still save position/geometry, right?
1898 return;
1899 }
1900
1901 if (restoreNormalState) {
1902 QAction *showCanvasOnly = d->viewManager->actionCollection()->action("view_show_canvas_only");
1903
1904 if (showCanvasOnly && showCanvasOnly->isChecked()) {
1905 showCanvasOnly->setChecked(false);
1906 }
1907
1908 d->windowStateConfig.writeEntry("ko_geometry", saveGeometry().toBase64());
1909 d->windowStateConfig.writeEntry("State", saveState().toBase64());
1910
1911 // if the dockers are hidden at this time, save their state.
1912 if (!d->toggleDockers->isChecked()) {
1913 restoreState(d->dockerStateBeforeHiding);
1914 }
1915
1916 statusBar()->setVisible(true);
1917 menuBar()->setVisible(true);
1918
1920
1921 } else {
1922 saveMainWindowSettings(d->windowStateConfig);
1923 }
1924
1925}
1926
1927bool KisMainWindow::restoreWorkspaceState(const QByteArray &state)
1928{
1929 QByteArray oldState = saveState();
1930 const bool showTitlebars = KisConfig(false).showDockerTitleBars();
1931
1932 // needed because otherwise the layout isn't correctly restored in some situations
1933 Q_FOREACH (QDockWidget *dock, dockWidgets()) {
1934 if (dock) {
1935 dock->setProperty("Locked", false); // Unlock invisible dockers
1936 dock->toggleViewAction()->setEnabled(true);
1937 dock->hide();
1938 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
1939 dock->titleBarWidget()->setVisible(showTitlebars);
1940 }
1941 }
1942 }
1943
1944 bool success = KXmlGuiWindow::restoreState(state);
1945
1946 if (!success) {
1947 KXmlGuiWindow::restoreState(oldState);
1948 Q_FOREACH (QDockWidget *dock, dockWidgets()) {
1949 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
1950 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
1951 }
1952 }
1953 return false;
1954 }
1955 return success;
1956}
1957
1959{
1960 QString md5 = sender()->property("md5").toString();
1962 KoResourceSP resource = rserver->resource(md5, "", "");
1963 if (resource) {
1964 restoreWorkspace(resource);
1965 }
1966 else {
1967 qWarning() << "Could not retrieve resource for" << md5;
1968 }
1969}
1970
1972{
1973 QList<KisKActionCollection *> actionCollections;
1974
1975 auto clients = guiFactory()->clients();
1976 int actionsCount = 0;
1977 for (const KisKXMLGUIClient *c : clients) {
1978 if (!c) {
1979 continue;
1980 }
1981 if (auto collection = c->actionCollection()) {
1982 actionCollections.append(collection);
1983 actionsCount += collection->count();
1984 }
1985 }
1986
1987 if (activeKisView()) {
1988 KisKActionCollection *layerActionCollection = new KisKActionCollection(0, "layeractions (disposable)");
1989 layerActionCollection->setComponentDisplayName(i18n("Layers/Masks"));
1990 KisNodeActivationActionCreatorVisitor v(layerActionCollection, viewManager()->nodeManager());
1991 activeKisView()->image()->rootLayer()->accept(v);
1992 actionCollections.append(layerActionCollection);
1993 actionsCount += layerActionCollection->count();
1994 }
1995
1996 d->commandBar->updateBar(actionCollections, actionsCount);
1997
1998 // The following line is needed to work around input method not working
1999 // on Windows.
2000 // See https://bugs.kde.org/show_bug.cgi?id=395598
2001 // and https://bugs.kde.org/show_bug.cgi?id=438122
2002 d->commandBar->activateWindow();
2003
2004 // The following line is present in Kate's version and was ported over
2005 // but I am sceptical of its use. I worry that it may subtly cause other
2006 // issues, and since the command bar appears to work fine without it, I
2007 // believe it may be better to leave it out. -- Alvin
2008 // centralWidget()->setFocusProxy(d->commandBar);
2009}
2010
2011void KisMainWindow::slotStoragesWarning(const QString &/*location*/)
2012{
2013 QString warning;
2015 warning = i18n("You don't have any resource bundles enabled.");
2016 }
2017
2018 if (!checkPaintOpAvailable()) {
2019 warning += i18n("\nThere are no brush presets available. Please enable a bundle that has presets before continuing.\n");
2020 QMessageBox::critical(this, i18nc("@title:window", "Krita"), warning);
2021
2022 QAction *action = actionCollection()->action("manage_bundles");
2023 if (action) {
2024 action->trigger();
2025 }
2026 }
2027
2029 QMessageBox::warning(this, i18nc("@title:window", "Krita"), warning + i18n("\nOnly your local resources are available."));
2030 }
2031
2032}
2033
2035{
2036 KisWorkspaceResourceSP workspace = res.dynamicCast<KisWorkspaceResource>();
2037
2038 bool success = restoreWorkspaceState(workspace->dockerState());
2039
2040 const bool showTitlebars = KisConfig(false).showDockerTitleBars();
2041 Q_FOREACH (QDockWidget *dock, dockWidgets()) {
2042 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
2043 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
2044 }
2045 }
2046
2047 if (activeKisView()) {
2048 activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace);
2049 }
2050
2051 d->viewManager->notifyWorkspaceLoaded();
2052
2053 return success;
2054}
2055
2057{
2058 QByteArray currentWorkspace = saveState();
2059
2060 if (!d->workspaceBorrowedBy.isNull()) {
2061 if (other->id() == d->workspaceBorrowedBy) {
2062 // We're swapping our original workspace back
2063 d->workspaceBorrowedBy = QUuid();
2064 return currentWorkspace;
2065 } else {
2066 // Get our original workspace back before swapping with a third window
2067 KisMainWindow *borrower = KisPart::instance()->windowById(d->workspaceBorrowedBy);
2068 if (borrower) {
2069 QByteArray originalLayout = borrower->borrowWorkspace(this);
2070 borrower->restoreWorkspaceState(currentWorkspace);
2071
2072 d->workspaceBorrowedBy = other->id();
2073 return originalLayout;
2074 }
2075 }
2076 }
2077
2078 d->workspaceBorrowedBy = other->id();
2079 return currentWorkspace;
2080}
2081
2083{
2084 QByteArray workspaceA = a->borrowWorkspace(b);
2085 QByteArray workspaceB = b->borrowWorkspace(a);
2086
2087 a->restoreWorkspaceState(workspaceB);
2088 b->restoreWorkspaceState(workspaceA);
2089}
2090
2092{
2093 return d->viewManager;
2094}
2095
2097{
2098 if (!d->activeView->document())
2099 return;
2100
2101 KoDocumentInfo *docInfo = d->activeView->document()->documentInfo();
2102
2103 if (!docInfo)
2104 return;
2105
2106 KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo);
2107
2108 if (dlg->exec()) {
2109 if (dlg->isDocumentSaved()) {
2110 d->activeView->document()->setModified(false);
2111 } else {
2112 d->activeView->document()->setModified(true);
2113 }
2114 }
2115
2116 delete dlg;
2117}
2118
2120{
2121 Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2122 if (subwin) {
2123 if(!subwin->close())
2124 return false;
2125 }
2126 }
2127
2128 return true;
2129}
2130
2132{
2133 // Do not close while KisMainWindow has the savingEntryMutex locked, bug409395.
2134 // After the background saving job is initiated, KisDocument blocks closing
2135 // while it saves itself.
2136 if (hackIsSaving()) {
2137 return;
2138 }
2140}
2141
2143{
2144 if (!activeView()) return;
2145
2146 KisDocument *document = activeView()->document();
2147 if (!document) return;
2148
2149 KisDlgImportImageSequence dlg(this, document);
2150
2151 if (dlg.exec() == QDialog::Accepted) {
2152 QStringList files = dlg.files();
2153 int firstFrame = dlg.firstFrame();
2154 int step = dlg.step();
2155 bool startFrom1 = dlg.startFrom1();
2156 bool autoAddHoldframes = dlg.autoAddHoldframes();
2157
2158
2159 KoUpdaterPtr updater =
2160 !document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0;
2161 KisAnimationImporter importer(document->image(), updater);
2162 int isAscending = dlg.isAscending();
2163 KisImportExportErrorCode status = importer.import(files, firstFrame, step, autoAddHoldframes, startFrom1, isAscending); // modify here, add a flag
2164
2165 if (!status.isOk() && !status.isInternalError()) {
2166 QString msg = status.errorMessage();
2167 if (!msg.isEmpty())
2168 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
2169 }
2170 activeView()->canvasBase()->refetchDataFromImage();
2171 }
2172}
2173
2175{
2176 KisDocument *document;
2178
2179 if (dlg.exec() == QDialog::Accepted) {
2180 const QTemporaryDir outputLocation(QDir::tempPath() + QDir::separator() + "krita" + QDir::separator() + "import_files");
2181 RenderedFrames renderedFrames = dlg.renderFrames(QDir(outputLocation.path()));
2182 dbgFile << "Frames rendered to directory: " << outputLocation.path();
2183 QStringList documentInfoList = dlg.documentInfo();
2184
2185 if (renderedFrames.isEmpty()) return;
2186
2187 dbgFile << "Animation Import options: " << documentInfoList;
2188
2189 int firstFrame = 0;
2190 const int step = documentInfoList[0].toInt();
2191 const int fps = documentInfoList[1].toInt();
2192 const int totalFrames = renderedFrames.framesNeedRelocation() ? (renderedFrames.renderedFrameTargetTimes.last() + 1) : renderedFrames.size() * step;
2193 const QString name = QFileInfo(documentInfoList[3]).fileName();
2194 const bool useCurrentDocument = documentInfoList[4].toInt();
2195 bool useDocumentColorSpace = false;
2196
2197 if ( useCurrentDocument ) {
2198 document = activeView()->document();
2199
2200 dbgFile << "Current frames:" << document->image()->animationInterface()->totalLength() << "total frames:" << totalFrames;
2201 if ( document->image()->animationInterface()->totalLength() < totalFrames ) {
2202 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2203 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2204 }
2205 } else {
2206 const int width = documentInfoList[5].toInt();
2207 const int height = documentInfoList[6].toInt();
2208 const double resolution = documentInfoList[7].toDouble();
2209
2210 const QString colorModel = documentInfoList[8];
2211 const QString colorDepth = documentInfoList[9];
2212 const QString profile = documentInfoList[10];
2213 useDocumentColorSpace = profile != "Default";
2214
2215 document = KisPart::instance()->createDocument();
2216 document->setObjectName(name);
2217
2218 KisPart::instance()->addDocument(document, false);
2219 const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile);
2220 Q_ASSERT(cs);
2221
2222 QColor qc(Qt::white);
2223 qc.setAlpha(0);
2224 KoColor bgColor(qc, cs);
2225
2226 if (!document->newImage(name, width, height, cs, bgColor, KisConfig::RASTER_LAYER, 1, "", double(resolution / 72) )) {
2227 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Failed to create new document. Animation import aborted."));
2228 return;
2229 }
2230
2231 document->image()->animationInterface()->setFramerate(fps);
2232 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2233 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2234
2235 this->showDocument(document);
2236 }
2237
2238 KoUpdaterPtr updater =
2239 !document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0;
2240 KisAnimationImporter importer(document->image(), updater);
2241 KisImportExportErrorCode status = importer.import(renderedFrames.renderedFrameFiles, firstFrame, step, false, false, 0, useDocumentColorSpace, renderedFrames.renderedFrameTargetTimes);
2242
2243 if (!status.isOk() && !status.isInternalError()) {
2244 QString msg = status.errorMessage();
2245 if (!msg.isEmpty())
2246 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
2247 }
2248
2249 activeView()->canvasBase()->refetchDataFromImage();
2250 document->image()->refreshGraphAsync();
2251 document->image()->waitForDone();
2252
2253 }
2254}
2255
2257{
2258 if (!activeView()) return;
2259
2260 KisImageSP image = viewManager()->image();
2261
2262 if (!image) return;
2263 if (!image->animationInterface()->hasAnimation()) return;
2264
2265 KisDocument *doc = viewManager()->document();
2266
2267 KisDlgAnimationRenderer dlgAnimationRenderer(doc, viewManager()->mainWindow());
2268 dlgAnimationRenderer.setCaption(i18n("Render Animation"));
2269 if (dlgAnimationRenderer.exec() == QDialog::Accepted) {
2270 KisAnimationRenderingOptions encoderOptions = dlgAnimationRenderer.getEncoderOptions();
2271 KisAnimationRender::render(doc, viewManager(), encoderOptions);
2272 }
2273}
2274
2276{
2277 if (!activeView()) return;
2278
2279 KisImageSP image = viewManager()->image();
2280
2281 if (!image) return;
2282 if (!image->animationInterface()->hasAnimation()) return;
2283
2284 KisDocument *doc = viewManager()->document();
2285
2286 KisConfig cfg(true);
2287
2288 KisPropertiesConfigurationSP settings = cfg.exportConfiguration("ANIMATION_EXPORT");
2289
2290 KisAnimationRenderingOptions encoderOptions;
2291 encoderOptions.fromProperties(settings);
2292
2293 KisAnimationRender::render(doc, viewManager(), encoderOptions);
2294}
2295
2297{
2299 KisKEditToolBar edit(factory(), this);
2300 connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
2301 (void) edit.exec();
2303}
2304
2306{
2307 KisApplication *kisApp = static_cast<KisApplication*>(qApp);
2308 kisApp->askResetConfig();
2309}
2310
2312{
2313 applyMainWindowSettings(d->windowStateConfig);
2314
2316 Q_UNUSED(factory);
2317
2318 // Check if there's an active view
2319 if (!d->activeView)
2320 return;
2321
2322 plugActionList("toolbarlist", d->toolbarList);
2323
2331}
2332
2334{
2335 //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true;
2336 // The action (sender) and the toolbar have the same name
2337 KisToolBar * bar = toolBar(sender()->objectName());
2338 if (bar) {
2339 if (toggle) {
2340 bar->show();
2341 }
2342 else {
2343 bar->hide();
2344 }
2345
2346 if (d->activeView && d->activeView->document()) {
2348 }
2349 } else
2350 warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!";
2351}
2352
2354{
2355 KisConfig cfg(false);
2356 cfg.setFullscreenMode(fullScreen);
2357
2358 if (fullScreen) {
2359 setWindowState(windowState() | Qt::WindowFullScreen); // set
2360 } else {
2361 setWindowState(windowState() & ~Qt::WindowFullScreen); // reset
2362 }
2363 d->fullScreenMode->setChecked(isFullScreen());
2364}
2365
2367{
2368 QDockWidget* dockWidget = 0;
2369 const bool showTitlebars = KisConfig(false).showDockerTitleBars();
2370
2371 if (!d->dockWidgetsMap.contains(factory->id())) {
2372 dockWidget = factory->createDockWidget();
2373 KAcceleratorManager::setNoAccel(dockWidget);
2374
2375 // It is quite possible that a dock factory cannot create the dock; don't
2376 // do anything in that case.
2377 if (!dockWidget) {
2378 warnKrita << "Could not create docker for" << factory->id();
2379 return 0;
2380 }
2381
2382 KoDockWidgetTitleBar *titleBar = dynamic_cast<KoDockWidgetTitleBar*>(dockWidget->titleBarWidget());
2383
2384 // Check if the dock widget is supposed to be collapsible
2385 if (!dockWidget->titleBarWidget()) {
2386 titleBar = new KoDockWidgetTitleBar(dockWidget);
2387 dockWidget->setTitleBarWidget(titleBar);
2388 }
2389 if (titleBar) {
2390 titleBar->setFont(KisUiFont::dockFont());
2391 }
2392
2393 if (dockWidget->titleBarWidget() && !dockWidget->titleBarWidget()->inherits("KisUtilityTitleBar")) {
2394 dockWidget->titleBarWidget()->setVisible(KisConfig(true).showDockerTitleBars());
2395 }
2396
2397 dockWidget->setObjectName(factory->id());
2398 dockWidget->setParent(this);
2399 if (!showTitlebars) {
2400 if (dockWidget->titleBarWidget() && !dockWidget->titleBarWidget()->inherits("KisUtilityTitleBar")) {
2401 dockWidget->titleBarWidget()->setVisible(false);
2402 }
2403 dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
2404 }
2405 if (dockWidget->widget() && dockWidget->widget()->layout())
2406 dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1);
2407
2408 Qt::DockWidgetArea side = Qt::RightDockWidgetArea;
2409 bool visible = true;
2410
2411 switch (factory->defaultDockPosition()) {
2413 dockWidget->setFloating(true); // position nicely?
2414 break;
2416 side = Qt::TopDockWidgetArea; break;
2418 side = Qt::LeftDockWidgetArea; break;
2420 side = Qt::BottomDockWidgetArea; break;
2422 side = Qt::RightDockWidgetArea; break;
2424 default:
2425 side = Qt::RightDockWidgetArea;
2426 visible = false;
2427 }
2428
2429 KConfigGroup group = d->windowStateConfig.group("DockWidget " + factory->id());
2430 side = static_cast<Qt::DockWidgetArea>(group.readEntry("DockArea", static_cast<int>(side)));
2431 if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea;
2432
2433 addDockWidget(side, dockWidget);
2434 if (!visible) {
2435 dockWidget->hide();
2436 }
2437
2438 bool locked = group.readEntry("Locked", false);
2439 if (titleBar && locked) {
2440 titleBar->setLocked(true);
2441 }
2442 else if ( dockWidget->titleBarWidget()->inherits("KisUtilityTitleBar") && locked){
2443 KisUtilityTitleBar *utilityTitleBar = dynamic_cast<KisUtilityTitleBar*>(dockWidget->titleBarWidget());
2444 utilityTitleBar->setLocked(true);
2445 }
2446
2447 d->dockWidgetsMap.insert(factory->id(), dockWidget);
2448 }
2449 else {
2450 dockWidget = d->dockWidgetsMap[factory->id()];
2451 }
2452
2453#ifdef Q_OS_MACOS
2454 dockWidget->setAttribute(Qt::WA_MacSmallSize, true);
2455#endif
2456 dockWidget->setFont(KisUiFont::dockFont());
2457
2458 connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts()));
2459
2460 return dockWidget;
2461}
2462
2464{
2465 Q_FOREACH (QObject *child, children()) {
2466 if (child->inherits("QTabBar")) {
2467 ((QTabBar *)child)->setFont(KisUiFont::dockFont());
2468 }
2469 }
2470}
2471
2473{
2474 KisConfig cfg(true);
2475 QString themeFromConfig = cfg.widgetStyle();
2476
2477 Q_FOREACH (auto key, d->actionMap.keys()) { // find checked style to save to config
2478 if(d->actionMap.value(key)->isChecked()) {
2479 cfg.setWidgetStyle(key);
2480 qApp->setProperty(currentUnderlyingStyleNameProperty, key);
2481 qApp->setStyle(key);
2482
2483 // When switching to a style that uses system colors, reset the theme
2484#ifndef Q_OS_HAIKU
2485#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
2486 if (qApp->style()->objectName() == "macintosh") {
2487 d->themeManager->setCurrentTheme("System");
2488 }
2489#else
2490 if (qApp->style()->name() == "macos") {
2491 d->themeManager->setCurrentTheme("System");
2492 }
2493#endif
2494#endif
2495 }
2496 }
2497}
2498
2500{
2501 return d->dockWidgetsMap.values();
2502}
2503
2504QDockWidget* KisMainWindow::dockWidget(const QString &id)
2505{
2506 if (!d->dockWidgetsMap.contains(id)) return 0;
2507 return d->dockWidgetsMap[id];
2508}
2509
2511{
2513
2514 observers.append(static_cast<KoCanvasObserverBase*>(KisPart::instance()->playbackEngine()));
2515
2516 Q_FOREACH (QDockWidget *docker, dockWidgets()) {
2517 KoCanvasObserverBase *observer = dynamic_cast<KoCanvasObserverBase*>(docker);
2518 if (observer) {
2519 observers << observer;
2520 }
2521 else {
2522 warnKrita << docker << "is not a canvas observer";
2523 }
2524 }
2525
2526 return observers;
2527}
2528
2529
2530void KisMainWindow::toggleDockersVisibility(bool visible, bool onWelcomePage)
2531{
2532 if (!visible) {
2533 d->dockerStateBeforeHiding = saveState();
2534
2535 Q_FOREACH (QObject* widget, children()) {
2536 if (widget->inherits("QDockWidget")) {
2537 QDockWidget* dw = static_cast<QDockWidget*>(widget);
2538 if (dw->isVisible() && !(onWelcomePage && dw->property("ShowOnWelcomePage").toBool())) {
2539 dw->hide();
2540 }
2541 }
2542 }
2543 }
2544 else {
2545 restoreState(d->dockerStateBeforeHiding);
2546 }
2547}
2548
2550{
2551 bool enabled = (activeKisView() != 0);
2552
2553 d->mdiCascade->setEnabled(enabled);
2554 d->mdiNextWindow->setEnabled(enabled);
2555 d->mdiPreviousWindow->setEnabled(enabled);
2556 d->mdiTile->setEnabled(enabled);
2557 d->close->setEnabled(enabled);
2558 d->closeAll->setEnabled(enabled);
2559
2560 setActiveSubWindow(d->mdiArea->activeSubWindow());
2561
2571 QMdiSubWindow *subWindow = d->mdiArea->currentSubWindow();
2572 if (subWindow) {
2573 QMenu *menu = subWindow->systemMenu();
2574 if (menu && menu->actions().size() == 8) {
2575 Q_FOREACH (QAction *action, menu->actions()) {
2576 action->setShortcut(QKeySequence());
2577
2578 }
2579 menu->actions().last()->deleteLater();
2580 }
2581 }
2582
2583 d->actionManager()->updateGUI();
2584}
2585
2587{
2591 if (viewManager() && viewManager()->selectionManager()) {
2593 }
2594
2595 KisPart *kisPart = KisPart::instance();
2597 if (!layoutManager->primaryWorkspaceFollowsFocus()) return;
2598
2599 QUuid primary = layoutManager->primaryWindowId();
2600 if (primary.isNull()) return;
2601
2602 if (d->id == primary) {
2603 if (!d->workspaceBorrowedBy.isNull()) {
2604 KisMainWindow *borrower = kisPart->windowById(d->workspaceBorrowedBy);
2605 if (!borrower) return;
2606 swapWorkspaces(this, borrower);
2607 }
2608 } else {
2609 if (d->workspaceBorrowedBy == primary) return;
2610
2611 KisMainWindow *primaryWindow = kisPart->windowById(primary);
2612 if (!primaryWindow) return;
2613 swapWorkspaces(this, primaryWindow);
2614 }
2615}
2616
2617
2619{
2620 QMenu *menu = d->windowMenu->menu();
2621 menu->clear();
2622
2623#ifndef Q_OS_ANDROID
2624 menu->addAction(d->newWindow);
2625#endif
2626 menu->addAction(d->documentMenu);
2627
2628 QMenu *docMenu = d->documentMenu->menu();
2629 docMenu->clear();
2630
2631 QFontMetrics fontMetrics = docMenu->fontMetrics();
2632 QRect geom = this->geometry();
2633 QPoint p(geom.width() / 2 + geom.left(), geom.height() / 2 + geom.top());
2634 QScreen *screen = qApp->screenAt(p);
2635 int fileStringWidth = 300;
2636 if (screen) {
2637 fileStringWidth = int(screen->availableGeometry().width() * .40f);
2638 }
2639 Q_FOREACH (QPointer<KisDocument> doc, KisPart::instance()->documents()) {
2640 if (doc) {
2641 QString title = fontMetrics.elidedText(doc->path(), Qt::ElideMiddle, fileStringWidth);
2642 if (title.isEmpty() && doc->image()) {
2643 title = doc->image()->objectName();
2644 }
2645 QAction *action = docMenu->addAction(title);
2646 action->setIcon(qApp->windowIcon());
2647 connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map()));
2648 d->documentMapper->setMapping(action, doc);
2649 }
2650 }
2651
2652 menu->addAction(d->workspaceMenu);
2653 QMenu *workspaceMenu = d->workspaceMenu->menu();
2654 workspaceMenu->clear();
2656 KisResourceIterator resourceIterator(&resourceModel);
2657 KisMainWindow *m_this = this;
2658
2659 while (resourceIterator.hasNext()) {
2660 KisResourceItemSP resource = resourceIterator.next();
2661 QAction *action = workspaceMenu->addAction(resource->name());
2662 action->setProperty("md5", QVariant::fromValue<QString>(resource->md5sum()));
2663 connect(action, SIGNAL(triggered()), this, SLOT(restoreWorkspace()));
2664 }
2665 workspaceMenu->addSeparator();
2666 connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&Import Workspace...")),
2667 &QAction::triggered,
2668 this,
2669 [&]()
2670 {
2672
2673 KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
2674 dialog.setMimeTypeFilters(mimeTypes);
2675 dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
2676 QString filename = dialog.filename();
2677
2679 });
2680
2681 connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&New Workspace...")),
2682 &QAction::triggered,
2683 [=]() {
2684 QString name;
2685 name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."),
2686 i18nc("@label:textbox", "Name:"));
2687 if (name.isEmpty()) {
2688 return;
2689 }
2690
2692 workspace->setDockerState(m_this->saveState());
2693 workspace->setImage(layoutThumbnail());
2694 workspace->setValid(true);
2695
2696 // this line must happen before we save the workspace to resource folder or other places
2697 // because it mostly just triggers palettes to be saved into the workspace
2698 d->viewManager->canvasResourceProvider()->notifySavingWorkspace(workspace);
2699 workspace->setValid(true);
2700
2701 workspace->setFilename(name.replace(" ", "_") + workspace->defaultFileExtension());
2702 workspace->setName(name);
2703
2705 });
2706
2707 // TODO: What to do about delete?
2708 // workspaceMenu->addAction(i18nc("@action:inmenu", "&Delete Workspace..."));
2709
2710 menu->addSeparator();
2711 menu->addAction(d->close);
2712 menu->addAction(d->closeAll);
2713 if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2714 menu->addSeparator();
2715 menu->addAction(d->mdiTile);
2716 menu->addAction(d->mdiCascade);
2717 }
2718 menu->addSeparator();
2719 menu->addAction(d->mdiNextWindow);
2720 menu->addAction(d->mdiPreviousWindow);
2721 menu->addSeparator();
2722
2723 QList<QMdiSubWindow *> windows = d->mdiArea->subWindowList();
2724 for (int i = 0; i < windows.size(); ++i) {
2725 QPointer<KisView>child = qobject_cast<KisView*>(windows.at(i)->widget());
2726 if (child && child->document()) {
2727 QString text;
2728 if (i < 9) {
2729 text = i18n("&%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2730 }
2731 else {
2732 text = i18n("%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2733 }
2734
2735 QAction *action = menu->addAction(text);
2736 action->setIcon(qApp->windowIcon());
2737 action->setCheckable(true);
2738 action->setChecked(child == activeKisView());
2739 connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map()));
2740 d->windowMapper->setMapping(action, windows.at(i));
2741 }
2742 }
2743
2744 bool showMdiArea = windows.count( ) > 0;
2745 if (!showMdiArea) {
2746 showWelcomeScreen(true); // see workaround in function in header
2747 }
2748
2749 // enable/disable the toolbox docker if there are no documents open
2750 Q_FOREACH (QObject* widget, children()) {
2751 if (widget->inherits("QDockWidget")) {
2752 QDockWidget* dw = static_cast<QDockWidget*>(widget);
2753
2754 if ( dw->objectName() == "ToolBox") {
2755 dw->setEnabled(showMdiArea);
2756 }
2757 }
2758 }
2759}
2760
2762{
2763 bool onlyOne = false;
2764 if (d->mdiArea->subWindowList().size() == 1 && d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2765 onlyOne = true;
2766 }
2767 Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2768 if (onlyOne) {
2769 subwin->setWindowFlags(subwin->windowFlags() | Qt::FramelessWindowHint);
2770 subwin->showMaximized();
2771 } else {
2772 subwin->setWindowFlags((subwin->windowFlags() | Qt::FramelessWindowHint) ^ Qt::FramelessWindowHint);
2773 }
2774 }
2775}
2776
2778{
2779 if (!window) {
2781 return;
2782 }
2783 QMdiSubWindow *subwin = qobject_cast<QMdiSubWindow *>(window);
2784 //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow;
2785
2786 if (subwin && subwin != d->activeSubWindow) {
2787 KisView *view = qobject_cast<KisView *>(subwin->widget());
2788 //dbgKrita << "\t" << view << activeView();
2789 if (view && view != activeView()) {
2790 setActiveView(view);
2791 }
2792 d->activeSubWindow = subwin;
2793 }
2795 d->actionManager()->updateGUI();
2796}
2797
2799{
2800 KisConfig cfg(true);
2801 QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry<int>("mdi_viewmode", (int)QMdiArea::TabbedView);
2802 d->mdiArea->setViewMode(viewMode);
2803 Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2804 subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
2805 subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
2806 if (viewMode == QMdiArea::TabbedView) {
2807 subwin->setWindowState(Qt::WindowMaximized);
2808 }
2809
2817 if (viewMode == QMdiArea::TabbedView) {
2818 Qt::WindowFlags oldFlags = subwin->windowFlags();
2819 Qt::WindowFlags flags = oldFlags;
2820
2821 flags &= ~Qt::WindowStaysOnTopHint;
2822 flags &= ~Qt::WindowStaysOnBottomHint;
2823
2824 if (flags != oldFlags) {
2825 subwin->setWindowFlags(flags);
2826 subwin->showMaximized();
2827 }
2828 }
2829 }
2830#ifdef Q_OS_MACOS
2832#endif
2833
2834 KConfigGroup group( KSharedConfig::openConfig(), "theme");
2835#ifndef Q_OS_HAIKU
2836 d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
2837#endif
2838 d->actionManager()->updateGUI();
2839
2840 QString s = cfg.getMDIBackgroundColor();
2842 QBrush brush(c.toQColor());
2843 d->mdiArea->setBackground(brush);
2844
2845 QString backgroundImage = cfg.getMDIBackgroundImage();
2846 if (backgroundImage != "") {
2847 QImage image(backgroundImage);
2848 QBrush brush(image);
2849 d->mdiArea->setBackground(brush);
2850 }
2851
2852 d->mdiArea->update();
2853
2854 qApp->setFont(KisUiFont::normalFont());
2855
2856 Q_FOREACH (QObject* widget, children()) {
2857 if (widget->inherits("QDockWidget")) {
2858 QDockWidget* dw = static_cast<QDockWidget*>(widget);
2859 dw->setFont(KisUiFont::dockFont());
2860 }
2861 }
2862}
2863
2864KisView* KisMainWindow::newView(QObject *document, QMdiSubWindow *subWindow)
2865{
2866 KisDocument *doc = qobject_cast<KisDocument*>(document);
2867 KisView *view = addViewAndNotifyLoadingCompleted(doc, subWindow);
2868 d->actionManager()->updateGUI();
2869
2870 return view;
2871}
2872
2874{
2875#ifdef Q_OS_ANDROID
2876 // Check if current mainwindow exists, just to be sure.
2877 if (KisPart::instance()->currentMainwindow()) {
2878 QMessageBox::warning(this, i18nc("@title:window", "Krita"),
2879 "Creating a New Main Window is unsupported on Android");
2880 return;
2881 }
2882#endif
2884 mainWindow->initializeGeometry();
2885 mainWindow->show();
2886}
2887
2889{
2890 if (d->mdiArea->currentSubWindow()) {
2891 d->mdiArea->currentSubWindow()->close();
2892 d->actionManager()->updateGUI();
2893 }
2894}
2895
2897{
2898 // print error if the lcms engine is not available
2899 if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) {
2900 // need to wait 1 event since exiting here would not work.
2901 m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now.");
2902 m_dieOnError = true;
2903 QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
2904 return;
2905 }
2906
2908
2909 // window is created signal (used in Python)
2910 // there must be some asynchronous things happening in the constructor, because the window cannot
2911 // be referenced until after this timeout is done
2913}
2914
2916{
2917 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Installation error"), m_errorMessage);
2918 if (m_dieOnError) {
2919 exit(10);
2920 }
2921}
2922
2924{
2925 KisAboutApplication dlg(this);
2926 dlg.exec();
2927}
2928
2930{
2931 if (!d->mdiArea) return 0;
2932 QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow();
2933 //dbgKrita << "activeKisView" << activeSubWindow;
2934 if (!activeSubWindow) return 0;
2935 return qobject_cast<KisView*>(activeSubWindow->widget());
2936}
2937
2939{
2940 KIS_ASSERT_RECOVER_NOOP(controller == KoToolManager::instance()->activeCanvasController());
2941 bool isOurOwnView = false;
2942
2943 Q_FOREACH (QPointer<KisView> view, KisPart::instance()->views()) {
2944 if (view && view->canvasController() == controller) {
2945 isOurOwnView = view->mainWindow() == this;
2946 }
2947 }
2948
2949 if (!isOurOwnView) return;
2950
2951 Q_FOREACH (QWidget *w, optionWidgetList) {
2952#ifdef Q_OS_MACOS
2953 w->setAttribute(Qt::WA_MacSmallSize, true);
2954#endif
2955 w->setFont(KisUiFont::dockFont());
2956 }
2957
2958 if (d->toolOptionsDocker) {
2959 d->toolOptionsDocker->setOptionWidgets(optionWidgetList);
2960 }
2961 else {
2962 d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList);
2963 }
2964}
2965
2967{
2968 KisActionManager *actionManager = d->actionManager();
2969
2970
2971
2976 d->fullScreenMode = actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool)));
2977
2978 d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
2979
2981 d->saveAction->setActivationFlags(KisAction::IMAGE_IS_WRITABLE);
2982
2984 d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE);
2985
2987 d->undo->setActivationFlags(KisAction::ACTIVE_IMAGE);
2988
2990 d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE);
2991
2992 d->undoActionsUpdateManager.reset(new KisUndoActionsUpdateManager(d->undo, d->redo));
2993 d->undoActionsUpdateManager->setCurrentDocument(d->activeView ? d->activeView->document() : 0);
2994
2995 d->importAnimation = actionManager->createAction("file_import_animation");
2996 connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation()));
2997
2998 d->importVideoAnimation = actionManager->createAction("file_import_video_animation");
2999 connect(d->importVideoAnimation, SIGNAL(triggered()), this, SLOT(importVideoAnimation()));
3000
3001 d->renderAnimation = actionManager->createAction("render_animation");
3002 d->renderAnimation->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
3003 connect( d->renderAnimation, SIGNAL(triggered()), this, SLOT(renderAnimation()));
3004
3005 d->renderAnimationAgain = actionManager->createAction("render_animation_again");
3006 d->renderAnimationAgain->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
3007 connect( d->renderAnimationAgain, SIGNAL(triggered()), this, SLOT(renderAnimationAgain()));
3008
3009 d->closeAll = actionManager->createAction("file_close_all");
3010 connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll()));
3011
3012 d->importFile = actionManager->createAction("file_import_file");
3013 d->importFile->setActivationFlags(KisAction::IMAGE_IS_WRITABLE);
3014 connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile()));
3015
3016 d->exportFile = actionManager->createAction("file_export_file");
3017 connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile()));
3018
3019 d->exportFileAdvance = actionManager->createAction("file_export_advanced");
3020 connect(d->exportFileAdvance, SIGNAL(triggered(bool)), this, SLOT(slotExportAdvance()));
3021
3022 /* The following entry opens the document information dialog. Since the action is named so it
3023 intends to show data this entry should not have a trailing ellipses (...). */
3024 d->showDocumentInfo = actionManager->createAction("file_documentinfo");
3025 connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo()));
3026#ifndef Q_OS_HAIKU
3027 d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this));
3028 d->themeManager->registerThemeActions(actionCollection());
3029 connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged()), Qt::QueuedConnection);
3030 connect(this, SIGNAL(themeChanged()), d->welcomePage, SLOT(slotUpdateThemeColors()), Qt::UniqueConnection);
3031#endif
3032 d->toggleDockers = actionManager->createAction("view_toggledockers");
3033
3034
3035 KisConfig(true).showDockers(true);
3036 d->toggleDockers->setChecked(true);
3037 connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool)));
3038
3039 d->resetConfigurations = actionManager->createAction("reset_configurations");
3040 connect(d->resetConfigurations, SIGNAL(triggered()), this, SLOT(slotResetConfigurations()));
3041
3042#ifndef Q_OS_ANDROID
3043 d->toggleDetachCanvas = actionManager->createAction("view_detached_canvas");
3044 d->toggleDetachCanvas->setChecked(false);
3045 connect(d->toggleDetachCanvas, SIGNAL(toggled(bool)), SLOT(setCanvasDetached(bool)));
3046#endif
3047 setCanvasDetached(false);
3048
3049 d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars");
3050 d->toggleDockerTitleBars->setChecked(KisConfig(false).showDockerTitleBars());
3051 connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool)));
3052
3053 actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu);
3054 actionCollection()->addAction("window", d->windowMenu);
3055
3056 actionCollection()->addAction("style_menu", d->styleMenu); // for widget styles: breeze, fusion, etc
3057
3058 d->mdiCascade = actionManager->createAction("windows_cascade");
3059 connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows()));
3060
3061 d->mdiTile = actionManager->createAction("windows_tile");
3062 connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows()));
3063
3064 d->mdiNextWindow = actionManager->createAction("windows_next");
3065 connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow()));
3066
3067 d->mdiPreviousWindow = actionManager->createAction("windows_previous");
3068 connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow()));
3069
3070#ifndef Q_OS_ANDROID
3071 d->newWindow = actionManager->createAction("view_newwindow");
3072 connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow()));
3073#endif
3074
3076
3077 d->showSessionManager = actionManager->createAction("file_sessions");
3078 connect(d->showSessionManager, SIGNAL(triggered(bool)), this, SLOT(slotShowSessionManager()));
3079
3080 d->commandBarAction = actionManager->createAction("command_bar_open");
3081 connect(d->commandBarAction, SIGNAL(triggered(bool)), this, SLOT(openCommandBar()));
3082
3084
3085 for (int i = 0; i < 2; i++) {
3086 d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer"));
3087 d->expandingSpacers[i]->setDefaultWidget(new QWidget(this));
3088 d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
3089 actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]);
3090 }
3091
3092 {
3093 using namespace KisWidgetConnectionUtils;
3094
3095 QAction *action = actionManager->createAction("lock_toolbars");
3096 connectControl(action, KisToolBar::toolBarStateModel(), "toolBarsLocked");
3097 }
3098}
3099
3101{
3102 Q_FOREACH (KisToolBar *toolBar, toolBars()) {
3103 toolBar->layout()->setSpacing(4);
3104 toolBar->setStyleSheet("QToolBar { border: none }"); // has a border in "Fusion" style that people don't like
3105
3106 // Hide text for buttons with an icon in the toolbar
3107 Q_FOREACH (QAction *ac, toolBar->actions()){
3108 if (ac->icon().pixmap(QSize(1,1)).isNull() == false){
3109 ac->setPriority(QAction::LowPriority);
3110 } else {
3111 ac->setIcon(QIcon());
3112 }
3113 }
3114 }
3115}
3116
3118{
3119 // if the user didn's specify the geometry on the command line (does anyone do that still?),
3120 // we first figure out some good default size and restore the x,y position. See bug 285804Z.
3121 KConfigGroup cfg = d->windowStateConfig;
3122 QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray()));
3123 if (!restoreGeometry(geom)) {
3124 QRect desk = this->screen()->availableGeometry();
3125
3126 quint32 x = desk.x();
3127 quint32 y = desk.y();
3128 quint32 w = 0;
3129 quint32 h = 0;
3130
3131 // Default size -- maximize on small screens, something useful on big screens
3132 const int deskWidth = desk.width();
3133 if (deskWidth > 1024) {
3134 // a nice width, and slightly less than total available
3135 // height to compensate for the window decs
3136 w = (deskWidth / 3) * 2;
3137 h = (desk.height() / 3) * 2;
3138 }
3139 else {
3140 w = desk.width();
3141 h = desk.height();
3142 }
3143
3144 x += (desk.width() - w) / 2;
3145 y += (desk.height() - h) / 2;
3146
3147 move(x,y);
3148 setGeometry(geometry().x(), geometry().y(), w, h);
3149 }
3150 d->fullScreenMode->setChecked(isFullScreen());
3151}
3152
3154{
3155 QDesktopServices::openUrl(QUrl("https://docs.krita.org"));
3156}
3157
3159{
3160 Q_FOREACH (QDockWidget *dock, dockWidgets()) {
3161 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
3162 dock->titleBarWidget()->setVisible(show || dock->isFloating());
3163 }
3164 }
3165
3166 KisConfig cfg(true);
3167 cfg.setShowDockerTitleBars(show);
3168}
3169
3171{
3172 if (finished) {
3174 }
3175}
3176
3178{
3179 QScreen *screen = QGuiApplication::primaryScreen();
3180
3181 for (QWindow* window: QGuiApplication::topLevelWindows()) {
3182 // Android: we shouldn't transform Window managers independent of its child
3183 if ((window->type() == Qt::Popup)
3184 && (window->flags() & Qt::FramelessWindowHint) == 0
3185 && (window->geometry().topLeft() != QPoint(0, 0))) {
3186 // We are using reversed values. Because geometry returned is not the updated
3187 // one, but the previous one.
3188 int screenHeight = screen->geometry().width();
3189 int screenWidth = screen->geometry().height();
3190
3191 // scaling
3192 int new_x = (window->position().x() * screenWidth) / screenHeight;
3193 int new_y = (window->position().y() * screenHeight) / screenWidth;
3194
3195 // window width or height shouldn't change
3196 int winWidth = window->geometry().width();
3197 int winHeight = window->geometry().height();
3198
3199 // Try best to not let the window go beyond screen.
3200 if (new_x > screenWidth - winWidth) {
3201 new_x = screenWidth - winWidth;
3202 if (new_x < 0)
3203 new_x = 0;
3204 }
3205 if (new_y > screenHeight - winHeight) {
3206 new_y = screenHeight - winHeight;
3207 if (new_y < 0)
3208 new_y = 0;
3209 }
3210
3211 window->setPosition(QPoint(new_x, new_y));
3212 }
3213 }
3214}
3215
3226
3232
3233// One action has only one icon override, local XML contains ActionIconOverrides like so:
3234// <ActionIconOverrides>
3235// <Action name="erase_action" icon="wheel-sectors"/>
3236// <Action name="about_krita" icon="application-exit"/>
3237// </ActionIconOverrides>
3238// This function will rerender the actions with icons in UI (toolbars, shortcuts options etc.)
3240{
3241 QString xmlPath = KoResourcePaths::locateLocal("data", "krita5.xmlgui");
3242 QFile file(xmlPath);
3243 if (!file.exists()) {
3244 return;
3245 }
3246
3247 QDomDocument doc;
3248 if (!file.open(QIODevice::ReadOnly) || !doc.setContent(&file)) {
3249 return;
3250 }
3251
3252 QDomNodeList overridesList = doc.elementsByTagName(QStringLiteral("ActionIconOverrides"));
3253 if (!overridesList.isEmpty()) {
3254 QDomElement overridesElement = overridesList.at(0).toElement();
3255 QDomNodeList actions = overridesElement.elementsByTagName(QStringLiteral("Action"));
3256 for (int i = 0; i < actions.count(); ++i) {
3257 QDomElement actionElement = actions.at(i).toElement();
3258 QString name = actionElement.attribute(QStringLiteral("name"));
3259 QString icon = actionElement.attribute(QStringLiteral("icon"));
3260 if (!name.isEmpty() && !icon.isEmpty()) {
3261 QAction *qa = actionCollection()->action(name);
3262 if (qa) {
3263 qa->setIcon(KisIconUtils::loadIcon(icon));
3264 }
3265 }
3266 }
3267 }
3268}
3269
3271{
3272 // Add all actions with a menu property to the main window
3273 Q_FOREACH(QAction *action, this->actionCollection()->actions()) {
3274 QString menuLocation = action->property("menulocation").toString();
3275 if (!menuLocation.isEmpty()) {
3276 QAction *found = 0;
3277 QList<QAction *> candidates = this->menuBar()->actions();
3278 Q_FOREACH(const QString &name, menuLocation.split("/")) {
3279 Q_FOREACH(QAction *candidate, candidates) {
3280 if (candidate->objectName().toLower() == name.toLower()) {
3281 found = candidate;
3282 candidates = candidate->menu()->actions();
3283 break;
3284 }
3285 }
3286 if (candidates.isEmpty()) {
3287 break;
3288 }
3289 }
3290
3291 if (found && found->menu()) {
3292 QList<QAction *> existingActions = found->menu()->actions();
3293
3294 if (std::find_if(existingActions.begin(),
3295 existingActions.end(),
3296 kismpl::mem_equal_to(&QAction::objectName, action->objectName()))
3297 == existingActions.end()) {
3298
3299 if (std::is_sorted(existingActions.begin(),
3300 existingActions.end(),
3301 kismpl::mem_less(&QAction::objectName))) {
3302
3303 auto it = std::upper_bound(existingActions.begin(),
3304 existingActions.end(),
3305 action->objectName(),
3306 kismpl::mem_less(&QAction::objectName));
3307 found->menu()->insertAction(it != existingActions.end() ? *it : nullptr, action);
3308
3309 } else {
3310 found->menu()->addAction(action);
3311 }
3312 }
3313 }
3314 }
3315 }
3316}
3317
3318#include <moc_KisMainWindow.cpp>
const Params2D p
qreal v
#define KIS_MIME_TYPE
Definition KisDocument.h:56
QList< QString > QStringList
Recent files action.
KDE top level main window with predefined action layout
QAction * toolBarMenuAction()
void setStandardToolBarMenuEnabled(bool enable)
void applyMainWindowSettings(const KConfigGroup &config) override
bool event(QEvent *event) override
virtual KisKXMLGUIFactory * guiFactory()
bool isHelpMenuEnabled() const
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
KisAction * createAction(const QString &name)
void addAction(const QString &name, KisAction *action)
KisAction * createStandardAction(KStandardAction::StandardAction, const QObject *receiver, const char *member)
@ ACTIVE_IMAGE
Activate if there is at least one image.
Definition kis_action.h:43
@ IMAGE_IS_WRITABLE
Activate KisDocument::isReadWrite() is active.
Definition kis_action.h:62
@ IMAGE_HAS_ANIMATION
Activate if the image has an animation.
Definition kis_action.h:58
void fromProperties(KisPropertiesConfigurationSP config)
Base class for the Krita app.
void setFavoriteResourceManager(KisFavoriteResourceManager *favoriteResourceManager)
static KisConfigNotifier * instance()
QString widgetStyle(bool defaultValue=false)
qint32 defImageHeight(bool defaultValue=false) const
void setFullscreenMode(const bool value) const
qint32 defImageWidth(bool defaultValue=false) const
void setWidgetStyle(QString name)
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:822
QString getMDIBackgroundColor(bool defaultValue=false) const
QString getMDIBackgroundImage(bool defaultValue=false) const
bool showDockerTitleBars(bool defaultValue=false) const
qreal defImageResolution(bool defaultValue=false) const
QString exportMimeType(bool defaultValue) const
QString defColorProfile(bool defaultValue=false) const
void setShowDockerTitleBars(const bool value) const
bool toolOptionsInDocker(bool defaultValue=false) const
T readEntry(const QString &name, const T &defaultValue=T())
Definition kis_config.h:832
QString defColorModel(bool defaultValue=false) const
bool showDockers(bool defaultValue=false) const
bool useOpenGL(bool defaultValue=false) const
QString defaultColorDepth(bool defaultValue=false) const
void setFileBatchMode(const bool batchMode)
KisImageSP image
void setPath(const QString &path)
bool importDocument(const QString &path)
bool openPath(const QString &path, OpenFlags flags=None)
openPath Open a Path
static KisImageConfigNotifier * instance()
KisGroupLayerSP rootLayer() const
KisImageAnimationInterface * animationInterface() const
static QStringList supportedMimeTypes(Direction direction)
void removeTrackedCanvas(KisCanvas2 *canvas)
void addTrackedCanvas(KisCanvas2 *canvas)
A container for a set of QAction objects.
void setComponentDisplayName(const QString &displayName)
Q_INVOKABLE QAction * addAction(const QString &name, QAction *action)
QAction * action(int index) const
void addAssociatedWidget(QWidget *widget)
A dialog used to customize or configure toolbars.
Standard KDE help menu with dialog boxes.
Definition khelpmenu.h:106
void setAutoSaveSettings(const QString &groupName=QLatin1String("MainWindow"), bool saveWindowSize=true)
void resetAutoSaveSettings()
void saveMainWindowSettings(KConfigGroup &config)
KisToolBar * toolBar(const QString &name=QString())
void closeEvent(QCloseEvent *) override
QList< KisToolBar * > toolBars() const
void setLocalXMLFile(const QString &file)
QAction * action(const char *name) const
KisKXMLGUIFactory * factory() const
void setXMLFile(const QString &file, bool merge=false, bool setXMLDoc=true)
void plugActionList(const QString &name, const QList< QAction * > &actionList)
virtual KisKActionCollection * actionCollection() const
void addClient(KisKXMLGUIClient *client)
QList< KisKXMLGUIClient * > clients() const
Main window for Krita.
QMenu * createPopupMenu() override
KisActionManager * actionManager()
bool installBundle(const QString &fileName) const
Copy the given file into the bundle directory.
QPointer< KisView > activeKisView()
virtual void showAboutApplication()
showAboutApplication show the about box
QScopedPointer< KisUndoActionsUpdateManager > undoActionsUpdateManager
QDockWidget * dockWidget(const QString &id)
void applyActionIconOverridesFromLocalXML()
KisMainWindow(QUuid id=QUuid())
void setCanvasDetached(bool detached)
void guiLoadingFinished()
void dragLeaveEvent(QDragLeaveEvent *event) override
QImage layoutThumbnail()
layoutThumbnail
QTabBar * findTabBarHACK()
KisAction * newWindow
KisSignalAutoConnectionsStore activeViewConnections
QStringList showOpenFileDialog(bool isImporting)
void slotUpdateWidgetStyle()
bool checkPaintOpAvailable()
void dragMove(QDragMoveEvent *event)
void showWelcomeScreen(bool show)
void setActiveSubWindow(QWidget *window)
bool windowsLayoutSavingAllowed() const override
void slotLoadCanceled(const QString &)
void slotConfigureToolbars()
QList< KoCanvasObserverBase * > canvasObservers() const override
void slotStoragesWarning(const QString &location=QString())
void slotNewToolbarConfig()
void themeChanged()
This signal is emitted when the color theme changes.
QMdiSubWindow * activeSubWindow
bool restoreWorkspaceState(const QByteArray &state)
KConfigGroup windowStateConfig
KisAction * renderAnimation
void updateSubwindowFlags()
void slotShowSessionManager()
void slotResetConfigurations()
void showErrorAndDie()
Quits Krita with error message from m_errorMessage.
QMap< QString, QAction * > actionMap
void slotUpdateReadWriteMode(bool readWrite)
bool checkActiveBundlesAvailable()
checkActiveStorages checks whether there is at least one bundle available and at least one paintop pr...
KisView * newView(QObject *document, QMdiSubWindow *subWindow=0)
bool openDocument(const QString &path, OpenFlags flags)
void toggleDockersVisibility(bool visible, bool onWelcomePage=false)
void showDocument(KisDocument *document)
void slotXmlGuiMakingChanges(bool finished)
void newOptionWidgets(KoCanvasController *controller, const QList< QPointer< QWidget > > &optionWidgetList)
KisView * addViewAndNotifyLoadingCompleted(KisDocument *document, QMdiSubWindow *subWindow=0)
QList< QDockWidget * > dockWidgets() const
Return the list of dock widgets belonging to this main window.
bool openDocumentInternal(const QString &path, KisMainWindow::OpenFlags f=KisMainWindow::OpenFlags())
QDockWidget * createDockWidget(KoDockFactoryBase *factory)
void loadCompleted()
void resizeEvent(QResizeEvent *e) override
KisAction * renderAnimationAgain
virtual void showView(KisView *view, QMdiSubWindow *subWindow=0)
showView shows the given view, in subWindow if not null, in a new tab otherwise.
void addView(KisView *view, QMdiSubWindow *subWindow=0)
void setMainWindowLayoutForCurrentMainWidget(int widgetIndex, bool widgetIndexChanged)
QPointer< KisView > activeView
void slotUpdateSaveActionTitle(const QString &documentPath)
Private *const d
void slotPreferences()
slotPreferences open the preferences dialog
void slotFileOpenRecent(const QUrl &)
QString lastExportLocation
bool saveDocument(KisDocument *document, bool saveas, bool isExporting, bool isAdvancedExporting=false)
QByteArray lastExportedFormat
void showEvent(QShowEvent *event) override
KisViewManager * viewManager
bool hackIsSaving() const
QString m_errorMessage
void slotFileOpen(bool isImporting=false)
KisAction * undo
QMap< QString, QDockWidget * > dockWidgetsMap
void saveWindowState(bool restoreNormalState=false)
bool canvasDetached() const
KActionMenu * workspaceMenu
void viewFullscreen(bool fullScreen)
void documentSaved()
QList< QAction * > toolbarList
void restoringDone()
This signal is emitted right after the docker states have been successfully restored from config.
void activeViewChanged()
emitted when the current view has changed
KisCanvasWindow * canvasWindow
void notifyChildViewDestroyed(KisView *view)
void synchronizeDynamicActions()
void adjustLayoutForWelcomePage()
int viewCount() const
void setActiveView(KisView *view)
Set the active view, this will update the undo/redo actions.
void showDockerTitleBars(bool show)
static void swapWorkspaces(KisMainWindow *a, KisMainWindow *b)
KisAction * importAnimation
QByteArray dockerStateBeforeHiding
void removeRecentFile(QString url)
void slotSaveCanceled(const QString &)
KisAction * importVideoAnimation
Private(KisMainWindow *parent, QUuid id)
KisAction * redo
void switchTab(int index)
QByteArray borrowWorkspace(KisMainWindow *borrower)
~KisMainWindow() override
QScopedPointer< KisSignalCompressorWithParam< int > > tabSwitchCompressor
void dragMoveEvent(QDragMoveEvent *event) override
void slotToolbarToggled(bool toggle)
void closeEvent(QCloseEvent *e) override
KoCanvasResourceProvider * resourceManager() const
The KisMainwindowObserver class is an interface for dock widgets that want to keep track of the main ...
virtual void setViewManager(KisViewManager *kisview)=0
static QStringList suffixesForMimeType(const QString &mimeType)
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
void addCustomDocumentWidget(QWidget *widget, const QString &title, const QString &untranslatedName, const QString &icon=QString())
bool closeSession(bool keepWindows=false)
Definition KisPart.cpp:382
void removeMainWindow(KisMainWindow *mainWindow)
Definition KisPart.cpp:440
KisView * createView(KisDocument *document, KisViewManager *viewManager, QWidget *parent)
Definition KisPart.cpp:277
static KisPart * instance()
Definition KisPart.cpp:131
void addDocument(KisDocument *document, bool notify=true)
Definition KisPart.cpp:211
void queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl url, QUrl oldUrl=QUrl())
Definition KisPart.cpp:606
void sigMainWindowCreated()
KisMainWindow * windowById(QUuid id) const
Definition KisPart.cpp:480
KisDocument * createDocument() const
Definition KisPart.cpp:230
void showSessionManager()
Definition KisPart.cpp:626
void notifyMainWindowIsBeingCreated(KisMainWindow *mainWindow)
notifyMainWindowIsBeingCreated emits the sigMainWindowCreated signal
Definition KisPart.cpp:271
KisMainWindow * createMainWindow(QUuid id=QUuid())
Definition KisPart.cpp:260
static KisRecentDocumentsModelWrapper * instance()
void remove(const QUrl &url)
static KisRecentFilesManager * instance()
The KisResourceIterator class provides an iterator for a KisResourceModel.
const KisResourceItemSP next()
static KisResourceLoaderRegistry * instance()
QStringList mimeTypes(const QString &resourceType) const
The KisResourceModel class provides the main access to resources. It is possible to filter the resour...
static KisResourceServerProvider * instance()
KoResourceServer< KisWorkspaceResource > * workspaceServer()
KisPaintOpPresetResourceServer * paintOpPresetServer()
static QString storageTypeToUntranslatedString(StorageType storageType)
static KoResourceSP importResourceFileWithUserInput(QWidget *widgetParent, QString storageLocation, QString resourceType, QString resourceFilepath)
static bool addResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString storageLocation="")
The KisSignalMapper class bundles signals from identifiable senders.
@ ByStorageType
Pass a string list of storage types.
void setFilter(FilterType filterType, QVariant filter)
static KisStorageModel * instance()
Floatable toolbar with auto resize.
Definition ktoolbar.h:47
static KisToolBarStateModel * toolBarStateModel()
static void log(const QString &message)
Logs with date/time.
A special utility titlebar with a title and controls, as well as a central area for adding frequently...
KisDocument * document() const
void setCurrentView(KisView *view)
KisInputManager * inputManager() const
Filters events and sends them to canvas actions.
QPointer< KoUpdater > createUnthreadedUpdater(const QString &name)
create a new progress updater
KisSelectionManager * selectionManager()
KisImageWSP image() const
Return the image this view is displaying.
KisCanvas2 * canvasBase() const
Definition KisView.cpp:427
void slotThemeChanged(QPalette pal)
Definition KisView.cpp:1223
void setSubWindow(QMdiSubWindow *subWindow)
Definition KisView.cpp:1094
QPointer< KisDocument > document
Definition KisView.cpp:121
void slotLoadingFinished()
Definition KisView.cpp:1397
void setViewManager(KisViewManager *view)
Definition KisView.cpp:337
A widget for displaying if no documents are open. This will display in the MDI area.
static KisWindowLayoutManager * instance()
void activeDocumentChanged(KisDocument *document)
Resource for storing of workspaces.
void setObservedCanvas(KoCanvasBase *canvas)
static KoColorSpaceEngineRegistry * instance()
static KoColor fromXML(const QDomElement &elt, const QString &channelDepthId)
Definition KoColor.cpp:350
void toQColor(QColor *c) const
a convenience method for the above.
Definition KoColor.cpp:198
@ DockRight
Right of the centra widget.
@ DockTop
Above the central widget.
@ DockBottom
Below the central widget.
@ DockMinimized
Not docked, but reachable via the menu.
@ DockLeft
Left of the centra widget.
@ DockTornOff
Floating as its own top level window.
static KoDockRegistry * instance()
A custom title bar for dock widgets.
The dialog that shows information about the document.
The class containing all meta information about a document.
const T value(const QString &id) const
void load(const QString &serviceType, const PluginsConfig &config=PluginsConfig(), QObject *owner=0, bool cache=true)
static KoPluginLoader * instance()
static QString locateLocal(const QString &type, const QString &filename, bool createDir=false)
static QString getAppDataLocation()
int resourceCount() const
QSharedPointer< T > resource(const QString &md5, const QString &fileName, const QString &name)
resource retrieves a resource. If the md5sum is not empty, the resource will only be retrieved if a r...
QList< QAction * > createActions(KisKActionCollection *actionCollection)
void initializeCurrentToolForCanvas()
static KoToolManager * instance()
Return the toolmanager singleton.
static KoToolRegistry * instance()
ScopedWidgetDisabler(QWidget *widget_)
DockPosition defaultDockPosition() const override
QDockWidget * createDockWidget() override
QString id() const override
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define dbgKrita
Definition kis_debug.h:45
#define warnKrita
Definition kis_debug.h:87
#define dbgUI
Definition kis_debug.h:52
#define dbgFile
Definition kis_debug.h:53
#define warnUI
Definition kis_debug.h:94
constexpr const char * currentUnderlyingStyleNameProperty
Definition kis_global.h:116
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
KRecentFilesAction * openRecent(const QObject *recvr, const char *slot, QObject *parent)
const char * name(StandardAction id)
KRITAUI_EXPORT bool render(KisDocument *doc, KisViewManager *viewManager, KisAnimationRenderingOptions encoderOptions)
int size(const Forest< T > &forest)
Definition KisForest.h:1232
QIcon loadIcon(const QString &name)
void updateIconCommon(QObject *object)
void updateIcon(QAbstractButton *button)
QFont normalFont()
Gets a font for normal UI widgets to use.
Definition KisUiFont.cpp:81
QFont dockFont()
Gets a font with a smallish font size for dock widgets to use.
Definition KisUiFont.cpp:98
const QString Workspaces
auto mem_equal_to(MemTypeNoRef Class::*ptr, MemType &&value)
mem_equal_to is an unary functor that compares a member of the object to a given value
Definition KisMpl.h:233
auto mem_less(MemTypeNoRef Class::*ptr, MemType &&value)
mem_less is an unary functor that compares a member of the object to a given value
Definition KisMpl.h:269
rgba palette[MAX_PALETTE]
Definition palette.c:35
virtual void updateSettings()
QString icon
icon used in the sidebar. If left empty it will use the unknown icon
QString title
title used in the sidebar. If left empty it will be displayed as "Custom Document"
QWidget * widget
Pointer to the custom document widget.
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
QList< int > renderedFrameTargetTimes