MainWindow.cpp 35.8 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
// Own
#include "MainWindow.h"
9
#include "AppColorSchemeChooser.h"
10

11
// Qt
12
13
#include <QMenu>
#include <QMenuBar>
14
#include <QMouseEvent>
15
#include <QWindow>
16

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

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

34
#include <kio_version.h>
35
#include <kwindowsystem_version.h>
36

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

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

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

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

57
#include "terminalDisplay/TerminalDisplay.h"
58
59
#include "widgets/ViewContainer.h"

60
61
#include <konsoledebug.h>

62
63
using namespace Konsole;

64
65
66
67
68
69
70
MainWindow::MainWindow()
    : KXmlGuiWindow()
    , _viewManager(nullptr)
    , _bookmarkHandler(nullptr)
    , _toggleMenuBarAction(nullptr)
    , _newTabMenuAction(nullptr)
    , _pluggedController(nullptr)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
71
{
72
73
74
75
76
    // Set the WA_NativeWindow attribute to force the creation of the QWindow.
    // Without this QWidget::windowHandle() returns 0.
    // See https://phabricator.kde.org/D23108
    setAttribute(Qt::WA_NativeWindow);

Eike Hein's avatar
Eike Hein committed
77
    updateUseTransparency();
78

79
80
81
82
    // create actions for menus
    setupActions();

    // create view manager
Kurt Hindenburg's avatar
Kurt Hindenburg committed
83
    _viewManager = new ViewManager(this, actionCollection());
84
    connect(_viewManager, &Konsole::ViewManager::empty, this, &QWidget::close);
85
86
87
88
89
90
91
92
93
    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);
    connect(_viewManager, &Konsole::ViewManager::blurSettingChanged, this, &Konsole::MainWindow::setBlur);

    connect(_viewManager, &Konsole::ViewManager::updateWindowIcon, this, &Konsole::MainWindow::updateWindowIcon);
    connect(_viewManager, &Konsole::ViewManager::newViewWithProfileRequest, this, &Konsole::MainWindow::newFromProfile);
    connect(_viewManager, &Konsole::ViewManager::newViewRequest, this, &Konsole::MainWindow::newTab);
    connect(_viewManager, &Konsole::ViewManager::terminalsDetached, this, &Konsole::MainWindow::terminalsDetached);
94

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
95
    setCentralWidget(_viewManager->widget());
96

97
98
99
100
    // disable automatically generated accelerators in top-level
    // menu items - to avoid conflicting with Alt+[Letter] shortcuts
    // in terminal applications
    KAcceleratorManager::setNoAccel(menuBar());
101

102
103
104
105
    constexpr KXmlGuiWindow::StandardWindowOptions guiOpts = ToolBar | Keys | Save | Create;
    const QString xmlFile = componentName() + QLatin1String("ui.rc"); // Typically "konsoleui.rc"
    // The "Create" flag will make it call createGUI()
    setupGUI(guiOpts, xmlFile);
106

107
    // remember the original menu accelerators for later use
108
109
    rememberMenuAccelerators();

110
    // replace standard shortcuts which cannot be used in a terminal
111
    // emulator (as they are reserved for use by terminal applications)
Jekyll Wu's avatar
Jekyll Wu committed
112
    correctStandardShortcuts();
113

114
    setProfileList(new ProfileList(true, this));
115

116
    // this must come at the end
Jekyll Wu's avatar
Jekyll Wu committed
117
    applyKonsoleSettings();
118
    connect(KonsoleSettings::self(), &Konsole::KonsoleSettings::configChanged, this, &Konsole::MainWindow::applyKonsoleSettings);
119

120
    KCrash::initialize();
121
}
122

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
bool MainWindow::wasWindowGeometrySaved() const
{
    KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig(QStringLiteral("konsolerc"));
    KConfigGroup cg = konsoleConfig->group(QStringLiteral("MainWindow"));
    if (!cg.exists()) { // First run, no existing konsolerc?
        return false;
    }

    const QMap<QString, QString> entries = cg.entryMap();
    for (auto it = entries.cbegin(), itEnd = entries.cend(); it != itEnd; ++it) {
        const QString configKey = it.key();
        if (configKey.contains(QLatin1String(" Width")) || configKey.contains(QLatin1String(" Height")) || configKey.contains(QLatin1String(" XPosition"))
            || configKey.contains(QLatin1String(" YPosition"))) {
            return true;
        }
    }

    return false;
}

