main.cpp 25.7 KB
Newer Older
1
/* This file is part of the KDE project
2 3
   SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
   SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
Anders Lund's avatar
Anders Lund committed
4

5
   SPDX-License-Identifier: LGPL-2.0-only
6 7
*/

Michal Humpula's avatar
Michal Humpula committed
8 9
#include "config.h"

10
#include "kateapp.h"
11
#include "katerunninginstanceinfo.h"
Christoph Cullmann's avatar
Christoph Cullmann committed
12
#include "katewaiter.h"
Michal Humpula's avatar
Michal Humpula committed
13

Michal Humpula's avatar
Michal Humpula committed
14
#include <KAboutData>
15
#include <KConfigGroup>
16
#include <KCrash>
Christoph Cullmann's avatar
Christoph Cullmann committed
17
#include <KDBusService>
Michal Humpula's avatar
Michal Humpula committed
18
#include <KLocalizedString>
19
#include <KSharedConfig>
20
#include <KStartupInfo>
21
#include <KWindowSystem>
Michal Humpula's avatar
Michal Humpula committed
22

23
#include <QApplication>
Michal Humpula's avatar
Michal Humpula committed
24 25 26 27 28
#include <QByteArray>
#include <QCommandLineParser>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusReply>
29
#include <QDir>
30
#include <QSessionManager>
31 32 33
#include <QTextCodec>
#include <QUrl>
#include <QVariant>
34

35
#include "../urlinfo.h"
36

37 38 39 40
#ifdef USE_QT_SINGLE_APP
#include "qtsingleapplication/qtsingleapplication.h"
#endif

41 42 43 44 45
#ifndef Q_OS_WIN
#include <unistd.h>
#endif
#include <iostream>

46
int main(int argc, char **argv)
47
{
48
#ifndef Q_OS_WIN
49
    // Prohibit using sudo or kdesu (but allow using the root user directly)
50
    if (getuid() == 0) {
51 52 53 54 55 56 57
        if (!qEnvironmentVariableIsEmpty("SUDO_USER")) {
            std::cout << "Executing Kate with sudo is not possible due to unfixable security vulnerabilities." << std::endl;
            return EXIT_FAILURE;
        } else if (!qEnvironmentVariableIsEmpty("KDESU_USER")) {
            std::cout << "Executing Kate with kdesu is not possible due to unfixable security vulnerabilities." << std::endl;
            return EXIT_FAILURE;
        }
58 59
    }
#endif
60 61 62 63 64
    /**
     * init resources from our static lib
     */
    Q_INIT_RESOURCE(kate);

65 66 67 68 69 70
    /**
     * enable high dpi support
     */
    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);

71 72
    /**
     * allow fractional scaling
73 74 75
     * we only activate this on Windows, it seems to creates problems on unices
     * (and there the fractional scaling with the QT_... env vars as set by KScreen works)
     * see bug 416078
76
     */
77
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && defined(Q_OS_WIN)
78 79 80
    QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif

81 82 83
    /**
     * Create application first
     */
84
#ifdef USE_QT_SINGLE_APP
85
    SharedTools::QtSingleApplication app(QStringLiteral("kate"), argc, argv);
86
#else
87
    QApplication app(argc, argv);
88 89
#endif

90 91 92 93 94 95 96 97
    /**
     * For Windows and macOS: use Breeze if available
     * Of all tested styles that works the best for us
     */
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
    QApplication::setStyle(QStringLiteral("breeze"));
#endif

98 99 100
    /**
     * Enforce application name even if the executable is renamed
     */
101
    app.setApplicationName(QStringLiteral("kate"));
102

Harald Sitter's avatar
Harald Sitter committed
103 104 105 106 107
    /**
     * Enable crash handling through KCrash.
     */
    KCrash::initialize();

108 109 110 111 112
    /**
     * Connect application with translation catalogs
     */
    KLocalizedString::setApplicationDomain("kate");

113 114 115
    /**
     * construct about data for Kate
     */
