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>
160#include <config-qmdiarea-always-show-subwindow-title.h>
161#include <config-qt-patches-present.h>
165#if defined(Q_OS_ANDROID) && KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
174 QString
id()
const override {
175 return "sharedtooldocker";
194 , styleMenu(new KActionMenu(i18nc(
"@action:inmenu",
"Styles"), parent))
195 , dockWidgetMenu(new KActionMenu(i18nc(
"@action:inmenu",
"&Dockers"), parent))
196 , windowMenu(new KActionMenu(i18nc(
"@action:inmenu",
"&Window"), parent))
197 , documentMenu(new KActionMenu(i18nc(
"@action:inmenu",
"New &View"), parent))
198 , workspaceMenu(new KActionMenu(i18nc(
"@action:inmenu",
"Wor&kspace"), parent))
200 , widgetStack(new QStackedWidget(parent))
201 , mdiArea(new QMdiArea(parent))
205 if (
id.isNull()) this->
id = QUuid::createUuid();
207 welcomeScroller =
new QScrollArea();
208 welcomeScroller->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
209 welcomeScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
210 welcomeScroller->setWidget(welcomePage);
211 welcomeScroller->setWidgetResizable(
true);
213 widgetStack->addWidget(welcomeScroller);
214 widgetStack->addWidget(mdiArea);
215 mdiArea->setTabsMovable(
true);
216 mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder);
217 mdiArea->setDocumentMode(
true);
218 mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation);
219#ifdef HAVE_QMDIAREA_ALWAYS_SHOW_SUBWINDOW_TITLE
220 mdiArea->setOption(QMdiArea::AlwaysShowSubwindowNameInTitleBar);
227 qDeleteAll(toolbarList);
240 bool firstTime {
true};
241 bool windowSizeDirty {
false};
243 bool flashWindowHackInProgress {
false};
268 KisAction *showDonationManagementDialog {
nullptr};
269 KisAction *manageSubscriptions {
nullptr};
270#if KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
271 KisAction *changeInterfaceScale {
nullptr};
282 KActionMenu *styleMenu {
nullptr};
283 QActionGroup* styleActions {
nullptr};
286 KActionMenu *dockWidgetMenu {
nullptr};
287 KActionMenu *windowMenu {
nullptr};
288 KActionMenu *documentMenu {
nullptr};
289 KActionMenu *workspaceMenu {
nullptr};
304 QCloseEvent *deferredClosingEvent {
nullptr};
308 QScrollArea *welcomeScroller {
nullptr};
312 QStackedWidget *widgetStack {
nullptr};
314 QMdiArea *mdiArea {
nullptr};
315 QMdiSubWindow *activeSubWindow {
nullptr};
331 return viewManager->actionManager();
335 QObjectList objects = mdiArea->children();
336 Q_FOREACH (QObject *
object, objects) {
337 QTabBar *bar = qobject_cast<QTabBar*>(
object);
353 widget->setEnabled(
false);
366 KAcceleratorManager::setNoAccel(
this);
369 connect(
d->workspacemodel, SIGNAL(modelReset()),
this, SLOT(
updateWindowMenu()));
370 connect(
d->workspacemodel, SIGNAL(rowsInserted(QModelIndex,
int,
int)),
this, SLOT(
updateWindowMenu()));
371 connect(
d->workspacemodel, SIGNAL(rowsRemoved(QModelIndex,
int,
int)),
this, SLOT(
updateWindowMenu()));
374 KConfigGroup group( KSharedConfig::openConfig(),
"theme");
378 d->windowStateConfig = KSharedConfig::openConfig()->group(
"MainWindow");
381 setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
382 setDockNestingEnabled(
true);
390 qApp->setStartDragDistance(25);
393 connect(
this, SIGNAL(
themeChanged()),
d->viewManager, SLOT(updateIcons()));
411 d->toolOptionsDocker = qobject_cast<KoToolDocker*>(
createDockWidget(&toolDockerFactory));
412 if (
d->toolOptionsDocker) {
413 d->toolOptionsDocker->toggleViewAction()->setEnabled(
true);
417 QMap<QString, QAction*> dockwidgetActions;
420 dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction();
426 dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction();
430 if (
d->toolOptionsDocker) {
431 dockwidgetActions[
d->toolOptionsDocker->toggleViewAction()->text()] =
d->toolOptionsDocker->toggleViewAction();
435 Q_FOREACH (QString title, dockwidgetActions.keys()) {
436 d->dockWidgetMenu->addAction(dockwidgetActions[title]);
441 d->styleActions =
new QActionGroup(
this);
444#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
450 Q_FOREACH (QString styleName, QStyleFactory::keys()) {
453 if (styleName.toLower().contains(
"android")) {
457 if (qgetenv(
"KRITA_NO_STYLE_OVERRIDE").isEmpty()) {
458 if (!allowableStyles.contains(styleName.toLower())) {
462 action =
new QAction(styleName,
d->styleActions);
463 action->setCheckable(
true);
464 d->actionMap.insert(styleName,
action);
465 d->styleMenu->addAction(
d->actionMap.value(styleName));
470 QString styleFromConfig = cfg.
widgetStyle().toLower();
471 QString styleToSelect = styleFromConfig ==
"" ? style()->objectName().toLower() : styleFromConfig;
473 Q_FOREACH (
auto key,
d->actionMap.keys()) {
474 if(key.toLower() == styleToSelect) {
475 d->actionMap.value(key)->setChecked(
true);
479 connect(
d->styleActions, SIGNAL(triggered(QAction*)),
491 if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) {
492 wdg->setVisible(
true);
499 if (mainwindowObserver) {
504 d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
505 d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
506 d->mdiArea->setTabPosition(QTabWidget::North);
507 d->mdiArea->setTabsClosable(
true);
508 d->mdiArea->setAcceptDrops(
true);
512 setCentralWidget(
d->widgetStack);
513 d->widgetStack->setCurrentIndex(0);
516 connect(
d->windowMapper, SIGNAL(mapped(QWidget*)),
this, SLOT(
setActiveSubWindow(QWidget*)));
517 connect(
d->documentMapper, SIGNAL(mapped(QObject*)),
this, SLOT(
newView(QObject*)));
525 d->welcomePage->setMainWindow(
this);
533 d->helpMenu =
new KisKHelpMenu(
this, KAboutData::applicationData(),
false);
548 if (helpContentsAction) {
549 actions->
addAction(helpContentsAction->objectName(), helpContentsAction);
551 if (whatsThisAction) {
552 actions->
addAction(whatsThisAction->objectName(), whatsThisAction);
554 if (reportBugAction) {
555 actions->
addAction(reportBugAction->objectName(), reportBugAction);
557 if (switchLanguageAction) {
558 actions->
addAction(switchLanguageAction->objectName(), switchLanguageAction);
560 if (aboutAppAction) {
561 actions->
addAction(aboutAppAction->objectName(), aboutAppAction);
563 if (aboutKdeAction) {
564 actions->
addAction(aboutKdeAction->objectName(), aboutKdeAction);
572 helpAction->disconnect();
573 connect(helpAction, SIGNAL(triggered()),
this, SLOT(
showManual()));
577 QSet<QKeySequence> existingShortcuts;
579 if(
action->shortcut() == QKeySequence(0)) {
583 Q_ASSERT(!existingShortcuts.contains(
action->shortcut()));
584 existingShortcuts.insert(
action->shortcut());
603 Q_FOREACH (QWidget* it,
guiFactory()->containers(
"ToolBar")) {
606 KToggleAction* act =
new KToggleAction(i18n(
"Show %1 Toolbar",
toolBar->windowTitle()),
this);
608 act->setCheckedState(KGuiItem(i18n(
"Hide %1 Toolbar",
toolBar->windowTitle())));
610 act->setChecked(!
toolBar->isHidden());
613 warnUI <<
"Toolbar list contains a " << it->metaObject()->className() <<
" which is not a toolbar!";
622 d->viewManager->updateGUI();
623 d->viewManager->updateIcons();
625 QTimer::singleShot(1000, Qt::CoarseTimer,
this, SLOT(
checkSanity()));
628 using namespace std::placeholders;
629 std::function<
void (
int)> callback(
632 d->tabSwitchCompressor.reset(
637 if (cfg.
readEntry(
"CanvasOnlyActive",
false)) {
638 QString currentWorkspace = cfg.
readEntry<QString>(
"CurrentWorkspace",
"Default");
645 menuBar()->setVisible(
true);
654 setFixedSize(KisApplication::primaryScreen()->availableGeometry().size());
656 QScreen *s = QGuiApplication::primaryScreen();
657 s->setOrientationUpdateMask(Qt::LandscapeOrientation|Qt::InvertedLandscapeOrientation|Qt::PortraitOrientation|Qt::InvertedPortraitOrientation);
660#if KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
664 if (androidScaling) {
665 connect(androidScaling,
668 &KisMainWindow::slotFlashWindowHack,
669 Qt::QueuedConnection);
676 QAndroidJniObject::callStaticMethod<void>(
"org/qtproject/qt5/android/QtNative",
"setApplicationState",
"(I)V", Qt::ApplicationActive);
679 setAcceptDrops(
true);
680 QTabBar *tabBar =
d->findTabBarHACK();
682 tabBar->setElideMode(Qt::ElideRight);
685 tabBar->setExpanding(
true);
686 tabBar->setAcceptDrops(
true);
687 tabBar->setChangeCurrentOnDrag(
true);
724 Q_FOREACH (QMdiSubWindow *subwin,
d->mdiArea->subWindowList()) {
726 (
void) subwin->close();
734 delete d->viewManager;
750 if (
d->activeView == view && !subWindow)
return;
753 d->activeView->disconnect(
this);
794 QMdiSubWindow *currentSubWin =
d->mdiArea->currentSubWindow();
795 bool shouldMaximize = currentSubWin ? currentSubWin->isMaximized() :
true;
796 subwin =
d->mdiArea->addSubWindow(imageView);
797 if (shouldMaximize) {
798 subwin->setWindowState(Qt::WindowMaximized);
801 subwin->setWidget(imageView);
804 subwin->setAttribute(Qt::WA_DeleteOnClose,
true);
808 subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.
readEntry<
int>(
"mdi_rubberband", cfg.
useOpenGL()));
809 subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.
readEntry<
int>(
"mdi_rubberband", cfg.
useOpenGL()));
810 subwin->setWindowIcon(qApp->windowIcon());
817 if (
d->mdiArea->subWindowList().size() == 1) {
818 imageView->showMaximized();
861 std::optional<KisDlgPreferences::PageDesc>page = std::nullopt;
864 QVariant data =
action->data();
865 if (data.userType() == QMetaType::QVariantList) {
867 action->setData(QVariant());
870 Q_ASSERT(list.length() == 2);
871 Q_ASSERT(list[0].userType() == QMetaType::Int);
872 Q_ASSERT(list[1].userType() == QMetaType::Int);
875 int t = list[1].toInt();
881 if (dlgPreferences->editPreferences(page)) {
904 d->viewManager->showHideScrollbars();
915 d->mdiArea->setPalette(qApp->palette());
916 for (
int i=0; i<
d->mdiArea->subWindowList().size(); i++) {
917 QMdiSubWindow *window =
d->mdiArea->subWindowList().at(i);
919 window->setPalette(qApp->palette());
920 KisView *view = qobject_cast<KisView*>(window->widget());
934 objects.append(aToolBar);
935 while (!objects.isEmpty()) {
936 QWidget*
widget = qobject_cast<QWidget*>(objects.takeFirst());
938 objects.append(
widget->children());
939 widget->setPalette(qApp->palette());
948 KConfigGroup group(KSharedConfig::openConfig(),
"theme");
950 if (group.readEntry(
"Theme",
"") ==
d->themeManager->currentThemeName())
return;
953 group.writeEntry(
"Theme",
d->themeManager->currentThemeName());
958 Q_FOREACH (QWidget* topLevelWidget, qApp->topLevelWidgets()) {
959 if (topLevelWidget ==
this) {
963 if (topLevelWidget->isHidden()) {
967 KisMainWindow *topLevelMainWindow = qobject_cast<KisMainWindow*>(topLevelWidget);
968 if (topLevelMainWindow) {
973 objects.append(topLevelWidget);
974 while (!objects.isEmpty()) {
975 QWidget*
widget = qobject_cast<QWidget*>(objects.takeFirst());
977 objects.append(
widget->children());
994 QString closeButtonImageUrl;
995 QString closeButtonHoverColor;
997 closeButtonImageUrl = QStringLiteral(
":/dark_close-tab.svg");
998 closeButtonHoverColor = QStringLiteral(
"lightcoral");
1000 closeButtonImageUrl = QStringLiteral(
":/light_close-tab.svg");
1001 closeButtonHoverColor = QStringLiteral(
"darkred");
1003 QString tabStyleSheet = QStringLiteral(R
"(
1004 QTabBar::close-button {
1008 QTabBar::close-button:hover {
1009 background-color: %2;
1011 QTabBar::close-button:pressed {
1012 background-color: red;
1015 QHeaderView::section {
1020 .arg(closeButtonImageUrl, closeButtonHoverColor);
1023 QTabBar* tabBar = d->findTabBarHACK();
1025 tabBar->setStyleSheet(tabStyleSheet);
1032 return centralWidget() !=
d->widgetStack;
1039 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"),
1040 "Detach Canvas is unsupported on Android");
1045 QWidget *outgoingWidget = centralWidget() ? takeCentralWidget() :
nullptr;
1046 QWidget *incomingWidget =
d->canvasWindow->swapMainWidget(outgoingWidget);
1048 if (incomingWidget) {
1049 setCentralWidget(incomingWidget);
1053 d->canvasWindow->show();
1055 d->canvasWindow->hide();
1057 d->toggleDetachCanvas->setChecked(detach);
1063 return d->canvasWindow;
1078 const QString fileName = QFileInfo(documentPath).fileName();
1080 if (!fileName.isEmpty()) {
1081 d->saveAction->setToolTip(i18n(
"Save as %1", fileName));
1084 d->saveAction->setToolTip(i18n(
"Save"));
1090 Q_UNUSED(readWrite);
1091 d->actionManager()->updateGUI();
1096 if (
d->activeView) {
1097 return d->activeView;
1105 QApplication::processEvents();
1107 if (!QFile(path).exists()) {
1109 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"The file %1 does not exist.", path));
1119 if (!QFile(path).exists()) {
1120 qWarning() <<
"KisMainWindow::openDocumentInternal. Could not open:" << path;
1130 d->firstTime =
true;
1132 connect(newdoc, SIGNAL(canceled(QString)),
this, SLOT(
slotLoadCanceled(QString)));
1151 ( path.startsWith(QDir::tempPath())
1152 || path.startsWith(QDir::homePath())
1154 ( QFileInfo(path).fileName().startsWith(
".krita")
1155 || QFileInfo(path).fileName().startsWith(
"krita")
1159 QString path = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1160 if (!QFileInfo(path).exists()) {
1161 path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
1163 newdoc->
setPath(path +
"/" + newdoc->objectName() +
".kra");
1170 Q_FOREACH(QMdiSubWindow *subwindow,
d->mdiArea->subWindowList()) {
1171 KisView *view = qobject_cast<KisView*>(subwindow->widget());
1175 if (view->
document() == document) {
1186 QMdiSubWindow *subWindow)
1206 slotFlashWindowHack();
1216 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1218 dialog.setCaption(isImporting ? i18n(
"Import Images") : i18n(
"Open Images"));
1229 KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
1230 if (newdoc && newdoc->
image()) {
1234 disconnect(newdoc, SIGNAL(canceled(QString)),
this, SLOT(
slotLoadCanceled(QString)));
1243 KisDocument* doc = qobject_cast<KisDocument*>(sender());
1246 disconnect(doc, SIGNAL(canceled(QString)),
this, SLOT(
slotLoadCanceled(QString)));
1251 if (!errMsg.isEmpty()) {
1253 QMessageBox::critical(
this, i18nc(
"@title:window",
"Krita"), errMsg);
1264 KisDocument* doc = qobject_cast<KisDocument*>(sender());
1267 disconnect(doc, SIGNAL(canceled(QString)),
this, SLOT(
slotSaveCanceled(QString)));
1270 if (
d->deferredClosingEvent) {
1277 std::unique_lock<QMutex> l(
d->savingEntryMutex, std::try_to_lock);
1278 return !l.owns_lock();
1283 QFileInfo from(fileName);
1286 QFile::remove(to.canonicalFilePath());
1288 return QFile::copy(fileName, to.absoluteFilePath());
1294 qreal scale = qreal(size)/qreal(qMax(geometry().width(), geometry().height()));
1295 QImage
layoutThumbnail = QImage(qRound(geometry().width()*scale), qRound(geometry().height()*scale), QImage::Format_ARGB32);
1299 Q_FOREACH(
const QObject *child, children()) {
1300 if (child->isWidgetType()) {
1301 const QWidget *w =
static_cast<const QWidget *
>(child);
1303 if (w->isVisible() && !w->property(
"_kis_excludeFromLayoutThumbnail").toBool()) {
1304 QRect wRect = QRectF(w->geometry().x()*scale
1305 , w->geometry().y()*scale
1306 , w->geometry().width()*scale
1307 , w->geometry().height()*scale
1310 wRect = wRect.intersected(
layoutThumbnail.rect().adjusted(-1, -1, -1, -1));
1312 gc.setBrush(this->
palette().window());
1313 if (w ==
d->widgetStack) {
1314 gc.setBrush(
d->mdiArea->background());
1316 gc.setPen(this->
palette().windowText().color());
1338 std::unique_lock<QMutex> l(
d->savingEntryMutex, std::try_to_lock);
1339 if (!l.owns_lock())
return false;
1349 QMessageBox::critical(qApp->activeWindow(),
1350 i18nc(
"@title:window",
"Krita"),
1351 i18n(
"You are saving a file while the image is "
1352 "still rendering. The saved file may be "
1353 "incomplete or corrupted.\n\n"
1354 "Please select a location where the original "
1355 "file will not be overridden!"));
1361 if (document->isRecovered()) {
1365 if (document->path().isEmpty()) {
1369 QFile
target(document->path());
1375 connect(document, SIGNAL(canceled(QString)),
this, SLOT(
slotSaveCanceled(QString)));
1377 QByteArray nativeFormat = document->nativeFormatMimeType();
1378 QByteArray oldMimeFormat = document->mimeType();
1380 QUrl suggestedURL = QUrl::fromLocalFile(document->path());
1385 if (!mimeFilter.contains(oldMimeFormat)) {
1386 dbgUI <<
"KisMainWindow::saveDocument no export filter for" << oldMimeFormat;
1392 QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1394 if (!suggestedFilename.isEmpty()) {
1396 suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename);
1397 suggestedURL.setPath(suggestedURL.path() + suggestedFilename);
1406 if (document->path().isEmpty() || isExporting || saveas) {
1409 bool justChangingFilterOptions =
false;
1412 dialog.setCaption(isExporting ? i18n(
"Exporting") : i18n(
"Saving As"));
1416 if (isExporting && !
d->lastExportLocation.isEmpty() && !
d->lastExportLocation.contains(QDir::tempPath())) {
1419 QString proposedPath = QFileInfo(
d->lastExportLocation).absolutePath();
1421 QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo(
"title") : QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
1423 QString proposedMimeType =
d->lastExportedFormat.isEmpty() ?
"" :
d->lastExportedFormat;
1427 dialog.setDefaultDir(proposedPath +
"/" + proposedFileName +
"." + proposedExtension,
true);
1428 dialog.setMimeTypeFilters(mimeFilter, proposedMimeType);
1433 QByteArray default_mime_type = cfg.
exportMimeType(
false).toUtf8();
1434 QString proposedMimeType = QString::fromLatin1(default_mime_type);
1437 KConfigGroup group = KSharedConfig::openConfig()->group(
"File Dialogs");
1438 QString proposedPath = group.readEntry(
"SaveAs",
"");
1440 if (proposedPath.isEmpty()) {
1441 proposedPath = group.readEntry(
"OpenDocument",
"");
1444 if (proposedPath.isEmpty()) {
1445 proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1451 if(default_mime_type ==
"all/mime"){
1452 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : proposedPath +
"/" + QFileInfo(suggestedURL.toLocalFile()).completeBaseName(),
true);
1455 if (default_mime_type !=
"all/mime" && !default_mime_type.isEmpty()) {
1458 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : proposedPath +
"/" + QFileInfo(suggestedURL.toLocalFile()).completeBaseName() +
"." + proposedExtension,
true);
1463 dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(),
true);
1464 default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType();
1466 dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type));
1469 QString newFilePath = dialog.filename();
1472 if (newFilePath.isEmpty()) {
1477 if (document->documentInfo()->aboutInfo(
"title") == i18n(
"Unnamed")) {
1478 QString fn = newFilePath;
1480 document->documentInfo()->setAboutInfo(
"title", info.completeBaseName());
1483 QByteArray outputFormat = nativeFormat;
1486 outputFormat = outputFormatString.toLatin1();
1490 justChangingFilterOptions = (newFilePath == document->path()) && (outputFormat == document->mimeType());
1493 QString path = QFileInfo(
d->lastExportLocation).absolutePath();
1494 QString filename = QFileInfo(document->path()).completeBaseName();
1495 justChangingFilterOptions = (QFileInfo(newFilePath).absolutePath() == path)
1496 && (QFileInfo(newFilePath).completeBaseName() == filename)
1497 && (outputFormat ==
d->lastExportedFormat);
1500 bool wantToSave =
true;
1503 if (!justChangingFilterOptions) {
1504 if (!document->isNativeFormat(outputFormat))
1510 ret = document->saveAs(newFilePath, outputFormat,
true);
1512 dbgUI <<
"Successful Save As!";
1515 dbgUI <<
"Failed Save As!";
1520 ret = document->exportDocument(newFilePath, outputFormat, isAdvancedExporting,
true);
1522 d->lastExportLocation = newFilePath;
1523 d->lastExportedFormat = outputFormat;
1539 if (document->isModified()) {
1540 ret = document->save(
true, 0);
1544 dbgUI <<
"Failed Save!";
1554 activeView()->document()->undoStack()->undo();
1561 activeView()->document()->undoStack()->redo();
1568 e->setAccepted(
false);
1573 QAction *
action=
d->viewManager->actionCollection()->action(
"view_show_canvas_only");
1575 action->setChecked(
false);
1582 if (!closeAllowed) {
1583 e->setAccepted(
false);
1589 d->mdiArea->closeAllSubWindows();
1593 if (childrenList.isEmpty()) {
1596 d->deferredClosingEvent = e;
1597 d->canvasWindow->close();
1599 e->setAccepted(
false);
1605 KSharedConfigPtr config = KSharedConfig::openConfig();
1607 if (
d->windowSizeDirty ) {
1608 dbgUI <<
"KisMainWindow::saveWindowSettings";
1609 KConfigGroup group =
d->windowStateConfig;
1610 KWindowConfig::saveWindowSize(windowHandle(), group);
1612 d->windowSizeDirty =
false;
1615 if (!
d->activeView ||
d->activeView->document()) {
1618 KConfigGroup group =
d->windowStateConfig;
1622 for (QMap<QString, QDockWidget*>::const_iterator i =
d->dockWidgetsMap.constBegin();
1623 i !=
d->dockWidgetsMap.constEnd(); ++i) {
1624 if (i.value()->widget()) {
1625 KConfigGroup dockGroup = group.group(QString(
"DockWidget ") + i.key());
1626 dockGroup.writeEntry(
"Locked", i.value()->property(
"Locked").toBool());
1627 dockGroup.writeEntry(
"DockArea", (
int) dockWidgetArea(i.value()));
1628 dockGroup.writeEntry(
"xPosition", (
int) i.value()->widget()->x());
1629 dockGroup.writeEntry(
"yPosition", (
int) i.value()->widget()->y());
1631 dockGroup.writeEntry(
"width", (
int) i.value()->widget()->width());
1632 dockGroup.writeEntry(
"height", (
int) i.value()->widget()->height());
1638 KSharedConfig::openConfig()->sync();
1645 d->windowSizeDirty =
true;
1646 KXmlGuiWindow::resizeEvent(e);
1664 QAction *
action=
d->viewManager->actionCollection()->action(
"view_show_canvas_only");
1672 if (!
event->spontaneous()) {
1681 QTimer::singleShot(0,
this, &KisMainWindow::slotShowDonationManagementDialog);
1684 return KXmlGuiWindow::showEvent(
event);
1690 if (widgetIndex == 0) {
1691 if (widgetIndexChanged) {
1697 if (
d->mdiArea->subWindowList().isEmpty()) {
1711 statusBar()->setVisible(
KisConfig(
true).showStatusBar());
1716 for (QAction *
action : actions) {
1718 action->setEnabled(widgetIndex);
1731 statusBar()->hide();
1734 for (QToolBar *toolbar : toolbars) {
1735 if (toolbar->objectName() !=
"mainToolBar") {
1743 d->activeView = view;
1745 if (
d->undoActionsUpdateManager) {
1746 d->undoActionsUpdateManager->setCurrentDocument(view ? view->
document() : 0);
1749 d->viewManager->setCurrentView(view);
1751 d->activeViewConnections.clear();
1752 d->activeViewConnections.addConnection(view->
document(),
1753 SIGNAL(sigPathChanged(QString)),
1756 d->activeViewConnections.addConnection(view->
document(),
1757 SIGNAL(sigReadWriteChanged(
bool)),
1768 d->activeViewConnections.clear();
1775 QTabBar *tabBar =
d->findTabBarHACK();
1777 if (!tabBar &&
d->mdiArea->viewMode() == QMdiArea::TabbedView) {
1778 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)!";
1781 if (tabBar && tabBar->isVisible()) {
1782 QPoint pos = tabBar->mapFromGlobal(mapToGlobal(
event->pos()));
1783 if (tabBar->rect().contains(pos)) {
1784 const int tabIndex = tabBar->tabAt(pos);
1786 if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) {
1787 d->tabSwitchCompressor->start(tabIndex);
1789 }
else if (
d->tabSwitchCompressor->isActive()) {
1790 d->tabSwitchCompressor->stop();
1797 if (
d->tabSwitchCompressor->isActive()) {
1798 d->tabSwitchCompressor->stop();
1805 QTabBar *tabBar =
d->findTabBarHACK();
1806 if (!tabBar)
return;
1808 tabBar->setCurrentIndex(index);
1813 const int currentIndex = show ? 0 : 1;
1814 if (
d->widgetStack->currentIndex() != currentIndex) {
1815 setUpdatesEnabled(
false);
1820 d->widgetStack->setCurrentIndex(currentIndex);
1822 d->widgetStack->setCurrentIndex(currentIndex);
1825 setUpdatesEnabled(
true);
1838 dbgUI <<
"slotImportFile()";
1850 Q_FOREACH (
const QString& url, urls) {
1852 if (!url.isEmpty()) {
1853 OpenFlags flags = isImporting ?
Import :
None;
1856 warnKrita <<
"Loading" << url <<
"failed";
1869 if (
saveDocument(
d->activeView->document(),
false,
false,
false)) {
1876#if defined(Q_OS_ANDROID) && KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
1878 if (androidScaling) {
1886 if (
saveDocument(
d->activeView->document(),
true,
false,
false)) {
1893 if (
saveDocument(
d->activeView->document(),
true,
true,
false)) {
1899 if (
saveDocument(
d->activeView->document(),
true,
true,
true)) {
1909void KisMainWindow::slotShowDonationManagementDialog()
1913 QWidget *win = qApp->activeWindow();
1914 if (win && !qobject_cast<KisMainWindow *>(win) && (win->isModal() || win->windowModality() != Qt::NonModal)) {
1922 QString objectName = QStringLiteral(
"kisdonationmanagementdialog");
1934 dlg->setAttribute(Qt::WA_DeleteOnClose);
1935 dlg->setObjectName(objectName);
1940void KisMainWindow::slotFlashWindowHack()
1942 if (!
d->flashWindowHackInProgress) {
1943 d->flashWindowHackInProgress =
true;
1944 QTimer::singleShot(0,
this, [
this] {
1946 QTimer::singleShot(0,
this, [
this] {
1948 d->flashWindowHackInProgress =
false;
1957 return d->viewManager->canvasResourceProvider()->resourceManager();
1962 return d->mdiArea->subWindowList().size();
1967 return d->windowStateConfig;
1973 if (
d->widgetStack->currentIndex() == 0) {
1978 if (restoreNormalState) {
1979 QAction *showCanvasOnly =
d->viewManager->actionCollection()->action(
"view_show_canvas_only");
1981 if (showCanvasOnly && showCanvasOnly->isChecked()) {
1982 showCanvasOnly->setChecked(
false);
1985 d->windowStateConfig.writeEntry(
"ko_geometry", saveGeometry().toBase64());
1986 d->windowStateConfig.writeEntry(
"State", saveState().toBase64());
1989 if (!
d->toggleDockers->isChecked()) {
1990 restoreState(
d->dockerStateBeforeHiding);
1993 statusBar()->setVisible(
true);
1994 menuBar()->setVisible(
true);
2006 QByteArray oldState = saveState();
2012 dock->setProperty(
"Locked",
false);
2013 dock->toggleViewAction()->setEnabled(
true);
2015 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2016 dock->titleBarWidget()->setVisible(showTitlebars);
2021 bool success = KXmlGuiWindow::restoreState(state);
2024 KXmlGuiWindow::restoreState(oldState);
2026 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2027 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
2037 QString md5 = sender()->property(
"md5").toString();
2044 qWarning() <<
"Could not retrieve resource for" << md5;
2053 int actionsCount = 0;
2058 if (
auto collection = c->actionCollection()) {
2059 actionCollections.append(collection);
2060 actionsCount += collection->count();
2069 actionCollections.append(layerActionCollection);
2070 actionsCount += layerActionCollection->
count();
2073 d->commandBar->updateBar(actionCollections, actionsCount);
2079 d->commandBar->activateWindow();
2092 warning = i18n(
"You don't have any resource bundles enabled.");
2096 warning += i18n(
"\nThere are no brush presets available. Please enable a bundle that has presets before continuing.\n");
2097 QMessageBox::critical(
this, i18nc(
"@title:window",
"Krita"), warning);
2106 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"), warning + i18n(
"\nOnly your local resources are available."));
2119 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2120 dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
2125 activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace);
2128 d->viewManager->notifyWorkspaceLoaded();
2135 QByteArray currentWorkspace = saveState();
2137 if (!
d->workspaceBorrowedBy.isNull()) {
2138 if (other->
id() ==
d->workspaceBorrowedBy) {
2140 d->workspaceBorrowedBy = QUuid();
2141 return currentWorkspace;
2149 d->workspaceBorrowedBy = other->
id();
2150 return originalLayout;
2155 d->workspaceBorrowedBy = other->
id();
2156 return currentWorkspace;
2162 QByteArray workspaceB = b->borrowWorkspace(a);
2165 b->restoreWorkspaceState(workspaceA);
2170 return d->viewManager;
2175 if (!
d->activeView->document())
2183 KoDocumentInfoDlg *dlg =
d->activeView->document()->createDocumentInfoDialog(
this, docInfo);
2187 d->activeView->document()->setModified(
false);
2189 d->activeView->document()->setModified(
true);
2198 Q_FOREACH (QMdiSubWindow *subwin,
d->mdiArea->subWindowList()) {
2200 if(!subwin->close())
2224 if (!document)
return;
2228 if (dlg.exec() == QDialog::Accepted) {
2230 int firstFrame = dlg.firstFrame();
2231 int step = dlg.step();
2232 bool startFrom1 = dlg.startFrom1();
2233 bool autoAddHoldframes = dlg.autoAddHoldframes();
2239 int isAscending = dlg.isAscending();
2240 KisImportExportErrorCode status = importer.import(files, firstFrame, step, autoAddHoldframes, startFrom1, isAscending);
2245 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"Could not finish import animation:\n%1", msg));
2247 activeView()->canvasBase()->refetchDataFromImage();
2258 if (dlg.exec() == QDialog::Accepted) {
2259 const QTemporaryDir outputLocation(QDir::tempPath() + QDir::separator() +
"krita" + QDir::separator() +
"import_files");
2260 RenderedFrames renderedFrames = dlg.renderFrames(QDir(outputLocation.path()));
2261 dbgFile <<
"Frames rendered to directory: " << outputLocation.path();
2262 QStringList documentInfoList = dlg.documentInfo();
2264 if (renderedFrames.
isEmpty())
return;
2266 dbgFile <<
"Animation Import options: " << documentInfoList;
2269 const int step = documentInfoList[0].toInt();
2270 const int fps = documentInfoList[1].toInt();
2272 const QString
name = QFileInfo(documentInfoList[3]).fileName();
2273 const bool useCurrentDocument = documentInfoList[4].toInt();
2274 bool useDocumentColorSpace =
false;
2276 if ( useCurrentDocument ) {
2279 dbgFile <<
"Current frames:" << document->image()->animationInterface()->totalLength() <<
"total frames:" << totalFrames;
2280 if ( document->image()->animationInterface()->totalLength() < totalFrames ) {
2281 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2282 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2285 const int width = documentInfoList[5].toInt();
2286 const int height = documentInfoList[6].toInt();
2287 const double resolution = documentInfoList[7].toDouble();
2289 const QString colorModel = documentInfoList[8];
2290 const QString colorDepth = documentInfoList[9];
2291 const QString profile = documentInfoList[10];
2292 useDocumentColorSpace = profile !=
"Default";
2295 document->setObjectName(name);
2301 QColor qc(Qt::white);
2305 if (!document->newImage(name, width, height, cs, bgColor,
KisConfig::RASTER_LAYER, 1,
"",
double(resolution / 72) )) {
2306 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"Failed to create new document. Animation import aborted."));
2310 document->image()->animationInterface()->setFramerate(fps);
2311 document->image()->animationInterface()->setDocumentRangeStartFrame(0);
2312 document->image()->animationInterface()->setDocumentRangeEndFrame(totalFrames);
2325 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"), i18n(
"Could not finish import animation:\n%1", msg));
2328 activeView()->canvasBase()->refetchDataFromImage();
2329 document->image()->refreshGraphAsync();
2330 document->image()->waitForDone();
2348 dlgAnimationRenderer.setCaption(i18n(
"Render Animation"));
2349 if (dlgAnimationRenderer.exec() == QDialog::Accepted) {
2426 if (
d->activeView &&
d->activeView->document()) {
2430 warnUI <<
"slotToolbarToggled : Toolbar " << sender()->objectName() <<
" not found!";
2441 d->fullScreenMode->setChecked(fullScreen);
2447 setWindowState(windowState() | Qt::WindowFullScreen);
2449 setWindowState(windowState() & ~Qt::WindowFullScreen);
2451 d->fullScreenMode->setChecked(isFullScreen());
2460 if (!
d->dockWidgetsMap.contains(
factory->id())) {
2482 if (
dockWidget->titleBarWidget() && !
dockWidget->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2488 if (!showTitlebars) {
2489 if (
dockWidget->titleBarWidget() && !
dockWidget->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
2490 dockWidget->titleBarWidget()->setVisible(
false);
2492 dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
2495 dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1);
2497 Qt::DockWidgetArea side = Qt::RightDockWidgetArea;
2498 bool visible =
true;
2500 switch (
factory->defaultDockPosition()) {
2505 side = Qt::TopDockWidgetArea;
break;
2507 side = Qt::LeftDockWidgetArea;
break;
2509 side = Qt::BottomDockWidgetArea;
break;
2511 side = Qt::RightDockWidgetArea;
break;
2514 side = Qt::RightDockWidgetArea;
2518 KConfigGroup group =
d->windowStateConfig.group(
"DockWidget " +
factory->id());
2519 side =
static_cast<Qt::DockWidgetArea
>(group.readEntry(
"DockArea",
static_cast<int>(side)));
2520 if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea;
2527 bool locked = group.readEntry(
"Locked",
false);
2528 if (titleBar && locked) {
2531 else if (
dockWidget->titleBarWidget()->inherits(
"KisUtilityTitleBar") && locked){
2543 dockWidget->setAttribute(Qt::WA_MacSmallSize,
true);
2554 Q_FOREACH (QObject *child, children()) {
2555 if (child->inherits(
"QTabBar")) {
2566 Q_FOREACH (
auto key,
d->actionMap.keys()) {
2567 if(
d->actionMap.value(key)->isChecked()) {
2570 qApp->setStyle(key);
2574#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
2575 if (qApp->style()->objectName() ==
"macintosh") {
2576 d->themeManager->setCurrentTheme(
"System");
2579 if (qApp->style()->name() ==
"macos") {
2580 d->themeManager->setCurrentTheme(
"System");
2590 return d->dockWidgetsMap.values();
2595 if (!
d->dockWidgetsMap.contains(
id))
return 0;
2596 return d->dockWidgetsMap[
id];
2608 observers << observer;
2611 warnKrita << docker <<
"is not a canvas observer";
2622 d->dockerStateBeforeHiding = saveState();
2624 Q_FOREACH (QObject*
widget, children()) {
2625 if (
widget->inherits(
"QDockWidget")) {
2626 QDockWidget* dw =
static_cast<QDockWidget*
>(
widget);
2627 if (dw->isVisible() && !(onWelcomePage && dw->property(
"ShowOnWelcomePage").toBool())) {
2634 restoreState(
d->dockerStateBeforeHiding);
2642 d->mdiCascade->setEnabled(enabled);
2643 d->mdiNextWindow->setEnabled(enabled);
2644 d->mdiPreviousWindow->setEnabled(enabled);
2645 d->mdiTile->setEnabled(enabled);
2646 d->close->setEnabled(enabled);
2647 d->closeAll->setEnabled(enabled);
2660 QMdiSubWindow *subWindow =
d->mdiArea->currentSubWindow();
2662 QMenu *menu = subWindow->systemMenu();
2663 if (menu && menu->actions().size() == 8) {
2664 Q_FOREACH (QAction *
action, menu->actions()) {
2665 action->setShortcut(QKeySequence());
2668 menu->actions().last()->deleteLater();
2672 d->actionManager()->updateGUI();
2689 if (primary.isNull())
return;
2691 if (
d->id == primary) {
2692 if (!
d->workspaceBorrowedBy.isNull()) {
2694 if (!borrower)
return;
2698 if (
d->workspaceBorrowedBy == primary)
return;
2701 if (!primaryWindow)
return;
2709 QMenu *menu =
d->windowMenu->menu();
2713 menu->addAction(
d->newWindow);
2715 menu->addAction(
d->documentMenu);
2717 QMenu *docMenu =
d->documentMenu->menu();
2720 QFontMetrics fontMetrics = docMenu->fontMetrics();
2721 QRect geom = this->geometry();
2722 QPoint
p(geom.width() / 2 + geom.left(), geom.height() / 2 + geom.top());
2723 QScreen *screen = qApp->screenAt(
p);
2724 int fileStringWidth = 300;
2726 fileStringWidth = int(screen->availableGeometry().width() * .40f);
2730 QString title = fontMetrics.elidedText(doc->path(), Qt::ElideMiddle, fileStringWidth);
2731 if (title.isEmpty() && doc->image()) {
2732 title = doc->image()->objectName();
2734 QAction *
action = docMenu->addAction(title);
2735 action->setIcon(qApp->windowIcon());
2736 connect(
action, SIGNAL(triggered()),
d->documentMapper, SLOT(map()));
2737 d->documentMapper->setMapping(
action, doc);
2741 menu->addAction(
d->workspaceMenu);
2748 while (resourceIterator.
hasNext()) {
2751 action->setProperty(
"md5", QVariant::fromValue<QString>(resource->md5sum()));
2755 connect(
workspaceMenu->addAction(i18nc(
"@action:inmenu",
"&Import Workspace...")),
2756 &QAction::triggered,
2763 dialog.setMimeTypeFilters(mimeTypes);
2764 dialog.setCaption(i18nc(
"@title:window",
"Choose File to Add"));
2765 QString filename = dialog.filename();
2770 connect(
workspaceMenu->addAction(i18nc(
"@action:inmenu",
"&New Workspace...")),
2771 &QAction::triggered,
2774 name = QInputDialog::getText(
this, i18nc(
"@title:window",
"New Workspace..."),
2775 i18nc(
"@label:textbox",
"Name:"));
2776 if (name.isEmpty()) {
2781 workspace->setDockerState(m_this->saveState());
2783 workspace->setValid(
true);
2787 d->viewManager->canvasResourceProvider()->notifySavingWorkspace(workspace);
2788 workspace->setValid(
true);
2790 workspace->setFilename(name.replace(
" ",
"_") + workspace->defaultFileExtension());
2791 workspace->setName(name);
2799 menu->addSeparator();
2800 menu->addAction(
d->close);
2801 menu->addAction(
d->closeAll);
2802 if (
d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2803 menu->addSeparator();
2804 menu->addAction(
d->mdiTile);
2805 menu->addAction(
d->mdiCascade);
2807 menu->addSeparator();
2808 menu->addAction(
d->mdiNextWindow);
2809 menu->addAction(
d->mdiPreviousWindow);
2810 menu->addSeparator();
2813 for (
int i = 0; i < windows.size(); ++i) {
2815 if (child && child->document()) {
2818 text = i18n(
"&%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2821 text = i18n(
"%1 %2", i + 1, fontMetrics.elidedText(child->document()->path(), Qt::ElideMiddle, fileStringWidth));
2824 QAction *
action = menu->addAction(text);
2825 action->setIcon(qApp->windowIcon());
2826 action->setCheckable(
true);
2828 connect(
action, SIGNAL(triggered()),
d->windowMapper, SLOT(map()));
2829 d->windowMapper->setMapping(
action, windows.at(i));
2833 bool showMdiArea = windows.count( ) > 0;
2839 Q_FOREACH (QObject*
widget, children()) {
2840 if (
widget->inherits(
"QDockWidget")) {
2841 QDockWidget* dw =
static_cast<QDockWidget*
>(
widget);
2843 if ( dw->objectName() ==
"ToolBox") {
2844 dw->setEnabled(showMdiArea);
2852 bool onlyOne =
false;
2853 if (
d->mdiArea->subWindowList().size() == 1 &&
d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
2856 Q_FOREACH (QMdiSubWindow *subwin,
d->mdiArea->subWindowList()) {
2858 subwin->setWindowFlags(subwin->windowFlags() | Qt::FramelessWindowHint);
2859 subwin->showMaximized();
2861 subwin->setWindowFlags((subwin->windowFlags() | Qt::FramelessWindowHint) ^ Qt::FramelessWindowHint);
2872 QMdiSubWindow *subwin = qobject_cast<QMdiSubWindow *>(window);
2875 if (subwin && subwin !=
d->activeSubWindow) {
2876 KisView *view = qobject_cast<KisView *>(subwin->widget());
2881 d->activeSubWindow = subwin;
2884 d->actionManager()->updateGUI();
2890 QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.
readEntry<
int>(
"mdi_viewmode", (
int)QMdiArea::TabbedView);
2891 d->mdiArea->setViewMode(viewMode);
2892 Q_FOREACH (QMdiSubWindow *subwin,
d->mdiArea->subWindowList()) {
2893 subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.
readEntry<
int>(
"mdi_rubberband", cfg.
useOpenGL()));
2894 subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.
readEntry<
int>(
"mdi_rubberband", cfg.
useOpenGL()));
2895 if (viewMode == QMdiArea::TabbedView) {
2896 subwin->setWindowState(Qt::WindowMaximized);
2906 if (viewMode == QMdiArea::TabbedView) {
2907 Qt::WindowFlags oldFlags = subwin->windowFlags();
2908 Qt::WindowFlags flags = oldFlags;
2910 flags &= ~Qt::WindowStaysOnTopHint;
2911 flags &= ~Qt::WindowStaysOnBottomHint;
2913 if (flags != oldFlags) {
2914 subwin->setWindowFlags(flags);
2915 subwin->showMaximized();
2923 KConfigGroup group( KSharedConfig::openConfig(),
"theme");
2925 d->themeManager->setCurrentTheme(group.readEntry(
"Theme",
"Krita dark"));
2927 d->actionManager()->updateGUI();
2932 d->mdiArea->setBackground(brush);
2935 if (backgroundImage !=
"") {
2936 QImage image(backgroundImage);
2937 QBrush brush(image);
2938 d->mdiArea->setBackground(brush);
2941 d->mdiArea->update();
2945 Q_FOREACH (QObject*
widget, children()) {
2946 if (
widget->inherits(
"QDockWidget")) {
2947 QDockWidget* dw =
static_cast<QDockWidget*
>(
widget);
2955 KisDocument *doc = qobject_cast<KisDocument*>(document);
2957 d->actionManager()->updateGUI();
2967 QMessageBox::warning(
this, i18nc(
"@title:window",
"Krita"),
2968 "Creating a New Main Window is unsupported on Android");
2979 if (
d->mdiArea->currentSubWindow()) {
2980 d->mdiArea->currentSubWindow()->close();
2981 d->actionManager()->updateGUI();
2990 m_errorMessage = i18n(
"The Krita LittleCMS color management plugin is not installed. Krita will quit now.");
3006 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Installation error"),
m_errorMessage);
3020 if (!
d->mdiArea)
return 0;
3030 bool isOurOwnView =
false;
3033 if (view && view->canvasController() == controller) {
3034 isOurOwnView = view->mainWindow() ==
this;
3038 if (!isOurOwnView)
return;
3040 Q_FOREACH (QWidget *w, optionWidgetList) {
3042 w->setAttribute(Qt::WA_MacSmallSize,
true);
3047 if (
d->toolOptionsDocker) {
3048 d->toolOptionsDocker->setOptionWidgets(optionWidgetList);
3051 d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList);
3082 d->undoActionsUpdateManager->setCurrentDocument(
d->activeView ?
d->activeView->document() : 0);
3085 connect(
d->importAnimation, SIGNAL(triggered()),
this, SLOT(
importAnimation()));
3095 connect(
d->renderAnimation, SIGNAL(triggered()),
this, SLOT(
renderAnimation()));
3106 connect(
d->importFile, SIGNAL(triggered(
bool)),
this, SLOT(
slotImportFile()));
3109 connect(
d->exportFile, SIGNAL(triggered(
bool)),
this, SLOT(
slotExportFile()));
3112 connect(
d->exportFileAdvance, SIGNAL(triggered(
bool)),
this, SLOT(
slotExportAdvance()));
3117 connect(
d->showDocumentInfo, SIGNAL(triggered(
bool)),
this, SLOT(
slotDocumentInfo()));
3119 d->themeManager->setThemeMenuAction(
new KActionMenu(i18nc(
"@action:inmenu",
"&Themes"),
this));
3121 connect(
d->themeManager, SIGNAL(signalThemeChanged()),
this, SLOT(
slotThemeChanged()), Qt::QueuedConnection);
3122 connect(
this, SIGNAL(
themeChanged()),
d->welcomePage, SLOT(slotUpdateThemeColors()), Qt::UniqueConnection);
3128 d->toggleDockers->setChecked(
true);
3136 d->toggleDetachCanvas->setChecked(
false);
3137 connect(
d->toggleDetachCanvas, SIGNAL(toggled(
bool)), SLOT(
setCanvasDetached(
bool)));
3151 connect(
d->mdiCascade, SIGNAL(triggered()),
d->mdiArea, SLOT(cascadeSubWindows()));
3154 connect(
d->mdiTile, SIGNAL(triggered()),
d->mdiArea, SLOT(tileSubWindows()));
3157 connect(
d->mdiNextWindow, SIGNAL(triggered()),
d->mdiArea, SLOT(activateNextSubWindow()));
3160 connect(
d->mdiPreviousWindow, SIGNAL(triggered()),
d->mdiArea, SLOT(activatePreviousSubWindow()));
3164 connect(
d->showDonationManagementDialog,
3165 &QAction::triggered,
3167 &KisMainWindow::slotShowDonationManagementDialog);
3170 if (androidDonations) {
3172 connect(
d->manageSubscriptions,
3173 &QAction::triggered,
3176 connect(androidDonations,
3179 &KisMainWindow::slotShowDonationManagementDialog,
3180 Qt::QueuedConnection);
3182#if KRITA_QT_HAS_ANDROID_QPLATFORMSCREEN_DENSITY_ADJUSTMENT
3184 if (androidScaling && androidScaling->
isSupported()) {
3191 connect(
d->newWindow, SIGNAL(triggered(
bool)),
this, SLOT(
newWindow()));
3200 connect(
d->commandBarAction, SIGNAL(triggered(
bool)),
this, SLOT(
openCommandBar()));
3204 for (
int i = 0; i < 2; i++) {
3205 d->expandingSpacers[i] =
new KisAction(i18n(
"Expanding Spacer"));
3206 d->expandingSpacers[i]->setDefaultWidget(
new QWidget(
this));
3207 d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
3222 toolBar->layout()->setSpacing(4);
3223 toolBar->setStyleSheet(
"QToolBar { border: none }");
3226 Q_FOREACH (QAction *ac,
toolBar->actions()){
3227 if (ac->icon().pixmap(QSize(1,1)).isNull() ==
false){
3228 ac->setPriority(QAction::LowPriority);
3230 ac->setIcon(QIcon());
3240 KConfigGroup cfg =
d->windowStateConfig;
3241 QByteArray geom = QByteArray::fromBase64(cfg.readEntry(
"ko_geometry", QByteArray()));
3242 if (!restoreGeometry(geom)) {
3243 QRect desk = this->screen()->availableGeometry();
3245 quint32 x = desk.x();
3246 quint32 y = desk.y();
3251 const int deskWidth = desk.width();
3252 if (deskWidth > 1024) {
3255 w = (deskWidth / 3) * 2;
3256 h = (desk.height() / 3) * 2;
3263 x += (desk.width() - w) / 2;
3264 y += (desk.height() - h) / 2;
3267 setGeometry(geometry().x(), geometry().y(), w, h);
3273 bool fullScreen = isFullScreen();
3275 d->fullScreenMode->setChecked(fullScreen);
3280 QDesktopServices::openUrl(QUrl(
"https://docs.krita.org"));
3286 if (dock->titleBarWidget() && !dock->titleBarWidget()->inherits(
"KisUtilityTitleBar")) {
3287 dock->titleBarWidget()->setVisible(show || dock->isFloating());
3304 QScreen *screen = QGuiApplication::primaryScreen();
3306 for (QWindow* window: QGuiApplication::topLevelWindows()) {
3308 if ((window->type() == Qt::Popup)
3309 && (window->flags() & Qt::FramelessWindowHint) == 0
3310 && (window->geometry().topLeft() != QPoint(0, 0))) {
3313 int screenHeight = screen->geometry().width();
3314 int screenWidth = screen->geometry().height();
3317 int new_x = (window->position().x() * screenWidth) / screenHeight;
3318 int new_y = (window->position().y() * screenHeight) / screenWidth;
3321 int winWidth = window->geometry().width();
3322 int winHeight = window->geometry().height();
3325 if (new_x > screenWidth - winWidth) {
3326 new_x = screenWidth - winWidth;
3330 if (new_y > screenHeight - winHeight) {
3331 new_y = screenHeight - winHeight;
3336 window->setPosition(QPoint(new_x, new_y));
3349 return (proxy.rowCount() > 0);
3367 QFile file(xmlPath);
3368 if (!file.exists()) {
3373 if (!file.open(QIODevice::ReadOnly) || !doc.setContent(&file)) {
3377 QDomNodeList overridesList = doc.elementsByTagName(QStringLiteral(
"ActionIconOverrides"));
3378 if (!overridesList.isEmpty()) {
3379 QDomElement overridesElement = overridesList.at(0).toElement();
3380 QDomNodeList actions = overridesElement.elementsByTagName(QStringLiteral(
"Action"));
3381 for (
int i = 0; i < actions.count(); ++i) {
3382 QDomElement actionElement = actions.at(i).toElement();
3383 QString name = actionElement.attribute(QStringLiteral(
"name"));
3384 QString icon = actionElement.attribute(QStringLiteral(
"icon"));
3385 if (!name.isEmpty() && !icon.isEmpty()) {
3399 QString menuLocation =
action->property(
"menulocation").toString();
3400 if (!menuLocation.isEmpty()) {
3403 Q_FOREACH(
const QString &name, menuLocation.split(
"/")) {
3404 Q_FOREACH(QAction *candidate, candidates) {
3405 if (candidate->objectName().toLower() == name.toLower()) {
3407 candidates = candidate->menu()->actions();
3411 if (candidates.isEmpty()) {
3416 if (found && found->menu()) {
3419 if (std::find_if(existingActions.begin(),
3420 existingActions.end(),
3422 == existingActions.end()) {
3424 if (std::is_sorted(existingActions.begin(),
3425 existingActions.end(),
3428 auto it = std::upper_bound(existingActions.begin(),
3429 existingActions.end(),
3432 found->menu()->insertAction(it != existingActions.end() ? *it :
nullptr,
action);
3435 found->menu()->addAction(
action);
3443#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)
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
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()
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...
#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.
auto mem_equal_to(MemTypeNoRef Class::*ptr, MemType &&value)
mem_equal_to is an unary functor that compares a member of the object to a given value
auto mem_less(MemTypeNoRef Class::*ptr, MemType &&value)
mem_less is an unary functor that compares a member of the object to a given value
rgba palette[MAX_PALETTE]
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