MainWindow.cpp 36.9 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 "MainWindow.h"

10
// Qt
11
#include <QMouseEvent>
12
13
14
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>
15

16
// KDE
17
#include <KAcceleratorManager>
18
19
#include <KActionCollection>
#include <KActionMenu>
20
#include <KIconUtils>
21
#include <KShortcutsDialog>
Laurent Montel's avatar
Laurent Montel committed
22
#include <KLocalizedString>
23
#include <KWindowEffects>
Alex Richardson's avatar
Alex Richardson committed
24

25

26
27
28
#include <KMessageBox>
#include <KStandardAction>
#include <KStandardGuiItem>
29
#include <KWindowSystem>
30
#include <KXMLGUIFactory>
31
#include <KNotifyConfigWidget>
32

33
34
#include <kio_version.h>

35
// Konsole
36
#include "BookmarkHandler.h"
Jekyll Wu's avatar
Jekyll Wu committed
37
#include "KonsoleSettings.h"
38
#include "ViewManager.h"
Eike Hein's avatar
Eike Hein committed
39
#include "WindowSystemInfo.h"
40
41
42
43
44
45
46
47

#include "profile/ProfileList.h"
#include "profile/ProfileManager.h"

#include "session/Session.h"
#include "session/SessionController.h"
#include "session/SessionManager.h"

48
#include "settings/ConfigurationDialog.h"
Jekyll Wu's avatar
Jekyll Wu committed
49
#include "settings/GeneralSettings.h"
50
#include "settings/ProfileSettings.h"
Jekyll Wu's avatar
Jekyll Wu committed
51
#include "settings/TabBarSettings.h"
52
#include "settings/TemporaryFilesSettings.h"
53
#include "settings/ThumbnailsSettings.h"
54

55
#include "terminalDisplay/TerminalDisplay.h"
56
57
#include "widgets/ViewContainer.h"

58
59
using namespace Konsole;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
60
61
MainWindow::MainWindow() :
    KXmlGuiWindow(),
62
    _viewManager(nullptr),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
63
    _bookmarkHandler(nullptr),
64
65
    _toggleMenuBarAction(nullptr),
    _newTabMenuAction(nullptr),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
66
    _pluggedController(nullptr),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
67
68
    _menuBarInitialVisibility(true),
    _menuBarInitialVisibilityApplied(false)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
