main.cpp 29.4 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 <KCrash>
Christoph Cullmann's avatar
Christoph Cullmann committed
16
#include <KDBusService>
Michal Humpula's avatar
Michal Humpula committed
17
#include <KLocalizedString>
18
#include <KStartupInfo>
19
#include <KWindowSystem>
20
#include <algorithm>
Michal Humpula's avatar
Michal Humpula committed
21

22
#include <QApplication>
Michal Humpula's avatar
Michal Humpula committed
23
24
25
26
27
#include <QByteArray>
#include <QCommandLineParser>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusReply>
28
#include <QDir>
Johnny Jazeix's avatar
Johnny Jazeix committed
29
#include <QJsonDocument>
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
        if (!qEnvironmentVariableIsEmpty("SUDO_USER")) {
52
53
54
55
            std::cout << "Executing Kate with sudo is not possible due to unfixable security vulnerabilities. "
                         "It is also not necessary; simply use Kate normally, and you will be prompted for "
                         "elevated privileges when saving documents if needed."
                      << std::endl;
56
57
            return EXIT_FAILURE;
        } else if (!qEnvironmentVariableIsEmpty("KDESU_USER")) {
58
59
60
61
            std::cout << "Executing Kate with kdesu is not possible due to unfixable security vulnerabilities. "
                         "It is also not necessary; simply use Kate normally, and you will be prompted for "
                         "elevated privileges when saving documents if needed."
                      << std::endl;
62
63
            return EXIT_FAILURE;
        }
64
65
    }
#endif
66
67
68
69
70
    /**
     * init resources from our static lib
     */
    Q_INIT_RESOURCE(kate);

71
72
73
74
75
76
    /**
     * enable high dpi support
     */
    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);

77
78
    /**
     * allow fractional scaling
79
80
81
     * 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
82
83
84
85
86
     *
     * we switched to Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor because of font rendering issues
     * we follow what Krita does here, see https://invent.kde.org/graphics/krita/-/blob/master/krita/main.cc
     * we raise the Qt requirement to  5.15 as it seems some patches went in after 5.14 that are needed
     * see Krita comments, too
87
     */
88
89
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) && defined(Q_OS_WIN)
    QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
90
91
#endif

92
93
94
    /**
     * Create application first
     */
95
#ifdef USE_QT_SINGLE_APP
96
    SharedTools::QtSingleApplication app(QStringLiteral("kate"), argc, argv);
97
#else
98
    QApplication app(argc, argv);
99
100
#endif

101
102
103
104
105
106
107
108
    /**
     * 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

109
110
111
    /**
     * Enforce application name even if the executable is renamed
     */
112
    app.setApplicationName(QStringLiteral("kate"));
113

Harald Sitter's avatar
Harald Sitter committed
114
115
116
117
118
    /**
     * Enable crash handling through KCrash.
     */
    KCrash::initialize();

119
120
121
122
123
    /**
     * Connect application with translation catalogs
     */
    KLocalizedString::setApplicationDomain("kate");

124
125
126
    /**
     * construct about data for Kate
     */
Alexander Lohnau's avatar
Alexander Lohnau committed
127
128
129
130
131
132
    KAboutData aboutData(QStringLiteral("kate"),
                         i18n("Kate"),
                         QStringLiteral(KATE_VERSION),
                         i18n("Kate - Advanced Text Editor"),
                         KAboutLicense::LGPL_V2,
                         i18n("(c) 2000-2021 The Kate Authors"),
133
134
                         // use the other text field to get our mascot into the about dialog
                         QStringLiteral("<img height=\"362\" width=\"512\" src=\":/kate/mascot.png\"/>"),
Alexander Lohnau's avatar
Alexander Lohnau committed
135
                         QStringLiteral("https://kate-editor.org"));
136
137
138
139
140
141

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

142
143
144
145
146
    /**
     * desktop file association to make application icon work (e.g. in Wayland window decoration)
     */
    aboutData.setDesktopFileName(QStringLiteral("org.kde.kate"));

147
148
149
    /**
     * authors & co.
     */
150
    aboutData.addAuthor(i18n("Christoph Cullmann"), i18n("Maintainer"), QStringLiteral("cullmann@kde.org"), QStringLiteral("https://cullmann.io"));
