File indexing completed on 2024-04-28 05:49:27
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org> 0003 SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include "kateapp.h" 0009 0010 #include "katemainwindow.h" 0011 #include "kateviewmanager.h" 0012 0013 #include <kcoreaddons_version.h> 0014 0015 #include <KAboutData> 0016 #include <KConfigGui> 0017 #include <KCrash> 0018 #include <KLazyLocalizedString> 0019 #include <KLocalizedString> 0020 #include <KMessageBox> 0021 #include <KNetworkMounts> 0022 #include <KSharedConfig> 0023 0024 // signal handler for SIGINT & SIGTERM 0025 #ifdef Q_OS_UNIX 0026 #include <KSignalHandler> 0027 #include <signal.h> 0028 #include <unistd.h> 0029 #endif 0030 0031 // X11 startup handling 0032 #define HAVE_X11 __has_include(<KStartupInfo>) 0033 #if HAVE_X11 0034 #include <KStartupInfo> 0035 #endif 0036 0037 #ifdef WITH_KUSERFEEDBACK 0038 #include <KUserFeedback/ApplicationVersionSource> 0039 #include <KUserFeedback/PlatformInfoSource> 0040 #include <KUserFeedback/QtVersionSource> 0041 #include <KUserFeedback/ScreenInfoSource> 0042 #include <KUserFeedback/StartCountSource> 0043 #include <KUserFeedback/UsageTimeSource> 0044 #endif 0045 0046 #include <QApplication> 0047 #include <QCommandLineParser> 0048 #include <QDBusConnection> 0049 #include <QFileInfo> 0050 #include <QFileOpenEvent> 0051 #include <QJsonArray> 0052 #include <QJsonDocument> 0053 #include <QLoggingCategory> 0054 #include <QRegularExpression> 0055 #include <QStringDecoder> 0056 #include <QTimer> 0057 #include <QUrlQuery> 0058 0059 #include <urlinfo.h> 0060 0061 #ifndef Q_OS_WIN 0062 #include <unistd.h> 0063 #ifndef Q_OS_HAIKU 0064 #include <libintl.h> 0065 #endif 0066 #endif 0067 0068 #include <iostream> 0069 0070 #ifdef Q_OS_WIN 0071 #include <windows.h> 0072 #endif 0073 0074 #ifdef HAVE_CTERMID 0075 #include <fcntl.h> 0076 #include <sys/stat.h> 0077 #include <sys/types.h> 0078 #include <termios.h> 0079 #include <unistd.h> 0080 #endif 0081 0082 #if HAVE_DAEMON 0083 #include <unistd.h> 0084 #endif 0085 0086 // remember if we were started inside a terminal 0087 static bool insideTerminal = false; 0088 0089 /** 0090 * singleton instance pointer 0091 */ 0092 static KateApp *appSelf = Q_NULLPTR; 0093 0094 Q_LOGGING_CATEGORY(LOG_KATE, "kate", QtWarningMsg) 0095 0096 void KateApp::initPreApplicationCreation(bool detach) 0097 { 0098 #if !defined(Q_OS_WIN) && !defined(Q_OS_HAIKU) 0099 // Prohibit using sudo or kdesu (but allow using the root user directly) 0100 if (getuid() == 0) { 0101 setlocale(LC_ALL, ""); 0102 bindtextdomain("kate", KDE_INSTALL_FULL_LOCALEDIR); 0103 if (!qEnvironmentVariableIsEmpty("SUDO_USER")) { 0104 auto message = kli18n( 0105 "Running this editor with sudo can cause bugs and expose you to security vulnerabilities. " 0106 "Instead use this editor normally and you will be prompted for elevated privileges when " 0107 "saving documents if needed."); 0108 std::cout << dgettext("kate", message.untranslatedText()) << std::endl; 0109 exit(EXIT_FAILURE); 0110 } else if (!qEnvironmentVariableIsEmpty("KDESU_USER")) { 0111 auto message = kli18n( 0112 "Running this editor with kdesu can cause bugs and expose you to security vulnerabilities. " 0113 "Instead use this editor normally and you will be prompted for elevated privileges when " 0114 "saving documents if needed."); 0115 std::cout << dgettext("kate", message.untranslatedText()) << std::endl; 0116 exit(EXIT_FAILURE); 0117 } 0118 } 0119 #endif 0120 0121 /** 0122 * enable dark mode for title bar on Windows 0123 */ 0124 #if defined(Q_OS_WIN) 0125 if (!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { 0126 qputenv("QT_QPA_PLATFORM", "windows:darkmode=1"); 0127 } 0128 #endif 0129 0130 /** 0131 * allow fractional scaling 0132 * we only activate this on Windows, it seems to creates problems on unices 0133 * (and there the fractional scaling with the QT_... env vars as set by KScreen works) 0134 * see bug 416078 0135 * 0136 * we switched to Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor because of font rendering issues 0137 * we follow what Krita does here, see https://invent.kde.org/graphics/krita/-/blob/master/krita/main.cc 0138 * we raise the Qt requirement to 5.15 as it seems some patches went in after 5.14 that are needed 0139 * see Krita comments, too 0140 */ 0141 #if defined(Q_OS_WIN) 0142 QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); 0143 #endif 0144 0145 #ifdef Q_OS_WIN 0146 // Enable on windows to see output in console 0147 if (AttachConsole(ATTACH_PARENT_PROCESS)) { 0148 // we are inside a terminal 0149 insideTerminal = true; 0150 0151 // don't enable output if we shall detach, to avoid terminal pollution 0152 if (!detach) { 0153 if (fileno(stdout) < 0) 0154 freopen("CON", "w", stdout); 0155 if (fileno(stderr) < 0) 0156 freopen("CON", "w", stderr); 0157 } 0158 } 0159 #endif 0160 0161 #ifdef HAVE_CTERMID 0162 /** 0163 * https://stackoverflow.com/questions/1312922/detect-if-stdin-is-a-terminal-or-pipe-in-c-c-qt 0164 */ 0165 char tty[L_ctermid + 1] = {0}; 0166 ctermid(tty); 0167 if (int fd = ::open(tty, O_RDONLY); fd >= 0) { 0168 insideTerminal = true; 0169 ::close(fd); 0170 } 0171 #endif 0172 0173 // blacklist macOS, crashes on start with this 0174 // TODO: investigate why 0175 #if !defined(Q_OS_MACOS) && defined(HAVE_DAEMON) 0176 if (detach) { 0177 // just try it, if it doesn't work we just continue in the foreground 0178 const int ret = daemon(1, 0 /* close in and outputs to avoid pollution of shell */); 0179 (void)ret; 0180 } 0181 #endif 0182 } 0183 0184 KateApp::KateApp(const QCommandLineParser &args, const ApplicationMode mode, const QString &sessionsDir) 0185 : m_args(args) 0186 , m_mode(mode) 0187 , m_wrapper(appSelf = this) 0188 , m_adaptor(this) 0189 , m_docManager(this) 0190 , m_sessionManager(this, sessionsDir) 0191 , m_stashManager(this) 0192 , m_lastActivationChange(QDateTime::currentMSecsSinceEpoch()) 0193 { 0194 /** 0195 * For Windows and macOS: use Breeze if available 0196 * Of all tested styles that works the best for us 0197 */ 0198 #if defined(Q_OS_MACOS) || defined(Q_OS_WIN) 0199 QApplication::setStyle(QStringLiteral("breeze")); 0200 #endif 0201 0202 /** 0203 * Enable crash handling through KCrash. 0204 */ 0205 KCrash::initialize(); 0206 0207 /** 0208 * re-route some signals to application wrapper 0209 */ 0210 connect(&m_docManager, &KateDocManager::documentCreated, &m_wrapper, &KTextEditor::Application::documentCreated); 0211 connect(&m_docManager, &KateDocManager::documentWillBeDeleted, &m_wrapper, &KTextEditor::Application::documentWillBeDeleted); 0212 connect(&m_docManager, &KateDocManager::documentDeleted, &m_wrapper, &KTextEditor::Application::documentDeleted); 0213 0214 /** 0215 * handle mac os x like file open request via event filter 0216 */ 0217 qApp->installEventFilter(this); 0218 0219 #ifdef WITH_KUSERFEEDBACK 0220 /** 0221 * defaults, inspired by plasma 0222 * important: choose between kate and kwrite mode here to submit the right product id 0223 */ 0224 m_userFeedbackProvider.setProductIdentifier(isKate() ? QStringLiteral("org.kde.kate") : QStringLiteral("org.kde.kwrite")); 0225 m_userFeedbackProvider.setFeedbackServer(QUrl(QStringLiteral("https://telemetry.kde.org/"))); 0226 m_userFeedbackProvider.setSubmissionInterval(7); 0227 m_userFeedbackProvider.setApplicationStartsUntilEncouragement(5); 0228 m_userFeedbackProvider.setEncouragementDelay(30); 0229 0230 /** 0231 * add some feedback providers 0232 */ 0233 0234 // software version info 0235 m_userFeedbackProvider.addDataSource(new KUserFeedback::ApplicationVersionSource); 0236 m_userFeedbackProvider.addDataSource(new KUserFeedback::QtVersionSource); 0237 0238 // info about the machine 0239 m_userFeedbackProvider.addDataSource(new KUserFeedback::PlatformInfoSource); 0240 m_userFeedbackProvider.addDataSource(new KUserFeedback::ScreenInfoSource); 0241 0242 // usage info 0243 m_userFeedbackProvider.addDataSource(new KUserFeedback::StartCountSource); 0244 m_userFeedbackProvider.addDataSource(new KUserFeedback::UsageTimeSource); 0245 #endif 0246 } 0247 0248 KateApp::~KateApp() 0249 { 0250 // we want no auto saving during application closing, we handle that explicitly 0251 KateSessionManager::AutoSaveBlocker blocker(sessionManager()); 0252 0253 /** 0254 * unregister from dbus before we get unusable... 0255 */ 0256 if (QDBusConnection::sessionBus().interface()) { 0257 m_adaptor.emitExiting(); 0258 QDBusConnection::sessionBus().unregisterObject(QStringLiteral("/MainApplication")); 0259 } 0260 0261 /** 0262 * delete all main windows before the document manager & co. die 0263 */ 0264 while (!m_mainWindows.isEmpty()) { 0265 // mainwindow itself calls KateApp::removeMainWindow(this) 0266 delete m_mainWindows[0]; 0267 } 0268 } 0269 0270 KateApp *KateApp::self() 0271 { 0272 return appSelf; 0273 } 0274 0275 void KateApp::fillAuthorsAndCredits(KAboutData &aboutData) 0276 { 0277 aboutData.addAuthor(i18n("Christoph Cullmann"), i18n("Maintainer"), QStringLiteral("cullmann@kde.org"), QStringLiteral("https://cullmann.io")); 0278 aboutData.addAuthor(i18n("Dominik Haumann"), i18n("Core Developer"), QStringLiteral("dhaumann@kde.org")); 0279 aboutData.addAuthor(i18n("Sven Brauch"), i18n("Developer"), QStringLiteral("mail@svenbrauch.de")); 0280 aboutData.addAuthor(i18n("Kåre Särs"), i18n("Developer"), QStringLiteral("kare.sars@iki.fi")); 0281 aboutData.addAuthor(i18n("Waqar Ahmed"), i18n("Core Developer"), QStringLiteral("waqar.17a@gmail.com")); 0282 aboutData.addAuthor(i18n("Anders Lund"), i18n("Core Developer"), QStringLiteral("anders@alweb.dk"), QStringLiteral("https://www.alweb.dk")); 0283 aboutData.addAuthor(i18n("Joseph Wenninger"), 0284 i18n("Core Developer"), 0285 QStringLiteral("jowenn@kde.org"), 0286 QStringLiteral("http://stud3.tuwien.ac.at/~e9925371")); 0287 aboutData.addAuthor(i18n("Hamish Rodda"), i18n("Core Developer"), QStringLiteral("rodda@kde.org")); 0288 aboutData.addAuthor(i18n("Alexander Neundorf"), i18n("Developer"), QStringLiteral("neundorf@kde.org")); 0289 aboutData.addAuthor(i18n("Waldo Bastian"), i18n("The cool buffersystem"), QStringLiteral("bastian@kde.org")); 0290 aboutData.addAuthor(i18n("Charles Samuels"), i18n("The Editing Commands"), QStringLiteral("charles@kde.org")); 0291 aboutData.addAuthor(i18n("Matt Newell"), i18n("Testing, ..."), QStringLiteral("newellm@proaxis.com")); 0292 aboutData.addAuthor(i18n("Michael Bartl"), i18n("Former Core Developer"), QStringLiteral("michael.bartl1@chello.at")); 0293 aboutData.addAuthor(i18n("Michael McCallum"), i18n("Core Developer"), QStringLiteral("gholam@xtra.co.nz")); 0294 aboutData.addAuthor(i18n("Jochen Wilhemly"), i18n("KWrite Author"), QStringLiteral("digisnap@cs.tu-berlin.de")); 0295 aboutData.addAuthor(i18n("Michael Koch"), i18n("KWrite port to KParts"), QStringLiteral("koch@kde.org")); 0296 aboutData.addAuthor(i18n("Christian Gebauer"), QString(), QStringLiteral("gebauer@kde.org")); 0297 aboutData.addAuthor(i18n("Simon Hausmann"), QString(), QStringLiteral("hausmann@kde.org")); 0298 aboutData.addAuthor(i18n("Glen Parker"), i18n("KWrite Undo History, Kspell integration"), QStringLiteral("glenebob@nwlink.com")); 0299 aboutData.addAuthor(i18n("Scott Manson"), i18n("KWrite XML Syntax highlighting support"), QStringLiteral("sdmanson@alltel.net")); 0300 aboutData.addAuthor(i18n("John Firebaugh"), i18n("Patches and more"), QStringLiteral("jfirebaugh@kde.org")); 0301 aboutData.addAuthor(i18n("Pablo Martín"), 0302 i18n("Python Plugin Developer"), 0303 QStringLiteral("goinnn@gmail.com"), 0304 QStringLiteral("https://github.com/goinnn/")); 0305 aboutData.addAuthor(i18n("Gerald Senarclens de Grancy"), 0306 i18n("QA and Scripting"), 0307 QStringLiteral("oss@senarclens.eu"), 0308 QStringLiteral("http://find-santa.eu/")); 0309 0310 aboutData.addCredit(i18n("Tyson Tan"), 0311 i18n("Designer of Kate's mascot 'Kate the Cyber Woodpecker'"), 0312 QString(), 0313 QStringLiteral("https://www.tysontan.com/")); 0314 aboutData.addCredit(i18n("Matteo Merli"), i18n("Highlighting for RPM Spec-Files, Perl, Diff and more"), QStringLiteral("merlim@libero.it")); 0315 aboutData.addCredit(i18n("Rocky Scaletta"), i18n("Highlighting for VHDL"), QStringLiteral("rocky@purdue.edu")); 0316 aboutData.addCredit(i18n("Yury Lebedev"), i18n("Highlighting for SQL")); 0317 aboutData.addCredit(i18n("Chris Ross"), i18n("Highlighting for Ferite")); 0318 aboutData.addCredit(i18n("Nick Roux"), i18n("Highlighting for ILERPG")); 0319 aboutData.addCredit(i18n("Carsten Niehaus"), i18n("Highlighting for LaTeX")); 0320 aboutData.addCredit(i18n("Per Wigren"), i18n("Highlighting for Makefiles, Python")); 0321 aboutData.addCredit(i18n("Jan Fritz"), i18n("Highlighting for Python")); 0322 aboutData.addCredit(i18n("Daniel Naber")); 0323 aboutData.addCredit(i18n("Roland Pabel"), i18n("Highlighting for Scheme")); 0324 aboutData.addCredit(i18n("Cristi Dumitrescu"), i18n("PHP Keyword/Datatype list")); 0325 aboutData.addCredit(i18n("Carsten Pfeiffer"), i18n("Very nice help")); 0326 aboutData.addCredit(i18n("All people who have contributed and I have forgotten to mention")); 0327 } 0328 0329 bool KateApp::init() 0330 { 0331 // we want no auto saving during application startup, we handle that explicitly 0332 KateSessionManager::AutoSaveBlocker blocker(sessionManager()); 0333 0334 // set KATE_PID for use in child processes 0335 if (isKate()) { 0336 qputenv("KATE_PID", QStringLiteral("%1").arg(QCoreApplication::applicationPid()).toLatin1().constData()); 0337 } 0338 0339 #ifdef Q_OS_UNIX 0340 /** 0341 * Set up signal handler for SIGINT and SIGTERM 0342 */ 0343 KSignalHandler::self()->watchSignal(SIGINT); 0344 KSignalHandler::self()->watchSignal(SIGTERM); 0345 connect(KSignalHandler::self(), &KSignalHandler::signalReceived, this, [this](int signal) { 0346 if (signal == SIGINT || signal == SIGTERM) { 0347 printf("Shutting down...\n"); 0348 quit(); 0349 } 0350 }); 0351 #endif 0352 0353 // handle restore different 0354 if (qApp->isSessionRestored()) { 0355 restoreKate(); 0356 } else { 0357 // let us handle our command line args and co ;) 0358 // we can exit here if session chooser decides 0359 if (!startupKate()) { 0360 // session chooser told to exit kate 0361 return false; 0362 } 0363 } 0364 0365 return true; 0366 } 0367 0368 void KateApp::restoreKate() 0369 { 0370 // we want no auto saving during application startup, we handle that explicitly 0371 KateSessionManager::AutoSaveBlocker blocker(sessionManager()); 0372 0373 KConfig *sessionConfig = KConfigGui::sessionConfig(); 0374 0375 // activate again correct session!!! 0376 QString lastSession(sessionConfig->group(QStringLiteral("General")).readEntry("Last Session", QString())); 0377 sessionManager()->activateSession(lastSession, false, false); 0378 0379 // plugins 0380 KateApp::self()->pluginManager()->loadConfig(sessionConfig); 0381 0382 // restore the files we need 0383 m_docManager.restoreDocumentList(sessionConfig); 0384 0385 // restore all windows ;) 0386 for (int n = 1; KMainWindow::canBeRestored(n); n++) { 0387 newMainWindow(sessionConfig, QString::number(n)); 0388 } 0389 0390 // oh, no mainwindow, create one, should not happen, but make sure ;) 0391 if (mainWindowsCount() == 0) { 0392 newMainWindow(); 0393 } 0394 } 0395 0396 bool KateApp::startupKate() 0397 { 0398 // we want no auto saving during application startup, we handle that explicitly 0399 KateSessionManager::AutoSaveBlocker blocker(sessionManager()); 0400 0401 // KWrite is session less 0402 if (isKWrite()) { 0403 sessionManager()->activateAnonymousSession(); 0404 } else { 0405 // user specified session to open 0406 if (m_args.isSet(QStringLiteral("start"))) { 0407 sessionManager()->activateSession(m_args.value(QStringLiteral("start")), false); 0408 } else if (m_args.isSet(QStringLiteral("startanon"))) { 0409 sessionManager()->activateAnonymousSession(); 0410 } else if (!m_args.isSet(QStringLiteral("stdin")) && (m_args.positionalArguments().count() == 0)) { // only start session if no files specified 0411 // let the user choose session if possible 0412 if (!sessionManager()->chooseSession()) { 0413 #if HAVE_X11 0414 // we will exit kate now, notify the rest of the world we are done 0415 KStartupInfo::appStarted(); 0416 #endif 0417 0418 return false; 0419 } 0420 } else { 0421 sessionManager()->activateAnonymousSession(); 0422 } 0423 } 0424 0425 // oh, no mainwindow, create one, should not happen, but make sure ;) 0426 if (mainWindowsCount() == 0) { 0427 newMainWindow(); 0428 } 0429 0430 bool tempfileSet = m_args.isSet(QStringLiteral("tempfile")); 0431 0432 KTextEditor::Document *doc = nullptr; 0433 const QString codec_name = m_args.isSet(QStringLiteral("encoding")) ? m_args.value(QStringLiteral("encoding")) : QString(); 0434 0435 const auto args = m_args.positionalArguments(); 0436 0437 for (const auto &positionalArgument : args) { 0438 UrlInfo info(positionalArgument); 0439 0440 // this file is no local dir, open it, else warn 0441 bool noDir = !info.url.isLocalFile() 0442 || KNetworkMounts::self()->isOptionEnabledForPath(info.url.toLocalFile(), KNetworkMounts::LowSideEffectsOptimizations) 0443 || !QFileInfo(info.url.toLocalFile()).isDir(); 0444 0445 if (noDir) { 0446 if (!info.cursor.isValid()) { 0447 if (hasCursorInArgs()) { 0448 info.cursor = cursorFromArgs(); 0449 } else if (info.url.hasQuery()) { 0450 info.cursor = cursorFromQueryString(info.url); 0451 } 0452 } 0453 doc = openDocUrl(info.url, codec_name, tempfileSet, /*activateView=*/false, info.cursor); 0454 } else if (!KateApp::self()->pluginManager()->plugin(QStringLiteral("kateprojectplugin"))) { 0455 KMessageBox::error(activeKateMainWindow(), i18n("Folders can only be opened when the projects plugin is enabled")); 0456 } 0457 } 0458 0459 // handle stdin input 0460 if (m_args.isSet(QStringLiteral("stdin"))) { 0461 QFile input; 0462 input.open(stdin, QIODevice::ReadOnly); 0463 auto decoder = QStringDecoder(codec_name.toUtf8().constData()); 0464 QString text = decoder.isValid() ? decoder.decode(input.readAll()) : QString::fromLocal8Bit(input.readAll()); 0465 0466 // normalize line endings, to e.g. catch issues with \r\n on Windows 0467 text.replace(QRegularExpression(QStringLiteral("\r\n?")), QStringLiteral("\n")); 0468 0469 openInput(text, codec_name); 0470 } else if (doc) { 0471 activeKateMainWindow()->viewManager()->activateView(doc); 0472 } 0473 0474 return true; 0475 } 0476 0477 void KateApp::shutdownKate(KateMainWindow *win) 0478 { 0479 // we want no auto saving during application closing, we handle that explicitly 0480 KateSessionManager::AutoSaveBlocker blocker(sessionManager()); 0481 0482 if (!win->queryClose_internal()) { 0483 return; 0484 } 0485 0486 sessionManager()->saveActiveSession(true); 0487 stashManager()->stashDocuments(sessionManager()->activeSession()->config(), documentManager()->documentList()); 0488 0489 /** 0490 * all main windows will be cleaned up 0491 * in the KateApp destructor after the event 0492 * loop is left 0493 * 0494 * NOTE: From Qt 6, quit() will ask all windows to close, 0495 * but we already do our cleanup (and save prompts) when 0496 * the event loop quits, so we explicitly call exit() here. 0497 */ 0498 QApplication::exit(); 0499 } 0500 0501 KatePluginManager *KateApp::pluginManager() 0502 { 0503 return &m_pluginManager; 0504 } 0505 0506 KateDocManager *KateApp::documentManager() 0507 { 0508 return &m_docManager; 0509 } 0510 0511 KateSessionManager *KateApp::sessionManager() 0512 { 0513 return &m_sessionManager; 0514 } 0515 0516 KateStashManager *KateApp::stashManager() 0517 { 0518 return &m_stashManager; 0519 } 0520 0521 KTextEditor::Document *KateApp::openDocUrl(const QUrl &url, const QString &encoding, bool isTempFile, bool activateView, KTextEditor::Cursor c) 0522 { 0523 // temporary file handling 0524 // ensure we will delete the local file we opened via --tempfile at end of program 0525 // we can only do this properly for local files 0526 isTempFile = (isTempFile && !url.isEmpty() && url.isLocalFile() && QFile::exists(url.toLocalFile())); 0527 0528 KateMainWindow *mainWindow = activeKateMainWindow(); 0529 0530 if (!mainWindow) { 0531 return nullptr; 0532 } 0533 0534 // this file is no local dir, open it, else warn 0535 bool noDir = !url.isLocalFile() || KNetworkMounts::self()->isOptionEnabledForPath(url.toLocalFile(), KNetworkMounts::LowSideEffectsOptimizations) 0536 || !QFileInfo(url.toLocalFile()).isDir(); 0537 0538 KTextEditor::Document *doc = nullptr; 0539 0540 if (noDir) { 0541 KateDocumentInfo docInfo; 0542 docInfo.startCursor = c; 0543 doc = mainWindow->viewManager()->openUrl(url, encoding, activateView, isTempFile, docInfo); 0544 } else { 0545 KMessageBox::error(mainWindow, i18n("The file '%1' could not be opened: it is not a normal file, it is a folder.", url.url())); 0546 } 0547 0548 // document was successfully opened, ensure we will handle destroy properly 0549 if (doc) { 0550 // connect to slot & register the temp file handling if needed 0551 connect(doc, &QObject::destroyed, this, &KateApp::openDocUrlDocumentDestroyed); 0552 if (isTempFile) { 0553 m_tempFilesToDelete[doc].push_back(url.toLocalFile()); 0554 } 0555 } 0556 0557 // unable to open document, directly dispose of the temporary file 0558 else if (isTempFile) { 0559 QFile::remove(url.toLocalFile()); 0560 } 0561 0562 return doc; 0563 } 0564 0565 void KateApp::openDocUrlDocumentDestroyed(QObject *document) 0566 { 0567 // do we need to kill the temporary files for this document? 0568 if (const auto tempFilesIt = m_tempFilesToDelete.find(document); tempFilesIt != m_tempFilesToDelete.end()) { 0569 for (const auto &file : tempFilesIt.value()) { 0570 QFile::remove(file); 0571 } 0572 m_tempFilesToDelete.erase(tempFilesIt); 0573 } 0574 0575 // emit token signal to unblock remove blocking instances 0576 m_adaptor.emitDocumentClosed(QString::number(reinterpret_cast<qptrdiff>(document))); 0577 } 0578 0579 KTextEditor::Cursor KateApp::cursorFromArgs() 0580 { 0581 bool hasLine = m_args.isSet(QStringLiteral("line")); 0582 bool hasColumn = m_args.isSet(QStringLiteral("column")); 0583 0584 if (!hasLine && !hasColumn) { 0585 return KTextEditor::Cursor::invalid(); 0586 } 0587 0588 int line = qMax(m_args.value(QStringLiteral("line")).toInt() - 1, 0); 0589 int column = qMax(m_args.value(QStringLiteral("column")).toInt() - 1, 0); 0590 0591 return {line, column}; 0592 } 0593 0594 KTextEditor::Cursor KateApp::cursorFromQueryString(const QUrl &url) 0595 { 0596 if (!url.hasQuery()) { 0597 return KTextEditor::Cursor::invalid(); 0598 } 0599 0600 QUrlQuery urlQuery(url); 0601 QString lineStr = urlQuery.queryItemValue(QStringLiteral("line")); 0602 QString columnStr = urlQuery.queryItemValue(QStringLiteral("column")); 0603 0604 if (lineStr.isEmpty() && columnStr.isEmpty()) { 0605 return KTextEditor::Cursor::invalid(); 0606 } 0607 0608 int line = qMax(urlQuery.queryItemValue(QStringLiteral("line")).toInt() - 1, 0); 0609 int column = qMax(urlQuery.queryItemValue(QStringLiteral("column")).toInt() - 1, 0); 0610 0611 return {line, column}; 0612 } 0613 0614 bool KateApp::setCursor(int line, int column) 0615 { 0616 KateMainWindow *mainWindow = activeKateMainWindow(); 0617 0618 if (!mainWindow) { 0619 return false; 0620 } 0621 0622 if (auto v = mainWindow->viewManager()->activeView()) { 0623 v->removeSelection(); 0624 v->setCursorPosition(KTextEditor::Cursor(line, column)); 0625 } 0626 0627 return true; 0628 } 0629 0630 bool KateApp::hasCursorInArgs() 0631 { 0632 return m_args.isSet(QStringLiteral("line")) || m_args.isSet(QStringLiteral("column")); 0633 } 0634 0635 bool KateApp::openInput(const QString &text, const QString &encoding) 0636 { 0637 activeKateMainWindow()->viewManager()->openUrl(QUrl(), encoding, true); 0638 0639 if (!activeKateMainWindow()->viewManager()->activeView()) { 0640 return false; 0641 } 0642 0643 KTextEditor::Document *doc = activeKateMainWindow()->viewManager()->activeView()->document(); 0644 0645 if (!doc) { 0646 return false; 0647 } 0648 0649 return doc->setText(text); 0650 } 0651 0652 KTextEditor::MainWindow *KateApp::activeMainWindow() 0653 { 0654 // either return wrapper or nullptr 0655 if (KateMainWindow *a = activeKateMainWindow()) { 0656 return a->wrapper(); 0657 } 0658 return nullptr; 0659 } 0660 0661 QList<KTextEditor::MainWindow *> KateApp::mainWindows() 0662 { 0663 // assemble right list 0664 QList<KTextEditor::MainWindow *> windows; 0665 windows.reserve(m_mainWindows.size()); 0666 0667 for (const auto mainWindow : std::as_const(m_mainWindows)) { 0668 windows.push_back(mainWindow->wrapper()); 0669 } 0670 return windows; 0671 } 0672 0673 KateMainWindow *KateApp::newMainWindow(KConfig *sconfig_, const QString &sgroup_, bool userTriggered) 0674 { 0675 KConfig *sconfig = sconfig_ ? sconfig_ : KSharedConfig::openConfig().data(); 0676 QString sgroup = !sgroup_.isEmpty() ? sgroup_ : QStringLiteral("MainWindow0"); 0677 0678 KateMainWindow *mainWindow = new KateMainWindow(sconfig, sgroup, userTriggered); 0679 mainWindow->show(); 0680 0681 return mainWindow; 0682 } 0683 0684 void KateApp::addMainWindow(KateMainWindow *mainWindow) 0685 { 0686 m_mainWindows.push_back(mainWindow); 0687 } 0688 0689 void KateApp::removeMainWindow(KateMainWindow *mainWindow) 0690 { 0691 m_mainWindows.removeAll(mainWindow); 0692 } 0693 0694 KateMainWindow *KateApp::activeKateMainWindow() 0695 { 0696 if (m_mainWindows.isEmpty()) { 0697 return nullptr; 0698 } 0699 0700 int n = m_mainWindows.indexOf(qApp->activeWindow()); 0701 0702 if (n < 0) { 0703 n = 0; 0704 } 0705 0706 return m_mainWindows[n]; 0707 } 0708 0709 int KateApp::mainWindowsCount() const 0710 { 0711 return m_mainWindows.size(); 0712 } 0713 0714 int KateApp::mainWindowID(KateMainWindow *window) 0715 { 0716 return m_mainWindows.indexOf(window); 0717 } 0718 0719 KateMainWindow *KateApp::mainWindow(int n) 0720 { 0721 if (n < m_mainWindows.size()) { 0722 return m_mainWindows[n]; 0723 } 0724 0725 return nullptr; 0726 } 0727 0728 bool KateApp::closeDocuments(const QList<KTextEditor::Document *> &documents) 0729 { 0730 bool shutdownKate = 0731 KateApp::self()->activeKateMainWindow()->modCloseAfterLast() && KateApp::self()->documentManager()->documentList().size() == documents.size(); 0732 bool success = m_docManager.closeDocumentList(documents, KateApp::self()->activeKateMainWindow()); 0733 0734 if (success && shutdownKate) { 0735 QTimer::singleShot(0, this, []() { 0736 KateApp::self()->shutdownKate(KateApp::self()->activeKateMainWindow()); 0737 }); 0738 return true; 0739 } 0740 0741 return success; 0742 } 0743 0744 KTextEditor::Plugin *KateApp::plugin(const QString &name) 0745 { 0746 return m_pluginManager.plugin(name); 0747 } 0748 0749 bool KateApp::eventFilter(QObject *obj, QEvent *event) 0750 { 0751 // keep track when we got activated last time 0752 if (event->type() == QEvent::ActivationChange) { 0753 m_lastActivationChange = QDateTime::currentMSecsSinceEpoch(); 0754 } 0755 0756 /** 0757 * handle mac os like file open 0758 */ 0759 else if (event->type() == QEvent::FileOpen) { 0760 /** 0761 * try to open and activate the new document, like we would do for stuff 0762 * opened via dbus 0763 */ 0764 QFileOpenEvent *foe = static_cast<QFileOpenEvent *>(event); 0765 KTextEditor::Document *doc = openDocUrl(foe->url(), QString(), false); 0766 if (doc && activeKateMainWindow()) { 0767 activeKateMainWindow()->viewManager()->activateView(doc); 0768 } 0769 return true; 0770 } 0771 0772 /** 0773 * else: pass over to default implementation 0774 */ 0775 return QObject::eventFilter(obj, event); 0776 } 0777 0778 void KateApp::remoteMessageReceived(quint32, QByteArray message) 0779 { 0780 /** 0781 * try to parse message, ignore if no object 0782 */ 0783 const QJsonDocument jsonMessage = QJsonDocument::fromJson(message); 0784 if (!jsonMessage.isObject()) { 0785 return; 0786 } 0787 0788 KTextEditor::Document *doc = nullptr; 0789 /** 0790 * open all passed urls 0791 */ 0792 const QJsonArray urls = jsonMessage.object().value(QLatin1String("urls")).toArray(); 0793 for (const QJsonValue &urlObject : urls) { 0794 /** 0795 * get url meta data 0796 */ 0797 const QUrl url = urlObject.toObject().value(QLatin1String("url")).toVariant().toUrl(); 0798 const int line = urlObject.toObject().value(QLatin1String("line")).toVariant().toInt(); 0799 const int column = urlObject.toObject().value(QLatin1String("column")).toVariant().toInt(); 0800 0801 /** 0802 * open file + save line/column if requested 0803 */ 0804 doc = openDocUrl(url, QString(), false, /*activateView=*/false, KTextEditor::Cursor{line, column}); 0805 } 0806 0807 // try to activate current window 0808 m_adaptor.activate(); 0809 if (doc && activeMainWindow()) { 0810 activeMainWindow()->activateView(doc); 0811 } 0812 } 0813 0814 bool KateApp::documentVisibleInOtherWindows(KTextEditor::Document *doc, KateMainWindow *window) const 0815 { 0816 for (auto win : m_mainWindows) { 0817 if (win != window && win->viewManager()->viewspaceCountForDoc(doc) > 0) { 0818 return true; 0819 } 0820 } 0821 return false; 0822 } 0823 0824 bool KateApp::isInsideTerminal() 0825 { 0826 return insideTerminal; 0827 } 0828 0829 #include "moc_kateapp.cpp"