116
    KAboutData aboutData(
Christoph Cullmann's avatar
Christoph Cullmann committed
117
        QStringLiteral("kate"), i18n("Kate"), QStringLiteral(KATE_VERSION), i18n("Kate - Advanced Text Editor"), KAboutLicense::LGPL_V2, i18n("(c) 2000-2020 The Kate Authors"), QString(), QStringLiteral("https://kate-editor.org"));
118 119 120 121 122 123

    /**
     * right dbus prefix == org.kde.
     */
    aboutData.setOrganizationDomain("kde.org");

124 125 126 127 128
    /**
     * desktop file association to make application icon work (e.g. in Wayland window decoration)
     */
    aboutData.setDesktopFileName(QStringLiteral("org.kde.kate"));

129 130 131
    /**
     * authors & co.
     */
132
    aboutData.addAuthor(i18n("Christoph Cullmann"), i18n("Maintainer"), QStringLiteral("cullmann@kde.org"), QStringLiteral("https://cullmann.io"));
Dominik Haumann's avatar
Dominik Haumann committed
133 134 135
    aboutData.addAuthor(i18n("Dominik Haumann"), i18n("Core Developer"), QStringLiteral("dhaumann@kde.org"));
    aboutData.addAuthor(i18n("Sven Brauch"), i18n("Developer"), QStringLiteral("mail@svenbrauch.de"));
    aboutData.addAuthor(i18n("Kåre Särs"), i18n("Developer"), QStringLiteral("kare.sars@iki.fi"));
136
    aboutData.addAuthor(i18n("Anders Lund"), i18n("Core Developer"), QStringLiteral("anders@alweb.dk"), QStringLiteral("https://www.alweb.dk"));
137 138
    aboutData.addAuthor(i18n("Joseph Wenninger"), i18n("Core Developer"), QStringLiteral("jowenn@kde.org"), QStringLiteral("http://stud3.tuwien.ac.at/~e9925371"));
    aboutData.addAuthor(i18n("Hamish Rodda"), i18n("Core Developer"), QStringLiteral("rodda@kde.org"));
Alex Neundorf's avatar
Alex Neundorf committed
139
    aboutData.addAuthor(i18n("Alexander Neundorf"), i18n("Developer"), QStringLiteral("neundorf@kde.org"));
140 141 142 143 144 145 146 147 148 149 150 151
    aboutData.addAuthor(i18n("Waldo Bastian"), i18n("The cool buffersystem"), QStringLiteral("bastian@kde.org"));
    aboutData.addAuthor(i18n("Charles Samuels"), i18n("The Editing Commands"), QStringLiteral("charles@kde.org"));
    aboutData.addAuthor(i18n("Matt Newell"), i18n("Testing, ..."), QStringLiteral("newellm@proaxis.com"));
    aboutData.addAuthor(i18n("Michael Bartl"), i18n("Former Core Developer"), QStringLiteral("michael.bartl1@chello.at"));
    aboutData.addAuthor(i18n("Michael McCallum"), i18n("Core Developer"), QStringLiteral("gholam@xtra.co.nz"));
    aboutData.addAuthor(i18n("Jochen Wilhemly"), i18n("KWrite Author"), QStringLiteral("digisnap@cs.tu-berlin.de"));
    aboutData.addAuthor(i18n("Michael Koch"), i18n("KWrite port to KParts"), QStringLiteral("koch@kde.org"));
    aboutData.addAuthor(i18n("Christian Gebauer"), QString(), QStringLiteral("gebauer@kde.org"));
    aboutData.addAuthor(i18n("Simon Hausmann"), QString(), QStringLiteral("hausmann@kde.org"));
    aboutData.addAuthor(i18n("Glen Parker"), i18n("KWrite Undo History, Kspell integration"), QStringLiteral("glenebob@nwlink.com"));
    aboutData.addAuthor(i18n("Scott Manson"), i18n("KWrite XML Syntax highlighting support"), QStringLiteral("sdmanson@alltel.net"));
    aboutData.addAuthor(i18n("John Firebaugh"), i18n("Patches and more"), QStringLiteral("jfirebaugh@kde.org"));