Eike Hein's avatar
Eike Hein committed
143
144
void MainWindow::updateUseTransparency()
{
145
146
147
148
149
    if (!WindowSystemInfo::HAVE_TRANSPARENCY) {
        return;
    }

    bool useTranslucency = KWindowSystem::compositingActive();
Eike Hein's avatar
Eike Hein committed
150
151
152
153
154
155

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

156
157
void MainWindow::rememberMenuAccelerators()
{
158
159
    const QList<QAction *> actions = menuBar()->actions();
    for (QAction *menuItem : actions) {
160
161
162
163
164
        QString itemText = menuItem->text();
        menuItem->setData(itemText);
    }
}

165
166
167
168
169
170
171
172
// 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
173
174
void MainWindow::removeMenuAccelerators()
{
175
176
    const QList<QAction *> actions = menuBar()->actions();
    for (QAction *menuItem : actions) {
177
        menuItem->setText(menuItem->text().replace(QLatin1Char('&'), QString()));
178
179
    }
}
180

Jekyll Wu's avatar
Jekyll Wu committed
181
void MainWindow::restoreMenuAccelerators()
182
{
183
184
    const QList<QAction *> actions = menuBar()->actions();
    for (QAction *menuItem : actions) {
185
186
187
188
189
        QString itemText = menuItem->data().toString();
        menuItem->setText(itemText);
    }
}

Jekyll Wu's avatar
Jekyll Wu committed
190
void MainWindow::correctStandardShortcuts()
191
192
{
    // replace F1 shortcut for help contents
Kurt Hindenburg's avatar
Kurt Hindenburg committed
193
    QAction *helpAction = actionCollection()->action(QStringLiteral("help_contents"));
194
    if (helpAction != nullptr) {
195
        actionCollection()->setDefaultShortcut(helpAction, QKeySequence());
196
    }
197
198
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
199
ViewManager *MainWindow::viewManager() const
200
201
202
203
{
    return _viewManager;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
204
void MainWindow::disconnectController(SessionController *controller)
205
{
206
207
208
    disconnect(controller, &Konsole::SessionController::titleChanged, this, &Konsole::MainWindow::activeViewTitleChanged);
    disconnect(controller, &Konsole::SessionController::rawTitleChanged, this, &Konsole::MainWindow::updateWindowCaption);
    disconnect(controller, &Konsole::SessionController::iconChanged, this, &Konsole::MainWindow::updateWindowIcon);
Robert Knight's avatar
   
Robert Knight committed
209

210
211
212
213
    if (auto view = controller->view()) {
        view->removeEventFilter(this);
    }

214
215
216
217
    // 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
218
    if (controller->isValid()) {
219
        guiFactory()->removeClient(controller);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
220
    }
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
221
222
223
224

    if (_pluggedController == controller) {
        _pluggedController = nullptr;
    }
225
226
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
227
void MainWindow::activeViewChanged(SessionController *controller)
228
{
229
230
231
    if (!SessionManager::instance()->sessionProfile(controller->session())) {
        return;
    }
232
    // associate bookmark menu with current session
233
    bookmarkHandler()->setActiveView(controller);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
234
    disconnect(bookmarkHandler(), &Konsole::BookmarkHandler::openUrl, nullptr, nullptr);
235
    connect(bookmarkHandler(), &Konsole::BookmarkHandler::openUrl, controller, &Konsole::SessionController::openUrl);
236

237
    if (!_pluggedController.isNull()) {
238
        disconnectController(_pluggedController);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
239
    }
240

Kurt Hindenburg's avatar
Kurt Hindenburg committed
241
    Q_ASSERT(controller);
242
    _pluggedController = controller;
243
    _pluggedController->view()->installEventFilter(this);
244

245
    setBlur(ViewManager::profileHasBlurEnabled(SessionManager::instance()->sessionProfile(_pluggedController->session())));
246

247
    // listen for title changes from the current session
248
249
250
    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);
251

Kurt Hindenburg's avatar
Kurt Hindenburg committed
252
    controller->setShowMenuAction(_toggleMenuBarAction);
253
254
255
256
257
    guiFactory()->addClient(controller);

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

258
259
    // Update window icon to newly activated session's icon
    updateWindowIcon();
260
261
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
262
void MainWindow::activeViewTitleChanged(ViewProperties *properties)
263
{
264
    Q_UNUSED(properties)
265
266
267
268
269
    updateWindowCaption();
}

void MainWindow::updateWindowCaption()
{
270
    if (_pluggedController.isNull()) {
271
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
272
    }
273

Kurt Hindenburg's avatar
Kurt Hindenburg committed
274
275
    const QString &title = _pluggedController->title();
    const QString &userTitle = _pluggedController->userTitle();
276
277
278
279

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

280
281
282
283
284
285
    // 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(" ");
286
287
    }

288
    setCaption(caption);
289
290
}

291
292
void MainWindow::updateWindowIcon()
{
293
    if ((!_pluggedController.isNull()) && !_pluggedController->icon().isNull()) {
294
        setWindowIcon(_pluggedController->icon());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
295
    }
296
297
}

298
void MainWindow::setupActions()
299
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
300
    KActionCollection *collection = actionCollection();
301

302
    // File Menu
303
    _newTabMenuAction = new KActionMenu(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@action:inmenu", "&New Tab"), collection);
304
    collection->setDefaultShortcut(_newTabMenuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_T);
305
    collection->setShortcutsConfigurable(_newTabMenuAction, true);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
306
    _newTabMenuAction->setAutoRepeat(false);
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
307
    connect(_newTabMenuAction, &KActionMenu::triggered, this, &MainWindow::newTab);
308
    collection->addAction(QStringLiteral("new-tab"), _newTabMenuAction);
309
    collection->setShortcutsConfigurable(_newTabMenuAction, true);
310

311
    QAction *menuAction = collection->addAction(QStringLiteral("clone-tab"));
Michal Humpula's avatar
Michal Humpula committed
312
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("tab-duplicate")));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
313
    menuAction->setText(i18nc("@action:inmenu", "&Clone Tab"));
314
    collection->setDefaultShortcut(menuAction, QKeySequence());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
315
    menuAction->setAutoRepeat(false);
316
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::cloneTab);
317

318
    menuAction = collection->addAction(QStringLiteral("new-window"));
Michal Humpula's avatar
Michal Humpula committed
319
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
320
    menuAction->setText(i18nc("@action:inmenu", "New &Window"));
321
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_N);
322
    menuAction->setAutoRepeat(false);
