Application.cpp 21.5 KB
Newer Older
1
/*
2
    SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
3

4
    SPDX-License-Identifier: GPL-2.0-or-later
5
6
*/

7
8
9
// Own
#include "Application.h"

10
// Qt
patrick pereira's avatar
patrick pereira committed
11
#include <QApplication>
12
13
14
#include <QHashIterator>
#include <QFileInfo>
#include <QDir>
15
#include <QCommandLineParser>
16
#include <QMenuBar>
17
#include <QStandardPaths>
18
#include <QTimer>
19

20
// KDE
21
#include <KActionCollection>
22
#include <KGlobalAccel>
23
#include <KLocalizedString>
24
25

// Konsole
26
#include "KonsoleSettings.h"
27
#include "MainWindow.h"
28
#include "ShellCommand.h"
29
#include "ViewManager.h"
30
#include "WindowSystemInfo.h"
31
#include "profile/ProfileManager.h"
32
#include "profile/ProfileCommandParser.h"
33
34
#include "session/Session.h"
#include "session/SessionManager.h"
35
#include "terminalDisplay/TerminalDisplay.h"
36
#include "widgets/ViewContainer.h"
37

38
39
#include "pluginsystem/IKonsolePlugin.h"

40
using namespace Konsole;
41

Kurt Hindenburg's avatar
Kurt Hindenburg committed
42
Application::Application(QSharedPointer<QCommandLineParser> parser,
Kurt Hindenburg's avatar
Kurt Hindenburg committed
43
                         const QStringList &customCommand) :
Kurt Hindenburg's avatar
Kurt Hindenburg committed
44
    _backgroundInstance(nullptr),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
45
46
    m_parser(parser),
    m_customCommand(customCommand)
47
{
48
    m_pluginManager.loadAllPlugins();
Stephan Binner's avatar
Stephan Binner committed
49
}
50

51
52
void Application::populateCommandLineParser(QCommandLineParser *parser)
{
53
    const auto options = QVector<QCommandLineOption> {
54
55
56
57
        { { QStringLiteral("profile") },
            i18nc("@info:shell", "Name of profile to use for new Konsole instance"),
            QStringLiteral("name")
        },
58
59
60
61
62
        { { QStringLiteral("layout") },
            i18nc("@info:shell", "json layoutfile to be loaded to use for new Konsole instance"),
            QStringLiteral("file")
        },
         { { QStringLiteral("fallback-profile") },
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
            i18nc("@info:shell", "Use the internal FALLBACK profile")
        },
        { { QStringLiteral("workdir") },
            i18nc("@info:shell", "Set the initial working directory of the new tab or window to 'dir'"),
            QStringLiteral("dir")
        },
        { { QStringLiteral("hold"), QStringLiteral("noclose") },
           i18nc("@info:shell", "Do not close the initial session automatically when it ends.")
        },
        {  {QStringLiteral("new-tab") },
            i18nc("@info:shell", "Create a new tab in an existing window rather than creating a new window")
        },
        { { QStringLiteral("tabs-from-file") },
            i18nc("@info:shell","Create tabs as specified in given tabs configuration"" file"),
            QStringLiteral("file")
        },
        { { QStringLiteral("background-mode") },
            i18nc("@info:shell", "Start Konsole in the background and bring to the front when Ctrl+Shift+F12 (by default) is pressed")
        },
        { { QStringLiteral("separate"), QStringLiteral("nofork") },
            i18nc("@info:shell", "Run in a separate process")
        },
        { { QStringLiteral("show-menubar") },
            i18nc("@info:shell", "Show the menubar, overriding the default setting")
        },
        { { QStringLiteral("hide-menubar") },
            i18nc("@info:shell", "Hide the menubar, overriding the default setting")
        },
        { { QStringLiteral("show-tabbar") },
            i18nc("@info:shell", "Show the tabbar, overriding the default setting")
        },
        { { QStringLiteral("hide-tabbar") },
            i18nc("@info:shell", "Hide the tabbar, overriding the default setting")
        },
        { { QStringLiteral("fullscreen") },
            i18nc("@info:shell", "Start Konsole in fullscreen mode")
        },
        { { QStringLiteral("notransparency") },
            i18nc("@info:shell", "Disable transparent backgrounds, even if the system supports them.")
        },
        { { QStringLiteral("list-profiles") },
            i18nc("@info:shell", "List the available profiles")
        },
        { { QStringLiteral("list-profile-properties") },
            i18nc("@info:shell", "List all the profile properties names and their type (for use with -p)")
        },
        { { QStringLiteral("p") },
            i18nc("@info:shell", "Change the value of a profile property."),
            QStringLiteral("property=value")
        },
        { { QStringLiteral("e") },
            i18nc("@info:shell", "Command to execute. This option will catch all following arguments, so use it as the last option."),
            QStringLiteral("cmd")
        }
    };
118
    for (const auto &option : options) {
119
120
121
        parser->addOption(option);
    }

122
    parser->addPositionalArgument(QStringLiteral("[args]"),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
123
                                  i18nc("@info:shell", "Arguments passed to command"));
124
125
126
127
128
129
130
131

    // Add a no-op compatibility option to make Konsole compatible with
    // Debian's policy on X terminal emulators.
    // -T is technically meant to set a title, that is not really meaningful
    // for Konsole as we have multiple user-facing options controlling
    // the title and overriding whatever is set elsewhere.
    // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=532029
    // https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s11.8.3
132
    auto titleOption = QCommandLineOption({ QStringLiteral("T") },
133
134
                                          QStringLiteral("Debian policy compatibility, not used"),
                                          QStringLiteral("value"));
135
    titleOption.setFlags(QCommandLineOption::HiddenFromHelp);
136
137
138
    parser->addOption(titleOption);
}