152
    aboutData.addAuthor(i18n("Pablo Martín"), i18n("Python Plugin Developer"), QStringLiteral("goinnn@gmail.com"), QStringLiteral("https://github.com/goinnn/"));
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
    aboutData.addAuthor(i18n("Gerald Senarclens de Grancy"), i18n("QA and Scripting"), QStringLiteral("oss@senarclens.eu"), QStringLiteral("http://find-santa.eu/"));

    aboutData.addCredit(i18n("Matteo Merli"), i18n("Highlighting for RPM Spec-Files, Perl, Diff and more"), QStringLiteral("merlim@libero.it"));
    aboutData.addCredit(i18n("Rocky Scaletta"), i18n("Highlighting for VHDL"), QStringLiteral("rocky@purdue.edu"));
    aboutData.addCredit(i18n("Yury Lebedev"), i18n("Highlighting for SQL"));
    aboutData.addCredit(i18n("Chris Ross"), i18n("Highlighting for Ferite"));
    aboutData.addCredit(i18n("Nick Roux"), i18n("Highlighting for ILERPG"));
    aboutData.addCredit(i18n("Carsten Niehaus"), i18n("Highlighting for LaTeX"));
    aboutData.addCredit(i18n("Per Wigren"), i18n("Highlighting for Makefiles, Python"));
    aboutData.addCredit(i18n("Jan Fritz"), i18n("Highlighting for Python"));
    aboutData.addCredit(i18n("Daniel Naber"));
    aboutData.addCredit(i18n("Roland Pabel"), i18n("Highlighting for Scheme"));
    aboutData.addCredit(i18n("Cristi Dumitrescu"), i18n("PHP Keyword/Datatype list"));
    aboutData.addCredit(i18n("Carsten Pfeiffer"), i18n("Very nice help"));
    aboutData.addCredit(i18n("All people who have contributed and I have forgotten to mention"));

169 170 171 172 173
    /**
     * set proper Kate icon for our about dialog
     */
    aboutData.setProgramLogo(QIcon(QStringLiteral(":/kate/kate.svg")));

174
    /**
175
     * set and register app about data
176 177 178
     */
    KAboutData::setApplicationData(aboutData);

Christoph Cullmann's avatar
Christoph Cullmann committed
179 180 181
    /**
     * set the program icon
     */
182
    QApplication::setWindowIcon(QIcon(QStringLiteral(":/kate/kate.svg")));
183

184 185 186 187 188 189 190
    /**
     * Create command line parser and feed it with known options
     */
    QCommandLineParser parser;
    aboutData.setupCommandLine(&parser);

    // -s/--start session option
191
    const QCommandLineOption startSessionOption(QStringList() << QStringLiteral("s") << QStringLiteral("start"), i18n("Start Kate with a given session."), i18n("session"));
192 193 194 195 196 197 198
    parser.addOption(startSessionOption);

    // --startanon session option
    const QCommandLineOption startAnonymousSessionOption(QStringList() << QStringLiteral("startanon"), i18n("Start Kate with a new anonymous session, implies '-n'."));
    parser.addOption(startAnonymousSessionOption);

    // -n/--new option
199 200 201
    const QCommandLineOption startNewInstanceOption(
        QStringList() << QStringLiteral("n") << QStringLiteral("new"),
        i18n("Force start of a new kate instance (is ignored if start is used and another kate instance already has the given session opened), forced if no parameters and no URLs are given at all."));
202 203 204 205 206 207 208
    parser.addOption(startNewInstanceOption);

    // -b/--block option
    const QCommandLineOption startBlockingOption(QStringList() << QStringLiteral("b") << QStringLiteral("block"), i18n("If using an already running kate instance, block until it exits, if URLs given to open."));
    parser.addOption(startBlockingOption);

    // -p/--pid option