323
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::newWindow);
324

325
    menuAction = collection->addAction(QStringLiteral("close-window"));
Michal Humpula's avatar
Michal Humpula committed
326
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
327
    menuAction->setText(i18nc("@action:inmenu", "Close Window"));
328
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_Q);
329
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::close);
330
331

    // Bookmark Menu
Kurt Hindenburg's avatar
Kurt Hindenburg committed
332
    KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), collection);
333
    bookmarkMenu->setPopupMode(QToolButton::InstantPopup); // Don't require press+hold
Kurt Hindenburg's avatar
Kurt Hindenburg committed
334
    _bookmarkHandler = new BookmarkHandler(collection, bookmarkMenu->menu(), true, this);
335
    collection->addAction(QStringLiteral("bookmark"), bookmarkMenu);
336
    connect(_bookmarkHandler, &Konsole::BookmarkHandler::openUrls, this, &Konsole::MainWindow::openUrls);
337

Jekyll Wu's avatar
Jekyll Wu committed
338
    // Settings Menu
339
    _toggleMenuBarAction = KStandardAction::showMenubar(menuBar(), &QMenuBar::setVisible, collection);
340
    collection->setDefaultShortcut(_toggleMenuBarAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_M);
341
    // Set up themes
342
    actionCollection()->addAction(QStringLiteral("window-colorscheme-menu"), new AppColorSchemeChooser(actionCollection()));
343

344
    // Full Screen
345
    menuAction = KStandardAction::fullScreen(this, &MainWindow::viewFullScreen, this, collection);
346
    collection->setDefaultShortcut(menuAction, Qt::Key_F11);
347

348
349
350
    KStandardAction::configureNotifications(this, &MainWindow::configureNotifications, collection);
    KStandardAction::keyBindings(this, &MainWindow::showShortcutsDialog, collection);
    KStandardAction::preferences(this, &MainWindow::showSettingsDialog, collection);
351

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

357
    // Set up an shortcut-only action for activating menu bar.
358
    menuAction = collection->addAction(QStringLiteral("activate-menu"));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
359
    menuAction->setText(i18nc("@item", "Activate Menu"));
360
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_F10);
361
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::activateMenuBar);
362
363
364
365
366

    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]() {
367
368
369
        if (viewManager()) {
            viewManager()->saveLayoutFile();
        }
370
371
372
373
374
375
    });

    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]() {
376
377
378
        if (viewManager()) {
            viewManager()->loadLayoutFile();
        }
379
    });