139
140
QStringList Application::getCustomCommand(QStringList &args)
{
141
    int i = args.indexOf(QStringLiteral("-e"));
142
143
144
145
146
147
148
149
150
151
152
153
154
    QStringList customCommand;
    if ((0 < i) && (i < (args.size() - 1))) {
        // -e was specified with at least one extra argument
        // if -e was specified without arguments, QCommandLineParser will deal
        // with that
        args.removeAt(i);
        while (args.size() > i) {
            customCommand << args.takeAt(i);
        }
    }
    return customCommand;
}

155
156
157
Application::~Application()
{
    SessionManager::instance()->closeAllSessions();
158
    ProfileManager::instance()->saveSettings();
159
160
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
161
MainWindow *Application::newMainWindow()
162
{
163
    WindowSystemInfo::HAVE_TRANSPARENCY = !m_parser->isSet(QStringLiteral("notransparency"));
164

Kurt Hindenburg's avatar
Kurt Hindenburg committed
165
    auto window = new MainWindow();
166

Kurt Hindenburg's avatar
Kurt Hindenburg committed
167
168
    connect(window, &Konsole::MainWindow::newWindowRequest, this,
            &Konsole::Application::createWindow);
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
169
    connect(window, &Konsole::MainWindow::terminalsDetached, this, &Konsole::Application::detachTerminals);
170

171
172
173
174
175
176
177
178
179
180
181
182
    if (!m_pluginManager.plugins().empty()) {
        m_pluginManager.registerMainWindow(window);
    } else {
        const QList<QAction *> allActions = window->menuBar()->actions();
        auto it = std::find_if(allActions.cbegin(), allActions.cend(), [](QAction *action) {
            return action->objectName() == QLatin1String("plugins");
        });

        if (it != allActions.cend()) {
            (*it)->setVisible(false);
        }
    }
183

184
185
186
    return window;
}

187
void Application::createWindow(const Profile::Ptr &profile, const QString &directory)
188
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
189
    MainWindow *window = newMainWindow();
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
190
    window->createSession(profile, directory);
191
    window->show();
192
193
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
194
void Application::detachTerminals(ViewSplitter *splitter,const QHash<TerminalDisplay*, Session*>& sessionsMap)
195
{
196
    auto *currentWindow = qobject_cast<MainWindow*>(sender());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
197
    MainWindow *window = newMainWindow();
198
199
    ViewManager *manager = window->viewManager();

200
201
    const QList<TerminalDisplay *> displays = splitter->findChildren<TerminalDisplay *>();
    for (TerminalDisplay* terminal : displays) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
202
203
204
        manager->attachView(terminal, sessionsMap[terminal]);
    }
    manager->activeContainer()->addSplitter(splitter);
205
206

    window->show();
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
207
208
    window->resize(currentWindow->width(), currentWindow->height());
    window->move(QCursor::pos());
209
210
}