209 210
    const QCommandLineOption usePidOption(
        QStringList() << QStringLiteral("p") << QStringLiteral("pid"), i18n("Only try to reuse kate instance with this pid (is ignored if start is used and another kate instance already has the given session opened)."), i18n("pid"));
211 212 213
    parser.addOption(usePidOption);

    // -e/--encoding option
214
    const QCommandLineOption useEncodingOption(QStringList() << QStringLiteral("e") << QStringLiteral("encoding"), i18n("Set encoding for the file to open."), i18n("encoding"));
215 216 217
    parser.addOption(useEncodingOption);

    // -l/--line option
218
    const QCommandLineOption gotoLineOption(QStringList() << QStringLiteral("l") << QStringLiteral("line"), i18n("Navigate to this line."), i18n("line"));
219 220 221
    parser.addOption(gotoLineOption);

    // -c/--column option
222
    const QCommandLineOption gotoColumnOption(QStringList() << QStringLiteral("c") << QStringLiteral("column"), i18n("Navigate to this column."), i18n("column"));
223 224 225 226 227 228 229 230 231 232 233
    parser.addOption(gotoColumnOption);

    // -i/--stdin option
    const QCommandLineOption readStdInOption(QStringList() << QStringLiteral("i") << QStringLiteral("stdin"), i18n("Read the contents of stdin."));
    parser.addOption(readStdInOption);

    // --tempfile option
    const QCommandLineOption tempfileOption(QStringList() << QStringLiteral("tempfile"), i18n("The files/URLs opened by the application will be deleted after use"));
    parser.addOption(tempfileOption);

    // urls to open
234
    parser.addPositionalArgument(QStringLiteral("urls"), i18n("Documents to open."), i18n("[urls...]"));
235 236 237 238 239 240 241 242 243 244 245

    /**
     * do the command line parsing
     */
    parser.process(app);

    /**
     * handle standard options
     */
    aboutData.processCommandLine(&parser);

246 247 248 249
    /**
     * remember the urls we shall open
     */
    const QStringList urls = parser.positionalArguments();
250

251
    /**
252 253 254 255 256 257 258
     * compute if we shall start a new instance or reuse
     * an old one
     * this will later be updated once more after detecting some
     * things about already running kate's, like their sessions
     */
    bool force_new = parser.isSet(startNewInstanceOption);
    if (!force_new) {
259 260 261
        if (!(parser.isSet(startSessionOption) || parser.isSet(startNewInstanceOption) || parser.isSet(usePidOption) || parser.isSet(useEncodingOption) || parser.isSet(gotoLineOption) || parser.isSet(gotoColumnOption) ||
              parser.isSet(readStdInOption)) &&
            (urls.isEmpty())) {
262 263 264 265 266 267 268 269 270 271 272
            force_new = true;
        }
    }

    /**
     * only block, if files to open there....
     */
    const bool needToBlock = parser.isSet(startBlockingOption) && !urls.isEmpty();

    /**
     * use dbus, if available for linux and co.
Yuri Chornoivan's avatar
Yuri Chornoivan committed
273
     * allows for reuse of running Kate instances
274
     */
