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 QScopedPointer<KisDlgPreferences> dlgPreferences(new KisDlgPreferences(this));
820
821 if (dlgPreferences->editPreferences()) {
825
826 // XXX: should this be changed for the views in other windows as well?
827 Q_FOREACH (QPointer<KisView> koview, KisPart::instance()->views()) {
828 KisViewManager *view = qobject_cast<KisViewManager*>(koview);
829 if (view) {
830 // Update the settings for all nodes -- they don't query
831 // KisConfig directly because they need the settings during
832 // compositing, and they don't connect to the config notifier
833 // because nodes are not QObjects (because only one base class
834 // can be a QObject).
835 KisNode* node = dynamic_cast<KisNode*>(view->image()->rootLayer().data());
836 if (node) {
837 node->updateSettings();
838 }
839 }
840
841 }
843
844 d->viewManager->showHideScrollbars();
845 }
846}
847
849{
850 // reload action icons!
851 Q_FOREACH (QAction *action, actionCollection()->actions()) {
853 }
854 if (d->mdiArea) {
855 d->mdiArea->setPalette(qApp->palette());
856 for (int i=0; i<d->mdiArea->subWindowList().size(); i++) {
857 QMdiSubWindow *window = d->mdiArea->subWindowList().at(i);
858 if (window) {
859 window->setPalette(qApp->palette());
860 KisView *view = qobject_cast<KisView*>(window->widget());
861 if (view) {
862 view->slotThemeChanged(qApp->palette());
863 }
864 }
865 }
866 }
867
869
870 // Update toolbars
871 {
872 Q_FOREACH (KisToolBar* aToolBar, toolBars()) {
873 QObjectList objects;
874 objects.append(aToolBar);
875 while (!objects.isEmpty()) {
876 QWidget* widget = qobject_cast<QWidget*>(objects.takeFirst());
877 if (widget) {
878 objects.append(widget->children());
879 widget->setPalette(qApp->palette());
880 }
881 }
882 }
883 }
884}
885
887{
888 KConfigGroup group(KSharedConfig::openConfig(), "theme");
889#ifndef Q_OS_HAIKU
890 if (group.readEntry("Theme", "") == d->themeManager->currentThemeName()) return;
891
892 // save theme changes instantly
893 group.writeEntry("Theme", d->themeManager->currentThemeName());
894#endif
895 updateTheme();
896
897 // Make the other top level windows update as well
898 Q_FOREACH (QWidget* topLevelWidget, qApp->topLevelWidgets()) {
899 if (topLevelWidget == this) {
900 // Skip if the current top level widget is this window
901 continue;
902 }
903 if (topLevelWidget->isHidden()) {
904 // Skip unwanted top level widgets like menus, dropdowns, tooltips, etc.
905 continue;
906 }
907 KisMainWindow *topLevelMainWindow = qobject_cast<KisMainWindow*>(topLevelWidget);
908 if (topLevelMainWindow) {
909 topLevelMainWindow->updateTheme();
910 Q_EMIT topLevelMainWindow->themeChanged();
911 } else {
912 QObjectList objects;
913 objects.append(topLevelWidget);
914 while (!objects.isEmpty()) {
915 QWidget* widget = qobject_cast<QWidget*>(objects.takeFirst());
916 if (widget) {
917 objects.append(widget->children());
919 }
920 }
921 }
922 }
923
925
926 Q_EMIT themeChanged();
927}
928
930{
931 // update MDI area theme
932 // Tab close button override
933 // just switch this icon out for all OSs so it is easier to see
934 QString closeButtonImageUrl;
935 QString closeButtonHoverColor;
937 closeButtonImageUrl = QStringLiteral(":/dark_close-tab.svg");
938 closeButtonHoverColor = QStringLiteral("lightcoral");
939 } else {
940 closeButtonImageUrl = QStringLiteral(":/light_close-tab.svg");
941 closeButtonHoverColor = QStringLiteral("darkred");
942 }
943 QString tabStyleSheet = QStringLiteral(R"(
944 QTabBar::close-button {
945 image: url(%1);
946 padding-top: 3px;
947 }
948 QTabBar::close-button:hover {
949 background-color: %2;
950 }
951 QTabBar::close-button:pressed {
952 background-color: red;
953 }
954
955 QHeaderView::section {
956 padding: 7px;
957 }
958
959 )")
960 .arg(closeButtonImageUrl, closeButtonHoverColor);
961
962
963 QTabBar* tabBar = d->findTabBarHACK();
964 if (tabBar) {
965 tabBar->setStyleSheet(tabStyleSheet);
966 }
967
968}
969
971{
972 return centralWidget() != d->widgetStack;
973}
974
976{
977#ifdef Q_OS_ANDROID
978 if (detach) {
979 QMessageBox::warning(this, i18nc("@title:window", "Krita"),
980 "Detach Canvas is unsupported on Android");
981 }
982#else
983 if (detach == canvasDetached()) return;
984
985 QWidget *outgoingWidget = centralWidget() ? takeCentralWidget() : nullptr;
986 QWidget *incomingWidget = d->canvasWindow->swapMainWidget(outgoingWidget);
987
988 if (incomingWidget) {
989 setCentralWidget(incomingWidget);
990 }
991
992 if (detach) {
993 d->canvasWindow->show();
994 } else {
995 d->canvasWindow->hide();
996 }
997 d->toggleDetachCanvas->setChecked(detach);
998#endif
999}
1000
1001QWidget * KisMainWindow::canvasWindow() const
1002{
1003 return d->canvasWindow;
1004}
1005
1010
1012{
1013 KisRecentFilesManager::instance()->remove(QUrl::fromLocalFile(url));
1014}
1015
1016void KisMainWindow::slotUpdateSaveActionTitle(const QString &documentPath)
1017{
1018 const QString fileName = QFileInfo(documentPath).fileName();
1019
1020 if (!fileName.isEmpty()) {
1021 d->saveAction->setToolTip(i18n("Save as %1", fileName));
1022 }
1023 else {
1024 d->saveAction->setToolTip(i18n("Save"));
1025 }
1026}
1027
1029{
1030 Q_UNUSED(readWrite);
1031 d->actionManager()->updateGUI();
1032}
1033
1035{
1036 if (d->activeView) {
1037 return d->activeView;
1038 }
1039 return 0;
1040}
1041
1042bool KisMainWindow::openDocument(const QString &path, OpenFlags flags)
1043{
1044 ScopedWidgetDisabler disabler(d->welcomePage->dropFrameBorder);
1045 QApplication::processEvents(); // make UI more responsive
1046
1047 if (!QFile(path).exists()) {
1048 if (!(flags & BatchMode)) {
1049 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", path));
1050 }
1051 KisRecentFilesManager::instance()->remove(QUrl::fromLocalFile(path)); //remove the file from the recent-opened-file-list
1052 return false;
1053 }
1054 return openDocumentInternal(path, flags);
1055}
1056
1057bool KisMainWindow::openDocumentInternal(const QString &path, OpenFlags flags)
1058{
1059 if (!QFile(path).exists()) {
1060 qWarning() << "KisMainWindow::openDocumentInternal. Could not open:" << path;
1061 return false;
1062 }
1063
1065
1066 if (flags & BatchMode) {
1067 newdoc->setFileBatchMode(true);
1068 }
1069
1070 d->firstTime = true;
1071 connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1072 connect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1073
1074 KisDocument::OpenFlags openFlags = KisDocument::None;
1075 // XXX: Why this duplication of OpenFlags...
1076 if (flags & RecoveryFile) {
1077 openFlags |= KisDocument::RecoveryFile;
1078 }
1079
1080 bool openRet = !(flags & Import) ? newdoc->openPath(path, openFlags) : newdoc->importDocument(path);
1081
1082 if (!openRet) {
1083 delete newdoc;
1084 return false;
1085 }
1086
1087 KisPart::instance()->addDocument(newdoc);
1088
1089 // Try to determine whether this was an unnamed autosave
1090 if (flags & RecoveryFile &&
1091 ( path.startsWith(QDir::tempPath())
1092 || path.startsWith(QDir::homePath())
1093 ) &&
1094 ( QFileInfo(path).fileName().startsWith(".krita")
1095 || QFileInfo(path).fileName().startsWith("krita")
1096 )
1097 )
1098 {
1099 QString path = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1100 if (!QFileInfo(path).exists()) {
1101 path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
1102 }
1103 newdoc->setPath(path + "/" + newdoc->objectName() + ".kra");
1104 }
1105
1106 return true;
1107}
1108
1110 Q_FOREACH(QMdiSubWindow *subwindow, d->mdiArea->subWindowList()) {
1111 KisView *view = qobject_cast<KisView*>(subwindow->widget());
1113
1114 if (view) {
1115 if (view->document() == document) {
1116 setActiveSubWindow(subwindow);
1117 return;
1118 }
1119 }
1120 }
1121
1123}
1124
1126 QMdiSubWindow *subWindow)
1127{
1128 showWelcomeScreen(false); // see workaround in function header
1129
1130 KisView *view = KisPart::instance()->createView(document, d->viewManager, this);
1131 addView(view, subWindow);
1132
1133 Q_EMIT guiLoadingFinished();
1134
1135#ifdef Q_OS_ANDROID
1136 // HACK: When opening documents on Android, the main window sometimes fails
1137 // to update until the application is shunted to the background and brought
1138 // back or the menu bar is fiddled with. Flickering the window fixes this.
1139 // Having a docker that uses QML somehow also fixes this, so this hack is
1140 // gone again in 5.3 with the introduction of the text properties docker.
1141 // UNHACK: But not on Xiaomi devices! There flickering the window makes it
1142 // sorta exit fullscreen, adding black bars at the top and bottom. Just
1143 // opening a document seems to work fine on these devices though, it delays
1144 // for a moment then displays fine, so just skip this there I guess.
1146 QTimer::singleShot(0, this, [this] {
1147 hide();
1148 QTimer::singleShot(0, this, [this] {
1149 show();
1150 });
1151 });
1152 }
1153#endif
1154
1155 return view;
1156}
1157
1159{
1160 KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument");
1161 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1163 dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images"));
1164
1165 QStringList filenames = dialog.filenames();
1166 filenames.sort();
1167
1168 return filenames;
1169}
1170
1171// Separate from openDocument to handle async loading (remote URLs)
1173{
1174 KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
1175 if (newdoc && newdoc->image()) {
1177
1178 disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1179 disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1180
1181 Q_EMIT loadCompleted();
1182 }
1183}
1184
1185void KisMainWindow::slotLoadCanceled(const QString &errMsg)
1186{
1187 KisUsageLogger::log(QString("Loading canceled: %1.").arg(errMsg));
1188 KisDocument* doc = qobject_cast<KisDocument*>(sender());
1189 Q_ASSERT(doc);
1190 disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
1191 disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
1192}
1193
1194void KisMainWindow::slotSaveCanceled(const QString &errMsg)
1195{
1196 if (!errMsg.isEmpty()) { // empty when cancelled by user
1197 KisUsageLogger::log(QString("Saving cancelled. Error:").arg(errMsg));
1198 QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
1199 }
1200 else {
1201 KisUsageLogger::log(QString("Saving cancelled by the user."));
1202 }
1204}
1205
1207{
1208 KisUsageLogger::log(QString("Saving Completed"));
1209 KisDocument* doc = qobject_cast<KisDocument*>(sender());
1210 if (doc) {
1211 disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
1212 disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
1213 }
1214
1215 if (d->deferredClosingEvent) {
1216 KXmlGuiWindow::closeEvent(d->deferredClosingEvent);
1217 }
1218}
1219
1221{
1222 std::unique_lock<QMutex> l(d->savingEntryMutex, std::try_to_lock);
1223 return !l.owns_lock();
1224}
1225
1226bool KisMainWindow::installBundle(const QString &fileName) const
1227{
1228 QFileInfo from(fileName);
1229 QFileInfo to(QStringLiteral("%1/%2").arg(KoResourcePaths::getAppDataLocation(), from.fileName()));
1230 if (to.exists()) {
1231 QFile::remove(to.canonicalFilePath());
1232 }
1233 return QFile::copy(fileName, to.absoluteFilePath());
1234}
1235
1237{
1238 int size = 256;
1239 qreal scale = qreal(size)/qreal(qMax(geometry().width(), geometry().height()));
1240 QImage layoutThumbnail = QImage(qRound(geometry().width()*scale), qRound(geometry().height()*scale), QImage::Format_ARGB32);
1241 QPainter gc(&layoutThumbnail);
1242 gc.fillRect(0, 0, layoutThumbnail.width(), layoutThumbnail.height(), this->palette().dark());
1243
1244 Q_FOREACH(const QObject *child, children()) {
1245 if (child->isWidgetType()) {
1246 const QWidget *w = static_cast<const QWidget *>(child);
1247
1248 if (w->isVisible() && !w->property("_kis_excludeFromLayoutThumbnail").toBool()) {
1249 QRect wRect = QRectF(w->geometry().x()*scale
1250 , w->geometry().y()*scale
1251 , w->geometry().width()*scale
1252 , w->geometry().height()*scale
1253 ).toRect();
1254
1255 wRect = wRect.intersected(layoutThumbnail.rect().adjusted(-1, -1, -1, -1));
1256
1257 gc.setBrush(this->palette().window());
1258 if (w == d->widgetStack) {
1259 gc.setBrush(d->mdiArea->background());
1260 }
1261 gc.setPen(this->palette().windowText().color());
1262 gc.drawRect(wRect);
1263 }
1264 }
1265 }
1266 gc.end();
1267 return layoutThumbnail;
1268}
1269
1270bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool isExporting, bool isAdvancedExporting )
1271{
1272 if (!document) {
1273 return true;
1274 }
1275
1283 std::unique_lock<QMutex> l(d->savingEntryMutex, std::try_to_lock);
1284 if (!l.owns_lock()) return false;
1285
1286 // no busy wait for saving because it is dangerous!
1287 KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this);
1288 dlg.blockIfImageIsBusy();
1289
1290 if (dlg.result() == KisDelayedSaveDialog::Rejected) {
1291 return false;
1292 }
1293 else if (dlg.result() == KisDelayedSaveDialog::Ignored) {
1294 QMessageBox::critical(qApp->activeWindow(),
1295 i18nc("@title:window", "Krita"),
1296 i18n("You are saving a file while the image is "
1297 "still rendering. The saved file may be "
1298 "incomplete or corrupted.\n\n"
1299 "Please select a location where the original "
1300 "file will not be overridden!"));
1301
1302
1303 saveas = true;
1304 }
1305
1306 if (document->isRecovered()) {
1307 saveas = true;
1308 }
1309
1310 if (document->path().isEmpty()) {
1311 saveas = true;
1312 }
1313
1314 connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
1315 connect(document, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
1316
1317 QByteArray nativeFormat = document->nativeFormatMimeType();
1318 QByteArray oldMimeFormat = document->mimeType();
1319
1320 QUrl suggestedURL = QUrl::fromLocalFile(document->path());
1321
1323
1325 if (!mimeFilter.contains(oldMimeFormat)) {
1326 dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat;
1327
1328 // --- don't setOutputMimeType in case the user cancels the Save As
1329 // dialog and then tries to just plain Save ---
1330
1331 // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :))
1332 QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1333
1334 if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name
1335 suggestedFilename = suggestedFilename + "." + KisMimeDatabase::suffixesForMimeType(KIS_MIME_TYPE).first();
1336 suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename);
1337 suggestedURL.setPath(suggestedURL.path() + suggestedFilename);
1338 }
1339
1340 // force the user to choose outputMimeType
1341 saveas = true;
1342 }
1343
1344 bool ret = false;
1345
1346 if (document->path().isEmpty() || isExporting || saveas) {
1347 // if you're just File/Save As'ing to change filter options you
1348 // don't want to be reminded about overwriting files etc.
1349 bool justChangingFilterOptions = false;
1350
1351 KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveAs");
1352 dialog.setCaption(isExporting ? i18n("Exporting") : i18n("Saving As"));
1353
1354 //qDebug() << ">>>>>" << isExporting << d->lastExportLocation << d->lastExportedFormat << QString::fromLatin1(document->mimeType());
1355
1356 if (isExporting && !d->lastExportLocation.isEmpty() && !d->lastExportLocation.contains(QDir::tempPath())) {
1357
1358 // Use the location where we last exported to, if it's set, as the opening location for the file dialog
1359 QString proposedPath = QFileInfo(d->lastExportLocation).absolutePath();
1360 // If the document doesn't have a filename yet, use the title
1361 QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo("title") : QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1362 // Use the last mimetype we exported to by default
1363 QString proposedMimeType = d->lastExportedFormat.isEmpty() ? "" : d->lastExportedFormat;
1364 QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,");
1365
1366 // Set the default dir: this overrides the one loaded from the config file, since we're exporting and the lastExportLocation is not empty
1367 dialog.setDefaultDir(proposedPath + "/" + proposedFileName + "." + proposedExtension, true);
1368 dialog.setMimeTypeFilters(mimeFilter, proposedMimeType);
1369 }
1370 else {
1371 //This section only runs when the user haven't exported yet during the session, behavior set in the File Handling, Default MimeType setting.
1372 KisConfig cfg(true);
1373 QByteArray default_mime_type = cfg.exportMimeType(false).toUtf8();
1374 QString proposedMimeType = QString::fromLatin1(default_mime_type);
1375
1376 // Get the last used location for saving
1377 KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
1378 QString proposedPath = group.readEntry("SaveAs", "");
1379 // if that is empty, get the last used location for loading
1380 if (proposedPath.isEmpty()) {
1381 proposedPath = group.readEntry("OpenDocument", "");
1382 }
1383 // If that is empty, too, use the Pictures location.
1384 if (proposedPath.isEmpty()) {
1385 proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1386 }
1387 // But only use that if the suggestedUrl, that is, the document's own url is empty, otherwise
1388 // open the location where the document currently is.
1389
1390 //For if the user picked All Files Supported as the default, where there would not be an extension
1391 if(default_mime_type == "all/mime"){
1392 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : proposedPath + "/" + QFileInfo(suggestedURL.toLocalFile()).completeBaseName(), true);
1393 }
1394
1395 if (default_mime_type != "all/mime" && !default_mime_type.isEmpty()) {
1396 QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,");
1397 //This line is responsible for setting filename, which also manipulates filters.
1398 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : proposedPath + "/" + QFileInfo(suggestedURL.toLocalFile()).completeBaseName() + "." + proposedExtension, true);
1399 }
1400
1401 if (!isExporting) {
1402 // If Saving, use the document's mimetype, or if that is empty, kra, which is the safest.
1403 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(), true);
1404 default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType();
1405 }
1406 dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type));
1407 }
1408
1409 QString newFilePath = dialog.filename();
1410
1411 bool bOk = true;
1412 if (newFilePath.isEmpty()) {
1413 bOk = false;
1414 }
1415
1416 if (bOk) {
1417 if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) {
1418 QString fn = newFilePath;
1419 QFileInfo info(fn);
1420 document->documentInfo()->setAboutInfo("title", info.completeBaseName());
1421 }
1422
1423 QByteArray outputFormat = nativeFormat;
1424
1425 QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newFilePath, false);
1426 outputFormat = outputFormatString.toLatin1();
1427
1428
1429 if (!isExporting) {
1430 justChangingFilterOptions = (newFilePath == document->path()) && (outputFormat == document->mimeType());
1431 }
1432 else {
1433 QString path = QFileInfo(d->lastExportLocation).absolutePath();
1434 QString filename = QFileInfo(document->path()).completeBaseName();
1435 justChangingFilterOptions = (QFileInfo(newFilePath).absolutePath() == path)
1436 && (QFileInfo(newFilePath).completeBaseName() == filename)
1437 && (outputFormat == d->lastExportedFormat);
1438 }
1439
1440 bool wantToSave = true;
1441
1442 // don't change this line unless you know what you're doing :)
1443 if (!justChangingFilterOptions) {
1444 if (!document->isNativeFormat(outputFormat))
1445 wantToSave = true;
1446 }
1447
1448 if (wantToSave) {
1449 if (!isExporting) { // Save As
1450 ret = document->saveAs(newFilePath, outputFormat, true);
1451 if (ret) {
1452 dbgUI << "Successful Save As!";
1453 KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath));
1454 } else {
1455 dbgUI << "Failed Save As!";
1456
1457 }
1458 }
1459 else { // Export
1460 ret = document->exportDocument(newFilePath, outputFormat, isAdvancedExporting, true);
1461 if (ret) {
1462 d->lastExportLocation = newFilePath;
1463 d->lastExportedFormat = outputFormat;
1464 }
1465 }
1466
1467 } // if (wantToSave) {
1468 else
1469 ret = false;
1470 } // if (bOk) {
1471 else
1472 ret = false;
1473 } else { // saving
1474 // We cannot "export" into the currently
1475 // opened document. We are not Gimp.
1476 KIS_ASSERT_RECOVER_NOOP(!isExporting);
1477
1478 // be sure document has the correct outputMimeType!
1479 if (document->isModified()) {
1480 ret = document->save(true, 0);
1481 }
1482
1483 if (!ret) {
1484 dbgUI << "Failed Save!";
1485 }
1486 }
1487
1488 return ret;
1489}
1490
1492{
1493 if (activeView()) {
1494 activeView()->document()->undoStack()->undo();
1495 }
1496}
1497
1499{
1500 if (activeView()) {
1501 activeView()->document()->undoStack()->redo();
1502 }
1503}
1504
1505void KisMainWindow::closeEvent(QCloseEvent *e)
1506{
1507 if (hackIsSaving()) {
1508 e->setAccepted(false);
1509 return;
1510 }
1511
1512 if (!KisPart::instance()->closingSession()) {
1513 QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
1514 if ((action) && (action->isChecked())) {
1515 action->setChecked(false);
1516 }
1517
1518 // Save session when last window is closed
1519 if (KisPart::instance()->mainwindowCount() == 1) {
1520 bool closeAllowed = KisPart::instance()->closeSession();
1521
1522 if (!closeAllowed) {
1523 e->setAccepted(false);
1524 return;
1525 }
1526 }
1527 }
1528
1529 d->mdiArea->closeAllSubWindows();
1530
1531 QList<QMdiSubWindow*> childrenList = d->mdiArea->subWindowList();
1532
1533 if (childrenList.isEmpty()) {
1534 // TODO: that assignment looks really suspicious, because `e`
1535 // is destroyed right after the exit of this function
1536 d->deferredClosingEvent = e;
1537 d->canvasWindow->close();
1538 } else {
1539 e->setAccepted(false);
1540 }
1541}
1542
1544{
1545 KSharedConfigPtr config = KSharedConfig::openConfig();
1546
1547 if (d->windowSizeDirty ) {
1548 dbgUI << "KisMainWindow::saveWindowSettings";
1549 KConfigGroup group = d->windowStateConfig;
1550 KWindowConfig::saveWindowSize(windowHandle(), group);
1551 config->sync();
1552 d->windowSizeDirty = false;
1553 }
1554
1555 if (!d->activeView || d->activeView->document()) {
1556
1557 // Save toolbar position into the config file of the app, under the doc's component name
1558 KConfigGroup group = d->windowStateConfig;
1560
1561 // Save state of dock widgets
1562 for (QMap<QString, QDockWidget*>::const_iterator i = d->dockWidgetsMap.constBegin();
1563 i != d->dockWidgetsMap.constEnd(); ++i) {
1564 if (i.value()->widget()) {
1565 KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key());
1566 dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool());
1567 dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value()));
1568 dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x());
1569 dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y());
1570
1571 dockGroup.writeEntry("width", (int) i.value()->widget()->width());
1572 dockGroup.writeEntry("height", (int) i.value()->widget()->height());
1573 }
1574 }
1575
1576 }
1577
1578 KSharedConfig::openConfig()->sync();
1579 resetAutoSaveSettings(); // Don't let KisKMainWindow override the good stuff we wrote down
1580
1581}
1582
1583void KisMainWindow::resizeEvent(QResizeEvent * e)
1584{
1585 d->windowSizeDirty = true;
1586 KXmlGuiWindow::resizeEvent(e);
1587}
1588
1589
1590void KisMainWindow::dragMoveEvent(QDragMoveEvent *event)
1591{
1592 dragMove(event);
1593 event->accept();
1594}
1595
1596void KisMainWindow::dragLeaveEvent(QDragLeaveEvent *event)
1597{
1598 dragLeave();
1599 event->accept();
1600}
1601
1603{
1604 QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
1605 return !action || !action->isChecked();
1606}
1607
1608void KisMainWindow::showEvent(QShowEvent *event)
1609{
1610 // we're here because, we need to make sure everything (dockers, toolbars etc) is loaded and ready before
1611 // we can hide it.
1612 if (!event->spontaneous()) {
1613 setMainWindowLayoutForCurrentMainWidget(d->widgetStack->currentIndex(), false);
1614 }
1615#ifdef Q_OS_ANDROID
1616 Q_EMIT sigFullscreenOnShow(true); // Android defaults to fullscreen.
1617#endif
1618 return KXmlGuiWindow::showEvent(event);
1619}
1620
1621void KisMainWindow::setMainWindowLayoutForCurrentMainWidget(int widgetIndex, bool widgetIndexChanged)
1622{
1623
1624 if (widgetIndex == 0) {
1625 if (widgetIndexChanged) {
1630
1631 if (d->mdiArea->subWindowList().isEmpty()) {
1636 saveWindowState(true);
1637 } else {
1638 saveMainWindowSettings(d->windowStateConfig);
1639 }
1640 }
1642 }
1643 else {
1644 setAutoSaveSettings(d->windowStateConfig, false);
1645 statusBar()->setVisible(KisConfig(true).showStatusBar());
1646 }
1647
1648 QList<QAction *> actions = d->dockWidgetMenu->menu()->actions();
1649 actions.append(toolBarMenuAction()->menu()->actions());
1650 for (QAction *action : actions) {
1651 if (action) {
1652 action->setEnabled(widgetIndex);
1653 }
1654 }
1655}
1656
1658{
1659 // This makes sure we don't save window state when we're in welcome page mode, because all the dockers
1660 // etc are hidden while the user is here.
1662
1663 toggleDockersVisibility(false, true);
1664 if (statusBar()) {
1665 statusBar()->hide();
1666 }
1667 QList<QToolBar *> toolbars = findChildren<QToolBar *>();
1668 for (QToolBar *toolbar : toolbars) {
1669 if (toolbar->objectName() != "mainToolBar") {
1670 toolbar->hide();
1671 }
1672 }
1673}
1674
1676{
1677 d->activeView = view;
1678
1679 if (d->undoActionsUpdateManager) {
1680 d->undoActionsUpdateManager->setCurrentDocument(view ? view->document() : 0);
1681 }
1682
1683 d->viewManager->setCurrentView(view);
1684
1685 d->activeViewConnections.clear();
1686 d->activeViewConnections.addConnection(view->document(),
1687 SIGNAL(sigPathChanged(QString)),
1688 this, SLOT(slotUpdateSaveActionTitle(QString)));
1689 slotUpdateSaveActionTitle(view->document()->path());
1690 d->activeViewConnections.addConnection(view->document(),
1691 SIGNAL(sigReadWriteChanged(bool)),
1692 this, SLOT(slotUpdateReadWriteMode(bool)));
1693 slotUpdateReadWriteMode(view->document()->isReadWrite());
1694
1696
1697 Q_EMIT activeViewChanged();
1698}
1699
1701{
1702 d->activeViewConnections.clear();
1703 slotUpdateSaveActionTitle(QString());
1705}
1706
1707void KisMainWindow::dragMove(QDragMoveEvent * event)
1708{
1709 QTabBar *tabBar = d->findTabBarHACK();
1710
1711 if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) {
1712 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)!";
1713 }
1714
1715 if (tabBar && tabBar->isVisible()) {
1716 QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos()));
1717 if (tabBar->rect().contains(pos)) {
1718 const int tabIndex = tabBar->tabAt(pos);
1719
1720 if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) {
1721 d->tabSwitchCompressor->start(tabIndex);
1722 }
1723 } else if (d->tabSwitchCompressor->isActive()) {
1724 d->tabSwitchCompressor->stop();
1725 }
1726 }
1727}
1728
1730{
1731 if (d->tabSwitchCompressor->isActive()) {
1732 d->tabSwitchCompressor->stop();
1733 }
1734}
1735
1736
1738{
1739 QTabBar *tabBar = d->findTabBarHACK();
1740 if (!tabBar) return;
1741
1742 tabBar->setCurrentIndex(index);
1743}
1744
1746{
1747 const int currentIndex = show ? 0 : 1;
1748 if (d->widgetStack->currentIndex() != currentIndex) {
1749 setUpdatesEnabled(false);
1750 // These have to be done in different sequence to avoid graphical
1751 // layout glitch during the switch.
1752 if (show) {
1753 setMainWindowLayoutForCurrentMainWidget(currentIndex, true);
1754 d->widgetStack->setCurrentIndex(currentIndex);
1755 } else {
1756 d->widgetStack->setCurrentIndex(currentIndex);
1757 setMainWindowLayoutForCurrentMainWidget(currentIndex, true);
1758 }
1759 setUpdatesEnabled(true);
1760 }
1761}
1762
1764{
1766 dlg->exec();
1767 dlg->deleteLater();
1768}
1769
1771{
1772 dbgUI << "slotImportFile()";
1773 slotFileOpen(true);
1774}
1775
1776
1777void KisMainWindow::slotFileOpen(bool isImporting)
1778{
1779 QStringList urls = showOpenFileDialog(isImporting);
1780
1781 if (urls.isEmpty())
1782 return;
1783
1784 Q_FOREACH (const QString& url, urls) {
1785
1786 if (!url.isEmpty()) {
1787 OpenFlags flags = isImporting ? Import : None;
1788 bool res = openDocument(url, flags);
1789 if (!res) {
1790 warnKrita << "Loading" << url << "failed";
1791 }
1792 }
1793 }
1794}
1795
1797{
1798 (void) openDocument(url.toLocalFile(), None);
1799}
1800
1802{
1803 if (saveDocument(d->activeView->document(), false, false,false)) {
1804 Q_EMIT documentSaved();
1805 }
1806}
1807
1809{
1810 if (saveDocument(d->activeView->document(), true, false,false)) {
1811 Q_EMIT documentSaved();
1812 }
1813}
1814
1816{
1817 if (saveDocument(d->activeView->document(), true, true,false)) {
1818 Q_EMIT documentSaved();
1819 }
1820}
1822{
1823 if (saveDocument(d->activeView->document(), true, true,true)) {
1824 Q_EMIT documentSaved();
1825 }
1826}
1827
1831
1833{
1834 return d->viewManager->canvasResourceProvider()->resourceManager();
1835}
1836
1838{
1839 return d->mdiArea->subWindowList().size();
1840}
1841
1842const KConfigGroup &KisMainWindow::windowStateConfig() const
1843{
1844 return d->windowStateConfig;
1845}
1846
1847void KisMainWindow::saveWindowState(bool restoreNormalState)
1848{
1849 // We don't need to save welcome page's layout
1850 if (d->widgetStack->currentIndex() == 0) {
1851 // TODO(sh_zam): We should still save position/geometry, right?
1852 return;
1853 }
1854
1855 if (restoreNormalState) {
1856 QAction *showCanvasOnly = d->viewManager->actionCollection()->action("view_show_canvas_only");
1857
1858 if (showCanvasOnly && showCanvasOnly->isChecked()) {
1859 showCanvasOnly->setChecked(false);
1860 }
1861
1862 d->windowStateConfig.writeEntry("ko_geometry", saveGeometry().toBase64());
1863 d->windowStateConfig.writeEntry("State", saveState().toBase64());
1864
1865 // if the dockers are hidden at this time, save their state.
1866 if (!d->toggleDockers->isChecked()) {
1867 restoreState(d->dockerStateBeforeHiding);
1868 }
1869
1870 statusBar()->setVisible(true);
1871 menuBar()->setVisible(true);
1872
1874
1875 } else {
1876 saveMainWindowSettings(d->windowStateConfig);
1877 }
1878
1879}
1880
1881bool KisMainWindow::restoreWorkspaceState(const QByteArray &state)
1882{
1883 QByteArray oldState = saveState();
1884 const bool showTitlebars = KisConfig(false).showDockerTitleBars();
1885
1886 // needed because otherwise the layout isn't correctly restored in some situations
1887 Q_FOREACH (QDockWidget *dock, dockWidgets()) {
1888 if (dock) {
1889 dock->setProperty("Locked", false); // Unlock invisible dockers
1890 dock->toggleViewAction()->setEnabled(true);
1891 dock->hide();
1892 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
1893 dock->titleBarWidget()->setVisible(showTitlebars);
1894 }
1895 }
1896 }
1897
1898 bool success = KXmlGuiWindow::restoreState(state);
1899
1900 if (!success) {
1901 KXmlGuiWindow::restoreState(oldState);
1902 Q_FOREACH (QDockWidget *dock, dockWidgets()) {
1903 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
1904 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
1905 }
1906 }
1907 return false;
1908 }
1909 return success;
1910}
1911
1913{
1914 QString md5 = sender()->property("md5").toString();
1916 KoResourceSP resource = rserver->resource(md5, "", "");
1917 if (resource) {
1918 restoreWorkspace(resource);
1919 }
1920 else {
1921 qWarning() << "Could not retrieve resource for" << md5;
1922 }
1923}
1924
1926{
1927 QList<KisKActionCollection *> actionCollections;
1928
1929 auto clients = guiFactory()->clients();
1930 int actionsCount = 0;
1931 for (const KisKXMLGUIClient *c : clients) {
1932 if (!c) {
1933 continue;
1934 }
1935 if (auto collection = c->actionCollection()) {
1936 actionCollections.append(collection);
1937 actionsCount += collection->count();
1938 }
1939 }
1940
1941 if (activeKisView()) {
1942 KisKActionCollection *layerActionCollection = new KisKActionCollection(0, "layeractions (disposable)");
1943 layerActionCollection->setComponentDisplayName(i18n("Layers/Masks"));
1944 KisNodeActivationActionCreatorVisitor v(layerActionCollection, viewManager()->nodeManager());
1945 activeKisView()->image()->rootLayer()->accept(v);
1946 actionCollections.append(layerActionCollection);
1947 actionsCount += layerActionCollection->count();
1948 }
1949
1950 d->commandBar->updateBar(actionCollections, actionsCount);
1951
1952 // The following line is needed to work around input method not working
1953 // on Windows.
1954 // See https://bugs.kde.org/show_bug.cgi?id=395598
1955 // and https://bugs.kde.org/show_bug.cgi?id=438122
1956 d->commandBar->activateWindow();
1957
1958 // The following line is present in Kate's version and was ported over
1959 // but I am sceptical of its use. I worry that it may subtly cause other
1960 // issues, and since the command bar appears to work fine without it, I
1961 // believe it may be better to leave it out. -- Alvin
1962 // centralWidget()->setFocusProxy(d->commandBar);
1963}
1964
1965void KisMainWindow::slotStoragesWarning(const QString &/*location*/)
1966{
1967 QString warning;
1969 warning = i18n("You don't have any resource bundles enabled.");
1970 }
1971
1972 if (!checkPaintOpAvailable()) {
1973 warning += i18n("\nThere are no brush presets available. Please enable a bundle that has presets before continuing.\n");
1974 QMessageBox::critical(this, i18nc("@title:window", "Krita"), warning);
1975
1976 QAction *action = actionCollection()->action("manage_bundles");
1977 if (action) {
1978 action->trigger();
1979 }
1980 }
1981
1983 QMessageBox::warning(this, i18nc("@title:window", "Krita"), warning + i18n("\nOnly your local resources are available."));
1984 }
1985
1986}
1987
1989{
1990 KisWorkspaceResourceSP workspace = res.dynamicCast<KisWorkspaceResource>();
1991
1992 bool success = restoreWorkspaceState(workspace->dockerState());
1993
1994 const bool showTitlebars = KisConfig(false).showDockerTitleBars();
1995 Q_FOREACH (QDockWidget *dock, dockWidgets()) {
1996 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
1997 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
1998 }
1999 }
2000
2001 if (activeKisView()) {
2002 activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace);
2003 }
2004
2005 d->viewManager->notifyWorkspaceLoaded();
2006
2007 return success;
2008}
2009
2011{
2012 QByteArray currentWorkspace = saveState();
2013
2014 if (!d->workspaceBorrowedBy.isNull()) {
2015 if (other->id() == d->workspaceBorrowedBy) {
2016 // We're swapping our original workspace back
2017 d->workspaceBorrowedBy = QUuid();
2018 return currentWorkspace;
2019 } else {
2020 // Get our original workspace back before swapping with a third window
2021 KisMainWindow *borrower = KisPart::instance()->windowById(d->workspaceBorrowedBy);
2022 if (borrower) {
2023 QByteArray originalLayout = borrower->borrowWorkspace(this);
2024 borrower->restoreWorkspaceState(currentWorkspace);
2025
2026 d->workspaceBorrowedBy = other->id();
2027 return originalLayout;
2028 }
2029 }
2030 }
2031
2032 d->workspaceBorrowedBy = other->id();
2033 return currentWorkspace;
2034}
2035
2037{
2038 QByteArray workspaceA = a->borrowWorkspace(b);
2039 QByteArray workspaceB = b->borrowWorkspace(a);
2040
2041 a->restoreWorkspaceState(workspaceB);
2042 b->restoreWorkspaceState(workspaceA);
2043}
2044
2046{
2047 return d->viewManager;
2048}
2049
2051{
2052 if (!d->activeView->document())
2053 return;
2054
2055 KoDocumentInfo *docInfo = d->activeView->document()->documentInfo();
2056
2057 if (!docInfo)
2058 return;
2059
2060 KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo);
2061
2062 if (dlg->exec()) {
2063 if (dlg->isDocumentSaved()) {
2064 d->activeView->document()->setModified(false);
2065 } else {
2066 d->activeView->document()->setModified(true);
2067 }
2068 }
2069
2070 delete dlg;
2071}
2072
2074{
2075 Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2076 if (subwin) {
2077 if(!subwin->close())
2078 return false;
2079 }
2080 }
2081
2082 return true;
2083}
2084
2086{
2087 // Do not close while KisMainWindow has the savingEntryMutex locked, bug409395.
2088 // After the background saving job is initiated, KisDocument blocks closing
2089 // while it saves itself.
2090 if (hackIsSaving()) {
2091 return;
2092 }
2094}
2095
2097{
2098 if (!activeView()) return;
2099
2100 KisDocument *document = activeView()->document();
2101 if (!document) return;
2102
2103 KisDlgImportImageSequence dlg(this, document);
2104
2105 if (dlg.exec() == QDialog::Accepted) {
2106 QStringList files = dlg.files();
2107 int firstFrame = dlg.firstFrame();
2108 int step = dlg.step();
2109 bool startFrom1 = dlg.startFrom1();
2110 bool autoAddHoldframes = dlg.autoAddHoldframes();
2111
2112
2113 KoUpdaterPtr updater =
2114 !document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0;
2115 KisAnimationImporter importer(document->image(), updater);
2116 int isAscending = dlg.isAscending();
2117 KisImportExportErrorCode status = importer.import(files, firstFrame, step, autoAddHoldframes, startFrom1, isAscending); // modify here, add a flag
2118
2119 if (!status.isOk() && !status.isInternalError()) {
2120 QString msg = status.errorMessage();
2121 if (!msg.isEmpty())
2122 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
2123 }
2124 activeView()->canvasBase()->refetchDataFromImage();
2125 }
2126}
2127
2129{
2130 KisDocument *document;
2132
2133 if (dlg.exec() == QDialog::Accepted) {
2134 const QTemporaryDir outputLocation(QDir::tempPath() + QDir::separator() + "krita" + QDir::separator() + "import_files");
2135 RenderedFrames renderedFrames = dlg.renderFrames(QDir(outputLocation.path()));
2136 dbgFile << "Frames rendered to directory: " << outputLocation.path();
2137 QStringList documentInfoList = dlg.documentInfo();
2138
2139 if (renderedFrames.isEmpty()) return;
2140
2141 dbgFile << "Animation Import options: " << documentInfoList;
2142
2143 int firstFrame = 0;
2144 const int step = documentInfoList[0].toInt();
2145 const int fps = documentInfoList[1].toInt();
2146 const int totalFrames = renderedFrames.framesNeedRelocation() ? (renderedFrames.renderedFrameTargetTimes.last() + 1) : renderedFrames.size() * step;
2147 const QString name = QFileInfo(documentInfoList[3]).fileName();
2148 const bool useCurrentDocument = documentInfoList[4].toInt();
2149 bool useDocumentColorSpace = false;
2150
2151 if ( useCurrentDocument ) {
2152 document = activeView()->document();
2153
2154 dbgFile << "Current frames:" << document->image()->animationInterface()->totalLength() << "total frames:" << totalFrames;
2155 if ( document->image()->animationInterface()->totalLength() < totalFrames ) {
2156 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2157 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2158 }
2159 } else {
2160 const int width = documentInfoList[5].toInt();
2161 const int height = documentInfoList[6].toInt();
2162 const double resolution = documentInfoList[7].toDouble();
2163
2164 const QString colorModel = documentInfoList[8];
2165 const QString colorDepth = documentInfoList[9];
2166 const QString profile = documentInfoList[10];
2167 useDocumentColorSpace = profile != "Default";
2168
2169 document = KisPart::instance()->createDocument();
2170 document->setObjectName(name);
2171
2172 KisPart::instance()->addDocument(document, false);
2173 const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile);
2174 Q_ASSERT(cs);
2175
2176 QColor qc(Qt::white);
2177 qc.setAlpha(0);
2178 KoColor bgColor(qc, cs);
2179
2180 if (!document->newImage(name, width, height, cs, bgColor, KisConfig::RASTER_LAYER, 1, "", double(resolution / 72) )) {
2181 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Failed to create new document. Animation import aborted."));
2182 return;
2183 }
2184
2185 document->image()->animationInterface()->setFramerate(fps);
2186 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2187 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2188
2189 this->showDocument(document);
2190 }
2191
2192 KoUpdaterPtr updater =
2193 !document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0;
2194 KisAnimationImporter importer(document->image(), updater);
2195 KisImportExportErrorCode status = importer.import(renderedFrames.renderedFrameFiles, firstFrame, step, false, false, 0, useDocumentColorSpace, renderedFrames.renderedFrameTargetTimes);
2196
2197 if (!status.isOk() && !status.isInternalError()) {
2198 QString msg = status.errorMessage();
2199 if (!msg.isEmpty())
2200 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
2201 }
2202
2203 activeView()->canvasBase()->refetchDataFromImage();
2204 document->image()->refreshGraphAsync();
2205 document->image()->waitForDone();
2206
2207 }
2208}
2209
2211{
2212 if (!activeView()) return;
2213
2214 KisImageSP image = viewManager()->image();
2215
2216 if (!image) return;
2217 if (!image->animationInterface()->hasAnimation()) return;
2218
2219 KisDocument *doc = viewManager()->document();
2220
2221 KisDlgAnimationRenderer dlgAnimationRenderer(doc, viewManager()->mainWindow());
2222 dlgAnimationRenderer.setCaption(i18n("Render Animation"));
2223 if (dlgAnimationRenderer.exec() == QDialog::Accepted) {
2224 KisAnimationRenderingOptions encoderOptions = dlgAnimationRenderer.getEncoderOptions();
2225 KisAnimationRender::render(doc, viewManager(), encoderOptions);
2226 }
2227}
2228
2230{
2231 if (!activeView()) return;
2232
2233 KisImageSP image = viewManager()->image();
2234
2235 if (!image) return;
2236 if (!image->animationInterface()->hasAnimation()) return;
2237
2238 KisDocument *doc = viewManager()->document();
2239
2240 KisConfig cfg(true);
2241
2242 KisPropertiesConfigurationSP settings = cfg.exportConfiguration("ANIMATION_EXPORT");
2243
2244 KisAnimationRenderingOptions encoderOptions;
2245 encoderOptions.fromProperties(settings);
2246
2247 KisAnimationRender::render(doc, viewManager(), encoderOptions);
2248}
2249
2251{
2253 KisKEditToolBar edit(factory(), this);
2254 connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
2255 (void) edit.exec();
2257}
2258
2260{
2261 KisApplication *kisApp = static_cast<KisApplication*>(qApp);
2262 kisApp->askResetConfig();
2263}
2264
2266{
2267 applyMainWindowSettings(d->windowStateConfig);
2268
2270 Q_UNUSED(factory);
2271
2272 // Check if there's an active view
2273 if (!d->activeView)
2274 return;
2275
2276 plugActionList("toolbarlist", d->toolbarList);
2277
2285}
2286
2288{
2289 //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true;
2290 // The action (sender) and the toolbar have the same name
2291 KisToolBar * bar = toolBar(sender()->objectName());
2292 if (bar) {
2293 if (toggle) {
2294 bar->show();
2295 }
2296 else {
2297 bar->hide();
2298 }
2299
2300 if (d->activeView && d->activeView->document()) {
2302 }
2303 } else
2304 warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!";
2305}
2306
2308{
2309 KisConfig cfg(false);
2310 cfg.setFullscreenMode(fullScreen);
2311
2312 if (fullScreen) {
2313 setWindowState(windowState() | Qt::WindowFullScreen); // set
2314 } else {
2315 setWindowState(windowState() & ~Qt::WindowFullScreen); // reset
2316 }
2317 d->fullScreenMode->setChecked(isFullScreen());
2318}
2319
2321{
2322 QDockWidget* dockWidget = 0;
2323 const bool showTitlebars = KisConfig(false).showDockerTitleBars();
2324
2325 if (!d->dockWidgetsMap.contains(factory->id())) {
2326 dockWidget = factory->createDockWidget();
2327 KAcceleratorManager::setNoAccel(dockWidget);
2328
2329 // It is quite possible that a dock factory cannot create the dock; don't
2330 // do anything in that case.
2331 if (!dockWidget) {
2332 warnKrita << "Could not create docker for" << factory->id();
2333 return 0;
2334 }
2335
2336 KoDockWidgetTitleBar *titleBar = dynamic_cast<KoDockWidgetTitleBar*>(dockWidget->titleBarWidget());
2337
2338 // Check if the dock widget is supposed to be collapsible
2339 if (!dockWidget->titleBarWidget()) {
2340 titleBar = new KoDockWidgetTitleBar(dockWidget);
2341 dockWidget->setTitleBarWidget(titleBar);
2342 }
2343 if (titleBar) {
2344 titleBar->setFont(KisUiFont::dockFont());
2345 }
2346
2347 if (dockWidget->titleBarWidget() && !dockWidget->titleBarWidget()->inherits("KisUtilityTitleBar")) {
2348 dockWidget->titleBarWidget()->setVisible(KisConfig(true).showDockerTitleBars());
2349 }
2350
2351 dockWidget->setObjectName(factory->id());
2352 dockWidget->setParent(this);
2353 if (!showTitlebars) {
2354 if (dockWidget->titleBarWidget() && !dockWidget->titleBarWidget()->inherits("KisUtilityTitleBar")) {
2355 dockWidget->titleBarWidget()->setVisible(false);
2356 }
2357 dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
2358 }
2359 if (dockWidget->widget() && dockWidget->widget()->layout())
2360 dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1);
2361
2362 Qt::DockWidgetArea side = Qt::RightDockWidgetArea;
2363 bool visible = true;
2364
2365 switch (factory->defaultDockPosition()) {
2367 dockWidget->setFloating(true); // position nicely?
2368 break;
2370 side = Qt::TopDockWidgetArea; break;
2372 side = Qt::LeftDockWidgetArea; break;
2374 side = Qt::BottomDockWidgetArea; break;
2376 side = Qt::RightDockWidgetArea; break;
2378 default:
2379 side = Qt::RightDockWidgetArea;
2380 visible = false;
2381 }
2382
2383 KConfigGroup group = d->windowStateConfig.group("DockWidget " + factory->id());
2384 side = static_cast<Qt::DockWidgetArea>(group.readEntry("DockArea", static_cast<int>(side)));
2385 if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea;
2386
2387 addDockWidget(side, dockWidget);
2388 if (!visible) {
2389 dockWidget->hide();
2390 }
2391
2392 bool locked = group.readEntry("Locked", false);
2393 if (titleBar && locked) {
2394 titleBar->setLocked(true);
2395 }
2396 else if ( dockWidget->titleBarWidget()->inherits("KisUtilityTitleBar") && locked){
2397 KisUtilityTitleBar *utilityTitleBar = dynamic_cast<KisUtilityTitleBar*>(dockWidget->titleBarWidget());
2398 utilityTitleBar->setLocked(true);
2399 }
2400
2401 d->dockWidgetsMap.insert(factory->id(), dockWidget);
2402 }
2403 else {
2404 dockWidget = d->dockWidgetsMap[factory->id()];
2405 }
2406
2407#ifdef Q_OS_MACOS
2408 dockWidget->setAttribute(Qt::WA_MacSmallSize, true);
2409#endif
2410 dockWidget->setFont(KisUiFont::dockFont());
2411
2412 connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts()));
2413
2414 return dockWidget;
2415}
2416
2418{
2419 Q_FOREACH (QObject *child, children()) {
2420 if (child->inherits("QTabBar")) {
2421 ((QTabBar *)child)->setFont(KisUiFont::dockFont());
2422 }
2423 }
2424}
2425
2427{
2428 KisConfig cfg(true);
2429 QString themeFromConfig = cfg.widgetStyle();
2430
2431 Q_FOREACH (auto key, d->actionMap.keys()) { // find checked style to save to config
2432 if(d->actionMap.value(key)->isChecked()) {
2433 cfg.setWidgetStyle(key);
2434 qApp->setProperty(currentUnderlyingStyleNameProperty, key);
2435 qApp->setStyle(key);
2436
2437 // When switching to a style that uses system colors, reset the theme
2438#ifndef Q_OS_HAIKU
2439#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
2440 if (qApp->style()->objectName() == "macintosh") {
2441 d->themeManager->setCurrentTheme("System");
2442 }
2443#else
2444 if (qApp->style()->name() == "macos") {
2445 d->themeManager->setCurrentTheme("System");
2446 }
2447#endif
2448#endif
2449 }
2450 }
2451}
2452
2454{
2455 return d->dockWidgetsMap.values();
2456}
2457
2458QDockWidget* KisMainWindow::dockWidget(const QString &id)
2459{
2460 if (!d->dockWidgetsMap.contains(id)) return 0;
2461 return d->dockWidgetsMap[id];
2462}
2463
2465{
2467
2468 observers.append(static_cast<KoCanvasObserverBase*>(KisPart::instance()->playbackEngine()));
2469
2470 Q_FOREACH (QDockWidget *docker, dockWidgets()) {
2471 KoCanvasObserverBase *observer = dynamic_cast<KoCanvasObserverBase*>(docker);
2472 if (observer) {
2473 observers << observer;
2474 }
2475 else {
2476 warnKrita << docker << "is not a canvas observer";
2477 }
2478 }
2479
2480 return observers;
2481}
2482
2483
2484void KisMainWindow::toggleDockersVisibility(bool visible, bool onWelcomePage)
2485{
2486 if (!visible) {
2487 d->dockerStateBeforeHiding = saveState();
2488
2489 Q_FOREACH (QObject* widget, children()) {
2490 if (widget->inherits("QDockWidget")) {
2491 QDockWidget* dw = static_cast<QDockWidget*>(widget);
2492 if (dw->isVisible() && !(onWelcomePage && dw->property("ShowOnWelcomePage").toBool())) {
2493 dw->hide();
2494 }
2495 }
2496 }
2497 }
2498 else {
2499 restoreState(d->dockerStateBeforeHiding);
2500 }
2501}
2502
2504{
2505 bool enabled = (activeKisView() != 0);
2506
2507 d->mdiCascade->setEnabled(enabled);
2508 d->mdiNextWindow->setEnabled(enabled);
2509 d->mdiPreviousWindow->setEnabled(enabled);
2510 d->mdiTile->setEnabled(enabled);
2511 d->close->setEnabled(enabled);
2512 d->closeAll->setEnabled(enabled);
2513
2514 setActiveSubWindow(d->mdiArea->activeSubWindow());
2515
2525 QMdiSubWindow *subWindow = d->mdiArea->currentSubWindow();
2526 if (subWindow) {
2527 QMenu *menu = subWindow->systemMenu();
2528 if (menu && menu->actions().size() == 8) {
2529 Q_FOREACH (QAction *action, menu->actions()) {
2530 action->setShortcut(QKeySequence());
2531
2532 }
2533 menu->actions().last()->deleteLater();
2534 }
2535 }
2536
2537 d->actionManager()->updateGUI();
2538}
2539
2541{
2545 if (viewManager() && viewManager()->selectionManager()) {
2547 }
2548
2549 KisPart *kisPart = KisPart::instance();
2551 if (!layoutManager->primaryWorkspaceFollowsFocus()) return;
2552
2553 QUuid primary = layoutManager->primaryWindowId();
2554 if (primary.isNull()) return;
2555
2556 if (d->id == primary) {
2557 if (!d->workspaceBorrowedBy.isNull()) {
2558 KisMainWindow *borrower = kisPart->windowById(d->workspaceBorrowedBy);
2559 if (!borrower) return;
2560 swapWorkspaces(this, borrower);
2561 }
2562 } else {
2563 if (d->workspaceBorrowedBy == primary) return;
2564
2565 KisMainWindow *primaryWindow = kisPart->windowById(primary);
2566 if (!primaryWindow) return;
2567 swapWorkspaces(this, primaryWindow);
2568 }
2569}
2570
2571
2573{
2574 QMenu *menu = d->windowMenu->menu();
2575 menu->clear();
2576
2577#ifndef Q_OS_ANDROID
2578 menu->addAction(d->newWindow);
2579#endif
2580 menu->addAction(d->documentMenu);
2581
2582 QMenu *docMenu = d->documentMenu->menu();
2583 docMenu->clear();
2584
2585 QFontMetrics fontMetrics = docMenu->fontMetrics();
2586 QRect geom = this->geometry();
2587 QPoint p(geom.width() / 2 + geom.left(), geom.height() / 2 + geom.top());
2588 QScreen *screen = qApp->screenAt(p);
2589 int fileStringWidth = 300;
2590 if (screen) {
2591 fileStringWidth = int(screen->availableGeometry().width() * .40f);
2592 }
2593 Q_FOREACH (QPointer<KisDocument> doc, KisPart::instance()->documents()) {
2594 if (doc) {
2595 QString title = fontMetrics.elidedText(doc->path(), Qt::ElideMiddle, fileStringWidth);
2596 if (title.isEmpty() && doc->image()) {
2597 title = doc->image()->objectName();
2598 }
2599 QAction *action = docMenu->addAction(title);
2600 action->setIcon(qApp->windowIcon());
2601 connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map()));
2602 d->documentMapper->setMapping(action, doc);
2603 }
2604 }
2605
2606 menu->addAction(d->workspaceMenu);
2607 QMenu *workspaceMenu = d->workspaceMenu->menu();
2608 workspaceMenu->clear();
2610 KisResourceIterator resourceIterator(&resourceModel);
2611 KisMainWindow *m_this = this;
2612
2613 while (resourceIterator.hasNext()) {
2614 KisResourceItemSP resource = resourceIterator.next();
2615 QAction *action = workspaceMenu->addAction(resource->name());
2616 action->setProperty("md5", QVariant::fromValue<QString>(resource->md5sum()));
2617 connect(action, SIGNAL(triggered()), this, SLOT(restoreWorkspace()));
2618 }
2619 workspaceMenu->addSeparator();
2620 connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&Import Workspace...")),
2621 &QAction::triggered,
2622 this,
2623 [&]()
2624 {
2626
2627 KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
2628 dialog.setMimeTypeFilters(mimeTypes);
2629 dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
2630 QString filename = dialog.filename();
2631
2633 });
2634
2635 connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&New Workspace...")),
2636 &QAction::triggered,
2637 [=]() {
2638 QString name;
2639 name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."),
2640 i18nc("@label:textbox", "Name:"));
2641 if (name.isEmpty()) {
2642 return;
2643 }
2644
2646 workspace->setDockerState(m_this->saveState());
2647 workspace->setImage(layoutThumbnail());
2648 workspace->setValid(true);
2649
2650 // this line must happen before we save the workspace to resource folder or other places
2651 // because it mostly just triggers palettes to be saved into the workspace
2652 d->viewManager->canvasResourceProvider()->notifySavingWorkspace(workspace);
2653 workspace->setValid(true);
2654
2655 workspace->setFilename(name.replace(" ", "_") + workspace->defaultFileExtension());
2656 workspace->setName(name);
2657
2659 });
2660
2661 // TODO: What to do about delete?
2662 // workspaceMenu->addAction(i18nc("@action:inmenu", "&Delete Workspace..."));
2663
2664 menu->addSeparator();
2665 menu->addAction(d->close);
2666 menu->addAction(d->closeAll);
2667 if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2668 menu->addSeparator();
2669 menu->addAction(d->mdiTile);
2670 menu->addAction(d->mdiCascade);
2671 }
2672 menu->addSeparator();
2673 menu->addAction(d->mdiNextWindow);
2674 menu->addAction(d->mdiPreviousWindow);
2675 menu->addSeparator();
2676
2677 QList<QMdiSubWindow *> windows = d->mdiArea->subWindowList();
2678 for (int i = 0; i < windows.size(); ++i) {
2679 QPointer<KisView>child = qobject_cast<KisView*>(windows.at(i)->widget());
2680 if (child && child->document()) {
2681 QString text;
2682 if (i < 9) {
2683 text = i18n("&%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2684 }
2685 else {
2686 text = i18n("%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2687 }
2688
2689 QAction *action = menu->addAction(text);
2690 action->setIcon(qApp->windowIcon());
2691 action->setCheckable(true);
2692 action->setChecked(child == activeKisView());
2693 connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map()));
2694 d->windowMapper->setMapping(action, windows.at(i));
2695 }
2696 }
2697
2698 bool showMdiArea = windows.count( ) > 0;
2699 if (!showMdiArea) {
2700 showWelcomeScreen(true); // see workaround in function in header
2701 }
2702
2703 // enable/disable the toolbox docker if there are no documents open
2704 Q_FOREACH (QObject* widget, children()) {
2705 if (widget->inherits("QDockWidget")) {
2706 QDockWidget* dw = static_cast<QDockWidget*>(widget);
2707
2708 if ( dw->objectName() == "ToolBox") {
2709 dw->setEnabled(showMdiArea);
2710 }
2711 }
2712 }
2713}
2714
2716{
2717 bool onlyOne = false;
2718 if (d->mdiArea->subWindowList().size() == 1 && d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2719 onlyOne = true;
2720 }
2721 Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2722 if (onlyOne) {
2723 subwin->setWindowFlags(subwin->windowFlags() | Qt::FramelessWindowHint);
2724 subwin->showMaximized();
2725 } else {
2726 subwin->setWindowFlags((subwin->windowFlags() | Qt::FramelessWindowHint) ^ Qt::FramelessWindowHint);
2727 }
2728 }
2729}
2730
2732{
2733 if (!window) {
2735 return;
2736 }
2737 QMdiSubWindow *subwin = qobject_cast<QMdiSubWindow *>(window);
2738 //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow;
2739
2740 if (subwin && subwin != d->activeSubWindow) {
2741 KisView *view = qobject_cast<KisView *>(subwin->widget());
2742 //dbgKrita << "\t" << view << activeView();
2743 if (view && view != activeView()) {
2744 setActiveView(view);
2745 }
2746 d->activeSubWindow = subwin;
2747 }
2749 d->actionManager()->updateGUI();
2750}
2751
2753{
2754 KisConfig cfg(true);
2755 QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry<int>("mdi_viewmode", (int)QMdiArea::TabbedView);
2756 d->mdiArea->setViewMode(viewMode);
2757 Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
2758 subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
2759 subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
2760 if (viewMode == QMdiArea::TabbedView) {
2761 subwin->setWindowState(Qt::WindowMaximized);
2762 }
2763
2771 if (viewMode == QMdiArea::TabbedView) {
2772 Qt::WindowFlags oldFlags = subwin->windowFlags();
2773 Qt::WindowFlags flags = oldFlags;
2774
2775 flags &= ~Qt::WindowStaysOnTopHint;
2776 flags &= ~Qt::WindowStaysOnBottomHint;
2777
2778 if (flags != oldFlags) {
2779 subwin->setWindowFlags(flags);
2780 subwin->showMaximized();
2781 }
2782 }
2783 }
2784#ifdef Q_OS_MACOS
2786#endif
2787
2788 KConfigGroup group( KSharedConfig::openConfig(), "theme");
2789#ifndef Q_OS_HAIKU
2790 d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
2791#endif
2792 d->actionManager()->updateGUI();
2793
2794 QString s = cfg.getMDIBackgroundColor();
2796 QBrush brush(c.toQColor());
2797 d->mdiArea->setBackground(brush);
2798
2799 QString backgroundImage = cfg.getMDIBackgroundImage();
2800 if (backgroundImage != "") {
2801 QImage image(backgroundImage);
2802 QBrush brush(image);
2803 d->mdiArea->setBackground(brush);
2804 }
2805
2806 d->mdiArea->update();
2807
2808 qApp->setFont(KisUiFont::normalFont());
2809
2810 Q_FOREACH (QObject* widget, children()) {
2811 if (widget->inherits("QDockWidget")) {
2812 QDockWidget* dw = static_cast<QDockWidget*>(widget);
2813 dw->setFont(KisUiFont::dockFont());
2814 }
2815 }
2816}
2817
2818KisView* KisMainWindow::newView(QObject *document, QMdiSubWindow *subWindow)
2819{
2820 KisDocument *doc = qobject_cast<KisDocument*>(document);
2821 KisView *view = addViewAndNotifyLoadingCompleted(doc, subWindow);
2822 d->actionManager()->updateGUI();
2823
2824 return view;
2825}
2826
2828{
2829#ifdef Q_OS_ANDROID
2830 // Check if current mainwindow exists, just to be sure.
2831 if (KisPart::instance()->currentMainwindow()) {
2832 QMessageBox::warning(this, i18nc("@title:window", "Krita"),
2833 "Creating a New Main Window is unsupported on Android");
2834 return;
2835 }
2836#endif
2838 mainWindow->initializeGeometry();
2839 mainWindow->show();
2840}
2841
2843{
2844 if (d->mdiArea->currentSubWindow()) {
2845 d->mdiArea->currentSubWindow()->close();
2846 d->actionManager()->updateGUI();
2847 }
2848}
2849
2851{
2852 // print error if the lcms engine is not available
2853 if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) {
2854 // need to wait 1 event since exiting here would not work.
2855 m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now.");
2856 m_dieOnError = true;
2857 QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
2858 return;
2859 }
2860
2862
2863 // window is created signal (used in Python)
2864 // there must be some asynchronous things happening in the constructor, because the window cannot
2865 // be referenced until after this timeout is done
2867}
2868
2870{
2871 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Installation error"), m_errorMessage);
2872 if (m_dieOnError) {
2873 exit(10);
2874 }
2875}
2876
2878{
2879 KisAboutApplication dlg(this);
2880 dlg.exec();
2881}
2882
2884{
2885 if (!d->mdiArea) return 0;
2886 QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow();
2887 //dbgKrita << "activeKisView" << activeSubWindow;
2888 if (!activeSubWindow) return 0;
2889 return qobject_cast<KisView*>(activeSubWindow->widget());
2890}
2891
2893{
2894 KIS_ASSERT_RECOVER_NOOP(controller == KoToolManager::instance()->activeCanvasController());
2895 bool isOurOwnView = false;
2896
2897 Q_FOREACH (QPointer<KisView> view, KisPart::instance()->views()) {
2898 if (view && view->canvasController() == controller) {
2899 isOurOwnView = view->mainWindow() == this;
2900 }
2901 }
2902
2903 if (!isOurOwnView) return;
2904
2905 Q_FOREACH (QWidget *w, optionWidgetList) {
2906#ifdef Q_OS_MACOS
2907 w->setAttribute(Qt::WA_MacSmallSize, true);
2908#endif
2909 w->setFont(KisUiFont::dockFont());
2910 }
2911
2912 if (d->toolOptionsDocker) {
2913 d->toolOptionsDocker->setOptionWidgets(optionWidgetList);
2914 }
2915 else {
2916 d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList);
2917 }
2918}
2919
2921{
2922 KisActionManager *actionManager = d->actionManager();
2923
2924
2925
2930 d->fullScreenMode = actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool)));
2931
2932 d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
2933
2935 d->saveAction->setActivationFlags(KisAction::IMAGE_IS_WRITABLE);
2936
2938 d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE);
2939
2941 d->undo->setActivationFlags(KisAction::ACTIVE_IMAGE);
2942
2944 d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE);
2945
2946 d->undoActionsUpdateManager.reset(new KisUndoActionsUpdateManager(d->undo, d->redo));
2947 d->undoActionsUpdateManager->setCurrentDocument(d->activeView ? d->activeView->document() : 0);
2948
2949 d->importAnimation = actionManager->createAction("file_import_animation");
2950 connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation()));
2951
2952 d->importVideoAnimation = actionManager->createAction("file_import_video_animation");
2953 connect(d->importVideoAnimation, SIGNAL(triggered()), this, SLOT(importVideoAnimation()));
2954
2955 d->renderAnimation = actionManager->createAction("render_animation");
2956 d->renderAnimation->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
2957 connect( d->renderAnimation, SIGNAL(triggered()), this, SLOT(renderAnimation()));
2958
2959 d->renderAnimationAgain = actionManager->createAction("render_animation_again");
2960 d->renderAnimationAgain->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
2961 connect( d->renderAnimationAgain, SIGNAL(triggered()), this, SLOT(renderAnimationAgain()));
2962
2963 d->closeAll = actionManager->createAction("file_close_all");
2964 connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll()));
2965
2966 d->importFile = actionManager->createAction("file_import_file");
2967 d->importFile->setActivationFlags(KisAction::IMAGE_IS_WRITABLE);
2968 connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile()));
2969
2970 d->exportFile = actionManager->createAction("file_export_file");
2971 connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile()));
2972
2973 d->exportFileAdvance = actionManager->createAction("file_export_advanced");
2974 connect(d->exportFileAdvance, SIGNAL(triggered(bool)), this, SLOT(slotExportAdvance()));
2975
2976 /* The following entry opens the document information dialog. Since the action is named so it
2977 intends to show data this entry should not have a trailing ellipses (...). */
2978 d->showDocumentInfo = actionManager->createAction("file_documentinfo");
2979 connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo()));
2980#ifndef Q_OS_HAIKU
2981 d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this));
2982 d->themeManager->registerThemeActions(actionCollection());
2983 connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged()), Qt::QueuedConnection);
2984 connect(this, SIGNAL(themeChanged()), d->welcomePage, SLOT(slotUpdateThemeColors()), Qt::UniqueConnection);
2985#endif
2986 d->toggleDockers = actionManager->createAction("view_toggledockers");
2987
2988
2989 KisConfig(true).showDockers(true);
2990 d->toggleDockers->setChecked(true);
2991 connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool)));
2992
2993 d->resetConfigurations = actionManager->createAction("reset_configurations");
2994 connect(d->resetConfigurations, SIGNAL(triggered()), this, SLOT(slotResetConfigurations()));
2995
2996#ifndef Q_OS_ANDROID
2997 d->toggleDetachCanvas = actionManager->createAction("view_detached_canvas");
2998 d->toggleDetachCanvas->setChecked(false);
2999 connect(d->toggleDetachCanvas, SIGNAL(toggled(bool)), SLOT(setCanvasDetached(bool)));
3000#endif
3001 setCanvasDetached(false);
3002
3003 d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars");
3004 d->toggleDockerTitleBars->setChecked(KisConfig(false).showDockerTitleBars());
3005 connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool)));
3006
3007 actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu);
3008 actionCollection()->addAction("window", d->windowMenu);
3009
3010 actionCollection()->addAction("style_menu", d->styleMenu); // for widget styles: breeze, fusion, etc
3011
3012 d->mdiCascade = actionManager->createAction("windows_cascade");
3013 connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows()));
3014
3015 d->mdiTile = actionManager->createAction("windows_tile");
3016 connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows()));
3017
3018 d->mdiNextWindow = actionManager->createAction("windows_next");
3019 connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow()));
3020
3021 d->mdiPreviousWindow = actionManager->createAction("windows_previous");
3022 connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow()));
3023
3024#ifndef Q_OS_ANDROID
3025 d->newWindow = actionManager->createAction("view_newwindow");
3026 connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow()));
3027#endif
3028
3030
3031 d->showSessionManager = actionManager->createAction("file_sessions");
3032 connect(d->showSessionManager, SIGNAL(triggered(bool)), this, SLOT(slotShowSessionManager()));
3033
3034 d->commandBarAction = actionManager->createAction("command_bar_open");
3035 connect(d->commandBarAction, SIGNAL(triggered(bool)), this, SLOT(openCommandBar()));
3036
3038
3039 for (int i = 0; i < 2; i++) {
3040 d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer"));
3041 d->expandingSpacers[i]->setDefaultWidget(new QWidget(this));
3042 d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
3043 actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]);
3044 }
3045
3046 {
3047 using namespace KisWidgetConnectionUtils;
3048
3049 QAction *action = actionManager->createAction("lock_toolbars");
3050 connectControl(action, KisToolBar::toolBarStateModel(), "toolBarsLocked");
3051 }
3052}
3053
3055{
3056 Q_FOREACH (KisToolBar *toolBar, toolBars()) {
3057 toolBar->layout()->setSpacing(4);
3058 toolBar->setStyleSheet("QToolBar { border: none }"); // has a border in "Fusion" style that people don't like
3059
3060 // Hide text for buttons with an icon in the toolbar
3061 Q_FOREACH (QAction *ac, toolBar->actions()){
3062 if (ac->icon().pixmap(QSize(1,1)).isNull() == false){
3063 ac->setPriority(QAction::LowPriority);
3064 } else {
3065 ac->setIcon(QIcon());
3066 }
3067 }
3068 }
3069}
3070
3072{
3073 // if the user didn's specify the geometry on the command line (does anyone do that still?),
3074 // we first figure out some good default size and restore the x,y position. See bug 285804Z.
3075 KConfigGroup cfg = d->windowStateConfig;
3076 QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray()));
3077 if (!restoreGeometry(geom)) {
3078 QRect desk = this->screen()->availableGeometry();
3079
3080 quint32 x = desk.x();
3081 quint32 y = desk.y();
3082 quint32 w = 0;
3083 quint32 h = 0;
3084
3085 // Default size -- maximize on small screens, something useful on big screens
3086 const int deskWidth = desk.width();
3087 if (deskWidth > 1024) {
3088 // a nice width, and slightly less than total available
3089 // height to compensate for the window decs
3090 w = (deskWidth / 3) * 2;
3091 h = (desk.height() / 3) * 2;
3092 }
3093 else {
3094 w = desk.width();
3095 h = desk.height();
3096 }
3097
3098 x += (desk.width() - w) / 2;
3099 y += (desk.height() - h) / 2;
3100
3101 move(x,y);
3102 setGeometry(geometry().x(), geometry().y(), w, h);
3103 }
3104 d->fullScreenMode->setChecked(isFullScreen());
3105}
3106
3108{
3109 QDesktopServices::openUrl(QUrl("https://docs.krita.org"));
3110}
3111
3113{
3114 Q_FOREACH (QDockWidget *dock, dockWidgets()) {
3115 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits("KisUtilityTitleBar")) {
3116 dock->titleBarWidget()->setVisible(show || dock->isFloating());
3117 }
3118 }
3119
3120 KisConfig cfg(true);
3121 cfg.setShowDockerTitleBars(show);
3122}
3123
3125{
3126 if (finished) {
3128 }
3129}
3130
3132{
3133 QScreen *screen = QGuiApplication::primaryScreen();
3134
3135 for (QWindow* window: QGuiApplication::topLevelWindows()) {
3136 // Android: we shouldn't transform Window managers independent of its child
3137 if ((window->type() == Qt::Popup)
3138 && (window->flags() & Qt::FramelessWindowHint) == 0
3139 && (window->geometry().topLeft() != QPoint(0, 0))) {
3140 // We are using reversed values. Because geometry returned is not the updated
3141 // one, but the previous one.
3142 int screenHeight = screen->geometry().width();
3143 int screenWidth = screen->geometry().height();
3144
3145 // scaling
3146 int new_x = (window->position().x() * screenWidth) / screenHeight;
3147 int new_y = (window->position().y() * screenHeight) / screenWidth;
3148
3149 // window width or height shouldn't change
3150 int winWidth = window->geometry().width();
3151 int winHeight = window->geometry().height();
3152
3153 // Try best to not let the window go beyond screen.
3154 if (new_x > screenWidth - winWidth) {
3155 new_x = screenWidth - winWidth;
3156 if (new_x < 0)
3157 new_x = 0;
3158 }
3159 if (new_y > screenHeight - winHeight) {
3160 new_y = screenHeight - winHeight;
3161 if (new_y < 0)
3162 new_y = 0;
3163 }
3164
3165 window->setPosition(QPoint(new_x, new_y));
3166 }
3167 }
3168}
3169
3180
3186
3187// One action has only one icon override, local XML contains ActionIconOverrides like so:
3188// <ActionIconOverrides>
3189// <Action name="erase_action" icon="wheel-sectors"/>
3190// <Action name="about_krita" icon="application-exit"/>
3191// </ActionIconOverrides>
3192// This function will rerender the actions with icons in UI (toolbars, shortcuts options etc.)
3194{
3195 QString xmlPath = KoResourcePaths::locateLocal("data", "krita5.xmlgui");
3196 QFile file(xmlPath);
3197 if (!file.exists()) {
3198 return;
3199 }
3200
3201 QDomDocument doc;
3202 if (!file.open(QIODevice::ReadOnly) || !doc.setContent(&file)) {
3203 return;
3204 }
3205
3206 QDomNodeList overridesList = doc.elementsByTagName(QStringLiteral("ActionIconOverrides"));
3207 if (!overridesList.isEmpty()) {
3208 QDomElement overridesElement = overridesList.at(0).toElement();
3209 QDomNodeList actions = overridesElement.elementsByTagName(QStringLiteral("Action"));
3210 for (int i = 0; i < actions.count(); ++i) {
3211 QDomElement actionElement = actions.at(i).toElement();
3212 QString name = actionElement.attribute(QStringLiteral("name"));
3213 QString icon = actionElement.attribute(QStringLiteral("icon"));
3214 if (!name.isEmpty() && !icon.isEmpty()) {
3215 QAction *qa = actionCollection()->action(name);
3216 if (qa) {
3217 qa->setIcon(KisIconUtils::loadIcon(icon));
3218 }
3219 }
3220 }
3221 }
3222}
3223
3225{
3226 // Add all actions with a menu property to the main window
3227 Q_FOREACH(QAction *action, this->actionCollection()->actions()) {
3228 QString menuLocation = action->property("menulocation").toString();
3229 if (!menuLocation.isEmpty()) {
3230 QAction *found = 0;
3231 QList<QAction *> candidates = this->menuBar()->actions();
3232 Q_FOREACH(const QString &name, menuLocation.split("/")) {
3233 Q_FOREACH(QAction *candidate, candidates) {
3234 if (candidate->objectName().toLower() == name.toLower()) {
3235 found = candidate;
3236 candidates = candidate->menu()->actions();
3237 break;
3238 }
3239 }
3240 if (candidates.isEmpty()) {
3241 break;
3242 }
3243 }
3244
3245 if (found && found->menu()) {
3246 QList<QAction *> existingActions = found->menu()->actions();
3247
3248 if (std::find_if(existingActions.begin(),
3249 existingActions.end(),
3250 kismpl::mem_equal_to(&QAction::objectName, action->objectName()))
3251 == existingActions.end()) {
3252
3253 if (std::is_sorted(existingActions.begin(),
3254 existingActions.end(),
3255 kismpl::mem_less(&QAction::objectName))) {
3256
3257 auto it = std::upper_bound(existingActions.begin(),
3258 existingActions.end(),
3259 action->objectName(),
3260 kismpl::mem_less(&QAction::objectName));
3261 found->menu()->insertAction(it != existingActions.end() ? *it : nullptr, action);
3262
3263 } else {
3264 found->menu()->addAction(action);
3265 }
3266 }
3267 }
3268 }
3269 }
3270}
3271
3272#include <moc_KisMainWindow.cpp>
const Params2D p
qreal v
#define KIS_MIME_TYPE
Definition KisDocument.h:56
QList< QString > QStringList
Recent files action.
KDE top level main window with predefined action layout
QAction * toolBarMenuAction()
void setStandardToolBarMenuEnabled(bool enable)
void applyMainWindowSettings(const KConfigGroup &config) override
bool event(QEvent *event) override
virtual KisKXMLGUIFactory * guiFactory()
bool isHelpMenuEnabled() const
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
KisAction * createAction(const QString &name)
void addAction(const QString &name, KisAction *action)
KisAction * createStandardAction(KStandardAction::StandardAction, const QObject *receiver, const char *member)
@ ACTIVE_IMAGE
Activate if there is at least one image.
Definition kis_action.h:43
@ IMAGE_IS_WRITABLE
Activate KisDocument::isReadWrite() is active.
Definition kis_action.h:62
@ IMAGE_HAS_ANIMATION
Activate if the image has an animation.
Definition kis_action.h:58
void fromProperties(KisPropertiesConfigurationSP config)
Base class for the Krita app.
void setFavoriteResourceManager(KisFavoriteResourceManager *favoriteResourceManager)
static KisConfigNotifier * instance()
QString widgetStyle(bool defaultValue=false)
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