211
int Application::newInstance()
212
{
213
    // handle session management
214

215
216
217
    // returns from processWindowArgs(args, createdNewMainWindow)
    // if a new window was created
    bool createdNewMainWindow = false;
218

219
220
    // check for arguments to print help or other information to the
    // terminal, quit if such an argument was found
Kurt Hindenburg's avatar
Kurt Hindenburg committed
221
    if (processHelpArgs()) {
222
        return 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
223
    }
Robert Knight's avatar
   
Robert Knight committed
224

225
    // create a new window or use an existing one
Kurt Hindenburg's avatar
Kurt Hindenburg committed
226
    MainWindow *window = processWindowArgs(createdNewMainWindow);
Robert Knight's avatar
   
Robert Knight committed
227

228
    if (m_parser->isSet(QStringLiteral("tabs-from-file"))) {
229
        // create new session(s) as described in file
230
231
232
233
234
235
        if (!processTabsFromFileArgs(window)) {
            return 0;
        }
    }
    // select profile to use
    Profile::Ptr baseProfile = processProfileSelectArgs();
236

237
238
239
    // process various command-line options which cause a property of the
    // selected profile to be changed
    Profile::Ptr newProfile = processProfileChangeArgs(baseProfile);
240

241
242
243
244
245
246
    // if layout file is enable load it and create session from definitions,
    // else create new session
    if (m_parser->isSet(QStringLiteral("layout"))) {
        window->viewManager()->loadLayout(m_parser->value(QStringLiteral("layout")));
    } else {
        Session *session = window->createSession(newProfile, QString());
247

248
249
250
        if (m_parser->isSet(QStringLiteral("noclose"))) {
            session->setAutoClose(false);
        }
251
    }
Robert Knight's avatar
   
Robert Knight committed
252

253
254

        // if the background-mode argument is supplied, start the background
255
    // session ( or bring to the front if it already exists )
256
    if (m_parser->isSet(QStringLiteral("background-mode"))) {
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
        startBackgroundMode(window);
    } else {
        // Qt constrains top-level windows which have not been manually
        // resized (via QWidget::resize()) to a maximum of 2/3rds of the
        //  screen size.
        //
        // This means that the terminal display might not get the width/
        // height it asks for.  To work around this, the widget must be
        // manually resized to its sizeHint().
        //
        // This problem only affects the first time the application is run.
        // run. After that KMainWindow will have manually resized the
        // window to its saved size at this point (so the Qt::WA_Resized
        // attribute will be set)

        // If not restoring size from last time or only adding new tab,
        // resize window to chosen profile size (see Bug:345403)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
274
        if (createdNewMainWindow) {
275
            QTimer::singleShot(0, window, &MainWindow::show);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
276
        } else {
277
            window->setWindowState(window->windowState() & (~Qt::WindowMinimized | Qt::WindowActive));
278
            window->show();
Martin Rys's avatar
Martin Rys committed
279
            window->activateWindow();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
280
        }
281
    }
282

283
    return 1;
284
285
}