275
#ifndef USE_QT_SINGLE_APP
276
    if (QDBusConnectionInterface *const sessionBusInterface = QDBusConnection::sessionBus().interface()) {
277 278 279 280 281 282 283
        /**
         * try to get the current running kate instances
         */
        KateRunningInstanceMap mapSessionRii;
        if (!fillinRunningKateAppInstances(&mapSessionRii)) {
            return 1;
        }
284

285
        QString currentActivity;
286
        QDBusMessage m = QDBusMessage::createMethodCall(QStringLiteral("org.kde.ActivityManager"), QStringLiteral("/ActivityManager/Activities"), QStringLiteral("org.kde.ActivityManager.Activities"), QStringLiteral("CurrentActivity"));
287 288 289 290 291 292
        QDBusMessage res = QDBusConnection::sessionBus().call(m);
        QList<QVariant> answer = res.arguments();
        if (answer.size() == 1) {
            currentActivity = answer.at(0).toString();
        }

293 294
        QStringList kateServices;
        for (KateRunningInstanceMap::const_iterator it = mapSessionRii.constBegin(); it != mapSessionRii.constEnd(); ++it) {
295 296 297
            QString serviceName = (*it)->serviceName;

            if (currentActivity.length() != 0) {
298
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("isOnActivity"));
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319

                QList<QVariant> dbargs;

                // convert to an url
                dbargs.append(currentActivity);
                m.setArguments(dbargs);

                QDBusMessage res = QDBusConnection::sessionBus().call(m);
                QList<QVariant> answer = res.arguments();
                if (answer.size() == 1) {
                    const bool canBeUsed = answer.at(0).toBool();

                    // If the Kate instance is in a specific activity, add it to
                    // the list of candidate reusable services
                    if (canBeUsed) {
                        kateServices << serviceName;
                    }
                }
            } else {
                kateServices << serviceName;
            }
320
        }
321

322 323 324 325 326
        QString serviceName;

        QString start_session;
        bool session_already_opened = false;

327
        // check if we try to start an already opened session
328
        if (parser.isSet(startAnonymousSessionOption)) {
329
            force_new = true;
330 331 332 333 334 335 336
        } else if (parser.isSet(startSessionOption)) {
            start_session = parser.value(startSessionOption);
            if (mapSessionRii.contains(start_session)) {
                serviceName = mapSessionRii[start_session]->serviceName;
                force_new = false;
                session_already_opened = true;
            }
337 338
        }

339
        // cleanup map
340 341
        cleanupRunningKateAppInstanceMap(&mapSessionRii);

342 343
        // if no new instance is forced and no already opened session is requested,
        // check if a pid is given, which should be reused.
344 345 346
        // two possibilities: pid given or not...
        if ((!force_new) && serviceName.isEmpty()) {
            if ((parser.isSet(usePidOption)) || (!qgetenv("KATE_PID").isEmpty())) {
347
                QString usePid = (parser.isSet(usePidOption)) ? parser.value(usePidOption) : QString::fromLocal8Bit(qgetenv("KATE_PID"));
348

349
                serviceName = QLatin1String("org.kde.kate-") + usePid;
350 351 352 353
                if (!kateServices.contains(serviceName)) {
                    serviceName.clear();
                }
            }
354
        }
355

356 357
        // prefer the Kate instance running on the current virtual desktop
        bool foundRunningService = false;
358
        if ((!force_new) && (serviceName.isEmpty())) {
359 360 361 362 363 364 365 366 367
            const int desktopnumber = KWindowSystem::currentDesktop();
            for (int s = 0; s < kateServices.count(); s++) {
                serviceName = kateServices[s];

                if (!serviceName.isEmpty()) {
                    QDBusReply<bool> there = sessionBusInterface->isServiceRegistered(serviceName);

                    if (there.isValid() && there.value()) {
                        // query instance current desktop
368
                        QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("desktopNumber"));
369 370 371 372

                        QDBusMessage res = QDBusConnection::sessionBus().call(m);
                        QList<QVariant> answer = res.arguments();
                        if (answer.size() == 1) {
373 374 375
                            // special case: on all desktops! that is -1 aka NET::OnAllDesktops, see KWindowInfo::desktop() docs
                            const int sessionDesktopNumber = answer.at(0).toInt();
                            if (sessionDesktopNumber == desktopnumber || sessionDesktopNumber == NET::OnAllDesktops) {
376 377 378 379 380 381 382 383
                                // stop searching. a candidate instance in the current desktop has been found
                                foundRunningService = true;
                                break;
                            }
                        }
                    }
                }
                serviceName.clear();
384
            }
385
        }
386

387
        // check again if service is still running
388
        foundRunningService = false;
389 390 391
        if (!serviceName.isEmpty()) {
            QDBusReply<bool> there = sessionBusInterface->isServiceRegistered(serviceName);
            foundRunningService = there.isValid() && there.value();
392
        }
