MainWindow.cpp 36.7 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
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
132
    setupGUI();
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
433
434
435
436
437
            auto setActionFont = [sessionAction](bool isBold) {
                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
443
444
445
                setActionFont(true);
            } else {
                sessionAction->setIcon(QIcon::fromTheme(profile->icon()));
                setActionFont(false);
446
            }
447
        }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
448
    } else {
449
        if (_newTabMenuAction->menu() != nullptr) {
450
451
            _newTabMenuAction->menu()->clear();
        } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
452
            _newTabMenuAction->setMenu(new QMenu());
453
        }
454
        Profile::Ptr profile = ProfileManager::instance()->defaultProfile();
455

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

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

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

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

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

498
499
500
501
void MainWindow::cloneTab()
{
    Q_ASSERT(_pluggedController);

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

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

519
520
    const QString newSessionDirectory = profile->startInCurrentSessionDir() ? directory : QString();
    Session *session = _viewManager->createSession(profile, newSessionDirectory);
521
522
523
524
525

    // 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
526
527
    auto newView = _viewManager->createView(session);
    _viewManager->activeContainer()->addView(newView);
528
    return session;
529
530
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
531
Session *MainWindow::createSSHSession(Profile::Ptr profile, const QUrl &url)
532
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
533
    if (!profile) {
534
        profile = ProfileManager::instance()->defaultProfile();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
535
    }
536

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

539
    QString sshCommand = QStringLiteral("ssh ");
540
    if (url.port() > -1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
541
        sshCommand += QStringLiteral("-p %1 ").arg(url.port());
542
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
543
    if (!url.userName().isEmpty()) {
544
        sshCommand += (url.userName() + QLatin1Char('@'));
545
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
546
    if (!url.host().isEmpty()) {
547
548
549
        sshCommand += url.host();
    }

550
    session->sendTextToTerminal(sshCommand, QLatin1Char('\r'));
551
552
553
554
555

    // 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
556
557
    auto newView = _viewManager->createView(session);
    _viewManager->activeContainer()->addView(newView);
558
559
560
    return session;
}

561
562
563
564
565
void MainWindow::setFocus()
{
    _viewManager->activeView()->setFocus();
}

566
void MainWindow::newWindow()
567
{
568
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
Ahmad Samir's avatar
Ahmad Samir committed
569
    Q_EMIT newWindowRequest(defaultProfile, activeSessionDir());
570
571
}

572
573
bool MainWindow::queryClose()
{
574
575
576
    // 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.
577
    if (qApp->isSavingSession()) {
578
579
580
        return true;
    }

581
    // Check what processes are running, excluding the shell
582
    QStringList processesRunning;
583
584
585
    // 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());
586

587
    for (Session *session : uniqueSessions) {
588
        if ((session == nullptr) || !session->isForegroundProcessActive()) {
589
            continue;
590
        }
591

592
593
        const QString defaultProc = session->program().split(QLatin1Char('/')).last();
        const QString currentProc = session->foregroundProcessName().split(QLatin1Char('/')).last();
594

Kurt Hindenburg's avatar
Kurt Hindenburg committed
595
        if (currentProc.isEmpty()) {
596
            continue;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
597
        }
598

599
600
601
602
        if (defaultProc != currentProc) {
            processesRunning.append(currentProc);
        }
    }
603
604
605
606
607
608

    // 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) {
609
        return true;
610
    }
611

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

615
616
    // make sure the window is shown on current desktop and is not minimized
    KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
617
    if (isMinimized()) {
618
        KWindowSystem::unminimizeWindow(winId());
619
    }
620
621
    int result;

Laurent Montel's avatar
Laurent Montel committed
622
    if (!processesRunning.isEmpty()) {
623
624
625
626
627
628
629
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
        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"));
        }
663
664
    } else {
        result = KMessageBox::warningYesNoCancel(this,
Kurt Hindenburg's avatar
Kurt Hindenburg committed
665
                                                 i18nc("@info",
666
                                                       "There are %1 open terminals in this window. "
Kurt Hindenburg's avatar
Kurt Hindenburg committed
667
668
669
670
671
672
673
674
675
                                                       "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(),
676
677
                                                 // 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
678
                                                 QStringLiteral("CloseAllEmptyTabs"));
679
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
680
681

    switch (result) {
682
683
684
    case KMessageBox::Yes:
        return true;
    case KMessageBox::No:
685
        if ((!_pluggedController.isNull()) && (!_pluggedController->session().isNull())) {
686
687
688
689
690
            if (!(_pluggedController->session()->closeInNormalWay())) {
                if (_pluggedController->confirmForceClose()) {
                    _pluggedController->session()->closeInForceWay();
                }
            }
691
        }
692
693
694
        return false;
    case KMessageBox::Cancel:
        return false;
695
696
697
698
    }

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
700
void MainWindow::saveProperties(KConfigGroup &group)
Robert Knight's avatar
   
Robert Knight committed
701
702
703
704
{
    _viewManager->saveSessions(group);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
705
void MainWindow::readProperties(const KConfigGroup &group)
Robert Knight's avatar
   
Robert Knight committed
706
707
708
709
{
    _viewManager->restoreSessions(group);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
710
void MainWindow::saveGlobalProperties(KConfig *config)
Robert Knight's avatar
   
Robert Knight committed
711
712
713
714
{
    SessionManager::instance()->saveSessions(config);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
715
void MainWindow::readGlobalProperties(KConfig *config)
Robert Knight's avatar
   
Robert Knight committed
716
717
718
719
{
    SessionManager::instance()->restoreSessions(config);
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
720
void MainWindow::syncActiveShortcuts(KActionCollection *dest, const KActionCollection *source)
721
{
722
723
    const QList<QAction *> actionsList = source->actions();
    for (QAction *qAction : actionsList) {
724
725
        if (QAction *destQAction = dest->action(qAction->objectName())) {
            destQAction->setShortcut(qAction->shortcut());
726
727
728
        }
    }
}
729

730
void MainWindow::showShortcutsDialog()
731
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
732
733
    KShortcutsDialog dialog(KShortcutsEditor::AllActions,
                            KShortcutsEditor::LetterShortcutsDisallowed, this);
734
735

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
741
    if (dialog.configure()) {
742
        // sync shortcuts for non-session actions (defined in "konsoleui.rc") in other main windows
743
744
        const QList<QWidget *> widgets = QApplication::topLevelWidgets();
        for (QWidget *mainWindowWidget : widgets) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
745
            auto *mainWindow = qobject_cast<MainWindow *>(mainWindowWidget);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
746
            if ((mainWindow != nullptr) && mainWindow != this) {
747
                syncActiveShortcuts(mainWindow->actionCollection(), actionCollection());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
748
            }
749
        }
750
        // sync shortcuts for session actions (defined in "sessionui.rc") in other session controllers.
751
752
753
        // 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.
754
755
        const QSet<SessionController *> allControllers = SessionController::allControllers();
        for (SessionController *controller : allControllers) {
756
            controller->reloadXML();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
757
            if ((controller->factory() != nullptr) && controller != _pluggedController) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
758
                syncActiveShortcuts(controller->actionCollection(), _pluggedController->actionCollection());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
759
            }
760
761
        }
    }
762
763
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
764
void MainWindow::newFromProfile(const Profile::Ptr &profile)
765
{
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
766
    createSession(profile, activeSessionDir());
767
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
768

769
void MainWindow::showManageProfilesDialog()
770
{
771
    showSettingsDialog(true);
772
}
773

774
void MainWindow::showSettingsDialog(const bool showProfilePage)
Jekyll Wu's avatar
Jekyll Wu committed
775
{
776
    static ConfigurationDialog *confDialog = nullptr;
777
    static KPageWidgetItem *profilePage = nullptr;
778
    if (confDialog != nullptr) {
779
780
781
        if (showProfilePage && profilePage != nullptr) {
            confDialog->setCurrentPage(profilePage);
        }
782
        confDialog->show();
Jekyll Wu's avatar
Jekyll Wu committed
783
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
784
    }
Jekyll Wu's avatar
Jekyll Wu committed
785

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

788
789
790
791
    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
792

793
    const QString profilePageName = i18nc("@title Preferences page name", "Profiles");
794
    profilePage = new KPageWidgetItem(new ProfileSettings(confDialog), profilePageName);
795
    profilePage->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-profiles")));
796
    confDialog->addPage(profilePage, true);
797

798
    const QString tabBarPageName = i18nc("@title Preferences page name", "Tab Bar / Splitters");
799
800
801
    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
802

803
804
    const QString temporaryFilesPageName = i18nc("@title Preferences page name", "Temporary Files");
    auto temporaryFilesPage = new KPageWidgetItem(new TemporaryFilesSettings(confDialog), temporaryFilesPageName);
805
    temporaryFilesPage->setIcon(QIcon::fromTheme(QStringLiteral("folder-temp")));
806
    confDialog->addPage(temporaryFilesPage, true);
807

808
809
810
811
812
    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
813
    if (showProfilePage) {
814
        confDialog->setCurrentPage(profilePage);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
815
    }
816

817
    confDialog->show();