Dominik Haumann's avatar
Dominik Haumann committed
151
152
153
    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"));
154
    aboutData.addAuthor(i18n("Anders Lund"), i18n("Core Developer"), QStringLiteral("anders@alweb.dk"), QStringLiteral("https://www.alweb.dk"));
Alexander Lohnau's avatar
Alexander Lohnau committed
155
156
157
158
    aboutData.addAuthor(i18n("Joseph Wenninger"),
                        i18n("Core Developer"),
                        QStringLiteral("jowenn@kde.org"),
                        QStringLiteral("http://stud3.tuwien.ac.at/~e9925371"));
159
    aboutData.addAuthor(i18n("Hamish Rodda"), i18n("Core Developer"), QStringLiteral("rodda@kde.org"));
Alex Neundorf's avatar
Alex Neundorf committed
160
    aboutData.addAuthor(i18n("Alexander Neundorf"), i18n("Developer"), QStringLiteral("neundorf@kde.org"));
161
162
163
164
165
166
167
168
169
170
171
172
    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"));
Alexander Lohnau's avatar
Alexander Lohnau committed
173
174
175
176
177
178
179
180
    aboutData.addAuthor(i18n("Pablo Martín"),
                        i18n("Python Plugin Developer"),
                        QStringLiteral("goinnn@gmail.com"),
                        QStringLiteral("https://github.com/goinnn/"));
    aboutData.addAuthor(i18n("Gerald Senarclens de Grancy"),
                        i18n("QA and Scripting"),
                        QStringLiteral("oss@senarclens.eu"),
                        QStringLiteral("http://find-santa.eu/"));
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

    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"));

196
197
198
199
200
    /**
     * set proper Kate icon for our about dialog
     */
    aboutData.setProgramLogo(QIcon(QStringLiteral(":/kate/kate.svg")));

201
    /**
202
     * set and register app about data
203
204
205
     */
    KAboutData::setApplicationData(aboutData);

Christoph Cullmann's avatar
Christoph Cullmann committed
206
207
208
    /**
     * set the program icon
     */
209
    QApplication::setWindowIcon(QIcon(QStringLiteral(":/kate/kate.svg")));
210

211
212
213
214
215
216
217
    /**
     * Create command line parser and feed it with known options
     */
    QCommandLineParser parser;
    aboutData.setupCommandLine(&parser);

    // -s/--start session option
Alexander Lohnau's avatar
Alexander Lohnau committed
218
219
220
    const QCommandLineOption startSessionOption(QStringList() << QStringLiteral("s") << QStringLiteral("start"),
                                                i18n("Start Kate with a given session."),
                                                i18n("session"));
221
222
223
    parser.addOption(startSessionOption);

    // --startanon session option
Alexander Lohnau's avatar
Alexander Lohnau committed
224
225
    const QCommandLineOption startAnonymousSessionOption(QStringList() << QStringLiteral("startanon"),
                                                         i18n("Start Kate with a new anonymous session, implies '-n'."));
226
227
228
    parser.addOption(startAnonymousSessionOption);

    // -n/--new option
Alexander Lohnau's avatar
Alexander Lohnau committed
229
230
231
    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."));
232
233
234
    parser.addOption(startNewInstanceOption);

    // -b/--block option
Alexander Lohnau's avatar
Alexander Lohnau committed
235
236
    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."));
237
238
239
    parser.addOption(startBlockingOption);

    // -p/--pid option