393

394 395 396
        if (foundRunningService) {
            // open given session
            if (parser.isSet(startSessionOption) && (!session_already_opened)) {
397
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("activateSession"));
398

399 400 401
                QList<QVariant> dbusargs;
                dbusargs.append(parser.value(startSessionOption));
                m.setArguments(dbusargs);
402

403 404
                QDBusConnection::sessionBus().call(m);
            }
405

406
            QString enc = parser.isSet(useEncodingOption) ? parser.value(useEncodingOption) : QString();
407

408
            bool tempfileSet = parser.isSet(tempfileOption);
409

410
            QStringList tokens;
411

412
            // open given files...
413 414 415
            // Bug 397913: Reverse the order here so the new tabs are opened in same order as the files were passed in on the command line
            for (int i = urls.size() - 1; i >= 0; --i) {
                const QString &url = urls[i];
416
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("tokenOpenUrlAt"));
417

418
                UrlInfo info(url);
419
                QList<QVariant> dbusargs;
420

421
                // convert to an url
422
                dbusargs.append(info.url.toString());
423 424
                dbusargs.append(info.cursor.line());
                dbusargs.append(info.cursor.column());
425 426 427 428 429 430 431 432 433 434
                dbusargs.append(enc);
                dbusargs.append(tempfileSet);
                m.setArguments(dbusargs);

                QDBusMessage res = QDBusConnection::sessionBus().call(m);
                if (res.type() == QDBusMessage::ReplyMessage) {
                    if (res.arguments().count() == 1) {
                        QVariant v = res.arguments()[0];
                        if (v.isValid()) {
                            QString s = v.toString();
435
                            if ((!s.isEmpty()) && (s != QLatin1String("ERROR"))) {
436 437
                                tokens << s;
                            }
438 439 440 441 442
                        }
                    }
                }
            }

443 444
            if (parser.isSet(readStdInOption)) {
                QTextStream input(stdin, QIODevice::ReadOnly);
445

446
                // set chosen codec
447
                QTextCodec *codec = parser.isSet(useEncodingOption) ? QTextCodec::codecForName(parser.value(useEncodingOption).toUtf8()) : nullptr;
448

449 450 451
                if (codec) {
                    input.setCodec(codec);
                }
452

453 454
                QString line;
                QString text;
455

456 457 458 459
                do {
                    line = input.readLine();
                    text.append(line + QLatin1Char('\n'));
                } while (!line.isNull());
460

461
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("openInput"));
462

463 464
                QList<QVariant> dbusargs;
                dbusargs.append(text);
465
                dbusargs.append(codec ? QString::fromLatin1(codec->name()) : QString());
466
                m.setArguments(dbusargs);
467

468 469
                QDBusConnection::sessionBus().call(m);
            }
470

471 472 473
            int line = 0;
            int column = 0;
            bool nav = false;
474

475 476 477 478
            if (parser.isSet(gotoLineOption)) {
                line = parser.value(gotoLineOption).toInt() - 1;
                nav = true;
            }
479

480 481 482 483
            if (parser.isSet(gotoColumnOption)) {
                column = parser.value(gotoColumnOption).toInt() - 1;
                nav = true;
            }
484

485
            if (nav) {
486
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("setCursor"));
487

488 489 490 491
                QList<QVariant> args;
                args.append(line);
                args.append(column);
                m.setArguments(args);
492

493 494
                QDBusConnection::sessionBus().call(m);
            }
495

496
            // activate the used instance
497
            QDBusMessage activateMsg = QDBusMessage::createMethodCall(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("activate"));
498
            QDBusConnection::sessionBus().call(activateMsg);
499