69
{
70
71
    if (!KonsoleSettings::saveGeometryOnExit()) {
        // If we are not using the global Konsole save geometry on exit,
72
73
        // remove all geometry data from [MainWindow] in Konsolerc, so KWin will
        // manage it directly
74
        KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig(QStringLiteral("konsolerc"));
75
76
77
        KConfigGroup group = konsoleConfig->group("MainWindow");
        QMap<QString, QString> configEntries = group.entryMap();
        QMapIterator<QString, QString> i(configEntries);
78

79
80
        while (i.hasNext()) {
            i.next();
81
82
83
// After https://bugs.kde.org/show_bug.cgi?id=415150 was fixed in 5.74,
// the config file keys changed
#if KIO_VERSION < QT_VERSION_CHECK(5, 75, 0)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
84
            if (i.key().startsWith(QLatin1String("Width"))
85
86
87
88
89
90
91
92
                || i.key().startsWith(QLatin1String("Height"))
#else
            if (i.key().contains(QLatin1String(" Width"))
                || i.key().contains(QLatin1String(" Height"))
                || i.key().contains(QLatin1String(" XPosition"))
                || i.key().contains(QLatin1String(" YPosition"))
#endif
            ) {
93
94
95
96
97
                group.deleteEntry(i.key());
            }
        }
    }

Eike Hein's avatar
Eike Hein committed
98
    updateUseTransparency();
99

100
101
102
103
    // create actions for menus
    setupActions();

    // create view manager
Kurt Hindenburg's avatar
Kurt Hindenburg committed
104
    _viewManager = new ViewManager(this, actionCollection());
105
    connect(_viewManager, &Konsole::ViewManager::empty, this, &Konsole::MainWindow::close);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
106
107
108
109
110
111
    connect(_viewManager, &Konsole::ViewManager::activeViewChanged, this,
            &Konsole::MainWindow::activeViewChanged);
    connect(_viewManager, &Konsole::ViewManager::unplugController, this,
            &Konsole::MainWindow::disconnectController);
    connect(_viewManager, &Konsole::ViewManager::viewPropertiesChanged,
            bookmarkHandler(), &Konsole::BookmarkHandler::setViews);
112
113
    connect(_viewManager, &Konsole::ViewManager::blurSettingChanged,
            this, &Konsole::MainWindow::setBlur);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
114
115
116

    connect(_viewManager, &Konsole::ViewManager::updateWindowIcon, this,
            &Konsole::MainWindow::updateWindowIcon);
117
118
119
120
    connect(_viewManager, &Konsole::ViewManager::newViewWithProfileRequest,
            this, &Konsole::MainWindow::newFromProfile);
    connect(_viewManager, &Konsole::ViewManager::newViewRequest,
            this, &Konsole::MainWindow::newTab);
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
121
122
    connect(_viewManager, &Konsole::ViewManager::terminalsDetached, this,
            &Konsole::MainWindow::terminalsDetached);
123

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
124
    setCentralWidget(_viewManager->widget());
125

126
127
128
129
    // disable automatically generated accelerators in top-level
    // menu items - to avoid conflicting with Alt+[Letter] shortcuts
    // in terminal applications
    KAcceleratorManager::setNoAccel(menuBar());
130

131
    // create menus
132
    createGUI();
133

134
    // remember the original menu accelerators for later use
135
136
    rememberMenuAccelerators();

137
    // replace standard shortcuts which cannot be used in a terminal
138
    // emulator (as they are reserved for use by terminal applications)
Jekyll Wu's avatar
Jekyll Wu committed
139
    correctStandardShortcuts();
140

141
    setProfileList(new ProfileList(true, this));
142

143
    // this must come at the end
Jekyll Wu's avatar
Jekyll Wu committed
144
    applyKonsoleSettings();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
145
146
    connect(KonsoleSettings::self(), &Konsole::KonsoleSettings::configChanged, this,
            &Konsole::MainWindow::applyKonsoleSettings);
147
148
149


    // KXMLGui is making the status bar always visible, we need to fix in a proper way.
150
    if (statusBar() != nullptr) {
151
152
        statusBar()->installEventFilter(this);
    }
153
}
154

Eike Hein's avatar
Eike Hein committed
155
156
void MainWindow::updateUseTransparency()
{
157
158
159
160
161
    if (!WindowSystemInfo::HAVE_TRANSPARENCY) {
        return;
    }

    bool useTranslucency = KWindowSystem::compositingActive();
Eike Hein's avatar
Eike Hein committed
162
163
164
165
166
167

    setAttribute(Qt::WA_TranslucentBackground, useTranslucency);
    setAttribute(Qt::WA_NoSystemBackground, false);
    WindowSystemInfo::HAVE_TRANSPARENCY = useTranslucency;
}

168
169
void MainWindow::rememberMenuAccelerators()
{
170
171
    const QList<QAction *> actions = menuBar()->actions();
    for (QAction *menuItem : actions) {
172
173
174
175
176
        QString itemText = menuItem->text();
        menuItem->setData(itemText);
    }
}

177
178
179
180
181
182
183
184
// remove accelerators for standard menu items (eg. &File, &View, &Edit)
// etc. which are defined in kdelibs/kdeui/xmlgui/ui_standards.rc, again,
// to avoid conflicting with Alt+[Letter] terminal shortcuts
//
// TODO - Modify XMLGUI so that it allows the text for standard actions
// defined in ui_standards.rc to be re-defined in the local application
// XMLGUI file (konsoleui.rc in this case) - the text for standard items
// can then be redefined there to exclude the standard accelerators
185
186
void MainWindow::removeMenuAccelerators()
{
187
188
    const QList<QAction *> actions = menuBar()->actions();
    for (QAction *menuItem : actions) {
189
        menuItem->setText(menuItem->text().replace(QLatin1Char('&'), QString()));
190
191
    }
}
192

Jekyll Wu's avatar
Jekyll Wu committed
193
void MainWindow::restoreMenuAccelerators()
194
{
195
196
    const QList<QAction *> actions = menuBar()->actions();
    for (QAction *menuItem : actions) {
197
198
199
200
201
        QString itemText = menuItem->data().toString();
        menuItem->setText(itemText);
    }
}

Jekyll Wu's avatar
Jekyll Wu committed
202
void MainWindow::correctStandardShortcuts()
203
204
{
    // replace F1 shortcut for help contents
Kurt Hindenburg's avatar
Kurt Hindenburg committed
205
    QAction *helpAction = actionCollection()->action(QStringLiteral("help_contents"));
206
    if (helpAction != nullptr) {
207
        actionCollection()->setDefaultShortcut(helpAction, QKeySequence());
208
    }
209
210
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
211
ViewManager *MainWindow::viewManager() const
212
213
214
215
{
    return _viewManager;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
216
void MainWindow::disconnectController(SessionController *controller)
217
{
218
219
220
221
    disconnect(controller, &Konsole::SessionController::titleChanged,
               this, &Konsole::MainWindow::activeViewTitleChanged);
    disconnect(controller, &Konsole::SessionController::rawTitleChanged,
               this, &Konsole::MainWindow::updateWindowCaption);
222
223
    disconnect(controller, &Konsole::SessionController::iconChanged,
               this, &Konsole::MainWindow::updateWindowIcon);
Robert Knight's avatar
   
Robert Knight committed
224

225
226
227
228
    if (auto view = controller->view()) {
        view->removeEventFilter(this);
    }

229
230
231
232
    // KXmlGuiFactory::removeClient() will try to access actions associated
    // with the controller internally, which may not be valid after the controller
    // itself is no longer valid (after the associated session and or view have
    // been destroyed)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
233
    if (controller->isValid()) {
234
        guiFactory()->removeClient(controller);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
235
    }
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
236
237
238
239

    if (_pluggedController == controller) {
        _pluggedController = nullptr;
    }
240
241
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
242
void MainWindow::activeViewChanged(SessionController *controller)
243
{
244
245
246
    if (!SessionManager::instance()->sessionProfile(controller->session())) {
        return;
    }
247
    // associate bookmark menu with current session
248
    bookmarkHandler()->setActiveView(controller);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
249
    disconnect(bookmarkHandler(), &Konsole::BookmarkHandler::openUrl, nullptr, nullptr);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
250
251
    connect(bookmarkHandler(), &Konsole::BookmarkHandler::openUrl, controller,
            &Konsole::SessionController::openUrl);
252

253
    if (!_pluggedController.isNull()) {
254
        disconnectController(_pluggedController);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
255
    }
256

Kurt Hindenburg's avatar
Kurt Hindenburg committed
257
    Q_ASSERT(controller);
258
    _pluggedController = controller;
259
    _pluggedController->view()->installEventFilter(this);
260

261
    setBlur(ViewManager::profileHasBlurEnabled(SessionManager::instance()->sessionProfile(_pluggedController->session())));
262

263
    // listen for title changes from the current session
Kurt Hindenburg's avatar
Kurt Hindenburg committed
264
265
266
267
268
269
    connect(controller, &Konsole::SessionController::titleChanged, this,
            &Konsole::MainWindow::activeViewTitleChanged);
    connect(controller, &Konsole::SessionController::rawTitleChanged, this,
            &Konsole::MainWindow::updateWindowCaption);
    connect(controller, &Konsole::SessionController::iconChanged, this,
            &Konsole::MainWindow::updateWindowIcon);
270

Kurt Hindenburg's avatar
Kurt Hindenburg committed
271
    controller->setShowMenuAction(_toggleMenuBarAction);
272
273
274
275
276
    guiFactory()->addClient(controller);

    // update session title to match newly activated session
    activeViewTitleChanged(controller);

277
278
    // Update window icon to newly activated session's icon
    updateWindowIcon();
279
280
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
281
void MainWindow::activeViewTitleChanged(ViewProperties *properties)
282
{
283
    Q_UNUSED(properties)
284
285
286
287
288
    updateWindowCaption();
}

void MainWindow::updateWindowCaption()
{
289
    if (_pluggedController.isNull()) {
290
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
291
    }
292

Kurt Hindenburg's avatar
Kurt Hindenburg committed
293
294
    const QString &title = _pluggedController->title();
    const QString &userTitle = _pluggedController->userTitle();
295
296
297
298

    // use tab title as caption by default
    QString caption = title;

299
300
301
302
303
304
    // use window title as caption when this setting is enabled
    // if the userTitle is empty, use a blank space (using an empty string
    // removes the dash — before the application name; leaving the dash
    // looks better)
    if (KonsoleSettings::showWindowTitleOnTitleBar()) {
        !userTitle.isEmpty() ? caption = userTitle : caption = QStringLiteral(" ");
305
306
    }

307
    setCaption(caption);
308
309
}

310
311
void MainWindow::updateWindowIcon()
{
312
    if ((!_pluggedController.isNull()) && !_pluggedController->icon().isNull()) {
313
        setWindowIcon(_pluggedController->icon());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
314
    }
315
316
}

317
void MainWindow::setupActions()
318
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
319
    KActionCollection *collection = actionCollection();
320

321
    // File Menu
Kurt Hindenburg's avatar
Kurt Hindenburg committed
322
323
    _newTabMenuAction = new KActionMenu(QIcon::fromTheme(QStringLiteral("tab-new")),
                                        i18nc("@action:inmenu", "&New Tab"), collection);
324
    collection->setDefaultShortcut(_newTabMenuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_T);
325
    collection->setShortcutsConfigurable(_newTabMenuAction, true);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
326
    _newTabMenuAction->setAutoRepeat(false);
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
327
    connect(_newTabMenuAction, &KActionMenu::triggered, this, &MainWindow::newTab);
328
    collection->addAction(QStringLiteral("new-tab"), _newTabMenuAction);
329
    collection->setShortcutsConfigurable(_newTabMenuAction, true);
330

331
    QAction* menuAction = collection->addAction(QStringLiteral("clone-tab"));
Michal Humpula's avatar
Michal Humpula committed
332
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("tab-duplicate")));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
333
    menuAction->setText(i18nc("@action:inmenu", "&Clone Tab"));
334
    collection->setDefaultShortcut(menuAction, QKeySequence());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
335
    menuAction->setAutoRepeat(false);
336
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::cloneTab);
337

338
    menuAction = collection->addAction(QStringLiteral("new-window"));
Michal Humpula's avatar
Michal Humpula committed
339
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
340
    menuAction->setText(i18nc("@action:inmenu", "New &Window"));
341
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_N);
342
    menuAction->setAutoRepeat(false);
343
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::newWindow);
344

