File indexing completed on 2024-05-12 16:01:44

0001 /*
0002  * SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org>
0003  * SPDX-FileCopyrightText: 2012 Boudewijn Rempt <boud@valdyas.org>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "KisApplication.h"
0009 
0010 #include <stdlib.h>
0011 #ifdef Q_OS_WIN
0012 #include <windows.h>
0013 #include <tchar.h>
0014 #endif
0015 
0016 #ifdef Q_OS_MACOS
0017 #include "osx.h"
0018 #endif
0019 
0020 #include <QStandardPaths>
0021 #include <QDesktopWidget>
0022 #include <QDir>
0023 #include <QFile>
0024 #include <QLocale>
0025 #include <QMessageBox>
0026 #include <QProcessEnvironment>
0027 #include <QStringList>
0028 #include <QStyle>
0029 #include <QStyleFactory>
0030 #include <QSysInfo>
0031 #include <QTimer>
0032 #include <QWidget>
0033 #include <QImageReader>
0034 #include <QImageWriter>
0035 #include <QThread>
0036 
0037 #include <klocalizedstring.h>
0038 #include <kdesktopfile.h>
0039 #include <kconfig.h>
0040 #include <kconfiggroup.h>
0041 
0042 #include <KoDockRegistry.h>
0043 #include <KoToolRegistry.h>
0044 #include <KoColorSpaceRegistry.h>
0045 #include <KoPluginLoader.h>
0046 #include <KoShapeRegistry.h>
0047 #include "KoConfig.h"
0048 #include <KoResourcePaths.h>
0049 #include <KisMimeDatabase.h>
0050 #include "thememanager.h"
0051 #include "KisDocument.h"
0052 #include "KisMainWindow.h"
0053 #include "KisAutoSaveRecoveryDialog.h"
0054 #include "KisPart.h"
0055 #include <kis_icon.h>
0056 #include "kis_splash_screen.h"
0057 #include "kis_config.h"
0058 #include "flake/kis_shape_selection.h"
0059 #include <filter/kis_filter.h>
0060 #include <filter/kis_filter_registry.h>
0061 #include <filter/kis_filter_configuration.h>
0062 #include <generator/kis_generator_registry.h>
0063 #include <generator/kis_generator.h>
0064 #include <brushengine/kis_paintop_registry.h>
0065 #include <kis_meta_data_io_backend.h>
0066 #include <kis_meta_data_backend_registry.h>
0067 #include "KisApplicationArguments.h"
0068 #include <kis_debug.h>
0069 #include "kis_action_registry.h"
0070 #include <KoResourceServer.h>
0071 #include <KisResourceServerProvider.h>
0072 #include <KoResourceServerProvider.h>
0073 #include "kis_image_barrier_locker.h"
0074 #include "opengl/kis_opengl.h"
0075 #include "kis_spin_box_unit_manager.h"
0076 #include "kis_document_aware_spin_box_unit_manager.h"
0077 #include "KisViewManager.h"
0078 #include <KisUsageLogger.h>
0079 
0080 #include <KritaVersionWrapper.h>
0081 #include <dialogs/KisSessionManagerDialog.h>
0082 
0083 #include <KisResourceCacheDb.h>
0084 #include <KisResourceLocator.h>
0085 #include <KisResourceLoader.h>
0086 #include <KisResourceLoaderRegistry.h>
0087 
0088 #include <KisBrushTypeMetaDataFixup.h>
0089 #include <kis_gbr_brush.h>
0090 #include <kis_png_brush.h>
0091 #include <kis_svg_brush.h>
0092 #include <kis_imagepipe_brush.h>
0093 #include <KoColorSet.h>
0094 #include <KoSegmentGradient.h>
0095 #include <KoStopGradient.h>
0096 #include <KoPattern.h>
0097 #include <kis_workspace_resource.h>
0098 #include <KisSessionResource.h>
0099 #include <resources/KoSvgSymbolCollectionResource.h>
0100 
0101 #include "widgets/KisScreenColorSampler.h"
0102 #include "KisDlgInternalColorSelector.h"
0103 
0104 #include <dialogs/KisAsyncAnimationFramesSaveDialog.h>
0105 #include <kis_image_animation_interface.h>
0106 #include "kis_file_layer.h"
0107 #include "kis_group_layer.h"
0108 #include "kis_node_commands_adapter.h"
0109 #include "KisSynchronizedConnection.h"
0110 #include <QThreadStorage>
0111 
0112 #include <kis_psd_layer_style.h>
0113 
0114 #include <config-seexpr.h>
0115 
0116 namespace {
0117 const QTime appStartTime(QTime::currentTime());
0118 }
0119 
0120 namespace {
0121 struct AppRecursionInfo {
0122     ~AppRecursionInfo() {
0123         KIS_SAFE_ASSERT_RECOVER_NOOP(!eventRecursionCount);
0124         KIS_SAFE_ASSERT_RECOVER_NOOP(postponedSynchronizationEvents.empty());
0125     }
0126 
0127     int eventRecursionCount {0};
0128     std::queue<KisSynchronizedConnectionEvent> postponedSynchronizationEvents;
0129 };
0130 
0131 struct AppRecursionGuard {
0132     AppRecursionGuard(AppRecursionInfo *info)
0133         : m_info(info)
0134     {
0135         m_info->eventRecursionCount++;
0136     }
0137 
0138     ~AppRecursionGuard()
0139     {
0140         m_info->eventRecursionCount--;
0141     }
0142 private:
0143     AppRecursionInfo *m_info {0};
0144 };
0145 
0146 }
0147 
0148 /**
0149  * We cannot make the recursion info be a part of KisApplication,
0150  * because KisApplication also owns QQmlThread, which is destroyed
0151  * after KisApplication::Private, which makes QThreadStorage spit
0152  * a warning about being destroyed too early
0153  */
0154 Q_GLOBAL_STATIC(QThreadStorage<AppRecursionInfo>, s_recursionInfo)
0155 
0156 class KisApplication::Private
0157 {
0158 public:
0159     Private() {}
0160     QPointer<KisSplashScreen> splashScreen;
0161     KisAutoSaveRecoveryDialog *autosaveDialog {0};
0162     QPointer<KisMainWindow> mainWindow; // The first mainwindow we create on startup
0163     bool batchRun {false};
0164     QVector<QByteArray> earlyRemoteArguments;
0165     QVector<QString> earlyFileOpenEvents;
0166 };
0167 
0168 class KisApplication::ResetStarting
0169 {
0170 public:
0171     ResetStarting(KisSplashScreen *splash, int fileCount)
0172         : m_splash(splash)
0173         , m_fileCount(fileCount)
0174     {
0175     }
0176 
0177     ~ResetStarting()  {
0178 
0179         if (m_splash) {
0180             m_splash->hide();
0181         }
0182     }
0183 
0184     QPointer<KisSplashScreen> m_splash;
0185     int m_fileCount;
0186 };
0187 
0188 
0189 KisApplication::KisApplication(const QString &key, int &argc, char **argv)
0190     : QtSingleApplication(key, argc, argv)
0191     , d(new Private)
0192 {
0193 #ifdef Q_OS_MACOS
0194     setMouseCoalescingEnabled(false);
0195 #endif
0196 
0197     QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
0198 
0199     setApplicationDisplayName("Krita");
0200     setApplicationName("krita");
0201     // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird.
0202     //    setOrganizationName("krita");
0203     setOrganizationDomain("krita.org");
0204 
0205     QString version = KritaVersionWrapper::versionString(true);
0206     setApplicationVersion(version);
0207     setWindowIcon(KisIconUtils::loadIcon("krita-branding"));
0208 
0209     if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) {
0210         QStringList styles = QStringList() << "macintosh" << "breeze" << "fusion";
0211         if (!styles.contains(style()->objectName().toLower())) {
0212             Q_FOREACH (const QString & style, styles) {
0213                 if (!setStyle(style)) {
0214                     qDebug() << "No" << style << "available.";
0215                 }
0216                 else {
0217                     qDebug() << "Set style" << style;
0218                     break;
0219                 }
0220             }
0221         }
0222 
0223         // if style is set from config, try to load that
0224         KisConfig cfg(true);
0225         QString widgetStyleFromConfig = cfg.widgetStyle();
0226         if(widgetStyleFromConfig != "") {
0227             qApp->setStyle(widgetStyleFromConfig);
0228         }
0229 
0230     }
0231     else {
0232         qDebug() << "Style override disabled, using" << style()->objectName();
0233     }
0234 
0235     // store the style name
0236     qApp->setProperty(currentUnderlyingStyleNameProperty, style()->objectName());
0237     KisSynchronizedConnectionBase::registerSynchronizedEventBarrier(std::bind(&KisApplication::processPostponedSynchronizationEvents, this));
0238 }
0239 
0240 #if defined(Q_OS_WIN) && defined(ENV32BIT)
0241 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
0242 
0243 LPFN_ISWOW64PROCESS fnIsWow64Process;
0244 
0245 BOOL isWow64()
0246 {
0247     BOOL bIsWow64 = FALSE;
0248 
0249     //IsWow64Process is not available on all supported versions of Windows.
0250     //Use GetModuleHandle to get a handle to the DLL that contains the function
0251     //and GetProcAddress to get a pointer to the function if available.
0252 
0253     fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
0254                 GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
0255 
0256     if(0 != fnIsWow64Process)
0257     {
0258         if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
0259         {
0260             //handle error
0261         }
0262     }
0263     return bIsWow64;
0264 }
0265 #endif
0266 
0267 void KisApplication::initializeGlobals(const KisApplicationArguments &args)
0268 {
0269     Q_UNUSED(args)
0270     // There are no globals to initialize from the arguments now. There used
0271     // to be the `dpi` argument, but it doesn't do anything anymore.
0272 }
0273 
0274 void KisApplication::addResourceTypes()
0275 {
0276     // All Krita's resource types
0277     KoResourcePaths::addAssetType("markers", "data", "/styles/");
0278     KoResourcePaths::addAssetType("kis_pics", "data", "/pics/");
0279     KoResourcePaths::addAssetType("kis_images", "data", "/images/");
0280     KoResourcePaths::addAssetType("metadata_schema", "data", "/metadata/schemas/");
0281     KoResourcePaths::addAssetType("gmic_definitions", "data", "/gmic/");
0282     KoResourcePaths::addAssetType("kis_shortcuts", "data", "/shortcuts/");
0283     KoResourcePaths::addAssetType("kis_actions", "data", "/actions");
0284     KoResourcePaths::addAssetType("kis_actions", "data", "/pykrita");
0285     KoResourcePaths::addAssetType("icc_profiles", "data", "/color/icc");
0286     KoResourcePaths::addAssetType("icc_profiles", "data", "/profiles/");
0287     KoResourcePaths::addAssetType(ResourceType::FilterEffects, "data", "/effects/");
0288     KoResourcePaths::addAssetType("tags", "data", "/tags/");
0289     KoResourcePaths::addAssetType("templates", "data", "/templates");
0290     KoResourcePaths::addAssetType("pythonscripts", "data", "/pykrita");
0291     KoResourcePaths::addAssetType("preset_icons", "data", "/preset_icons");
0292 #if defined HAVE_SEEXPR
0293     KoResourcePaths::addAssetType(ResourceType::SeExprScripts, "data", "/seexpr_scripts/", true);
0294 #endif
0295 
0296     // Make directories for all resources we can save, and tags
0297     KoResourcePaths::saveLocation("data", "/asl/", true);
0298     KoResourcePaths::saveLocation("data", "/input/", true);
0299     KoResourcePaths::saveLocation("data", "/pykrita/", true);
0300     KoResourcePaths::saveLocation("data", "/color-schemes/", true);
0301     KoResourcePaths::saveLocation("data", "/preset_icons/", true);
0302     KoResourcePaths::saveLocation("data", "/preset_icons/tool_icons/", true);
0303     KoResourcePaths::saveLocation("data", "/preset_icons/emblem_icons/", true);
0304 }
0305 
0306 
0307 bool KisApplication::event(QEvent *event)
0308 {
0309 
0310     #ifdef Q_OS_MACOS
0311     if (event->type() == QEvent::FileOpen) {
0312         QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
0313         emit fileOpenRequest(openEvent->file());
0314         return true;
0315     }
0316     #endif
0317     return QApplication::event(event);
0318 }
0319 
0320 
0321 bool KisApplication::registerResources()
0322 {
0323     KisResourceLoaderRegistry *reg = KisResourceLoaderRegistry::instance();
0324 
0325     reg->add(new KisResourceLoader<KisPaintOpPreset>(ResourceSubType::KritaPaintOpPresets, ResourceType::PaintOpPresets, i18n("Brush presets"),
0326                                                      QStringList() << "application/x-krita-paintoppreset"));
0327 
0328     reg->add(new KisResourceLoader<KisGbrBrush>(ResourceSubType::GbrBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush"));
0329     reg->add(new KisResourceLoader<KisImagePipeBrush>(ResourceSubType::GihBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush-animated"));
0330     reg->add(new KisResourceLoader<KisSvgBrush>(ResourceSubType::SvgBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/svg+xml"));
0331     reg->add(new KisResourceLoader<KisPngBrush>(ResourceSubType::PngBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/png"));
0332 
0333     reg->add(new KisResourceLoader<KoSegmentGradient>(ResourceSubType::SegmentedGradients, ResourceType::Gradients, i18n("Gradients"), QStringList() << "application/x-gimp-gradient"));
0334     reg->add(new KisResourceLoader<KoStopGradient>(ResourceSubType::StopGradients, ResourceType::Gradients, i18n("Gradients"), QStringList() << "image/svg+xml"));
0335 
0336     reg->add(new KisResourceLoader<KoColorSet>(ResourceType::Palettes, ResourceType::Palettes, i18n("Palettes"),
0337                                      QStringList() << KisMimeDatabase::mimeTypeForSuffix("kpl")
0338                                                << KisMimeDatabase::mimeTypeForSuffix("gpl")
0339                                                << KisMimeDatabase::mimeTypeForSuffix("pal")
0340                                                << KisMimeDatabase::mimeTypeForSuffix("act")
0341                                                << KisMimeDatabase::mimeTypeForSuffix("aco")
0342                                                << KisMimeDatabase::mimeTypeForSuffix("css")
0343                                                << KisMimeDatabase::mimeTypeForSuffix("colors")
0344                                                << KisMimeDatabase::mimeTypeForSuffix("xml")
0345                                                << KisMimeDatabase::mimeTypeForSuffix("sbz")));
0346 
0347 
0348     reg->add(new KisResourceLoader<KoPattern>(ResourceType::Patterns, ResourceType::Patterns, i18n("Patterns"), {"application/x-gimp-pattern", "image/x-gimp-pat", "application/x-gimp-pattern", "image/bmp", "image/jpeg", "image/png", "image/tiff"}));
0349     reg->add(new KisResourceLoader<KisWorkspaceResource>(ResourceType::Workspaces, ResourceType::Workspaces, i18n("Workspaces"), QStringList() << "application/x-krita-workspace"));
0350     reg->add(new KisResourceLoader<KoSvgSymbolCollectionResource>(ResourceType::Symbols, ResourceType::Symbols, i18n("SVG symbol libraries"), QStringList() << "image/svg+xml"));
0351     reg->add(new KisResourceLoader<KisWindowLayoutResource>(ResourceType::WindowLayouts, ResourceType::WindowLayouts, i18n("Window layouts"), QStringList() << "application/x-krita-windowlayout"));
0352     reg->add(new KisResourceLoader<KisSessionResource>(ResourceType::Sessions, ResourceType::Sessions, i18n("Sessions"), QStringList() << "application/x-krita-session"));
0353     reg->add(new KisResourceLoader<KoGamutMask>(ResourceType::GamutMasks, ResourceType::GamutMasks, i18n("Gamut masks"), QStringList() << "application/x-krita-gamutmasks"));
0354 #if defined HAVE_SEEXPR
0355     reg->add(new KisResourceLoader<KisSeExprScript>(ResourceType::SeExprScripts, ResourceType::SeExprScripts, i18n("SeExpr Scripts"), QStringList() << "application/x-krita-seexpr-script"));
0356 #endif
0357     // XXX: this covers only individual styles, not the library itself!
0358     reg->add(new KisResourceLoader<KisPSDLayerStyle>(ResourceType::LayerStyles,
0359                                                      ResourceType::LayerStyles,
0360                                                      i18nc("Resource type name", "Layer styles"),
0361                                                      QStringList() << "application/x-photoshop-style"));
0362 
0363     reg->registerFixup(10, new KisBrushTypeMetaDataFixup());
0364 
0365 #ifndef Q_OS_ANDROID
0366     QString databaseLocation = KoResourcePaths::getAppDataLocation();
0367 #else
0368     // Sqlite doesn't support content URIs (obviously). So, we make database location unconfigurable on android.
0369     QString databaseLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
0370 #endif
0371 
0372     if (!KisResourceCacheDb::initialize(databaseLocation)) {
0373         QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita: Fatal error"), i18n("%1\n\nKrita will quit now.", KisResourceCacheDb::lastError()));
0374     }
0375 
0376     KisResourceLocator::LocatorError r = KisResourceLocator::instance()->initialize(KoResourcePaths::getApplicationRoot() + "/share/krita");
0377     connect(KisResourceLocator::instance(), SIGNAL(progressMessage(const QString&)), this, SLOT(setSplashScreenLoadingText(const QString&)));
0378     if (r != KisResourceLocator::LocatorError::Ok && qApp->inherits("KisApplication")) {
0379         QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita: Fatal error"), KisResourceLocator::instance()->errorMessages().join('\n') + i18n("\n\nKrita will quit now."));
0380         return false;
0381     }
0382     return true;
0383 }
0384 
0385 void KisApplication::loadPlugins()
0386 {
0387     //    qDebug() << "loadPlugins();";
0388 
0389     KoShapeRegistry* r = KoShapeRegistry::instance();
0390     r->add(new KisShapeSelectionFactory());
0391     KoColorSpaceRegistry::instance();
0392     KisActionRegistry::instance();
0393     KisFilterRegistry::instance();
0394     KisGeneratorRegistry::instance();
0395     KisPaintOpRegistry::instance();
0396     KoToolRegistry::instance();
0397     KoDockRegistry::instance();
0398     KisMetadataBackendRegistry::instance();
0399 }
0400 
0401 bool KisApplication::start(const KisApplicationArguments &args)
0402 {
0403     KisConfig cfg(false);
0404 
0405 #if defined(Q_OS_WIN)
0406 #ifdef ENV32BIT
0407 
0408     if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) {
0409         QMessageBox::information(qApp->activeWindow(),
0410                                  i18nc("@title:window", "Krita: Warning"),
0411                                  i18n("You are running a 32 bits build on a 64 bits Windows.\n"
0412                                       "This is not recommended.\n"
0413                                       "Please download and install the x64 build instead."));
0414         cfg.writeEntry("WarnedAbout32Bits", true);
0415 
0416     }
0417 #endif
0418 #endif
0419 
0420     QString opengl = cfg.canvasState();
0421     if (opengl == "OPENGL_NOT_TRIED" ) {
0422         cfg.setCanvasState("TRY_OPENGL");
0423     }
0424     else if (opengl != "OPENGL_SUCCESS" && opengl != "TRY_OPENGL") {
0425         cfg.setCanvasState("OPENGL_FAILED");
0426     }
0427 
0428     setSplashScreenLoadingText(i18n("Initializing Globals..."));
0429     processEvents();
0430     initializeGlobals(args);
0431 
0432     const bool doNewImage = args.doNewImage();
0433     const bool doTemplate = args.doTemplate();
0434     const bool exportAs = args.exportAs();
0435     const bool exportSequence = args.exportSequence();
0436     const QString exportFileName = args.exportFileName();
0437 
0438     d->batchRun = (exportAs || exportSequence || !exportFileName.isEmpty());
0439     const bool needsMainWindow = (!exportAs && !exportSequence);
0440     // only show the mainWindow when no command-line mode option is passed
0441     bool showmainWindow = (!exportAs && !exportSequence); // would be !batchRun;
0442 
0443     const bool showSplashScreen = !d->batchRun && qEnvironmentVariableIsEmpty("NOSPLASH");
0444     if (showSplashScreen && d->splashScreen) {
0445         d->splashScreen->show();
0446         d->splashScreen->repaint();
0447         processEvents();
0448     }
0449 
0450     KConfigGroup group(KSharedConfig::openConfig(), "theme");
0451     Digikam::ThemeManager themeManager;
0452     themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark"));
0453 
0454 
0455     ResetStarting resetStarting(d->splashScreen, args.filenames().count()); // remove the splash when done
0456     Q_UNUSED(resetStarting);
0457 
0458     // Make sure we can save resources and tags
0459     setSplashScreenLoadingText(i18n("Adding resource types..."));
0460     processEvents();
0461     addResourceTypes();
0462 
0463     // Load the plugins
0464     loadPlugins();
0465 
0466     // Load all resources
0467     if (!registerResources()) {
0468         return false;
0469     }
0470 
0471     KisPart *kisPart = KisPart::instance();
0472     if (needsMainWindow) {
0473         // show a mainWindow asap, if we want that
0474         setSplashScreenLoadingText(i18n("Loading Main Window..."));
0475         processEvents();
0476 
0477 
0478         bool sessionNeeded = true;
0479         auto sessionMode = cfg.sessionOnStartup();
0480 
0481         if (!args.session().isEmpty()) {
0482             sessionNeeded = !kisPart->restoreSession(args.session());
0483         } else if (sessionMode == KisConfig::SOS_ShowSessionManager) {
0484             showmainWindow = false;
0485             sessionNeeded = false;
0486             kisPart->showSessionManager();
0487         } else if (sessionMode == KisConfig::SOS_PreviousSession) {
0488             KConfigGroup sessionCfg = KSharedConfig::openConfig()->group("session");
0489             const QString &sessionName = sessionCfg.readEntry("previousSession");
0490 
0491             sessionNeeded = !kisPart->restoreSession(sessionName);
0492         }
0493 
0494         if (sessionNeeded) {
0495             kisPart->startBlankSession();
0496         }
0497 
0498         if (!args.windowLayout().isEmpty()) {
0499             KoResourceServer<KisWindowLayoutResource> * rserver = KisResourceServerProvider::instance()->windowLayoutServer();
0500             KisWindowLayoutResourceSP windowLayout = rserver->resource("", "", args.windowLayout());
0501             if (windowLayout) {
0502                 windowLayout->applyLayout();
0503             }
0504         }
0505 
0506         if (showmainWindow) {
0507             d->mainWindow = kisPart->currentMainwindow();
0508 
0509             if (!args.workspace().isEmpty()) {
0510                 KoResourceServer<KisWorkspaceResource> * rserver = KisResourceServerProvider::instance()->workspaceServer();
0511                 KisWorkspaceResourceSP workspace = rserver->resource("", "", args.workspace());
0512                 if (workspace) {
0513                     d->mainWindow->restoreWorkspace(workspace);
0514                 }
0515             }
0516 
0517             if (args.canvasOnly()) {
0518                 d->mainWindow->viewManager()->switchCanvasOnly(true);
0519             }
0520 
0521             if (args.fullScreen()) {
0522                 d->mainWindow->showFullScreen();
0523             }
0524         } else {
0525             d->mainWindow = kisPart->createMainWindow();
0526         }
0527     }
0528     short int numberOfOpenDocuments = 0; // number of documents open
0529 
0530     // Check for autosave files that can be restored, if we're not running a batchrun (test)
0531     if (!d->batchRun) {
0532         checkAutosaveFiles();
0533     }
0534 
0535     setSplashScreenLoadingText(QString()); // done loading, so clear out label
0536     processEvents();
0537 
0538     //configure the unit manager
0539     KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder());
0540     connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave.
0541     //the new syntax slot syntax allow to connect to a non q_object static method.
0542 
0543     // Create a new image, if needed
0544     if (doNewImage) {
0545         KisDocument *doc = args.createDocumentFromArguments();
0546         if (doc) {
0547             kisPart->addDocument(doc);
0548             d->mainWindow->addViewAndNotifyLoadingCompleted(doc);
0549         }
0550     }
0551 
0552     // Get the command line arguments which we have to parse
0553     int argsCount = args.filenames().count();
0554     if (argsCount > 0) {
0555         // Loop through arguments
0556         for (int argNumber = 0; argNumber < argsCount; argNumber++) {
0557             QString fileName = args.filenames().at(argNumber);
0558             // are we just trying to open a template?
0559             if (doTemplate) {
0560                 // called in mix with batch options? ignore and silently skip
0561                 if (d->batchRun) {
0562                     continue;
0563                 }
0564                 if (createNewDocFromTemplate(fileName, d->mainWindow)) {
0565                     ++numberOfOpenDocuments;
0566                 }
0567                 // now try to load
0568             }
0569             else {
0570                 if (exportAs) {
0571                     QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName, false);
0572                     if (outputMimetype == "application/octetstream") {
0573                         dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl;
0574                         return false;
0575                     }
0576 
0577                     KisDocument *doc = kisPart->createDocument();
0578                     doc->setFileBatchMode(d->batchRun);
0579                     bool result = doc->openPath(fileName);
0580 
0581                     if (!result) {
0582                         errKrita << "Could not load " << fileName << ":" << doc->errorMessage();
0583                         QTimer::singleShot(0, this, SLOT(quit()));
0584                         return false;
0585                     }
0586 
0587                     if (exportFileName.isEmpty()) {
0588                         errKrita << "Export destination is not specified for" << fileName << "Please specify export destination with --export-filename option";
0589                         QTimer::singleShot(0, this, SLOT(quit()));
0590                         return false;
0591                     }
0592 
0593                     qApp->processEvents(); // For vector layers to be updated
0594 
0595                     doc->setFileBatchMode(true);
0596                     doc->image()->waitForDone();
0597 
0598                     if (!doc->exportDocumentSync(exportFileName, outputMimetype.toLatin1())) {
0599                         errKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage();
0600                     }
0601                     QTimer::singleShot(0, this, SLOT(quit()));
0602                     return true;
0603                 }
0604                 else if (exportSequence) {
0605                     KisDocument *doc = kisPart->createDocument();
0606                     doc->setFileBatchMode(d->batchRun);
0607                     doc->openPath(fileName);
0608                     qApp->processEvents(); // For vector layers to be updated
0609                     
0610                     if (!doc->image()->animationInterface()->hasAnimation()) {
0611                         errKrita << "This file has no animation." << endl;
0612                         QTimer::singleShot(0, this, SLOT(quit()));
0613                         return false;
0614                     }
0615 
0616                     doc->setFileBatchMode(true);
0617                     int sequenceStart = 0;
0618 
0619                     KisAsyncAnimationFramesSaveDialog exporter(doc->image(),
0620                                                doc->image()->animationInterface()->fullClipRange(),
0621                                                exportFileName,
0622                                                sequenceStart,
0623                                                false,
0624                                                0);
0625                     exporter.setBatchMode(d->batchRun);
0626                     KisAsyncAnimationFramesSaveDialog::Result result =
0627                         exporter.regenerateRange(0);
0628                     if (result != KisAsyncAnimationFramesSaveDialog::RenderComplete) {
0629                         errKrita << i18n("Failed to render animation frames!") << endl;
0630                     }
0631                     QTimer::singleShot(0, this, SLOT(quit()));
0632                     return true;
0633                 }
0634                 else if (d->mainWindow) {
0635                     if (QFileInfo(fileName).fileName().endsWith(".bundle", Qt::CaseInsensitive)) {
0636                         d->mainWindow->installBundle(fileName);
0637                     }
0638                     else {
0639                         KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
0640 
0641                         if (d->mainWindow->openDocument(fileName, flags)) {
0642                             // Normal case, success
0643                             numberOfOpenDocuments++;
0644                         }
0645                     }
0646                 }
0647             }
0648         }
0649     }
0650 
0651     //add an image as file-layer
0652     if (!args.fileLayer().isEmpty()){
0653         if (d->mainWindow->viewManager()->image()){
0654             KisFileLayer *fileLayer = new KisFileLayer(d->mainWindow->viewManager()->image(), "",
0655                                                     args.fileLayer(), KisFileLayer::None,
0656                                                     d->mainWindow->viewManager()->image()->nextLayerName(i18n("File layer")), OPACITY_OPAQUE_U8);
0657             QFileInfo fi(fileLayer->path());
0658             if (fi.exists()){
0659                 KisNodeCommandsAdapter adapter(d->mainWindow->viewManager());
0660                 adapter.addNode(fileLayer, d->mainWindow->viewManager()->activeNode()->parent(),
0661                                     d->mainWindow->viewManager()->activeNode());
0662             }
0663             else{
0664                 QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita:Warning"),
0665                                             i18n("Cannot add %1 as a file layer: the file does not exist.", fileLayer->path()));
0666             }
0667         }
0668         else if (this->isRunning()){
0669             QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita:Warning"),
0670                                 i18n("Cannot add the file layer: no document is open.\n\n"
0671 "You can create a new document using the --new-image option, or you can open an existing file.\n\n"
0672 "If you instead want to add the file layer to a document in an already running instance of Krita, check the \"Allow only one instance of Krita\" checkbox in the settings (Settings -> General -> Window)."));
0673         }
0674         else {
0675             QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita: Warning"),
0676                                 i18n("Cannot add the file layer: no document is open.\n"
0677                                      "You can either create a new file using the --new-image option, or you can open an existing file."));
0678         }
0679     }
0680 
0681     // fixes BUG:369308  - Krita crashing on splash screen when loading.
0682     // trying to open a file before Krita has loaded can cause it to hang and crash
0683     if (d->splashScreen) {
0684         d->splashScreen->displayLinks(true);
0685         d->splashScreen->displayRecentFiles(true);
0686     }
0687 
0688     Q_FOREACH(const QByteArray &message, d->earlyRemoteArguments) {
0689         executeRemoteArguments(message, d->mainWindow);
0690     }
0691 
0692     KisUsageLogger::writeSysInfo(KisUsageLogger::screenInformation());
0693 
0694     // process File open event files
0695     if (!d->earlyFileOpenEvents.isEmpty()) {
0696         hideSplashScreen();
0697         Q_FOREACH(QString fileName, d->earlyFileOpenEvents) {
0698             d->mainWindow->openDocument(fileName, QFlags<KisMainWindow::OpenFlag>());
0699         }
0700     }
0701 
0702     // not calling this before since the program will quit there.
0703     return true;
0704 }
0705 
0706 KisApplication::~KisApplication()
0707 {
0708     if (!isRunning()) {
0709         KisResourceCacheDb::deleteTemporaryResources();
0710     }
0711 }
0712 
0713 void KisApplication::setSplashScreen(QWidget *splashScreen)
0714 {
0715     d->splashScreen = qobject_cast<KisSplashScreen*>(splashScreen);
0716 }
0717 
0718 void KisApplication::setSplashScreenLoadingText(const QString &textToLoad)
0719 {
0720     if (d->splashScreen) {
0721         d->splashScreen->setLoadingText(textToLoad);
0722         d->splashScreen->repaint();
0723     }
0724 }
0725 
0726 void KisApplication::hideSplashScreen()
0727 {
0728     if (d->splashScreen) {
0729         // hide the splashscreen to see the dialog
0730         d->splashScreen->hide();
0731     }
0732 }
0733 
0734 
0735 bool KisApplication::notify(QObject *receiver, QEvent *event)
0736 {
0737     try {
0738         bool result = true;
0739 
0740         /**
0741          * KisApplication::notify() is called for every event loop processed in
0742          * any thread, so we need to make sure our counters and postponed events
0743          * queues are stored in a per-thread way.
0744          */
0745         AppRecursionInfo &info = s_recursionInfo->localData();
0746 
0747         {
0748             // QApplication::notify() can throw, so use RAII for counters
0749             AppRecursionGuard guard(&info);
0750 
0751             if (event->type() == KisSynchronizedConnectionBase::eventType()) {
0752 
0753                 if (info.eventRecursionCount > 1) {
0754                     KisSynchronizedConnectionEvent *typedEvent = static_cast<KisSynchronizedConnectionEvent*>(event);
0755                     KIS_SAFE_ASSERT_RECOVER_NOOP(typedEvent->destination == receiver);
0756 
0757                     info.postponedSynchronizationEvents.emplace(KisSynchronizedConnectionEvent(*typedEvent));
0758                 } else {
0759                     result = QApplication::notify(receiver, event);
0760                 }
0761             } else {
0762                 result = QApplication::notify(receiver, event);
0763             }
0764         }
0765 
0766         if (!info.eventRecursionCount) {
0767             processPostponedSynchronizationEvents();
0768 
0769         }
0770 
0771         return result;
0772 
0773     } catch (std::exception &e) {
0774         qWarning("Error %s sending event %i to object %s",
0775                  e.what(), event->type(), qPrintable(receiver->objectName()));
0776     } catch (...) {
0777         qWarning("Error <unknown> sending event %i to object %s",
0778                  event->type(), qPrintable(receiver->objectName()));
0779     }
0780     return false;
0781 }
0782 
0783 void KisApplication::processPostponedSynchronizationEvents()
0784 {
0785     AppRecursionInfo &info = s_recursionInfo->localData();
0786 
0787     while (!info.postponedSynchronizationEvents.empty()) {
0788         // QApplication::notify() can throw, so use RAII for counters
0789         AppRecursionGuard guard(&info);
0790 
0791         /// We must pop event from the queue **before** we call
0792         /// QApplication::notify(), because it can throw!
0793         KisSynchronizedConnectionEvent typedEvent = info.postponedSynchronizationEvents.front();
0794         info.postponedSynchronizationEvents.pop();
0795 
0796         if (!typedEvent.destination) {
0797             qWarning() << "WARNING: the destination object of KisSynchronizedConnection has been destroyed during postponed delivery";
0798             continue;
0799         }
0800 
0801         QApplication::notify(typedEvent.destination, &typedEvent);
0802     }
0803 }
0804 
0805 void KisApplication::executeRemoteArguments(QByteArray message, KisMainWindow *mainWindow)
0806 {
0807     KisApplicationArguments args = KisApplicationArguments::deserialize(message);
0808     const bool doTemplate = args.doTemplate();
0809     const bool doNewImage = args.doNewImage();
0810     const int argsCount = args.filenames().count();
0811     bool documentCreated = false;
0812 
0813     // Create a new image, if needed
0814     if (doNewImage) {
0815         KisDocument *doc = args.createDocumentFromArguments();
0816         if (doc) {
0817             KisPart::instance()->addDocument(doc);
0818             d->mainWindow->addViewAndNotifyLoadingCompleted(doc);
0819         }
0820     }
0821     if (argsCount > 0) {
0822         // Loop through arguments
0823         for (int argNumber = 0; argNumber < argsCount; ++argNumber) {
0824             QString filename = args.filenames().at(argNumber);
0825             // are we just trying to open a template?
0826             if (doTemplate) {
0827                 documentCreated |= createNewDocFromTemplate(filename, mainWindow);
0828             }
0829             else if (QFile(filename).exists()) {
0830                 KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
0831                 documentCreated |= mainWindow->openDocument(filename, flags);
0832             }
0833         }
0834     }
0835 
0836     //add an image as file-layer if called in another process and singleApplication is enabled
0837     if (!args.fileLayer().isEmpty()){
0838         if (argsCount > 0  && !documentCreated){
0839             //arg was passed but document was not created so don't add the file layer.
0840             QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"),
0841                                             i18n("Couldn't open file %1",args.filenames().at(argsCount - 1)));
0842         }
0843         else if (mainWindow->viewManager()->image()){
0844             KisFileLayer *fileLayer = new KisFileLayer(mainWindow->viewManager()->image(), "",
0845                                                     args.fileLayer(), KisFileLayer::None,
0846                                                     mainWindow->viewManager()->image()->nextLayerName(i18n("File layer")), OPACITY_OPAQUE_U8);
0847             QFileInfo fi(fileLayer->path());
0848             if (fi.exists()){
0849                 KisNodeCommandsAdapter adapter(d->mainWindow->viewManager());
0850                 adapter.addNode(fileLayer, d->mainWindow->viewManager()->activeNode()->parent(),
0851                                     d->mainWindow->viewManager()->activeNode());
0852             }
0853             else{
0854                 QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"),
0855                                             i18n("Cannot add %1 as a file layer: the file does not exist.", fileLayer->path()));
0856             }
0857         }
0858         else {
0859             QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"),
0860                                             i18n("Cannot add the file layer: no document is open."));
0861         }
0862     }
0863 }
0864 
0865 
0866 void KisApplication::remoteArguments(QByteArray message, QObject *socket)
0867 {
0868     Q_UNUSED(socket);
0869 
0870     // check if we have any mainwindow
0871     KisMainWindow *mw = qobject_cast<KisMainWindow*>(qApp->activeWindow());
0872 
0873     if (!mw && KisPart::instance()->mainWindows().size() > 0) {
0874         mw = KisPart::instance()->mainWindows().first();
0875     }
0876 
0877     if (!mw) {
0878         d->earlyRemoteArguments << message;
0879         return;
0880     }
0881     executeRemoteArguments(message, mw);
0882 }
0883 
0884 void KisApplication::fileOpenRequested(const QString &url)
0885 {
0886     if (!d->mainWindow) {
0887         d->earlyFileOpenEvents.append(url);
0888         return;
0889     }
0890 
0891     KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
0892     d->mainWindow->openDocument(url, flags);
0893 }
0894 
0895 
0896 void KisApplication::checkAutosaveFiles()
0897 {
0898     if (d->batchRun) return;
0899 
0900     QDir dir = KisAutoSaveRecoveryDialog::autoSaveLocation();
0901 
0902     // Check for autosave files from a previous run. There can be several, and
0903     // we want to offer a restore for every one. Including a nice thumbnail!
0904 
0905     // Hidden autosave files
0906     QStringList filters = QStringList() << QString(".krita-*-*-autosave.kra");
0907 
0908     // all autosave files for our application
0909     QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden);
0910 
0911     // Visible autosave files
0912     filters = QStringList() << QString("krita-*-*-autosave.kra");
0913     autosaveFiles += dir.entryList(filters, QDir::Files);
0914 
0915     // Allow the user to make their selection
0916     if (autosaveFiles.size() > 0) {
0917         if (d->splashScreen) {
0918             // hide the splashscreen to see the dialog
0919             d->splashScreen->hide();
0920         }
0921         d->autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow());
0922         QDialog::DialogCode result = (QDialog::DialogCode) d->autosaveDialog->exec();
0923 
0924         if (result == QDialog::Accepted) {
0925             QStringList filesToRecover = d->autosaveDialog->recoverableFiles();
0926             Q_FOREACH (const QString &autosaveFile, autosaveFiles) {
0927                 if (!filesToRecover.contains(autosaveFile)) {
0928                     KisUsageLogger::log(QString("Removing autosave file %1").arg(dir.absolutePath() + "/" + autosaveFile));
0929                     QFile::remove(dir.absolutePath() + "/" + autosaveFile);
0930                 }
0931             }
0932             autosaveFiles = filesToRecover;
0933         } else {
0934             autosaveFiles.clear();
0935         }
0936 
0937         if (autosaveFiles.size() > 0) {
0938             QList<QString> autosavePaths;
0939             Q_FOREACH (const QString &autoSaveFile, autosaveFiles) {
0940                 const QString path = dir.absolutePath() + QLatin1Char('/') + autoSaveFile;
0941                 autosavePaths << path;
0942             }
0943             if (d->mainWindow) {
0944                 Q_FOREACH (const QString &path, autosavePaths) {
0945                     KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
0946                     d->mainWindow->openDocument(path, flags | KisMainWindow::RecoveryFile);
0947                 }
0948             }
0949         }
0950         // cleanup
0951         delete d->autosaveDialog;
0952         d->autosaveDialog = nullptr;
0953     }
0954 }
0955 
0956 bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow)
0957 {
0958     QString templatePath;
0959 
0960     if (QFile::exists(fileName)) {
0961         templatePath = fileName;
0962         dbgUI << "using full path...";
0963     }
0964     else {
0965         QString desktopName(fileName);
0966         const QString templatesResourcePath =  QStringLiteral("templates/");
0967 
0968         QStringList paths = KoResourcePaths::findAllAssets("data", templatesResourcePath + "*/" + desktopName);
0969         if (paths.isEmpty()) {
0970             paths = KoResourcePaths::findAllAssets("data", templatesResourcePath + desktopName);
0971         }
0972 
0973         if (paths.isEmpty()) {
0974             QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"),
0975                                   i18n("No template found for: %1", desktopName));
0976         } else if (paths.count() > 1) {
0977             QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"),
0978                                   i18n("Too many templates found for: %1", desktopName));
0979         } else {
0980             templatePath = paths.at(0);
0981         }
0982     }
0983 
0984     if (!templatePath.isEmpty()) {
0985         KDesktopFile templateInfo(templatePath);
0986 
0987         KisMainWindow::OpenFlags batchFlags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
0988         if (mainWindow->openDocument(templatePath, KisMainWindow::Import | batchFlags)) {
0989             dbgUI << "Template loaded...";
0990             return true;
0991         }
0992         else {
0993             QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"),
0994                                   i18n("Template %1 failed to load.", fileName));
0995         }
0996     }
0997 
0998     return false;
0999 }
1000 
1001 void KisApplication::resetConfig()
1002 {
1003     KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread());
1004 
1005     KSharedConfigPtr config =  KSharedConfig::openConfig();
1006     config->markAsClean();
1007     
1008     // find user settings file
1009     const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
1010     QString kritarcPath = configPath + QStringLiteral("/kritarc");
1011     
1012     QFile kritarcFile(kritarcPath);
1013     
1014     if (kritarcFile.exists()) {
1015         if (kritarcFile.open(QFile::ReadWrite)) {
1016             QString backupKritarcPath = kritarcPath + QStringLiteral(".backup");
1017     
1018             QFile backupKritarcFile(backupKritarcPath);
1019     
1020             if (backupKritarcFile.exists()) {
1021                 backupKritarcFile.remove();
1022             }
1023 
1024             QMessageBox::information(qApp->activeWindow(),
1025                                  i18nc("@title:window", "Krita"),
1026                                  i18n("Krita configurations reset!\n\n"
1027                                       "Backup file was created at: %1\n\n"
1028                                       "Restart Krita for changes to take effect.",
1029                                       backupKritarcPath),
1030                                  QMessageBox::Ok, QMessageBox::Ok);
1031 
1032             // clear file
1033             kritarcFile.rename(backupKritarcPath);
1034 
1035             kritarcFile.close();
1036         }
1037         else {
1038             QMessageBox::warning(qApp->activeWindow(),
1039                                  i18nc("@title:window", "Krita"),
1040                                  i18n("Failed to clear %1\n\n"
1041                                       "Please make sure no other program is using the file and try again.",
1042                                       kritarcPath),
1043                                  QMessageBox::Ok, QMessageBox::Ok);
1044         }
1045     }
1046 
1047     // reload from disk; with the user file settings cleared,
1048     // this should load any default configuration files shipping with the program
1049     config->reparseConfiguration();
1050     config->sync();
1051 
1052     // Restore to default workspace
1053     KConfigGroup cfg = KSharedConfig::openConfig()->group("MainWindow");
1054 
1055     QString currentWorkspace = cfg.readEntry<QString>("CurrentWorkspace", "Default");
1056     KoResourceServer<KisWorkspaceResource> * rserver = KisResourceServerProvider::instance()->workspaceServer();
1057     KisWorkspaceResourceSP workspace = rserver->resource("", "", currentWorkspace);
1058 
1059     if (workspace) {
1060         d->mainWindow->restoreWorkspace(workspace);
1061     }
1062 }
1063 
1064 void KisApplication::askResetConfig()
1065 {
1066     bool ok = QMessageBox::question(qApp->activeWindow(),
1067                                     i18nc("@title:window", "Krita"),
1068                                     i18n("Do you want to clear the settings file?"),
1069                                     QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes;
1070     if (ok) {
1071         resetConfig();
1072     }
1073 }