240
    const QCommandLineOption usePidOption(
Alexander Lohnau's avatar
Alexander Lohnau committed
241
242
243
        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"));
244
245
246
    parser.addOption(usePidOption);

    // -e/--encoding option
Alexander Lohnau's avatar
Alexander Lohnau committed
247
248
249
    const QCommandLineOption useEncodingOption(QStringList() << QStringLiteral("e") << QStringLiteral("encoding"),
                                               i18n("Set encoding for the file to open."),
                                               i18n("encoding"));
250
251
252
    parser.addOption(useEncodingOption);

    // -l/--line option
253
    const QCommandLineOption gotoLineOption(QStringList() << QStringLiteral("l") << QStringLiteral("line"), i18n("Navigate to this line."), i18n("line"));
254
255
256
    parser.addOption(gotoLineOption);

    // -c/--column option
Alexander Lohnau's avatar
Alexander Lohnau committed
257
258
259
    const QCommandLineOption gotoColumnOption(QStringList() << QStringLiteral("c") << QStringLiteral("column"),
                                              i18n("Navigate to this column."),
                                              i18n("column"));
260
261
262
263
264
265
266
    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
Alexander Lohnau's avatar
Alexander Lohnau committed
267
268
    const QCommandLineOption tempfileOption(QStringList() << QStringLiteral("tempfile"),
                                            i18n("The files/URLs opened by the application will be deleted after use"));
269
270
271
    parser.addOption(tempfileOption);

    // urls to open
272
    parser.addPositionalArgument(QStringLiteral("urls"), i18n("Documents to open."), i18n("[urls...]"));
273
274
275
276
277
278
279
280
281
282
283

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

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

284
285
286
287
    /**
     * remember the urls we shall open
     */
    const QStringList urls = parser.positionalArguments();
288

289
    /**
290
291
292
293
294
295
296
     * 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) {
Alexander Lohnau's avatar
Alexander Lohnau committed
297
298
299
        if (!(parser.isSet(startSessionOption) || parser.isSet(startNewInstanceOption) || parser.isSet(usePidOption) || parser.isSet(useEncodingOption)
              || parser.isSet(gotoLineOption) || parser.isSet(gotoColumnOption) || parser.isSet(readStdInOption))
            && (urls.isEmpty())) {
300
            force_new = true;
301
302
303
304
        } else {
            force_new = std::any_of(urls.begin(), urls.end(), [](const QString &url) {
                return QFileInfo(url).isDir();
            });
305
306
307
308
309
310
311
312
313
314
        }
    }

    /**
     * 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
315
     * allows for reuse of running Kate instances
316
     */
317
#ifndef USE_QT_SINGLE_APP
318
    if (QDBusConnectionInterface *const sessionBusInterface = QDBusConnection::sessionBus().interface()) {
319
320
321
322
323
324
325
        /**
         * try to get the current running kate instances
         */
        KateRunningInstanceMap mapSessionRii;
        if (!fillinRunningKateAppInstances(&mapSessionRii)) {
            return 1;
        }
326

327
        QString currentActivity;
Alexander Lohnau's avatar
Alexander Lohnau committed
328
329
330
331
        QDBusMessage m = QDBusMessage::createMethodCall(QStringLiteral("org.kde.ActivityManager"),
                                                        QStringLiteral("/ActivityManager/Activities"),
                                                        QStringLiteral("org.kde.ActivityManager.Activities"),
                                                        QStringLiteral("CurrentActivity"));
332
333
334
335
336
337
        QDBusMessage res = QDBusConnection::sessionBus().call(m);
        QList<QVariant> answer = res.arguments();
        if (answer.size() == 1) {
            currentActivity = answer.at(0).toString();
        }

338
339
        QStringList kateServices;
        for (KateRunningInstanceMap::const_iterator it = mapSessionRii.constBegin(); it != mapSessionRii.constEnd(); ++it) {
340
341
342
            QString serviceName = (*it)->serviceName;

            if (currentActivity.length() != 0) {
Alexander Lohnau's avatar
Alexander Lohnau committed
343
344
345
346
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName,
                                                                QStringLiteral("/MainApplication"),
                                                                QStringLiteral("org.kde.Kate.Application"),
                                                                QStringLiteral("isOnActivity"));
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367

                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;
            }
368
        }
369

370
371
372
373
374
        QString serviceName;

        QString start_session;
        bool session_already_opened = false;

375
        // check if we try to start an already opened session
376
        if (parser.isSet(startAnonymousSessionOption)) {
377
            force_new = true;
378
379
380
381
382
383
384
        } 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;
            }
385
386
        }

387
        // cleanup map
388
389
        cleanupRunningKateAppInstanceMap(&mapSessionRii);

390
391
        // if no new instance is forced and no already opened session is requested,
        // check if a pid is given, which should be reused.
