15#include <QApplication>
18#include <QStandardPaths>
19#include <QDesktopServices>
24#include <QInputDialog>
27#include <QMdiSubWindow>
29#include <QMutexLocker>
37#include <QStyleFactory>
42#include <QStackedWidget>
47#include <QTemporaryDir>
49#include <QActionGroup>
52#include <kactionmenu.h>
56#include <klocalizedstring.h>
57#include <kaboutdata.h>
65#include <ktoggleaction.h>
72#include <kwindowconfig.h>
73#include <kacceleratormanager.h>
161#include <config-qmdiarea-always-show-subwindow-title.h>
162#include <config-qt-patches-present.h>
167#if defined(Q_OS_ANDROID) && KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
176 QString
id()
const override {
177 return "sharedtooldocker";
196 , styleMenu(new KActionMenu(i18nc(
"@action:inmenu",
"Styles"), parent))
197 , dockWidgetMenu(new KActionMenu(i18nc(
"@action:inmenu",
"&Dockers"), parent))
198 , windowMenu(new KActionMenu(i18nc(
"@action:inmenu",
"&Window"), parent))
199 , documentMenu(new KActionMenu(i18nc(
"@action:inmenu",
"New &View"), parent))
200 , workspaceMenu(new KActionMenu(i18nc(
"@action:inmenu",
"Wor&kspace"), parent))
202 , widgetStack(new QStackedWidget(parent))
203 , mdiArea(new QMdiArea(parent))
207 if (
id.isNull()) this->
id = QUuid::createUuid();
209 welcomeScroller =
new QScrollArea();
210 welcomeScroller->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
211 welcomeScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
212 welcomeScroller->setWidget(welcomePage);
213 welcomeScroller->setWidgetResizable(
true);
215 widgetStack->addWidget(welcomeScroller);
216 widgetStack->addWidget(mdiArea);
217 mdiArea->setTabsMovable(
true);
218 mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder);
219 mdiArea->setDocumentMode(
true);
220 mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation);
221#ifdef HAVE_QMDIAREA_ALWAYS_SHOW_SUBWINDOW_TITLE
222 mdiArea->setOption(QMdiArea::AlwaysShowSubwindowNameInTitleBar);
229 qDeleteAll(toolbarList);
242 bool firstTime {
true};
243 bool windowSizeDirty {
false};
245 bool flashWindowHackInProgress {
false};
270 KisAction *showDonationManagementDialog {
nullptr};
271 KisAction *manageSubscriptions {
nullptr};
272#if KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
273 KisAction *changeInterfaceScale {
nullptr};
284 KActionMenu *styleMenu {
nullptr};
285 QActionGroup* styleActions {
nullptr};
288 KActionMenu *dockWidgetMenu {
nullptr};
289 KActionMenu *windowMenu {
nullptr};
290 KActionMenu *documentMenu {
nullptr};
291 KActionMenu *workspaceMenu {
nullptr};
306 QCloseEvent *deferredClosingEvent {
nullptr};
310 QScrollArea *welcomeScroller {
nullptr};
314 QStackedWidget *widgetStack {
nullptr};
316 QMdiArea *mdiArea {
nullptr};
317 QMdiSubWindow *activeSubWindow {
nullptr};
333 return viewManager->actionManager();
337 QObjectList objects = mdiArea->children();
338 Q_FOREACH (QObject *
object, objects) {
339 QTabBar *bar = qobject_cast<QTabBar*>(
object);
355 widget->setEnabled(
false);
370 KAcceleratorManager::setNoAccel(
this);
373 connect(
d->workspacemodel, SIGNAL(modelReset()),
this, SLOT(
updateWindowMenu()));
374 connect(
d->workspacemodel, SIGNAL(rowsInserted(QModelIndex,
int,
int)),
this, SLOT(
updateWindowMenu()));
375 connect(
d->workspacemodel, SIGNAL(rowsRemoved(QModelIndex,
int,
int)),
this, SLOT(
updateWindowMenu()));
378 KConfigGroup group( KSharedConfig::openConfig(),
"theme");
382 d->windowStateConfig = KSharedConfig::openConfig()->group(
"MainWindow");
385 setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
386 setDockNestingEnabled(
true);
394 qApp->setStartDragDistance(25);
397 connect(
this, SIGNAL(
themeChanged()),
d->viewManager, SLOT(updateIcons()));
415 d->toolOptionsDocker = qobject_cast<KoToolDocker*>(
createDockWidget(&toolDockerFactory));
416 if (
d->toolOptionsDocker) {
417 d->toolOptionsDocker->toggleViewAction()->setEnabled(
true);
421 QMap<QString, QAction*> dockwidgetActions;
424 dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction();
430 dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction();
434 if (
d->toolOptionsDocker) {
435 dockwidgetActions[
d->toolOptionsDocker->toggleViewAction()->text()] =
d->toolOptionsDocker->toggleViewAction();
439 Q_FOREACH (QString title, dockwidgetActions.keys()) {
440 d->dockWidgetMenu->addAction(dockwidgetActions[title]);
445 d->styleActions =
new QActionGroup(
this);
450 Q_FOREACH (QString styleName, QStyleFactory::keys()) {
452 action =
new QAction(styleName,
d->styleActions);
453 action->setCheckable(
true);
454 d->actionMap.insert(styleName,
action);
455 d->styleMenu->addAction(
d->actionMap.value(styleName));
459 QString styleFromConfig = cfg.
widgetStyle().toLower();
460 QString styleToSelect = styleFromConfig.isEmpty() ? style()->objectName().toLower() : styleFromConfig;
462 Q_FOREACH (
auto key,
d->actionMap.keys()) {
463 if(key.toLower() == styleToSelect) {
464 d->actionMap.value(key)->setChecked(
true);
468 connect(
d->styleActions, SIGNAL(triggered(QAction*)),
480 if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) {
481 wdg->setVisible(
true);
488 if (mainwindowObserver) {
493 d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
494 d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
495 d->mdiArea->setTabPosition(QTabWidget::North);
496 d->mdiArea->setTabsClosable(
true);
497 d->mdiArea->setAcceptDrops(
true);
501 setCentralWidget(
d->widgetStack);
502 d->widgetStack->setCurrentIndex(0);
505 connect(
d->windowMapper, SIGNAL(mapped(QWidget*)),
this, SLOT(
setActiveSubWindow(QWidget*)));
506 connect(
d->documentMapper, SIGNAL(mapped(QObject*)),
this, SLOT(
newView(QObject*)));
514 d->welcomePage->setMainWindow(
this);
522 d->helpMenu =
new KisKHelpMenu(
this, KAboutData::applicationData(),
false);
537 if (helpContentsAction) {
538 actions->
addAction(helpContentsAction->objectName(), helpContentsAction);
540 if (whatsThisAction) {
541 actions->
addAction(whatsThisAction->objectName(), whatsThisAction);
543 if (reportBugAction) {
544 actions->
addAction(reportBugAction->objectName(), reportBugAction);
546 if (switchLanguageAction) {
547 actions->
addAction(switchLanguageAction->objectName(), switchLanguageAction);
549 if (aboutAppAction) {
550 actions->
addAction(aboutAppAction->objectName(), aboutAppAction);
552 if (aboutKdeAction) {
553 actions->
addAction(aboutKdeAction->objectName(), aboutKdeAction);
561 helpAction->disconnect(SIGNAL(triggered()));
562 connect(helpAction, SIGNAL(triggered()),
this, SLOT(
showManual()));
566 QSet<QKeySequence> existingShortcuts;
568 if(
action->shortcut() == QKeySequence(0)) {
572 Q_ASSERT(!existingShortcuts.contains(
action->shortcut()));
573 existingShortcuts.insert(
action->shortcut());
592 Q_FOREACH (QWidget* it,
guiFactory()->containers(
"ToolBar")) {
595 KToggleAction* act =
new KToggleAction(i18n(
"Show %1 Toolbar",
toolBar->windowTitle()),
this);
597 act->setCheckedState(KGuiItem(i18n(
"Hide %1 Toolbar",
toolBar->windowTitle())));
599 act->setChecked(!
toolBar->isHidden());
602 warnUI <<
"Toolbar list contains a " << it->metaObject()->className() <<
" which is not a toolbar!";
611 d->viewManager->updateGUI();
612 d->viewManager->updateIcons();
614 QTimer::singleShot(1000, Qt::CoarseTimer,
this, SLOT(
checkSanity()));
617 using namespace std::placeholders;
618 std::function<
void (
int)> callback(
621 d->tabSwitchCompressor.reset(
626 if (cfg.
readEntry(
"CanvasOnlyActive",
false)) {
627 QString currentWorkspace = cfg.
readEntry<QString>(
"CurrentWorkspace",
"Default");
634 menuBar()->setVisible(
true);
643 setFixedSize(KisApplication::primaryScreen()->availableGeometry().size());
645 QScreen *s = QGuiApplication::primaryScreen();
646 s->setOrientationUpdateMask(Qt::LandscapeOrientation|Qt::InvertedLandscapeOrientation|Qt::PortraitOrientation|Qt::InvertedPortraitOrientation);
649#if KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
653 if (androidScaling) {
654 connect(androidScaling,
657 &KisMainWindow::slotFlashWindowHack,
658 Qt::QueuedConnection);
665 QAndroidJniObject::callStaticMethod<void>(
"org/qtproject/qt5/android/QtNative",
"setApplicationState",
"(I)V", Qt::ApplicationActive);
668 setAcceptDrops(
true);
669 QTabBar *tabBar =
d->findTabBarHACK();
671 tabBar->setElideMode(Qt::ElideRight);
674 tabBar->setExpanding(
true);
675 tabBar->setAcceptDrops(
true);
676 tabBar->setChangeCurrentOnDrag(
true);
713 Q_FOREACH (QMdiSubWindow *subwin,
d->mdiArea->subWindowList()) {
715 (
void) subwin->close();
723 delete d->viewManager;
739 if (
d->activeView == view && !subWindow)
return;
742 d->activeView->disconnect(
this);
783 QMdiSubWindow *currentSubWin =
d->mdiArea->currentSubWindow();
784 bool shouldMaximize = currentSubWin ? currentSubWin->isMaximized() :
true;
785 subwin =
d->mdiArea->addSubWindow(imageView);
801 if (useNativeSurfaceForCanvas) {
802 subwin->setAttribute(Qt::WA_DontCreateNativeAncestors);
803 subwin->setAttribute(Qt::WA_NativeWindow);
806 if (shouldMaximize) {
807 subwin->setWindowState(Qt::WindowMaximized);
810 subwin->setWidget(imageView);
813 subwin->setAttribute(Qt::WA_DeleteOnClose,
true);
817 subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.
readEntry<
int>(
"mdi_rubberband", cfg.
useOpenGL()));
818 subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.
readEntry<
int>(
"mdi_rubberband", cfg.
useOpenGL()));
819 subwin->setWindowIcon(qApp->windowIcon());
826 if (
d->mdiArea->subWindowList().size() == 1) {
827 imageView->showMaximized();
870 std::optional<KisDlgPreferences::PageDesc>page = std::nullopt;
873 QVariant data =
action->data();
874 if (data.userType() == QMetaType::QVariantList) {
876 action->setData(QVariant());
879 Q_ASSERT(list.length() == 2);
880 Q_ASSERT(list[0].userType() == QMetaType::Int);
881 Q_ASSERT(list[1].userType() == QMetaType::Int);
884 int t = list[1].toInt();
890 if (dlgPreferences->editPreferences(page)) {
913 d->viewManager->showHideScrollbars();
924 d->mdiArea->setPalette(qApp->palette());
925 for (
int i=0; i<
d->mdiArea->subWindowList().size(); i++) {
926 QMdiSubWindow *window =
d->mdiArea->subWindowList().at(i);
928 window->setPalette(qApp->palette());
929 KisView *view = qobject_cast<KisView*>(window->widget());
943 objects.append(aToolBar);
944 while (!objects.isEmpty()) {
945 QWidget*
widget = qobject_cast<QWidget*>(objects.takeFirst());
947 objects.append(
widget->children());
948 widget->setPalette(qApp->palette());
957 KConfigGroup group(KSharedConfig::openConfig(),
"theme");
959 if (group.readEntry(
"Theme",
"") ==
d->themeManager->currentThemeName())
return;
962 group.writeEntry(
"Theme",
d->themeManager->currentThemeName());
967 Q_FOREACH (QWidget* topLevelWidget, qApp->topLevelWidgets()) {
968 if (topLevelWidget ==
this) {
972 if (topLevelWidget->isHidden()) {
976 KisMainWindow *topLevelMainWindow = qobject_cast<KisMainWindow*>(topLevelWidget);
977 if (topLevelMainWindow) {
982 objects.append(topLevelWidget);
983 while (!objects.isEmpty()) {
984 QWidget*
widget = qobject_cast<QWidget*>(objects.takeFirst());
986 objects.append(
widget->children());
1003 QString closeButtonImageUrl;
1004 QString closeButtonHoverColor;
1006 closeButtonImageUrl = QStringLiteral(
":/dark_close-tab.svg");
1007 closeButtonHoverColor = QStringLiteral(
"lightcoral");
1009 closeButtonImageUrl = QStringLiteral(
":/light_close-tab.svg");
1010 closeButtonHoverColor = QStringLiteral(
"darkred");
1012 QString tabStyleSheet = QStringLiteral(R
"(
1013 QTabBar::close-button {
1018 QTabBar::close-button:hover {
1019 background-color: %2;
1021 QTabBar::close-button:pressed {
1022 background-color: red;
1025 QHeaderView::section {
1030 .arg(closeButtonImageUrl, closeButtonHoverColor);
1033 QTabBar* tabBar = d->findTabBarHACK();
1035 tabBar->setStyleSheet(tabStyleSheet);
1042 return centralWidget() !=
d->widgetStack;
1049 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"),
1050 "Detach Canvas is unsupported on Android");
1055 QWidget *outgoingWidget = centralWidget() ? takeCentralWidget() :
nullptr;
1056 QWidget *incomingWidget =
d->canvasWindow->swapMainWidget(outgoingWidget);
1058 if (incomingWidget) {
1059 setCentralWidget(incomingWidget);
1063 d->canvasWindow->show();
1065 d->canvasWindow->hide();
1067 d->toggleDetachCanvas->setChecked(detach);
1073 return d->canvasWindow;
1088 const QString fileName = QFileInfo(documentPath).fileName();
1090 if (!fileName.isEmpty()) {
1091 d->saveAction->setToolTip(i18n(
"Save as %1", fileName));
1094 d->saveAction->setToolTip(i18n(
"Save"));
1100 Q_UNUSED(readWrite);
1101 d->actionManager()->updateGUI();
1106 if (
d->activeView) {
1107 return d->activeView;
1115 QApplication::processEvents();
1117 if (!QFile(path).exists()) {
1119 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"The file %1 does not exist.", path));
1129 if (!QFile(path).exists()) {
1130 qWarning() <<
"KisMainWindow::openDocumentInternal. Could not open:" << path;
1140 d->firstTime =
true;
1142 connect(newdoc, SIGNAL(canceled(QString)),
this, SLOT(
slotLoadCanceled(QString)));
1161 ( path.startsWith(QDir::tempPath())
1162 || path.startsWith(QDir::homePath())
1164 ( QFileInfo(path).fileName().startsWith(
".krita")
1165 || QFileInfo(path).fileName().startsWith(
"krita")
1169 QString path = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1170 if (!QFileInfo(path).exists()) {
1171 path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
1173 newdoc->
setPath(path +
"/" + newdoc->objectName() +
".kra");
1180 Q_FOREACH(QMdiSubWindow *subwindow,
d->mdiArea->subWindowList()) {
1181 KisView *view = qobject_cast<KisView*>(subwindow->widget());
1185 if (view->
document() == document) {
1196 QMdiSubWindow *subWindow)
1216 slotFlashWindowHack();
1226 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1228 dialog.setCaption(isImporting ? i18n(
"Import Images") : i18n(
"Open Images"));
1239 KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
1240 if (newdoc && newdoc->
image()) {
1244 disconnect(newdoc, SIGNAL(canceled(QString)),
this, SLOT(
slotLoadCanceled(QString)));
1253 KisDocument* doc = qobject_cast<KisDocument*>(sender());
1256 disconnect(doc, SIGNAL(canceled(QString)),
this, SLOT(
slotLoadCanceled(QString)));
1261 if (!errMsg.isEmpty()) {
1263 QMessageBox::critical(
this, i18nc(
"@title:window",
"Krita"), errMsg);
1274 KisDocument* doc = qobject_cast<KisDocument*>(sender());
1277 disconnect(doc, SIGNAL(canceled(QString)),
this, SLOT(
slotSaveCanceled(QString)));
1280 if (
d->deferredClosingEvent) {
1287 std::unique_lock<QMutex> l(
d->savingEntryMutex, std::try_to_lock);
1288 return !l.owns_lock();
1293 QFileInfo from(fileName);
1296 QFile::remove(to.canonicalFilePath());
1298 return QFile::copy(fileName, to.absoluteFilePath());
1304 qreal scale = qreal(size)/qreal(qMax(geometry().width(), geometry().height()));
1305 QImage
layoutThumbnail = QImage(qRound(geometry().width()*scale), qRound(geometry().height()*scale), QImage::Format_ARGB32);
1309 Q_FOREACH(
const QObject *child, children()) {
1310 if (child->isWidgetType()) {
1311 const QWidget *w =
static_cast<const QWidget *
>(child);
1313 if (w->isVisible() && !w->property(
"_kis_excludeFromLayoutThumbnail").toBool()) {
1314 QRect wRect = QRectF(w->geometry().x()*scale
1315 , w->geometry().y()*scale
1316 , w->geometry().width()*scale
1317 , w->geometry().height()*scale
1320 wRect = wRect.intersected(
layoutThumbnail.rect().adjusted(-1, -1, -1, -1));
1322 gc.setBrush(this->palette().window());
1323 if (w ==
d->widgetStack) {
1324 gc.setBrush(
d->mdiArea->background());
1326 gc.setPen(this->palette().windowText().color());
1348 std::unique_lock<QMutex> l(
d->savingEntryMutex, std::try_to_lock);
1349 if (!l.owns_lock())
return false;
1359 QMessageBox::critical(qApp->activeWindow(),
1360 i18nc(
"@title:window",
"Krita"),
1361 i18n(
"You are saving a file while the image is "
1362 "still rendering. The saved file may be "
1363 "incomplete or corrupted.\n\n"
1364 "Please select a location where the original "
1365 "file will not be overridden!"));
1371 if (document->isRecovered()) {
1375 if (document->path().isEmpty()) {
1379 QFile
target(document->path());
1385 connect(document, SIGNAL(canceled(QString)),
this, SLOT(
slotSaveCanceled(QString)));
1387 QByteArray nativeFormat = document->nativeFormatMimeType();
1388 QByteArray oldMimeFormat = document->mimeType();
1390 QUrl suggestedURL = QUrl::fromLocalFile(document->path());
1395 if (!mimeFilter.contains(oldMimeFormat)) {
1396 dbgUI <<
"KisMainWindow::saveDocument no export filter for" << oldMimeFormat;
1402 QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1404 if (!suggestedFilename.isEmpty()) {
1406 suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename);
1407 suggestedURL.setPath(suggestedURL.path() + suggestedFilename);
1416 if (document->path().isEmpty() || isExporting || saveas) {
1419 bool justChangingFilterOptions =
false;
1422 dialog.setCaption(isExporting ? i18n(
"Exporting") : i18n(
"Saving As"));
1426 if (isExporting && !
d->lastExportLocation.isEmpty() && !
d->lastExportLocation.contains(QDir::tempPath())) {
1429 QString proposedPath = QFileInfo(
d->lastExportLocation).absolutePath();
1431 QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo(
"title") : QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1433 QString proposedMimeType =
d->lastExportedFormat.isEmpty() ?
"" :
d->lastExportedFormat;
1437 dialog.setDefaultDir(proposedPath +
"/" + proposedFileName +
"." + proposedExtension,
true);
1438 dialog.setMimeTypeFilters(mimeFilter, proposedMimeType);
1443 QByteArray default_mime_type = cfg.
exportMimeType(
false).toUtf8();
1444 QString proposedMimeType = QString::fromLatin1(default_mime_type);
1447 KConfigGroup group = KSharedConfig::openConfig()->group(
"File Dialogs");
1448 QString proposedPath = group.readEntry(
"SaveAs",
"");
1450 if (proposedPath.isEmpty()) {
1451 proposedPath = group.readEntry(
"OpenDocument",
"");
1454 if (proposedPath.isEmpty()) {
1455 proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1461 if(default_mime_type ==
"all/mime"){
1462 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : proposedPath +
"/" + QFileInfo(suggestedURL.toLocalFile()).completeBaseName(),
true);
1465 if (default_mime_type !=
"all/mime" && !default_mime_type.isEmpty()) {
1468 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : proposedPath +
"/" + QFileInfo(suggestedURL.toLocalFile()).completeBaseName() +
"." + proposedExtension,
true);
1473 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(),
true);
1474 default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType();
1476 dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type));
1479 QString newFilePath = dialog.filename();
1482 if (newFilePath.isEmpty()) {
1487 if (document->documentInfo()->aboutInfo(
"title") == i18n(
"Unnamed")) {
1488 QString fn = newFilePath;
1490 document->documentInfo()->setAboutInfo(
"title", info.completeBaseName());
1493 QByteArray outputFormat = nativeFormat;
1496 outputFormat = outputFormatString.toLatin1();
1500 justChangingFilterOptions = (newFilePath == document->path()) && (outputFormat == document->mimeType());
1503 QString path = QFileInfo(
d->lastExportLocation).absolutePath();
1504 QString filename = QFileInfo(document->path()).completeBaseName();
1505 justChangingFilterOptions = (QFileInfo(newFilePath).absolutePath() == path)
1506 && (QFileInfo(newFilePath).completeBaseName() == filename)
1507 && (outputFormat ==
d->lastExportedFormat);
1510 bool wantToSave =
true;
1513 if (!justChangingFilterOptions) {
1514 if (!document->isNativeFormat(outputFormat))
1520 ret = document->saveAs(newFilePath, outputFormat,
true);
1522 dbgUI <<
"Successful Save As!";
1525 dbgUI <<
"Failed Save As!";
1530 ret = document->exportDocument(newFilePath, outputFormat, isAdvancedExporting,
false);
1532 d->lastExportLocation = newFilePath;
1533 d->lastExportedFormat = outputFormat;
1549 if (document->isModified()) {
1550 ret = document->save(
true, 0);
1554 dbgUI <<
"Failed Save!";
1564 activeView()->document()->undoStack()->undo();
1571 activeView()->document()->undoStack()->redo();
1578 e->setAccepted(
false);
1583 QAction *
action=
d->viewManager->actionCollection()->action(
"view_show_canvas_only");
1585 action->setChecked(
false);
1592 if (!closeAllowed) {
1593 e->setAccepted(
false);
1599 d->mdiArea->closeAllSubWindows();
1603 if (childrenList.isEmpty()) {
1606 d->deferredClosingEvent = e;
1607 d->canvasWindow->close();
1609 e->setAccepted(
false);
1615 KSharedConfigPtr config = KSharedConfig::openConfig();
1617 if (
d->windowSizeDirty ) {
1618 dbgUI <<
"KisMainWindow::saveWindowSettings";
1619 KConfigGroup group =
d->windowStateConfig;
1620 KWindowConfig::saveWindowSize(windowHandle(), group);
1622 d->windowSizeDirty =
false;
1625 if (!
d->activeView ||
d->activeView->document()) {
1628 KConfigGroup group =
d->windowStateConfig;
1632 for (QMap<QString, QDockWidget*>::const_iterator i =
d->dockWidgetsMap.constBegin();
1633 i !=
d->dockWidgetsMap.constEnd(); ++i) {
1634 if (i.value()->widget()) {
1635 KConfigGroup dockGroup = group.group(QString(
"DockWidget ") + i.key());
1636 dockGroup.writeEntry(
"Locked", i.value()->property(
"Locked").toBool());
1637 dockGroup.writeEntry(
"DockArea", (
int) dockWidgetArea(i.value()));
1638 dockGroup.writeEntry(
"xPosition", (
int) i.value()->widget()->x());
1639 dockGroup.writeEntry(
"yPosition", (
int) i.value()->widget()->y());
1641 dockGroup.writeEntry(
"width", (
int) i.value()->widget()->width());
1642 dockGroup.writeEntry(
"height", (
int) i.value()->widget()->height());
1648 KSharedConfig::openConfig()->sync();
1655 d->windowSizeDirty =
true;
1656 KXmlGuiWindow::resizeEvent(e);
1674 QAction *
action=
d->viewManager->actionCollection()->action(
"view_show_canvas_only");
1682 if (!
event->spontaneous()) {
1691 QTimer::singleShot(0,
this, &KisMainWindow::slotShowDonationManagementDialog);
1694 return KXmlGuiWindow::showEvent(
event);
1700 if (widgetIndex == 0) {
1701 if (widgetIndexChanged) {
1707 if (
d->mdiArea->subWindowList().isEmpty()) {
1721 statusBar()->setVisible(
KisConfig(
true).showStatusBar());
1726 for (QAction *
action : actions) {
1728 action->setEnabled(widgetIndex);
1741 statusBar()->hide();
1744 for (QToolBar *toolbar : toolbars) {
1745 if (toolbar->objectName() !=
"mainToolBar") {
1753 d->activeView = view;
1755 if (
d->undoActionsUpdateManager) {
1756 d->undoActionsUpdateManager->setCurrentDocument(view ? view->
document() : 0);
1759 d->viewManager->setCurrentView(view);
1761 d->activeViewConnections.clear();
1762 d->activeViewConnections.addConnection(view->
document(),
1763 SIGNAL(sigPathChanged(QString)),
1766 d->activeViewConnections.addConnection(view->
document(),
1767 SIGNAL(sigReadWriteChanged(
bool)),
1778 d->activeViewConnections.clear();
1785 QTabBar *tabBar =
d->findTabBarHACK();
1787 if (!tabBar &&
d->mdiArea->viewMode() == QMdiArea::TabbedView) {
1788 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)!";
1791 if (tabBar && tabBar->isVisible()) {
1792 QPoint pos = tabBar->mapFromGlobal(mapToGlobal(
event->pos()));
1793 if (tabBar->rect().contains(pos)) {
1794 const int tabIndex = tabBar->tabAt(pos);
1796 if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) {
1797 d->tabSwitchCompressor->start(tabIndex);
1799 }
else if (
d->tabSwitchCompressor->isActive()) {
1800 d->tabSwitchCompressor->stop();
1807 if (
d->tabSwitchCompressor->isActive()) {
1808 d->tabSwitchCompressor->stop();
1815 QTabBar *tabBar =
d->findTabBarHACK();
1816 if (!tabBar)
return;
1818 tabBar->setCurrentIndex(index);
1823 const int currentIndex = show ? 0 : 1;
1824 if (
d->widgetStack->currentIndex() != currentIndex) {
1825 setUpdatesEnabled(
false);
1830 d->widgetStack->setCurrentIndex(currentIndex);
1832 d->widgetStack->setCurrentIndex(currentIndex);
1835 setUpdatesEnabled(
true);
1848 dbgUI <<
"slotImportFile()";
1860 Q_FOREACH (
const QString& url, urls) {
1862 if (!url.isEmpty()) {
1863 OpenFlags flags = isImporting ?
Import :
None;
1866 warnKrita <<
"Loading" << url <<
"failed";
1879 if (
saveDocument(
d->activeView->document(),
false,
false,
false)) {
1886#if defined(Q_OS_ANDROID) && KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
1888 if (androidScaling) {
1896 if (
saveDocument(
d->activeView->document(),
true,
false,
false)) {
1903 if (
saveDocument(
d->activeView->document(),
true,
true,
false)) {
1909 if (
saveDocument(
d->activeView->document(),
true,
true,
true)) {
1919void KisMainWindow::slotShowDonationManagementDialog()
1923 QWidget *win = qApp->activeWindow();
1924 if (win && !qobject_cast<KisMainWindow *>(win) && (win->isModal() || win->windowModality() != Qt::NonModal)) {
1932 QString objectName = QStringLiteral(
"kisdonationmanagementdialog");
1944 dlg->setAttribute(Qt::WA_DeleteOnClose);
1945 dlg->setObjectName(objectName);
1950void KisMainWindow::slotFlashWindowHack()
1952 if (!
d->flashWindowHackInProgress) {
1953 d->flashWindowHackInProgress =
true;
1954 QTimer::singleShot(0,
this, [
this] {
1956 QTimer::singleShot(0,
this, [
this] {
1958 d->flashWindowHackInProgress =
false;
1967 return d->viewManager->canvasResourceProvider()->resourceManager();
1972 return d->mdiArea->subWindowList().size();
1977 return d->windowStateConfig;
1983 if (
d->widgetStack->currentIndex() == 0) {
1988 if (restoreNormalState) {
1989 QAction *showCanvasOnly =
d->viewManager->actionCollection()->action(
"view_show_canvas_only");
1991 if (showCanvasOnly && showCanvasOnly->isChecked()) {
1992 showCanvasOnly->setChecked(
false);
1995 d->windowStateConfig.writeEntry(
"ko_geometry", saveGeometry().toBase64());
1996 d->windowStateConfig.writeEntry(
"State", saveState().toBase64());
1999 if (!
d->toggleDockers->isChecked()) {
2000 restoreState(
d->dockerStateBeforeHiding);
2003 statusBar()->setVisible(
true);
2004 menuBar()->setVisible(
true);
2016 QByteArray oldState = saveState();
2022 dock->setProperty(
"Locked",
false);
2023 dock->toggleViewAction()->setEnabled(
true);
2025 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2026 dock->titleBarWidget()->setVisible(showTitlebars);
2031 bool success = KXmlGuiWindow::restoreState(state);
2034 KXmlGuiWindow::restoreState(oldState);
2036 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2037 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
2047 QString md5 = sender()->property(
"md5").toString();
2054 qWarning() <<
"Could not retrieve resource for" << md5;
2063 int actionsCount = 0;
2068 if (
auto collection = c->actionCollection()) {
2069 actionCollections.append(collection);
2070 actionsCount += collection->count();
2079 actionCollections.append(layerActionCollection);
2080 actionsCount += layerActionCollection->
count();
2083 d->commandBar->updateBar(actionCollections, actionsCount);
2089 d->commandBar->activateWindow();
2102 warning = i18n(
"You don't have any resource bundles enabled.");
2106 warning += i18n(
"\nThere are no brush presets available. Please enable a bundle that has presets before continuing.\n");
2107 QMessageBox::critical(
this, i18nc(
"@title:window",
"Krita"), warning);
2116 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"), warning + i18n(
"\nOnly your local resources are available."));
2129 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2130 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
2135 activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace);
2138 d->viewManager->notifyWorkspaceLoaded();
2145 QByteArray currentWorkspace = saveState();
2147 if (!
d->workspaceBorrowedBy.isNull()) {
2148 if (other->
id() ==
d->workspaceBorrowedBy) {
2150 d->workspaceBorrowedBy = QUuid();
2151 return currentWorkspace;
2159 d->workspaceBorrowedBy = other->
id();
2160 return originalLayout;
2165 d->workspaceBorrowedBy = other->
id();
2166 return currentWorkspace;
2172 QByteArray workspaceB = b->borrowWorkspace(a);
2175 b->restoreWorkspaceState(workspaceA);
2180 return d->viewManager;
2185 if (!
d->activeView->document())
2193 KoDocumentInfoDlg *dlg =
d->activeView->document()->createDocumentInfoDialog(
this, docInfo);
2197 d->activeView->document()->setModified(
false);
2199 d->activeView->document()->setModified(
true);
2208 Q_FOREACH (QMdiSubWindow *subwin,
d->mdiArea->subWindowList()) {
2210 if(!subwin->close())
2234 if (!document)
return;
2238 if (dlg.exec() == QDialog::Accepted) {
2240 int firstFrame = dlg.firstFrame();
2241 int step = dlg.step();
2242 bool startFrom1 = dlg.startFrom1();
2243 bool autoAddHoldframes = dlg.autoAddHoldframes();
2249 int isAscending = dlg.isAscending();
2250 KisImportExportErrorCode status = importer.import(files, firstFrame, step, autoAddHoldframes, startFrom1, isAscending);
2255 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"Could not finish import animation:\n%1", msg));
2257 activeView()->canvasBase()->refetchDataFromImage();
2268 if (dlg.exec() == QDialog::Accepted) {
2269 const QTemporaryDir outputLocation(QDir::tempPath() + QDir::separator() +
"krita" + QDir::separator() +
"import_files");
2270 RenderedFrames renderedFrames = dlg.renderFrames(QDir(outputLocation.path()));
2271 dbgFile <<
"Frames rendered to directory: " << outputLocation.path();
2272 QStringList documentInfoList = dlg.documentInfo();
2274 if (renderedFrames.
isEmpty())
return;
2276 dbgFile <<
"Animation Import options: " << documentInfoList;
2279 const int step = documentInfoList[0].toInt();
2280 const int fps = documentInfoList[1].toInt();
2282 const QString
name = QFileInfo(documentInfoList[3]).fileName();
2283 const bool useCurrentDocument = documentInfoList[4].toInt();
2284 bool useDocumentColorSpace =
false;
2286 if ( useCurrentDocument ) {
2289 dbgFile <<
"Current frames:" << document->image()->animationInterface()->totalLength() <<
"total frames:" << totalFrames;
2290 if ( document->image()->animationInterface()->totalLength() < totalFrames ) {
2291 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2292 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2295 const int width = documentInfoList[5].toInt();
2296 const int height = documentInfoList[6].toInt();
2297 const double resolution = documentInfoList[7].toDouble();
2299 const QString colorModel = documentInfoList[8];
2300 const QString colorDepth = documentInfoList[9];
2301 const QString profile = documentInfoList[10];
2302 useDocumentColorSpace = profile !=
"Default";
2305 document->setObjectName(name);
2311 QColor qc(Qt::white);
2315 if (!document->newImage(name, width, height, cs, bgColor,
KisConfig::RASTER_LAYER, 1,
"",
double(resolution / 72) )) {
2316 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"Failed to create new document. Animation import aborted."));
2320 document->image()->animationInterface()->setFramerate(fps);
2321 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2322 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2335 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"Could not finish import animation:\n%1", msg));
2338 activeView()->canvasBase()->refetchDataFromImage();
2339 document->image()->refreshGraphAsync();
2340 document->image()->waitForDone();
2358 dlgAnimationRenderer.setCaption(i18n(
"Render Animation"));
2359 if (dlgAnimationRenderer.exec() == QDialog::Accepted) {
2436 if (
d->activeView &&
d->activeView->document()) {
2440 warnUI <<
"slotToolbarToggled : Toolbar " << sender()->objectName() <<
" not found!";
2451 d->fullScreenMode->setChecked(fullScreen);
2457 setWindowState(windowState() | Qt::WindowFullScreen);
2459 setWindowState(windowState() & ~Qt::WindowFullScreen);
2461 d->fullScreenMode->setChecked(isFullScreen());
2470 if (!
d->dockWidgetsMap.contains(
factory->id())) {
2493 if (
dockWidget->titleBarWidget() && !
dockWidget->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2499 if (!showTitlebars) {
2500 if (
dockWidget->titleBarWidget() && !
dockWidget->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2501 dockWidget->titleBarWidget()->setVisible(
false);
2503 dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
2506 dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1);
2508 Qt::DockWidgetArea side = Qt::RightDockWidgetArea;
2509 bool visible =
true;
2511 switch (
factory->defaultDockPosition()) {
2516 side = Qt::TopDockWidgetArea;
break;
2518 side = Qt::LeftDockWidgetArea;
break;
2520 side = Qt::BottomDockWidgetArea;
break;
2522 side = Qt::RightDockWidgetArea;
break;
2525 side = Qt::RightDockWidgetArea;
2529 KConfigGroup group =
d->windowStateConfig.group(
"DockWidget " +
factory->id());
2530 side =
static_cast<Qt::DockWidgetArea
>(group.readEntry(
"DockArea",
static_cast<int>(side)));
2531 if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea;
2538 bool locked = group.readEntry(
"Locked",
false);
2539 if (titleBar && locked) {
2542 else if (
dockWidget->titleBarWidget()->inherits(
"KisUtilityTitleBar") && locked){
2554 dockWidget->setAttribute(Qt::WA_MacSmallSize,
true);
2565 Q_FOREACH (QObject *child, children()) {
2566 if (child->inherits(
"QTabBar")) {
2577 Q_FOREACH (
auto key,
d->actionMap.keys()) {
2578 if(
d->actionMap.value(key)->isChecked()) {
2581 qApp->setStyle(key);
2585 QString currentStyle = qApp->style()->objectName();
2586#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
2587 if (currentStyle ==
"macintosh" || currentStyle ==
"windowsvista") {
2588 d->themeManager->setCurrentTheme(
"System");
2591 if (currentStyle ==
"macos" || currentStyle ==
"windowsvista" || currentStyle ==
"windows11") {
2592 d->themeManager->setCurrentTheme(
"System");
2602 return d->dockWidgetsMap.values();
2607 if (!
d->dockWidgetsMap.contains(
id))
return 0;
2608 return d->dockWidgetsMap[
id];
2620 observers << observer;
2623 warnKrita << docker <<
"is not a canvas observer";
2634 d->dockerStateBeforeHiding = saveState();
2636 Q_FOREACH (QObject*
widget, children()) {
2637 if (
widget->inherits(
"QDockWidget")) {
2638 QDockWidget* dw =
static_cast<QDockWidget*
>(
widget);
2639 if (dw->isVisible() && !(onWelcomePage && dw->property(
"ShowOnWelcomePage").toBool())) {
2646 restoreState(
d->dockerStateBeforeHiding);
2654 d->mdiCascade->setEnabled(enabled);
2655 d->mdiNextWindow->setEnabled(enabled);
2656 d->mdiPreviousWindow->setEnabled(enabled);
2657 d->mdiTile->setEnabled(enabled);
2658 d->close->setEnabled(enabled);
2659 d->closeAll->setEnabled(enabled);
2672 QMdiSubWindow *subWindow =
d->mdiArea->currentSubWindow();
2674 QMenu *menu = subWindow->systemMenu();
2675 if (menu && menu->actions().size() == 8) {
2676 Q_FOREACH (QAction *
action, menu->actions()) {
2677 action->setShortcut(QKeySequence());
2680 menu->actions().last()->deleteLater();
2684 d->actionManager()->updateGUI();
2701 if (primary.isNull())
return;
2703 if (
d->id == primary) {
2704 if (!
d->workspaceBorrowedBy.isNull()) {
2706 if (!borrower)
return;
2710 if (
d->workspaceBorrowedBy == primary)
return;
2713 if (!primaryWindow)
return;
2721 QMenu *menu =
d->windowMenu->menu();
2725 menu->addAction(
d->newWindow);
2727 menu->addAction(
d->documentMenu);
2729 QMenu *docMenu =
d->documentMenu->menu();
2732 QFontMetrics fontMetrics = docMenu->fontMetrics();
2733 QRect geom = this->geometry();
2734 QPoint
p(geom.width() / 2 + geom.left(), geom.height() / 2 + geom.top());
2735 QScreen *screen = qApp->screenAt(
p);
2736 int fileStringWidth = 300;
2738 fileStringWidth = int(screen->availableGeometry().width() * .40f);
2742 QString title = fontMetrics.elidedText(doc->path(), Qt::ElideMiddle, fileStringWidth);
2743 if (title.isEmpty() && doc->image()) {
2744 title = doc->image()->objectName();
2746 QAction *
action = docMenu->addAction(title);
2747 action->setIcon(qApp->windowIcon());
2748 connect(
action, SIGNAL(triggered()),
d->documentMapper, SLOT(map()));
2749 d->documentMapper->setMapping(
action, doc);
2753 menu->addAction(
d->workspaceMenu);
2760 while (resourceIterator.
hasNext()) {
2763 action->setProperty(
"md5", QVariant::fromValue<QString>(resource->md5sum()));
2767 connect(
workspaceMenu->addAction(i18nc(
"@action:inmenu",
"&Import Workspace...")),
2768 &QAction::triggered,
2775 dialog.setMimeTypeFilters(mimeTypes);
2776 dialog.setCaption(i18nc(
"@title:window",
"Choose File to Add"));
2777 QString filename = dialog.filename();
2782 connect(
workspaceMenu->addAction(i18nc(
"@action:inmenu",
"&New Workspace...")),
2783 &QAction::triggered,
2786 name = QInputDialog::getText(
this, i18nc(
"@title:window",
"New Workspace..."),
2787 i18nc(
"@label:textbox",
"Name:"));
2788 if (name.isEmpty()) {
2793 workspace->setDockerState(m_this->saveState());
2795 workspace->setValid(
true);
2799 d->viewManager->canvasResourceProvider()->notifySavingWorkspace(workspace);
2800 workspace->setValid(
true);
2802 workspace->setFilename(name.replace(
" ",
"_") + workspace->defaultFileExtension());
2803 workspace->setName(name);
2811 menu->addSeparator();
2812 menu->addAction(
d->close);
2813 menu->addAction(
d->closeAll);
2814 if (
d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2815 menu->addSeparator();
2816 menu->addAction(
d->mdiTile);
2817 menu->addAction(
d->mdiCascade);
2819 menu->addSeparator();
2820 menu->addAction(
d->mdiNextWindow);
2821 menu->addAction(
d->mdiPreviousWindow);
2822 menu->addSeparator();
2825 for (
int i = 0; i < windows.size(); ++i) {
2827 if (child && child->document()) {
2830 text = i18n(
"&%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2833 text = i18n(
"%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2836 QAction *
action = menu->addAction(text);
2837 action->setIcon(qApp->windowIcon());
2838 action->setCheckable(
true);
2840 connect(
action, SIGNAL(triggered()),
d->windowMapper, SLOT(map()));
2841 d->windowMapper->setMapping(
action, windows.at(i));
2845 bool showMdiArea = windows.count( ) > 0;
2851 Q_FOREACH (QObject*
widget, children()) {
2852 if (
widget->inherits(
"QDockWidget")) {
2853 QDockWidget* dw =
static_cast<QDockWidget*
>(
widget);
2855 if ( dw->objectName() ==
"ToolBox") {
2856 dw->setEnabled(showMdiArea);
2864 bool onlyOne =
false;
2865 if (
d->mdiArea->subWindowList().size() == 1 &&
d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2868 Q_FOREACH (QMdiSubWindow *subwin,
d->mdiArea->subWindowList()) {
2870 subwin->setWindowFlags(subwin->windowFlags() | Qt::FramelessWindowHint);
2871 subwin->showMaximized();
2873 subwin->setWindowFlags((subwin->windowFlags() | Qt::FramelessWindowHint) ^ Qt::FramelessWindowHint);
2884 QMdiSubWindow *subwin = qobject_cast<QMdiSubWindow *>(window);
2887 if (subwin && subwin !=
d->activeSubWindow) {
2888 KisView *view = qobject_cast<KisView *>(subwin->widget());
2893 d->activeSubWindow = subwin;
2896 d->actionManager()->updateGUI();
2902 QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.
readEntry<
int>(
"mdi_viewmode", (
int)QMdiArea::TabbedView);
2903 d->mdiArea->setViewMode(viewMode);
2907 Q_FOREACH (QMdiSubWindow *subwin,
d->mdiArea->subWindowList()) {
2908 subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.
readEntry<
int>(
"mdi_rubberband", cfg.
useOpenGL()));
2909 subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.
readEntry<
int>(
"mdi_rubberband", cfg.
useOpenGL()));
2910 if (viewMode == QMdiArea::TabbedView) {
2911 subwin->setWindowState(Qt::WindowMaximized);
2921 if (viewMode == QMdiArea::TabbedView) {
2922 Qt::WindowFlags oldFlags = subwin->windowFlags();
2923 Qt::WindowFlags flags = oldFlags;
2925 flags &= ~Qt::WindowStaysOnTopHint;
2926 flags &= ~Qt::WindowStaysOnBottomHint;
2928 if (flags != oldFlags) {
2929 subwin->setWindowFlags(flags);
2930 subwin->showMaximized();
2938 KConfigGroup group( KSharedConfig::openConfig(),
"theme");
2940 d->themeManager->setCurrentTheme(group.readEntry(
"Theme",
"Krita dark"));
2942 d->actionManager()->updateGUI();
2947 d->mdiArea->setBackground(brush);
2950 if (backgroundImage !=
"") {
2951 QImage image(backgroundImage);
2952 QBrush brush(image);
2953 d->mdiArea->setBackground(brush);
2956 d->mdiArea->update();
2960 Q_FOREACH (QObject*
widget, children()) {
2961 if (
widget->inherits(
"QDockWidget")) {
2962 QDockWidget* dw =
static_cast<QDockWidget*
>(
widget);
2970 KisDocument *doc = qobject_cast<KisDocument*>(document);
2972 d->actionManager()->updateGUI();
2982 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"),
2983 "Creating a New Main Window is unsupported on Android");
2994 if (
d->mdiArea->currentSubWindow()) {
2995 d->mdiArea->currentSubWindow()->close();
2996 d->actionManager()->updateGUI();
3005 m_errorMessage = i18n(
"The Krita LittleCMS color management plugin is not installed. Krita will quit now.");
3021 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Installation error"),
m_errorMessage);
3035 if (!
d->mdiArea)
return 0;
3045 bool isOurOwnView =
false;
3048 if (view && view->canvasController() == controller) {
3049 isOurOwnView = view->mainWindow() ==
this;
3053 if (!isOurOwnView)
return;
3055 Q_FOREACH (QWidget *w, optionWidgetList) {
3057 w->setAttribute(Qt::WA_MacSmallSize,
true);
3062 if (
d->toolOptionsDocker) {
3063 d->toolOptionsDocker->setOptionWidgets(optionWidgetList);
3066 d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList);
3097 d->undoActionsUpdateManager->setCurrentDocument(
d->activeView ?
d->activeView->document() : 0);
3100 connect(
d->importAnimation, SIGNAL(triggered()),
this, SLOT(
importAnimation()));
3110 connect(
d->renderAnimation, SIGNAL(triggered()),
this, SLOT(
renderAnimation()));
3121 connect(
d->importFile, SIGNAL(triggered(
bool)),
this, SLOT(
slotImportFile()));
3124 connect(
d->exportFile, SIGNAL(triggered(
bool)),
this, SLOT(
slotExportFile()));
3127 connect(
d->exportFileAdvance, SIGNAL(triggered(
bool)),
this, SLOT(
slotExportAdvance()));
3132 connect(
d->showDocumentInfo, SIGNAL(triggered(
bool)),
this, SLOT(
slotDocumentInfo()));
3134 d->themeManager->setThemeMenuAction(
new KActionMenu(i18nc(
"@action:inmenu",
"&Themes"),
this));
3136 connect(
d->themeManager, SIGNAL(signalThemeChanged()),
this, SLOT(
slotThemeChanged()), Qt::QueuedConnection);
3137 connect(
this, SIGNAL(
themeChanged()),
d->welcomePage, SLOT(slotUpdateThemeColors()), Qt::UniqueConnection);
3143 d->toggleDockers->setChecked(
true);
3151 d->toggleDetachCanvas->setChecked(
false);
3152 connect(
d->toggleDetachCanvas, SIGNAL(toggled(
bool)), SLOT(
setCanvasDetached(
bool)));
3166 connect(
d->mdiCascade, SIGNAL(triggered()),
d->mdiArea, SLOT(cascadeSubWindows()));
3169 connect(
d->mdiTile, SIGNAL(triggered()),
d->mdiArea, SLOT(tileSubWindows()));
3172 connect(
d->mdiNextWindow, SIGNAL(triggered()),
d->mdiArea, SLOT(activateNextSubWindow()));
3175 connect(
d->mdiPreviousWindow, SIGNAL(triggered()),
d->mdiArea, SLOT(activatePreviousSubWindow()));
3179 connect(
d->showDonationManagementDialog,
3180 &QAction::triggered,
3182 &KisMainWindow::slotShowDonationManagementDialog);
3185 if (androidDonations) {
3187 connect(
d->manageSubscriptions,
3188 &QAction::triggered,
3191 connect(androidDonations,
3194 &KisMainWindow::slotShowDonationManagementDialog,
3195 Qt::QueuedConnection);
3197#if KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
3199 if (androidScaling && androidScaling->
isSupported()) {
3206 connect(
d->newWindow, SIGNAL(triggered(
bool)),
this, SLOT(
newWindow()));
3215 connect(
d->commandBarAction, SIGNAL(triggered(
bool)),
this, SLOT(
openCommandBar()));
3219 for (
int i = 0; i < 2; i++) {
3220 d->expandingSpacers[i] =
new KisAction(i18n(
"Expanding Spacer"));
3221 d->expandingSpacers[i]->setDefaultWidget(
new QWidget(
this));
3222 d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
3237 toolBar->layout()->setSpacing(4);
3238 toolBar->setStyleSheet(
"QToolBar { border: none }");
3241 Q_FOREACH (QAction *ac,
toolBar->actions()){
3242 if (ac->icon().pixmap(QSize(1,1)).isNull() ==
false){
3243 ac->setPriority(QAction::LowPriority);
3245 ac->setIcon(QIcon());
3255 KConfigGroup cfg =
d->windowStateConfig;
3256 QByteArray geom = QByteArray::fromBase64(cfg.readEntry(
"ko_geometry", QByteArray()));
3257 if (!restoreGeometry(geom)) {
3258 QRect desk = this->screen()->availableGeometry();
3260 quint32 x = desk.x();
3261 quint32 y = desk.y();
3266 const int deskWidth = desk.width();
3267 if (deskWidth > 1024) {
3270 w = (deskWidth / 3) * 2;
3271 h = (desk.height() / 3) * 2;
3278 x += (desk.width() - w) / 2;
3279 y += (desk.height() - h) / 2;
3282 setGeometry(geometry().x(), geometry().y(), w, h);
3288 bool fullScreen = isFullScreen();
3290 d->fullScreenMode->setChecked(fullScreen);
3295 QDesktopServices::openUrl(QUrl(
"https://docs.krita.org"));
3301 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
3302 dock->titleBarWidget()->setVisible(show || dock->isFloating());
3319 QScreen *screen = QGuiApplication::primaryScreen();
3321 for (QWindow* window: QGuiApplication::topLevelWindows()) {
3323 if ((window->type() == Qt::Popup)
3324 && (window->flags() & Qt::FramelessWindowHint) == 0
3325 && (window->geometry().topLeft() != QPoint(0, 0))) {
3328 int screenHeight = screen->geometry().width();
3329 int screenWidth = screen->geometry().height();
3332 int new_x = (window->position().x() * screenWidth) / screenHeight;
3333 int new_y = (window->position().y() * screenHeight) / screenWidth;
3336 int winWidth = window->geometry().width();
3337 int winHeight = window->geometry().height();
3340 if (new_x > screenWidth - winWidth) {
3341 new_x = screenWidth - winWidth;
3345 if (new_y > screenHeight - winHeight) {
3346 new_y = screenHeight - winHeight;
3351 window->setPosition(QPoint(new_x, new_y));
3364 return (proxy.rowCount() > 0);
3382 QFile file(xmlPath);
3383 if (!file.exists()) {
3388 if (!file.open(QIODevice::ReadOnly) || !doc.setContent(&file)) {
3392 QDomNodeList overridesList = doc.elementsByTagName(QStringLiteral(
"ActionIconOverrides"));
3393 if (!overridesList.isEmpty()) {
3394 QDomElement overridesElement = overridesList.at(0).toElement();
3395 QDomNodeList actions = overridesElement.elementsByTagName(QStringLiteral(
"Action"));
3396 for (
int i = 0; i < actions.count(); ++i) {
3397 QDomElement actionElement = actions.at(i).toElement();
3398 QString name = actionElement.attribute(QStringLiteral(
"name"));
3399 QString icon = actionElement.attribute(QStringLiteral(
"icon"));
3400 if (!name.isEmpty() && !icon.isEmpty()) {
3416#include <moc_KisMainWindow.cpp>
KisMagneticGraph::vertex_descriptor target(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
QList< QString > QStringList
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)
static void synchronizeDynamicActions(QList< QAction * > newActions, const QList< QAction * > &menuBarActions)
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.
@ IMAGE_IS_WRITABLE
Activate KisDocument::isReadWrite() is active.
@ IMAGE_HAS_ANIMATION
Activate if the image has an animation.
void slotManageSubscriptions()
static KisAndroidDonations * instance()
void sigShowDonationManagementDialogRequested()
bool isShowDonationManagementDialogPending() const
void sigInterfaceScaleChanged()
static KisAndroidScaling * instance()
void fromProperties(KisPropertiesConfigurationSP config)
Base class for the Krita app.
void setFavoriteResourceManager(KisFavoriteResourceManager *favoriteResourceManager)
static KisConfigNotifier * instance()
void notifyPixelGridModeChanged()
void notifyConfigChanged(void)
QString widgetStyle(bool defaultValue=false)
void setFullscreenMode(const bool value) const
void setWidgetStyle(QString name)
void writeEntry(const QString &name, const T &value)
QString getMDIBackgroundColor(bool defaultValue=false) const
QString getMDIBackgroundImage(bool defaultValue=false) const
bool showDockerTitleBars(bool defaultValue=false) const
bool effectiveShouldUseNativeSurfaceForCanvas(bool osManagedSurfacePresent) const
QString exportMimeType(bool defaultValue) const
void setShowDockerTitleBars(const bool value) const
bool toolOptionsInDocker(bool defaultValue=false) const
T readEntry(const QString &name, const T &defaultValue=T())
bool showDockers(bool defaultValue=false) const
bool useOpenGL(bool defaultValue=false) const
void setFileBatchMode(const bool batchMode)
void setPath(const QString &path)
bool importDocument(const QString &path)
bool openPath(const QString &path, OpenFlags flags=None)
openPath Open a Path
void sigShowSupporterBundles()
bool hasAnimation() const
static KisImageConfigNotifier * instance()
void notifyConfigChanged(void)
KisGroupLayerSP rootLayer() const
KisImageAnimationInterface * animationInterface() const
QString errorMessage() const
bool isInternalError() const
static QStringList supportedMimeTypes(Direction direction)
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)
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
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 closeCurrentWindow()
void setCanvasDetached(bool detached)
void guiLoadingFinished()
void dragLeaveEvent(QDragLeaveEvent *event) override
QImage layoutThumbnail()
layoutThumbnail
QTabBar * findTabBarHACK()
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 orientationChanged()
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()
QUuid workspaceBorrowedBy
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 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)
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
void slotFileOpen(bool isImporting=false)
QMap< QString, QDockWidget * > dockWidgetsMap
void subWindowActivated()
void saveWindowState(bool restoreNormalState=false)
bool canvasDetached() const
void slotChangeInterfaceScale()
KActionMenu * workspaceMenu
void viewFullscreen(bool fullScreen)
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 initializeGeometry()
void notifyChildViewDestroyed(KisView *view)
void synchronizeDynamicActions()
void adjustLayoutForWelcomePage()
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)
void switchTab(int index)
QByteArray borrowWorkspace(KisMainWindow *borrower)
void applyToolBarLayout()
void saveWindowSettings()
~KisMainWindow() override
QScopedPointer< KisSignalCompressorWithParam< int > > tabSwitchCompressor
void dragMoveEvent(QDragMoveEvent *event) override
void slotToolbarToggled(bool toggle)
void closeEvent(QCloseEvent *e) override
KoCanvasResourceProvider * resourceManager() const
The KisMainwindowObserver class is an interface for dock widgets that want to keep track of the main ...
virtual void setViewManager(KisViewManager *kisview)=0
static QStringList suffixesForMimeType(const QString &mimeType)
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
bool closeSession(bool keepWindows=false)
void removeMainWindow(KisMainWindow *mainWindow)
KisView * createView(KisDocument *document, KisViewManager *viewManager, QWidget *parent)
static KisPart * instance()
void addDocument(KisDocument *document, bool notify=true)
void queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl url, QUrl oldUrl=QUrl())
void sigMainWindowCreated()
KisMainWindow * windowById(QUuid id) const
KisDocument * createDocument() const
void showSessionManager()
void notifyMainWindowIsBeingCreated(KisMainWindow *mainWindow)
notifyMainWindowIsBeingCreated emits the sigMainWindowCreated signal
KisMainWindow * createMainWindow(QUuid id=QUuid())
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()
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...
void setLocked(bool locked)
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
void slotThemeChanged(QPalette pal)
void setSubWindow(QMdiSubWindow *subWindow)
QPointer< KisDocument > document
void slotLoadingFinished()
void setViewManager(KisViewManager *view)
A widget for displaying if no documents are open. This will display in the MDI area.
QUuid primaryWindowId() const
bool primaryWorkspaceFollowsFocus() const
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)
void toQColor(QColor *c) const
a convenience method for the above.
@ 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()
QList< KoDockFactoryBase * > sortedDockWidgetFactories()
The dialog that shows information about the document.
The class containing all meta information about a document.
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...
#define KIS_ASSERT_RECOVER_NOOP(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
constexpr const char * currentUnderlyingStyleNameProperty
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
KRecentFilesAction * openRecent(const QObject *recvr, const char *slot, QObject *parent)
const char * name(StandardAction id)
KRITAUI_EXPORT void show()
bool looksLikeXiaomiDevice()
void setFullScreen(bool fullScreen)
KRITAUI_EXPORT bool render(KisDocument *doc, KisViewManager *viewManager, KisAnimationRenderingOptions encoderOptions)
int size(const Forest< T > &forest)
QIcon loadIcon(const QString &name)
void updateIconCommon(QObject *object)
void updateIcon(QAbstractButton *button)
QFont normalFont()
Gets a font for normal UI widgets to use.
QFont dockFont()
Gets a font with a smallish font size for dock widgets to use.
virtual void updateSettings()
void blockIfImageIsBusy()
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
bool framesNeedRelocation() const
QList< int > renderedFrameTargetTimes
QStringList renderedFrameFiles