286
/* Documentation for tab file:
287
 *
288
 * ;; is the token separator
289
 * # at the beginning of line results in line being ignored.
290
 * supported tokens: title, command, profile and workdir
291
 *
292
 * Note that the title is static and the tab will close when the
293
 * command is complete (do not use --noclose).  You can start new tabs.
294
 *
295
296
297
 * Example below will create 6 tabs as listed and a 7th default tab
title: This is the title;; command: ssh localhost
title: This is the title;; command: ssh localhost;; profile: Shell
298
title: Top this!;; command: top
299
title: mc this!;; command: mc;; workdir: /tmp
300
#this line is comment
301
302
command: ssh localhost
profile: Shell
303
*/
Kurt Hindenburg's avatar
Kurt Hindenburg committed
304
bool Application::processTabsFromFileArgs(MainWindow *window)
305
306
{
    // Open tab configuration file
307
    const QString tabsFileName(m_parser->value(QStringLiteral("tabs-from-file")));
308
    QFile tabsFile(tabsFileName);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
309
    if (!tabsFile.open(QFile::ReadOnly)) {
Laurent Montel's avatar
Laurent Montel committed
310
        qWarning() << "ERROR: Cannot open tabs file "
311
                   << tabsFileName.toLocal8Bit().data();
312
        return false;
313
    }
314

315
316
    unsigned int sessions = 0;
    while (!tabsFile.atEnd()) {
317
        QString lineString(QString::fromUtf8(tabsFile.readLine()).trimmed());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
318
        if ((lineString.isEmpty()) || (lineString[0] == QLatin1Char('#'))) {
319
            continue;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
320
        }
321
322

        QHash<QString, QString> lineTokens;
323
        QStringList lineParts = lineString.split(QStringLiteral(";;"), Qt::SkipEmptyParts);
324
325

        for (int i = 0; i < lineParts.size(); ++i) {
326
327
            QString key = lineParts.at(i).section(QLatin1Char(':'), 0, 0).trimmed().toLower();
            QString value = lineParts.at(i).section(QLatin1Char(':'), 1, -1).trimmed();
328
329
            lineTokens[key] = value;
        }
330
        // should contain at least one of 'command' and 'profile'
331
332
        if (lineTokens.contains(QStringLiteral("command"))
            || lineTokens.contains(QStringLiteral("profile"))) {
333
            createTabFromArgs(window, lineTokens);
334
335
            sessions++;
        } else {
Laurent Montel's avatar
Laurent Montel committed
336
            qWarning() << "Each line should contain at least one of 'command' and 'profile'.";
337
338
339
340
341
        }
    }
    tabsFile.close();

    if (sessions < 1) {
Laurent Montel's avatar
Laurent Montel committed
342
        qWarning() << "No valid lines found in "
343
                   << tabsFileName.toLocal8Bit().data();
344
        return false;
345
    }
346
347

    return true;
348
349
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
350
void Application::createTabFromArgs(MainWindow *window, const QHash<QString, QString> &tokens)
351
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
352
353
354
355
    const QString &title = tokens[QStringLiteral("title")];
    const QString &command = tokens[QStringLiteral("command")];
    const QString &profile = tokens[QStringLiteral("profile")];
    const QString &workdir = tokens[QStringLiteral("workdir")];
356
    const QColor &color = tokens[QStringLiteral("tabcolor")];
357

358
359
    Profile::Ptr baseProfile;
    if (!profile.isEmpty()) {
360
        baseProfile = ProfileManager::instance()->loadProfile(profile);
361
362
363
    }
    if (!baseProfile) {
        // fallback to default profile
364
        baseProfile = ProfileManager::instance()->defaultProfile();
365
    }
366

367
    Profile::Ptr newProfile = Profile::Ptr(new Profile(baseProfile));
368
    newProfile->setHidden(true);
369

370
    // FIXME: the method of determining whether to use newProfile does not
371
372
373
374
    // scale well when we support more fields in the future
    bool shouldUseNewProfile = false;

    if (!command.isEmpty()) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
375
        newProfile->setProperty(Profile::Command, command);
376
        newProfile->setProperty(Profile::Arguments, command.split(QLatin1Char(' ')));
377
378
379
380
381
382
383
384
385
        shouldUseNewProfile = true;
    }

    if (!title.isEmpty()) {
        newProfile->setProperty(Profile::LocalTabTitleFormat, title);
        newProfile->setProperty(Profile::RemoteTabTitleFormat, title);
        shouldUseNewProfile = true;
    }

386
387
388
389
390
391
    // For tab color support
    if (color.isValid()) {
        newProfile->setProperty(Profile::TabColor, color);
        shouldUseNewProfile = true;
    }

392
393
    if (m_parser->isSet(QStringLiteral("workdir"))) {
        newProfile->setProperty(Profile::Directory, m_parser->value(QStringLiteral("workdir")));
394
        shouldUseNewProfile = true;
395
    }
396

397
398
399
400
401
    if (!workdir.isEmpty()) {
        newProfile->setProperty(Profile::Directory, workdir);
        shouldUseNewProfile = true;
    }

402
    // Create the new session
Kurt Hindenburg's avatar
Kurt Hindenburg committed
403
    Profile::Ptr theProfile = shouldUseNewProfile ? newProfile : baseProfile;
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
404
    Session *session = window->createSession(theProfile, QString());
405

406
    if (m_parser->isSet(QStringLiteral("noclose"))) {
407
408
        session->setAutoClose(false);
    }
409

410
411
412
413
    if (!window->testAttribute(Qt::WA_Resized)) {
        window->resize(window->sizeHint());
    }