392
393
        // two possibilities: pid given or not...
        if ((!force_new) && serviceName.isEmpty()) {
394
            if ((parser.isSet(usePidOption)) || (!qEnvironmentVariableIsEmpty("KATE_PID"))) {
395
                QString usePid = (parser.isSet(usePidOption)) ? parser.value(usePidOption) : QString::fromLocal8Bit(qgetenv("KATE_PID"));
396

397
                serviceName = QLatin1String("org.kde.kate-") + usePid;
398
399
400
401
                if (!kateServices.contains(serviceName)) {
                    serviceName.clear();
                }
            }
402
        }
403

404
405
        // prefer the Kate instance running on the current virtual desktop
        bool foundRunningService = false;
406
        if ((!force_new) && (serviceName.isEmpty())) {
407
408
409
410
411
412
413
414
415
            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
Alexander Lohnau's avatar
Alexander Lohnau committed
416
417
418
419
                        QDBusMessage m = QDBusMessage::createMethodCall(serviceName,
                                                                        QStringLiteral("/MainApplication"),
                                                                        QStringLiteral("org.kde.Kate.Application"),
                                                                        QStringLiteral("desktopNumber"));
420
421
422
423

                        QDBusMessage res = QDBusConnection::sessionBus().call(m);
                        QList<QVariant> answer = res.arguments();
                        if (answer.size() == 1) {
424
425
426
                            // 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) {
427
428
429
430
431
432
433
434
                                // stop searching. a candidate instance in the current desktop has been found
                                foundRunningService = true;
                                break;
                            }
                        }
                    }
                }
                serviceName.clear();
435
            }
436
        }
437

438
        // check again if service is still running
439
        foundRunningService = false;
440
441
442
        if (!serviceName.isEmpty()) {
            QDBusReply<bool> there = sessionBusInterface->isServiceRegistered(serviceName);
            foundRunningService = there.isValid() && there.value();
443
        }
444

445
446
447
        if (foundRunningService) {
            // open given session
            if (parser.isSet(startSessionOption) && (!session_already_opened)) {
Alexander Lohnau's avatar
Alexander Lohnau committed
448
449
450
451
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName,
                                                                QStringLiteral("/MainApplication"),
                                                                QStringLiteral("org.kde.Kate.Application"),
                                                                QStringLiteral("activateSession"));
452

453
454
455
                QList<QVariant> dbusargs;
                dbusargs.append(parser.value(startSessionOption));
                m.setArguments(dbusargs);
456

457
458
                QDBusConnection::sessionBus().call(m);
            }
459

460
            QString enc = parser.isSet(useEncodingOption) ? parser.value(useEncodingOption) : QString();
461

462
            bool tempfileSet = parser.isSet(tempfileOption);
463

464
            QStringList tokens;
465

466
            // open given files...
467
            for (int i = 0; i < urls.size(); ++i) {
468
                const QString &url = urls[i];
Alexander Lohnau's avatar
Alexander Lohnau committed
469
470
471
472
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName,
                                                                QStringLiteral("/MainApplication"),
                                                                QStringLiteral("org.kde.Kate.Application"),
                                                                QStringLiteral("tokenOpenUrlAt"));
473

474
                UrlInfo info(url);
475
                QList<QVariant> dbusargs;
476

477
                // convert to an url
478
                dbusargs.append(info.url.toString());
479
480
                dbusargs.append(info.cursor.line());
                dbusargs.append(info.cursor.column());
481
482
483
484
485
486
487
488
489
490
                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();
491
                            if ((!s.isEmpty()) && (s != QLatin1String("ERROR"))) {
492
493
                                tokens << s;
                            }
494
495
496
497
498
                        }
                    }
                }
            }

499
500
            if (parser.isSet(readStdInOption)) {
                QTextStream input(stdin, QIODevice::ReadOnly);
501

502
                // set chosen codec
503
                QTextCodec *codec = parser.isSet(useEncodingOption) ? QTextCodec::codecForName(parser.value(useEncodingOption).toUtf8()) : nullptr;
504

505
506
507
                if (codec) {
                    input.setCodec(codec);
                }
508

509
510
                QString line;
                QString text;
511

512
513
514
515
                do {
                    line = input.readLine();
                    text.append(line + QLatin1Char('\n'));
                } while (!line.isNull());
516

Alexander Lohnau's avatar
Alexander Lohnau committed
517
518
519
520
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName,
                                                                QStringLiteral("/MainApplication"),
                                                                QStringLiteral("org.kde.Kate.Application"),
                                                                QStringLiteral("openInput"));