345
    menuAction = collection->addAction(QStringLiteral("close-window"));
Michal Humpula's avatar
Michal Humpula committed
346
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
347
    menuAction->setText(i18nc("@action:inmenu", "Close Window"));
348
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_Q);
349
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::close);
350
351

    // Bookmark Menu
Kurt Hindenburg's avatar
Kurt Hindenburg committed
352
    KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), collection);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
353
    _bookmarkHandler = new BookmarkHandler(collection, bookmarkMenu->menu(), true, this);
354
    collection->addAction(QStringLiteral("bookmark"), bookmarkMenu);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
355
356
    connect(_bookmarkHandler, &Konsole::BookmarkHandler::openUrls, this,
            &Konsole::MainWindow::openUrls);
357

Jekyll Wu's avatar
Jekyll Wu committed
358
    // Settings Menu
359
    _toggleMenuBarAction = KStandardAction::showMenubar(menuBar(), &QMenuBar::setVisible, collection);
360
    collection->setDefaultShortcut(_toggleMenuBarAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_M);
361

362
    // Full Screen
363
    menuAction = KStandardAction::fullScreen(this, &MainWindow::viewFullScreen, this, collection);
364
    collection->setDefaultShortcut(menuAction, Qt::Key_F11);
365

366
367
368
    KStandardAction::configureNotifications(this, &MainWindow::configureNotifications, collection);
    KStandardAction::keyBindings(this, &MainWindow::showShortcutsDialog, collection);
    KStandardAction::preferences(this, &MainWindow::showSettingsDialog, collection);
