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 }