414
415
416
    // FIXME: this ugly hack here is to make the session start running, so that
    // its tab title is displayed as expected.
    //
Jekyll Wu's avatar
Jekyll Wu committed
417
    // This is another side effect of the commit fixing BKO 176902.
418
419
    window->show();
    window->hide();
420
421
}

422
423
// Creates a new Konsole window.
// If --new-tab is given, use existing window.
Kurt Hindenburg's avatar
Kurt Hindenburg committed
424
MainWindow *Application::processWindowArgs(bool &createdNewMainWindow)
425
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
426
    MainWindow *window = nullptr;
427
    if (m_parser->isSet(QStringLiteral("new-tab"))) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
428
        QListIterator<QWidget *> iter(QApplication::topLevelWidgets());
429
        iter.toBack();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
430
        while (iter.hasPrevious()) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
431
            window = qobject_cast<MainWindow *>(iter.previous());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
432
            if (window != nullptr) {
433
                break;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
434
            }
435
        }
436
    }
437

Kurt Hindenburg's avatar
Kurt Hindenburg committed
438
    if (window == nullptr) {
439
        createdNewMainWindow = true;
440
        window = newMainWindow();
441
442

        // override default menubar visibility
443
        if (m_parser->isSet(QStringLiteral("show-menubar"))) {
444
445
            window->setMenuBarInitialVisibility(true);
        }
446
        if (m_parser->isSet(QStringLiteral("hide-menubar"))) {
447
448
            window->setMenuBarInitialVisibility(false);
        }
449
        if (m_parser->isSet(QStringLiteral("fullscreen"))) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
450
            window->viewFullScreen(true);
451
        }
452
453
454
455
456
457
        if (m_parser->isSet(QStringLiteral("show-tabbar"))) {
            window->viewManager()->setNavigationVisibility(ViewManager::AlwaysShowNavigation);
        }
        else if (m_parser->isSet(QStringLiteral("hide-tabbar"))) {
            window->viewManager()->setNavigationVisibility(ViewManager::AlwaysHideNavigation);
        }
458
    }
459
460
    return window;
}
461

462
463
464
465
466
// Loads a profile.
// If --profile <name> is given, loads profile <name>.
// If --fallback-profile is given, loads profile FALLBACK/.
// Else loads the default profile.
Profile::Ptr Application::processProfileSelectArgs()
467
{
468
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
469

470
    if (m_parser->isSet(QStringLiteral("profile"))) {
471
        Profile::Ptr profile = ProfileManager::instance()->loadProfile(
Kurt Hindenburg's avatar
Kurt Hindenburg committed
472
473
            m_parser->value(QStringLiteral("profile")));
        if (profile) {
474
            return profile;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
475
        }
476
    } else if (m_parser->isSet(QStringLiteral("fallback-profile"))) {
477
        Profile::Ptr profile = ProfileManager::instance()->loadProfile(QStringLiteral("FALLBACK/"));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
478
        if (profile) {
479
            return profile;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
480
        }
481
    }
482
483

    return defaultProfile;
484
485
}

486
bool Application::processHelpArgs()
487
{
488
    if (m_parser->isSet(QStringLiteral("list-profiles"))) {
489
490
        listAvailableProfiles();
        return true;
491
    } else if (m_parser->isSet(QStringLiteral("list-profile-properties"))) {
492
493
494
        listProfilePropertyInfo();
        return true;
    }
495
    return false;
496
}
497

498
499
void Application::listAvailableProfiles()
{
500
    const QStringList paths = ProfileManager::instance()->availableProfilePaths();
501

502
    for (const QString &path : paths) {
503
        QFileInfo info(path);
504
        printf("%s\n", info.completeBaseName().toLocal8Bit().constData());
505
506
507
508
509
    }
}

void Application::listProfilePropertyInfo()
{
510
    Profile::Ptr tempProfile = ProfileManager::instance()->defaultProfile();
511
512
    const QStringList names = tempProfile->propertiesInfoList();

513
    for (const QString &name : names) {
514
        printf("%s\n", name.toLocal8Bit().constData());
515
516
517
    }
}