369

370
    menuAction = collection->addAction(QStringLiteral("manage-profiles"));
371
    menuAction->setText(i18nc("@action:inmenu", "Manage Profiles..."));
Michal Humpula's avatar
Michal Humpula committed
372
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
373
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::showManageProfilesDialog);
374

375
    // Set up an shortcut-only action for activating menu bar.
376
    menuAction = collection->addAction(QStringLiteral("activate-menu"));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
377
    menuAction->setText(i18nc("@item", "Activate Menu"));
378
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_F10);
379
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::activateMenuBar);
380
381
382
383
384
385
386
387
388
389
390
391
392
393

    auto action = collection->addAction(QStringLiteral("save-layout"));
    action->setEnabled(true);
    action->setText(i18nc("@action:inmenu", "Save tab layout to file"));
    connect(action, &QAction::triggered, this, [this]() {
        if (viewManager()) { viewManager()->saveLayoutFile(); }
    });

    action = collection->addAction(QStringLiteral("load-layout"));
    action->setEnabled(true);
    action->setText(i18nc("@action:inmenu", "Load tab layout from file"));
    connect(action, &QAction::triggered, this, [this]() {
        if (viewManager()) { viewManager()->loadLayoutFile(); }
    });
394
395
}

396
void MainWindow::viewFullScreen(bool fullScreen)
397
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
398
    if (fullScreen) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
399
        setWindowState(windowState() | Qt::WindowFullScreen);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
400
    } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
401
        setWindowState(windowState() & ~Qt::WindowFullScreen);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
402
    }
403
}
404