521

522
523
                QList<QVariant> dbusargs;
                dbusargs.append(text);
524
                dbusargs.append(codec ? QString::fromLatin1(codec->name()) : QString());
525
                m.setArguments(dbusargs);
526

527
528
                QDBusConnection::sessionBus().call(m);
            }
529

530
531
532
            int line = 0;
            int column = 0;
            bool nav = false;
533

534
535
536
537
            if (parser.isSet(gotoLineOption)) {
                line = parser.value(gotoLineOption).toInt() - 1;
                nav = true;
            }
538

539
540
541
542
            if (parser.isSet(gotoColumnOption)) {
                column = parser.value(gotoColumnOption).toInt() - 1;
                nav = true;
            }
543

544
            if (nav) {
Alexander Lohnau's avatar
Alexander Lohnau committed
545
546
547
548
                QDBusMessage m = QDBusMessage::createMethodCall(serviceName,
                                                                QStringLiteral("/MainApplication"),
                                                                QStringLiteral("org.kde.Kate.Application"),
                                                                QStringLiteral("setCursor"));
549

550
551
552
553
                QList<QVariant> args;
                args.append(line);
                args.append(column);
                m.setArguments(args);
554

555
556
                QDBusConnection::sessionBus().call(m);
            }
557

558
            // activate the used instance
Alexander Lohnau's avatar
Alexander Lohnau committed
559
560
561
562
            QDBusMessage activateMsg = QDBusMessage::createMethodCall(serviceName,
                                                                      QStringLiteral("/MainApplication"),
                                                                      QStringLiteral("org.kde.Kate.Application"),
                                                                      QStringLiteral("activate"));
563
            QDBusConnection::sessionBus().call(activateMsg);
564

565
566
            // connect dbus signal
            if (needToBlock) {
567
                KateWaiter *waiter = new KateWaiter(serviceName, tokens);
Alexander Lohnau's avatar
Alexander Lohnau committed
568
569
570
571
572
573
574
575
576
577
578
579
                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)));
580
            }
581

582
583
584
585
            // KToolInvocation (and KRun) will wait until we register on dbus
            KDBusService dbusService(KDBusService::Multiple);
            dbusService.unregister();

586
587
            // make the world happy, we are started, kind of...
            KStartupInfo::appStarted();
588

589
590
591
            // We don't want the session manager to restart us on next login
            // if we block
            if (needToBlock) {
592
                QObject::connect(
Alexander Lohnau's avatar
Alexander Lohnau committed
593
594
595
596
597
598
599
                    qApp,
                    &QGuiApplication::saveStateRequest,
                    qApp,
                    [](QSessionManager &session) {
                        session.setRestartHint(QSessionManager::RestartNever);
                    },
                    Qt::DirectConnection);
600
601
            }

602
603
604
            // this will wait until exiting is emitted by the used instance, if wanted...
            return needToBlock ? app.exec() : 0;
        }
605
    }
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635

    /**
     * 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;
636
            for (const QString &url : urls) {
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
                /**
                 * 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()));
653
654
        }
    }
655
#endif // USE_QT_SINGLE_APP
656

657
658
659
    /**
     * if we arrive here, we need to start a new kate instance!
     */
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676

    /**
     * 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;
    }

677
#ifndef USE_QT_SINGLE_APP
678
    /**
679
     * finally register this kate instance for dbus, don't die if no dbus is around!
680
     */
681
    const KDBusService dbusService(KDBusService::Multiple | KDBusService::NoExitOnFailure);
682
683
684
685
#else
    /**
     * else: connect the single application notifications
     */
686
    QObject::connect(&app, &SharedTools::QtSingleApplication::messageReceived, &kateApp, &KateApp::remoteMessageReceived);
687
688
689
690

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

691
#endif
692

693
694
695
696
    /**
     * start main event loop for our application
     */
    return app.exec();
697
}