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