380
381
}

382
void MainWindow::viewFullScreen(bool fullScreen)
383
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
384
    if (fullScreen) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
385
        setWindowState(windowState() | Qt::WindowFullScreen);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
386
    } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
387
        setWindowState(windowState() & ~Qt::WindowFullScreen);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
388
    }
389
}
390

391
392
393
394
395
396
397
398
399
400
401
402
void MainWindow::applyMainWindowSettings(const KConfigGroup &config)
{
    KMainWindow::applyMainWindowSettings(config);

    // Override the menubar state from the config file
    if (_windowArgsMenuBarVisible.enabled) {
        menuBar()->setVisible(_windowArgsMenuBarVisible.showMenuBar);
    }

    _toggleMenuBarAction->setChecked(menuBar()->isVisibleTo(this));
}

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

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

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
416
void MainWindow::profileListChanged(const QList<QAction *> &sessionActions)
417
{
418
419
420
421
    // Update the 'New Tab' KActionMenu
    _newTabMenuAction->menu()->clear();
    for (QAction *sessionAction : sessionActions) {
        _newTabMenuAction->menu()->addAction(sessionAction);
422

423
424
425
426
427
428
429
430
431
432
433
        auto setActionFontBold = [sessionAction](bool isBold) {
            QFont actionFont = sessionAction->font();
            actionFont.setBold(isBold);
            sessionAction->setFont(actionFont);
        };

        Profile::Ptr profile = ProfileManager::instance()->defaultProfile();
        if (profile && profile->name() == sessionAction->text().remove(QLatin1Char('&'))) {
            QIcon icon = KIconUtils::addOverlay(QIcon::fromTheme(profile->icon()), QIcon::fromTheme(QStringLiteral("emblem-favorite")), Qt::BottomRightCorner);
            sessionAction->setIcon(icon);
            setActionFontBold(true);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
434
        } else {
435
            setActionFontBold(false);
436
437
        }
    }
438
439
}

440
441
QString MainWindow::activeSessionDir() const
{
442
    if (!_pluggedController.isNull()) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
443
        if (Session *session = _pluggedController->session()) {
444
445
446
447
            // For new tabs to get the correct working directory,
            // force the updating of the currentWorkingDirectory.
            session->getDynamicTitle();
        }
448
        return _pluggedController->currentDir();
449
    }
450
    return QString();
451
452
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
453
void MainWindow::openUrls(const QList<QUrl> &urls)
454
{
455
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
456

457
    for (const auto &url : urls) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
458
        if (url.isLocalFile()) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
459
            createSession(defaultProfile, url.path());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
460
        } else if (url.scheme() == QLatin1String("ssh")) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
461
            createSSHSession(defaultProfile, url);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
462
        }
463
    }
464
465
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
466
void MainWindow::newTab()
467
{
468
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
469
    createSession(defaultProfile, activeSessionDir());
470
471
}

472
473
474
void MainWindow::addPlugin(IKonsolePlugin *plugin)
{
    _plugins.append(plugin);
475
    connect(_viewManager, &Konsole::ViewManager::activeViewChanged, plugin, &IKonsolePlugin::activeViewChanged);
476
477
}

478
479
480
481
void MainWindow::cloneTab()
{
    Q_ASSERT(_pluggedController);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
482
    Session *session = _pluggedController->session();
483
    Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
484
    if (profile) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
485
        createSession(profile, activeSessionDir());
486
    } else {
487
488
        // something must be wrong: every session should be associated with profile
        Q_ASSERT(false);
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
489
        newTab();
490
491
492
    }
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
493
Session *MainWindow::createSession(Profile::Ptr profile, const QString &directory)
494
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
495
    if (!profile) {
496
        profile = ProfileManager::instance()->defaultProfile();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
497
    }
498

499
500
    const QString newSessionDirectory = profile->startInCurrentSessionDir() ? directory : QString();
    Session *session = _viewManager->createSession(profile, newSessionDirectory);
501
502
503
504
505

    // 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
506
507
    auto newView = _viewManager->createView(session);
    _viewManager->activeContainer()->addView(newView);
508

509
    return session;
510
511
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
512
Session *MainWindow::createSSHSession(Profile::Ptr profile, const QUrl &url)
513
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
514
    if (!profile) {
515
        profile = ProfileManager::instance()->defaultProfile();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
516
    }
517

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

520
    QString sshCommand = QStringLiteral("ssh ");