Kurt Hindenburg's avatar
Kurt Hindenburg committed
405
BookmarkHandler *MainWindow::bookmarkHandler() const
406
407
408
409
{
    return _bookmarkHandler;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
410
void MainWindow::setProfileList(ProfileList *list)
411
{
412
    profileListChanged(list->actions());
413

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
414
415
    connect(list, &Konsole::ProfileList::profileSelected, this, &MainWindow::newFromProfile);
    connect(list, &Konsole::ProfileList::actionsChanged, this, &Konsole::MainWindow::profileListChanged);
416
417
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
418
void MainWindow::profileListChanged(const QList<QAction *> &sessionActions)
419
{
420
421
    // If only 1 profile is to be shown in the menu, only display
    // it if it is the non-default profile.
422
    if (sessionActions.size() > 2) {
423
        // Update the 'New Tab' KActionMenu
424
        if (_newTabMenuAction->menu() != nullptr) {
425
426
427
428
            _newTabMenuAction->menu()->clear();
        } else {
            _newTabMenuAction->setMenu(new QMenu());
        }
429
        for (QAction *sessionAction : sessionActions) {
430
            _newTabMenuAction->menu()->addAction(sessionAction);
431

432
            auto setActionFontBold = [sessionAction](bool isBold) {
433
434
435
436
437
                QFont actionFont = sessionAction->font();
                actionFont.setBold(isBold);
                sessionAction->setFont(actionFont);
            };

438
            Profile::Ptr profile = ProfileManager::instance()->defaultProfile();
439
            if (profile && profile->name() == sessionAction->text().remove(QLatin1Char('&'))) {
440
                QIcon icon = KIconUtils::addOverlay(QIcon::fromTheme(profile->icon()), QIcon::fromTheme(QStringLiteral("emblem-favorite")), Qt::BottomRightCorner);
Michal Humpula's avatar
Michal Humpula committed
441
                sessionAction->setIcon(icon);
442
                setActionFontBold(true);
443
            } else {
444
                setActionFontBold(false);
445
            }
446
        }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
447
    } else {
448
        if (_newTabMenuAction->menu() != nullptr) {
449
450
            _newTabMenuAction->menu()->clear();
        } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
451
            _newTabMenuAction->setMenu(new QMenu());
452
        }
453
        Profile::Ptr profile = ProfileManager::instance()->defaultProfile();
454

455
        // NOTE: Compare names w/o any '&'
Kurt Hindenburg's avatar
Kurt Hindenburg committed
456
457
        if (sessionActions.size() == 2
            && sessionActions[1]->text().remove(QLatin1Char('&')) != profile->name()) {
458
            _newTabMenuAction->menu()->addAction(sessionActions[1]);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
459
        } else {
460
            _newTabMenuAction->menu()->deleteLater();
461
462
        }
    }
463
464
}

465
466
QString MainWindow::activeSessionDir() const
{
467
    if (!_pluggedController.isNull()) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
468
        if (Session *session = _pluggedController->session()) {
469
470
471
472
            // For new tabs to get the correct working directory,
            // force the updating of the currentWorkingDirectory.
            session->getDynamicTitle();
        }
473
        return _pluggedController->currentDir();
474
    }
475
    return QString();
476
477
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
478
void MainWindow::openUrls(const QList<QUrl> &urls)
479
{
480
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
481

482
    for (const auto &url : urls) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
483
        if (url.isLocalFile()) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
484
            createSession(defaultProfile, url.path());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
485
        } else if (url.scheme() == QLatin1String("ssh")) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
486
            createSSHSession(defaultProfile, url);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
487
        }
488
    }
489
490
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
491
void MainWindow::newTab()
492
{
493
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
494
    createSession(defaultProfile, activeSessionDir());
495
496
}

497
498
499
500
501
502
503
void MainWindow::addPlugin(IKonsolePlugin *plugin)
{
    _plugins.append(plugin);
    connect(_viewManager, &Konsole::ViewManager::activeViewChanged, plugin,
        &IKonsolePlugin::activeViewChanged);
}

504
505
506
507
void MainWindow::cloneTab()
{
    Q_ASSERT(_pluggedController);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
508
    Session *session = _pluggedController->session();
509
    Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
510
    if (profile) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
511
        createSession(profile, activeSessionDir());
512
    } else {
513
514
        // something must be wrong: every session should be associated with profile
        Q_ASSERT(false);
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
515
        newTab();
516
517
518
    }
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
519
Session *MainWindow::createSession(Profile::Ptr profile, const QString &directory)
520
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
521
    if (!profile) {
522
        profile = ProfileManager::instance()->defaultProfile();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
523
    }
524