500 501
            // connect dbus signal
            if (needToBlock) {
502
                KateWaiter *waiter = new KateWaiter(serviceName, tokens);
503 504 505
                QDBusConnection::sessionBus().connect(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("exiting"), waiter, SLOT(exiting()));
                QDBusConnection::sessionBus().connect(serviceName, QStringLiteral("/MainApplication"), QStringLiteral("org.kde.Kate.Application"), QStringLiteral("documentClosed"), waiter, SLOT(documentClosed(QString)));
            }
506

507 508 509 510
            // KToolInvocation (and KRun) will wait until we register on dbus
            KDBusService dbusService(KDBusService::Multiple);
            dbusService.unregister();

511 512
            // make the world happy, we are started, kind of...
            KStartupInfo::appStarted();
513

514 515 516
            // We don't want the session manager to restart us on next login
            // if we block
            if (needToBlock) {
517 518
                QObject::connect(
                    qApp, &QGuiApplication::saveStateRequest, qApp, [](QSessionManager &session) { session.setRestartHint(QSessionManager::RestartNever); }, Qt::DirectConnection);
519 520
            }

521 522 523
            // this will wait until exiting is emitted by the used instance, if wanted...
            return needToBlock ? app.exec() : 0;
        }
524
    }
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554

    /**
     * for mac & windows: use QtSingleApplication
     */
#else
    /**
     * only try to reuse existing kate instances if not already forbidden by arguments
     */
    if (!force_new) {
        /**
         * any instance running we can use?
         * later we could do here pid checks and stuff
         */
        bool instanceFound = app.isRunning();

        /**
         * if instance was found, send over all urls to be opened
         */
        if (instanceFound) {
            /**
             * tell single application to block if needed
             */
            app.setBlock(needToBlock);

            /**
             * construct one big message with all urls to open
             * later we will add additional data to this
             */
            QVariantMap message;
            QVariantList messageUrls;
555
            for (const QString &url : urls) {
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
                /**
                 * get url info and pack them into the message as extra element in urls list
                 */
                UrlInfo info(url);
                QVariantMap urlMessagePart;
                urlMessagePart[QLatin1String("url")] = info.url;
                urlMessagePart[QLatin1String("line")] = info.cursor.line();
                urlMessagePart[QLatin1String("column")] = info.cursor.column();
                messageUrls.append(urlMessagePart);
            }
            message[QLatin1String("urls")] = messageUrls;

            /**
             * try to send message, return success
             */
            return !app.sendMessage(QString::fromUtf8(QJsonDocument::fromVariant(QVariant(message)).toJson()));
572 573
        }
    }
574
#endif // USE_QT_SINGLE_APP
575

576 577 578
    /**
     * if we arrive here, we need to start a new kate instance!
     */
579

580 581 582 583 584 585 586 587 588 589 590
    /**
     * set some KTextEditor defaults
     */
    {
        KConfigGroup viewConfig(KSharedConfig::openConfig(), QStringLiteral("KTextEditor View"));
        if (!viewConfig.exists()) {
            viewConfig.writeEntry("Line Modification", true);
            viewConfig.writeEntry("Line Numbers", true);
        }
    }

591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
    /**
     * construct the real kate app object ;)
     * behaves like a singleton, one unique instance
     * we are passing our local command line parser to it
     */
    KateApp kateApp(parser);

    /**
     * init kate
     * if this returns false, we shall exit
     * else we may enter the main event loop
     */
    if (!kateApp.init()) {
        return 0;
    }

607
#ifndef USE_QT_SINGLE_APP
608
    /**
609
     * finally register this kate instance for dbus, don't die if no dbus is around!
610
     */
611
    const KDBusService dbusService(KDBusService::Multiple | KDBusService::NoExitOnFailure);
612 613 614 615
#else
    /**
     * else: connect the single application notifications
     */
616
    QObject::connect(&app, &SharedTools::QtSingleApplication::messageReceived, &kateApp, &KateApp::remoteMessageReceived);
617 618 619 620

    KateMainWindow *win = kateApp.activeKateMainWindow();
    app.setActivationWindow(win, true);

621
#endif
622

623 624 625 626
    /**
     * start main event loop for our application
     */
    return app.exec();
627
}