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