521
    if (url.port() > -1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
522
        sshCommand += QStringLiteral("-p %1 ").arg(url.port());
523
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
524
    if (!url.userName().isEmpty()) {
525
        sshCommand += (url.userName() + QLatin1Char('@'));
526
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
527
    if (!url.host().isEmpty()) {
528
529
530
        sshCommand += url.host();
    }

531
    session->sendTextToTerminal(sshCommand, QLatin1Char('\r'));
532
533
534
535
536

    // 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
537
538
    auto newView = _viewManager->createView(session);
    _viewManager->activeContainer()->addView(newView);
539
540
541
    return session;
}

542
543
544
545
546
void MainWindow::setFocus()
{
    _viewManager->activeView()->setFocus();
}

547
void MainWindow::newWindow()
548
{
549
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
Ahmad Samir's avatar
Ahmad Samir committed
550
    Q_EMIT newWindowRequest(defaultProfile, activeSessionDir());
551
552
}

553
554
bool MainWindow::queryClose()
{
555
556
557
    // 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.
558
    if (qApp->isSavingSession()) {
559
560
561
        return true;
    }

562
    // Check what processes are running, excluding the shell
563
    QStringList processesRunning;
564
    // Need to make a local copy so the begin() and end() point to the same QList
565
566
    const QList<Session *> sessionList = _viewManager->sessions();
    const QSet<Session *> uniqueSessions(sessionList.begin(), sessionList.end());
567

568
    for (Session *session : uniqueSessions) {
569
        if ((session == nullptr) || !session->isForegroundProcessActive()) {
570
            continue;
571
        }
572

573
574
        const QString defaultProc = session->program().split(QLatin1Char('/')).last();
        const QString currentProc = session->foregroundProcessName().split(QLatin1Char('/')).last();
575

Kurt Hindenburg's avatar
Kurt Hindenburg committed
576
        if (currentProc.isEmpty()) {
577
            continue;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
578
        }
579

580
581
582
583
        if (defaultProc != currentProc) {
            processesRunning.append(currentProc);
        }
    }
584
585
586
587
588
589

    // 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) {
590
        return true;
591
    }
592

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

596
597
    // make sure the window is shown on current desktop and is not minimized
    KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
598
    if (isMinimized()) {
599
        KWindowSystem::unminimizeWindow(winId());
600
    }
601
602
    int result;

Laurent Montel's avatar
Laurent Montel committed
603
    if (!processesRunning.isEmpty()) {
604
605
        if (openTabs == 1) {
            result = KMessageBox::warningYesNoList(this,
606
607
608
609
610
611
612
613
614
615
616
617
618
                                                   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"));
619
620
621
622
623
624
625
626
627
628
629
630
            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"),
631
632
                                                         KGuiItem(i18nc("@action:button", "Close &Window"), QStringLiteral("window-close")),
                                                         KGuiItem(i18nc("@action:button", "Close Current &Tab"), QStringLiteral("tab-close")),
633
634
635
636
637
                                                         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"));
        }
638
639
    } else {
        result = KMessageBox::warningYesNoCancel(this,
Kurt Hindenburg's avatar
Kurt Hindenburg committed
640
                                                 i18nc("@info",
641
                                                       "There are %1 open terminals in this window. "
Kurt Hindenburg's avatar
Kurt Hindenburg committed
642
643
644
                                                       "Do you still want to quit?",
                                                       openTabs),
                                                 i18nc("@title", "Confirm Close"),
645
646
                                                 KGuiItem(i18nc("@action:button", "Close &Window"), QStringLiteral("window-close")),
                                                 KGuiItem(i18nc("@action:button", "Close Current &Tab"), QStringLiteral("tab-close")),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
647
                                                 KStandardGuiItem::cancel(),
648
649
                                                 // 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
650
                                                 QStringLiteral("CloseAllEmptyTabs"));
651
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
652
653

    switch (result) {
654
655
656
    case KMessageBox::Yes:
        return true;
    case KMessageBox::No:
657
        if ((!_pluggedController.isNull()) && (!_pluggedController->session().isNull())) {
658
659
660
661
662
            if (!(_pluggedController->session()->closeInNormalWay())) {
                if (_pluggedController->confirmForceClose()) {
                    _pluggedController->session()->closeInForceWay();
                }
            }
663
        }
664
665
666
        return false;
    case KMessageBox::Cancel:
        return false;
667
668
669
670
    }

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
672
void MainWindow::saveProperties(KConfigGroup &group)
Robert Knight's avatar
   
Robert Knight committed
673
674
675
676
{
    _viewManager->saveSessions(group);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
677
void MainWindow::readProperties(const KConfigGroup &group)
Robert Knight's avatar
   
Robert Knight committed
678
679
680
681
{
    _viewManager->restoreSessions(group);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
682
void MainWindow::saveGlobalProperties(KConfig *config)
Robert Knight's avatar
   
Robert Knight committed
683
684
685
686
{
    SessionManager::instance()->saveSessions(config);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
687
void MainWindow::readGlobalProperties(KConfig *config)
Robert Knight's avatar
   
Robert Knight committed
688
689
690
691
{
    SessionManager::instance()->restoreSessions(config);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
692
void MainWindow::syncActiveShortcuts(KActionCollection *dest, const KActionCollection *source)
693
{
694
695
    const QList<QAction *> actionsList = source->actions();
    for (QAction *qAction : actionsList) {
696
697
        if (QAction *destQAction = dest->action(qAction->objectName())) {
            destQAction->setShortcut(qAction->shortcut());
698
699
700
        }
    }
}
701

702
void MainWindow::showShortcutsDialog()
703
{
704
    KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this);
705
706

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
712
    if (dialog.configure()) {
713
        // sync shortcuts for non-session actions (defined in "konsoleui.rc") in other main windows
714
715
        const QList<QWidget *> widgets = QApplication::topLevelWidgets();
        for (QWidget *mainWindowWidget : widgets) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
716
            auto *mainWindow = qobject_cast<MainWindow *>(mainWindowWidget);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
717
            if ((mainWindow != nullptr) && mainWindow != this) {
718
                syncActiveShortcuts(mainWindow->actionCollection(), actionCollection());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
719
            }
720
        }
721
        // sync shortcuts for session actions (defined in "sessionui.rc") in other session controllers.
722
723
724
        // 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.
725
726
        const QSet<SessionController *> allControllers = SessionController::allControllers();
        for (SessionController *controller : allControllers) {
727
            controller->reloadXML();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
728
            if ((controller->factory() != nullptr) && controller != _pluggedController) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
729
                syncActiveShortcuts(controller->actionCollection(), _pluggedController->actionCollection());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
730
            }
731
732
        }
    }
733
734
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
735
void MainWindow::newFromProfile(const Profile::Ptr &profile)
736
{
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
737
    createSession(profile, activeSessionDir());
738
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
739

740
void MainWindow::showManageProfilesDialog()
741
{
742
    showSettingsDialog(true);
743
}
744

745
void MainWindow::showSettingsDialog(const bool showProfilePage)
Jekyll Wu's avatar
Jekyll Wu committed
746
{
747
748
749
    ConfigurationDialog *confDialog = findChild<ConfigurationDialog *>();
    const QString profilePageName = i18nc("@title Preferences page name", "Profiles");

750
    if (confDialog != nullptr) {
751
752
753
754
755
756
757
        if (showProfilePage) {
            for (auto page : confDialog->findChildren<KPageWidgetItem *>()) {
                if (page->name().contains(profilePageName)) {
                    confDialog->setCurrentPage(page);
                    break;
                }
            }
758
        }
759
        confDialog->show();
Jekyll Wu's avatar
Jekyll Wu committed
760
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
761
    }
Jekyll Wu's avatar
Jekyll Wu committed
762

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

765
766
767
768
    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
769

770
771
    auto *profileSettings = new ProfileSettings(confDialog);
    auto *profilePage = new KPageWidgetItem(profileSettings, profilePageName);
772
    profilePage->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-profiles")));
773
    confDialog->addPage(profilePage, true);
774
    connect(confDialog, &QDialog::accepted, profileSettings, &ProfileSettings::slotAccepted);
775

776
    const QString tabBarPageName = i18nc("@title Preferences page name", "Tab Bar / Splitters");
777
778
779
    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
780

781
782
    const QString temporaryFilesPageName = i18nc("@title Preferences page name", "Temporary Files");
    auto temporaryFilesPage = new KPageWidgetItem(new TemporaryFilesSettings(confDialog), temporaryFilesPageName);
783
    temporaryFilesPage->setIcon(QIcon::fromTheme(QStringLiteral("folder-temp")));
784
    confDialog->addPage(temporaryFilesPage, true);
785

786
787
788
789
790
    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);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
791
    if (showProfilePage) {
792
        confDialog