525
526
    const QString newSessionDirectory = profile->startInCurrentSessionDir() ? directory : QString();
    Session *session = _viewManager->createSession(profile, newSessionDirectory);
527
528
529
530
531

    // create view before starting the session process so that the session
    // doesn't suffer a change in terminal size right after the session
    // starts.  Some applications such as GNU Screen and Midnight Commander
    // don't like this happening
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
532
533
    auto newView = _viewManager->createView(session);
    _viewManager->activeContainer()->addView(newView);
534

535
    return session;
536
537
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
538
Session *MainWindow::createSSHSession(Profile::Ptr profile, const QUrl &url)
539
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
540
    if (!profile) {
541
        profile = ProfileManager::instance()->defaultProfile();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
542
    }
543

Kurt Hindenburg's avatar
Kurt Hindenburg committed
544
    Session *session = SessionManager::instance()->createSession(profile);
545

546
    QString sshCommand = QStringLiteral("ssh ");
547
    if (url.port() > -1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
548
        sshCommand += QStringLiteral("-p %1 ").arg(url.port());
549
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
550
    if (!url.userName().isEmpty()) {
551
        sshCommand += (url.userName() + QLatin1Char('@'));
552
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
553
    if (!url.host().isEmpty()) {
554
555
556
        sshCommand += url.host();
    }

557
    session->sendTextToTerminal(sshCommand, QLatin1Char('\r'));
558
559
560
561
562

    // create view before starting the session process so that the session
    // doesn't suffer a change in terminal size right after the session
    // starts.  some applications such as GNU Screen and Midnight Commander
    // don't like this happening
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
563
564
    auto newView = _viewManager->createView(session);
    _viewManager->activeContainer()->addView(newView);
565
566
567
    return session;
}

568
569
570
571
572
void MainWindow::setFocus()
{
    _viewManager->activeView()->setFocus();
}

573
void MainWindow::newWindow()
574
{
575
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
Ahmad Samir's avatar
Ahmad Samir committed
576
    Q_EMIT newWindowRequest(defaultProfile, activeSessionDir());
577
578
}

579
580
bool MainWindow::queryClose()
{
581
582
583
    // Do not ask for confirmation during log out and power off
    // TODO: rework the dealing of this case to make it has its own confirmation
    // dialog.
584
    if (qApp->isSavingSession()) {
585
586
587
        return true;
    }

588
    // Check what processes are running, excluding the shell
589
    QStringList processesRunning;
590
591
592
    // Need to make a local copy so the begin() and end() point to the same QList
    const QList<Session*> sessionList = _viewManager->sessions();
    const QSet<Session*> uniqueSessions(sessionList.begin(), sessionList.end());
593

594
    for (Session *session : uniqueSessions) {
595
        if ((session == nullptr) || !session->isForegroundProcessActive()) {
596
            continue;
597
        }
598

599
600
        const QString defaultProc = session->program().split(QLatin1Char('/')).last();
        const QString currentProc = session->foregroundProcessName().split(QLatin1Char('/')).last();
601

Kurt Hindenburg's avatar
Kurt Hindenburg committed
602
        if (currentProc.isEmpty()) {
603
            continue;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
604
        }
605

606
607
608
609
        if (defaultProc != currentProc) {
            processesRunning.append(currentProc);
        }
    }
610
611
612
613
614
615

    // Get number of open tabs
    const int openTabs = _viewManager->viewProperties().count();

    // If no processes running (except the shell) and no extra tabs, just close
    if (processesRunning.count() == 0 && openTabs < 2) {
616
        return true;
617
    }
618

619
620
621
    // NOTE: Some, if not all, of the below KWindowSystem calls are only
    //       implemented under x11 (KDE4.8 kdelibs/kdeui/windowmanagement).

622
623
    // make sure the window is shown on current desktop and is not minimized
    KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
624
    if (isMinimized()) {
625
        KWindowSystem::unminimizeWindow(winId());
626
    }
627
628
    int result;

Laurent Montel's avatar
Laurent Montel committed
629
    if (!processesRunning.isEmpty()) {
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
        if (openTabs == 1) {
            result = KMessageBox::warningYesNoList(this,
                                                         i18ncp("@info",
                                                                "There is a process running in this window. "
                                                                "Do you still want to quit?",
                                                                "There are %1 processes running in this window. "
                                                                "Do you still want to quit?",
                                                                processesRunning.count()),
                                                         processesRunning,
                                                         i18nc("@title", "Confirm Close"),
                                                         KGuiItem(i18nc("@action:button",
                                                                        "Close &Window"),
                                                                  QStringLiteral("window-close")),
                                                         KStandardGuiItem::cancel(),
                                                         // don't ask again name is wrong but I can't update.
                                                         // this is not about tabs anymore. it's about empty tabs *or* splits.
                                                         QStringLiteral("CloseAllTabs"));
            if (result == KMessageBox::No) // No is equal to cancel closing
                result = KMessageBox::Cancel;
        } else {
            result = KMessageBox::warningYesNoCancelList(this,
                                                         i18ncp("@info",
                                                                "There is a process running in this window. "
                                                                "Do you still want to quit?",
                                                                "There are %1 processes running in this window. "
                                                                "Do you still want to quit?",
                                                                processesRunning.count()),
                                                         processesRunning,
                                                         i18nc("@title", "Confirm Close"),
                                                         KGuiItem(i18nc("@action:button",
                                                                        "Close &Window"),
                                                                  QStringLiteral("window-close")),
                                                         KGuiItem(i18nc("@action:button",
                                                                        "Close Current &Tab"),
                                                                  QStringLiteral("tab-close")),
                                                         KStandardGuiItem::cancel(),
                                                         // don't ask again name is wrong but I can't update.
                                                         // this is not about tabs anymore. it's about empty tabs *or* splits.
                                                         QStringLiteral("CloseAllTabs"));
        }
670
671
    } else {
        result = KMessageBox::warningYesNoCancel(this,
Kurt Hindenburg's avatar
Kurt Hindenburg committed
672
                                                 i18nc("@info",
673
                                                       "There are %1 open terminals in this window. "
Kurt Hindenburg's avatar
Kurt Hindenburg committed
674
675
676
677
678
679
680
681
682
                                                       "Do you still want to quit?",
                                                       openTabs),
                                                 i18nc("@title", "Confirm Close"),
                                                 KGuiItem(i18nc("@action:button", "Close &Window"),
                                                          QStringLiteral("window-close")),
                                                 KGuiItem(i18nc("@action:button",
                                                                "Close Current &Tab"),
                                                          QStringLiteral("tab-close")),
                                                 KStandardGuiItem::cancel(),
683
684
                                                 // don't ask again name is wrong but I can't update.
                                                 // this is not about tabs anymore. it's about empty tabs *or* splits.
Kurt Hindenburg's avatar
Kurt Hindenburg committed
685
                                                 QStringLiteral("CloseAllEmptyTabs"));
686
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
687
688

    switch (result) {
689
690
691
    case KMessageBox::Yes:
        return true;
    case KMessageBox::No:
692
        if ((!_pluggedController.isNull()) && (!_pluggedController->session().isNull())) {
693
694
695
696
697
            if (!(_pluggedController->session()->closeInNormalWay())) {
                if (_pluggedController->confirmForceClose()) {
                    _pluggedController->session()->closeInForceWay();
                }
            }
698
        }
699
700
701
        return false;
    case KMessageBox::Cancel:
        return false;
702
703
704
705
    }

    return true;
}
Robert Knight's avatar
   
Robert Knight committed
706

Kurt Hindenburg's avatar
Kurt Hindenburg committed
707
void MainWindow::saveProperties(KConfigGroup &group)
Robert Knight's avatar
   
Robert Knight committed
708
709
710
711
{
    _viewManager->saveSessions(group);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
712
void MainWindow::readProperties(const KConfigGroup &group)
Robert Knight's avatar
   
Robert Knight committed
713
714
715
716
{
    _viewManager->restoreSessions(group);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
717
void MainWindow::saveGlobalProperties(KConfig *config)
Robert Knight's avatar
   
Robert Knight committed
718
719
720
721
{
    SessionManager::instance()->saveSessions(config);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
722
void MainWindow::readGlobalProperties(KConfig *config)
Robert Knight's avatar
   
Robert Knight committed
723
724
725
726
{
    SessionManager::instance()->restoreSessions(config);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
727
void MainWindow::syncActiveShortcuts(KActionCollection *dest, const KActionCollection *source)
728
{
729
730
    const QList<QAction *> actionsList = source->actions();
    for (QAction *qAction : actionsList) {
731
732
        if (QAction *destQAction = dest->action(qAction->objectName())) {
            destQAction->setShortcut(qAction->shortcut());
733
734
735
        }
    }
}
736

737
void MainWindow::showShortcutsDialog()
738
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
739
740
    KShortcutsDialog dialog(KShortcutsEditor::AllActions,
                            KShortcutsEditor::LetterShortcutsDisallowed, this);
741
742

    // add actions from this window and the current session controller
743
744
    const QList<KXMLGUIClient *> clientsList = guiFactory()->clients();
    for (KXMLGUIClient *client : clientsList) {
Jekyll Wu's avatar
Jekyll Wu committed
745
746
        dialog.addCollection(client->actionCollection());
    }
747

Kurt Hindenburg's avatar
Kurt Hindenburg committed
748
    if (dialog.configure()) {
749
        // sync shortcuts for non-session actions (defined in "konsoleui.rc") in other main windows
750
751
        const QList<QWidget *> widgets = QApplication::topLevelWidgets();
        for (QWidget *mainWindowWidget : widgets) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
752
            auto *mainWindow = qobject_cast<MainWindow *>(mainWindowWidget);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
753
            if ((mainWindow != nullptr) && mainWindow != this) {
754
                syncActiveShortcuts(mainWindow->actionCollection(), actionCollection());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
755
            }
756
        }
757
        // sync shortcuts for session actions (defined in "sessionui.rc") in other session controllers.
758
759
760
        // Controllers which are currently plugged in (ie. their actions are part of the current menu)
        // must be updated immediately via syncActiveShortcuts().  Other controllers will be updated
        // when they are plugged into a main window.
761
762
        const QSet<SessionController *> allControllers = SessionController::allControllers();
        for (SessionController *controller : allControllers) {
763
            controller->reloadXML();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
764
            if ((controller->factory() != nullptr) && controller != _pluggedController) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
765
                syncActiveShortcuts(controller->actionCollection(), _pluggedController->actionCollection());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
766
            }
767
768
        }
    }
769
770
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
771
void MainWindow::newFromProfile(const Profile::Ptr &profile)
772
{
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
773
    createSession(profile, activeSessionDir());
774
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
775

776
void MainWindow::showManageProfilesDialog()
777
{
778
    showSettingsDialog(true);
779
}
780

781
void MainWindow::showSettingsDialog(const bool showProfilePage)
Jekyll Wu's avatar
Jekyll Wu committed
782
{
783
784
785
    ConfigurationDialog *confDialog = findChild<ConfigurationDialog *>();
    const QString profilePageName = i18nc("@title Preferences page name", "Profiles");

786
    if (confDialog != nullptr) {
787
788
789
790
791
792
793
        if (showProfilePage) {
            for (auto page : confDialog->findChildren<KPageWidgetItem *>()) {
                if (page->name().contains(profilePageName)) {
                    confDialog->setCurrentPage(page);
                    break;
                }
            }
794
        }
795
        confDialog->show();
Jekyll Wu's avatar
Jekyll Wu committed
796
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
797
    }
Jekyll Wu's avatar
Jekyll Wu committed
798

799
    confDialog = new ConfigurationDialog(this, KonsoleSettings::self());
Jekyll Wu's avatar
Jekyll Wu committed
800

801
802
803
804
    const QString generalPageName = i18nc("@title Preferences page name", "General");
    auto *generalPage = new KPageWidgetItem(new GeneralSettings(confDialog), generalPageName);
    generalPage->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
    confDialog->addPage(generalPage, true);
Jekyll Wu's avatar
Jekyll Wu committed
805

806
    auto *profilePage = new KPageWidgetItem(new ProfileSettings(confDialog), profilePageName);
807
    profilePage->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-profiles")));
808
    confDialog->addPage(profilePage, true);
809

810
    const QString tabBarPageName = i18nc("@title Preferences page name", "Tab Bar / Splitters");
811
812
813
    auto tabBarPage = new KPageWidgetItem(new TabBarSettings(confDialog), tabBarPageName);
    tabBarPage->setIcon(QIcon::fromTheme(QStringLiteral("system-run")));
    confDialog->addPage(tabBarPage, true);
Jekyll Wu's avatar
Jekyll Wu committed
814

815
816
    const QString temporaryFilesPageName = i18nc("@title Preferences page name", "Temporary Files");
    auto temporaryFilesPage = new KPageWidgetItem(new TemporaryFilesSettings(confDialog), temporaryFilesPageName);
817
    temporaryFilesPage->setIcon(QIcon::fromTheme(QStringLiteral("folder-temp")));
818
    confDialog->addPage(temporaryFilesPage, true);
819

820
821
822
823
824
    const QString thumbnailPageName = i18nc("@title Preferences page name", "Thumbnails");
    auto thumbnailPage = new KPageWidgetItem(new ThumbnailsSettings(confDialog), thumbnailPageName);
    thumbnailPage->setIcon(QIcon::fromTheme(QStringLiteral("image-jpeg")));
    confDialog->addPage(thumbnailPage, true);