518
Profile::Ptr Application::processProfileChangeArgs(Profile::Ptr baseProfile)
519
{
Jekyll Wu's avatar
Jekyll Wu committed
520
521
    bool shouldUseNewProfile = false;

522
    Profile::Ptr newProfile = Profile::Ptr(new Profile(baseProfile));
Jordi Polo's avatar
   
Jordi Polo committed
523
    newProfile->setHidden(true);
524

525
    // change the initial working directory
526
527
    if (m_parser->isSet(QStringLiteral("workdir"))) {
        newProfile->setProperty(Profile::Directory, m_parser->value(QStringLiteral("workdir")));
Jekyll Wu's avatar
Jekyll Wu committed
528
        shouldUseNewProfile = true;
529
    }
530
531

    // temporary changes to profile options specified on the command line
532
    const QStringList profileProperties = m_parser->values(QStringLiteral("p"));
533
    for (const QString &value : profileProperties) {
534
        ProfileCommandParser parser;
535

Kurt Hindenburg's avatar
Kurt Hindenburg committed
536
537
        QHashIterator<Profile::Property, QVariant> iter(parser.parse(value));
        while (iter.hasNext()) {
538
            iter.next();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
539
            newProfile->setProperty(iter.key(), iter.value());
540
        }
Jekyll Wu's avatar
Jekyll Wu committed
541
542

        shouldUseNewProfile = true;
543
    }
544

545
    // run a custom command
546
547
548
549
    if (!m_customCommand.isEmpty()) {
        // Example: konsole -e man ls
        QString commandExec = m_customCommand[0];
        QStringList commandArguments(m_customCommand);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
550
551
        if ((m_customCommand.size() == 1)
            && (QStandardPaths::findExecutable(commandExec).isEmpty())) {
552
            // Example: konsole -e "man ls"
553
            ShellCommand shellCommand(commandExec);
554
555
556
            commandExec = shellCommand.command();
            commandArguments = shellCommand.arguments();
        }
557

Kurt Hindenburg's avatar
Kurt Hindenburg committed
558
        if (commandExec.startsWith(QLatin1String("./"))) {
559
            commandExec = QDir::currentPath() + commandExec.mid(1);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
560
        }
561

562
563
        newProfile->setProperty(Profile::Command, commandExec);
        newProfile->setProperty(Profile::Arguments, commandArguments);
Jekyll Wu's avatar
Jekyll Wu committed
564
565

        shouldUseNewProfile = true;
566
567
    }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
568
    if (shouldUseNewProfile) {
Jekyll Wu's avatar
Jekyll Wu committed
569
570
        return newProfile;
    }
571
    return baseProfile;
572
573
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
574
void Application::startBackgroundMode(MainWindow *window)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
575
{
576
    if (_backgroundInstance != nullptr) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
577
578
579
        return;
    }

580
    KActionCollection* collection = window->actionCollection();
581
582
583
    QAction* action = collection->addAction(QStringLiteral("toggle-background-window"));
    action->setObjectName(QStringLiteral("Konsole Background Mode"));
    action->setText(i18nc("@item", "Toggle Background Window"));
584
    KGlobalAccel::self()->setGlobalShortcut(action, QKeySequence(Konsole::ACCEL | Qt::SHIFT | Qt::Key_F12));
585
    connect(action, &QAction::triggered, this, &Application::toggleBackgroundInstance);
586

Kurt Hindenburg's avatar
Kurt Hindenburg committed
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
    _backgroundInstance = window;
}

void Application::toggleBackgroundInstance()
{
    Q_ASSERT(_backgroundInstance);

    if (!_backgroundInstance->isVisible()) {
        _backgroundInstance->show();
        // ensure that the active terminal display has the focus. Without
        // this, an odd problem occurred where the focus widget would change
        // each time the background instance was shown
        _backgroundInstance->setFocus();
    } else {
        _backgroundInstance->hide();
    }
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
605
void Application::slotActivateRequested(QStringList args, const QString & /*workingDir*/)
606
{
607
608
609
610
    // QCommandLineParser expects the first argument to be the executable name
    // In the current version it just strips it away
    args.prepend(qApp->applicationFilePath());

611
612
    m_customCommand = getCustomCommand(args);

613
    // We can't re-use QCommandLineParser instances, it preserves earlier parsed values
Kurt Hindenburg's avatar
Kurt Hindenburg committed
614
    auto parser = new QCommandLineParser;
615
616
617
618
    populateCommandLineParser(parser);
    parser->parse(args);
    m_parser.reset(parser);

619
620
621
    newInstance();
}