main.cpp 29.3 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
133
134
    KAboutData aboutData(QStringLiteral("kate"),
                         i18n("Kate"),
                         QStringLiteral(KATE_VERSION),
                         i18n("Kate - Advanced Text Editor"),
                         KAboutLicense::LGPL_V2,
                         i18n("(c) 2000-2021 The Kate Authors"),
                         QString(),
                         QStringLiteral("https://kate-editor.org"));
135
136
137
138
139
140

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

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

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

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

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

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

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

210
211
212
213
214
215
216
    /**
     * 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
217
218
219
    const QCommandLineOption startSessionOption(QStringList() << QStringLiteral("s") << QStringLiteral("start"),
                                                i18n("Start Kate with a given session."),
                                                i18n("session"));
220
221
222
    parser.addOption(startSessionOption);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

369
370
371
372
373
        QString serviceName;

        QString start_session;
        bool session_already_opened = false;

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

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

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

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

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

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

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

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

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

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

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

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

463
            QStringList tokens;
464

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

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

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

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

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

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

508
509
                QString line;
                QString text;
510

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

601
602
603
            // this will wait until exiting is emitted by the used instance, if wanted...
            return needToBlock ? app.exec() : 0;
        }
604
    }
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

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

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

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

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

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

690
#endif
691

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