MainWindow.cpp 35.8 KB
Newer Older
1
/*
2
    Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301  USA.
*/

20 21 22
// Own
#include "MainWindow.h"

23
// Qt
24
#include <QMouseEvent>
25

26
// KDE
27
#include <KAcceleratorManager>
28 29
#include <KActionCollection>
#include <KActionMenu>
30
#include <KShortcutsDialog>
Laurent Montel's avatar
Laurent Montel committed
31
#include <KLocalizedString>
32
#include <KWindowEffects>
Alex Richardson's avatar
Alex Richardson committed
33

Laurent Montel's avatar
Laurent Montel committed
34
#include <QMenu>
Laurent Montel's avatar
Laurent Montel committed
35
#include <QMenuBar>
36 37 38
#include <KMessageBox>
#include <KStandardAction>
#include <KStandardGuiItem>
39
#include <KWindowSystem>
40
#include <KXMLGUIFactory>
41
#include <KNotifyConfigWidget>
Michal Humpula's avatar
Michal Humpula committed
42
#include <KIconLoader>
43 44

// Konsole
45
#include "BookmarkHandler.h"
Jekyll Wu's avatar
Jekyll Wu committed
46
#include "KonsoleSettings.h"
47
#include "ViewManager.h"
Eike Hein's avatar
Eike Hein committed
48
#include "WindowSystemInfo.h"
49 50 51 52 53 54 55 56

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

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

57
#include "settings/ConfigurationDialog.h"
Jekyll Wu's avatar
Jekyll Wu committed
58
#include "settings/GeneralSettings.h"
59
#include "settings/ProfileSettings.h"
Jekyll Wu's avatar
Jekyll Wu committed
60
#include "settings/TabBarSettings.h"
61
#include "settings/TemporaryFilesSettings.h"
62
#include "settings/ThumbnailsSettings.h"
63

64 65 66
#include "widgets/TerminalDisplay.h"
#include "widgets/ViewContainer.h"

67 68
using namespace Konsole;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
69 70
MainWindow::MainWindow() :
    KXmlGuiWindow(),
71
    _viewManager(nullptr),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
72
    _bookmarkHandler(nullptr),
73 74
    _toggleMenuBarAction(nullptr),
    _newTabMenuAction(nullptr),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
75
    _pluggedController(nullptr),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
76 77
    _menuBarInitialVisibility(true),
    _menuBarInitialVisibilityApplied(false)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
78
{
79 80 81 82
    if (!KonsoleSettings::saveGeometryOnExit()) {
        // If we are not using the global Konsole save geometry on exit,
        // remove all Height and Width from [MainWindow] from konsolerc
        // Each screen resolution will have entries (Width 1280=619)
83
        KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig(QStringLiteral("konsolerc"));
84 85 86 87 88
        KConfigGroup group = konsoleConfig->group("MainWindow");
        QMap<QString, QString> configEntries = group.entryMap();
        QMapIterator<QString, QString> i(configEntries);
        while (i.hasNext()) {
            i.next();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
89
            if (i.key().startsWith(QLatin1String("Width"))
Kurt Hindenburg's avatar
Kurt Hindenburg committed
90
                || i.key().startsWith(QLatin1String("Height"))) {
91 92 93 94 95
                group.deleteEntry(i.key());
            }
        }
    }

Eike Hein's avatar
Eike Hein committed
96
    updateUseTransparency();
97

98 99 100 101
    // create actions for menus
    setupActions();

    // create view manager
Kurt Hindenburg's avatar
Kurt Hindenburg committed
102
    _viewManager = new ViewManager(this, actionCollection());
103
    connect(_viewManager, &Konsole::ViewManager::empty, this, &Konsole::MainWindow::close);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
104 105 106 107 108 109
    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);
110 111
    connect(_viewManager, &Konsole::ViewManager::blurSettingChanged,
            this, &Konsole::MainWindow::setBlur);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
112 113 114

    connect(_viewManager, &Konsole::ViewManager::updateWindowIcon, this,
            &Konsole::MainWindow::updateWindowIcon);
115 116 117 118
    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
119 120
    connect(_viewManager, &Konsole::ViewManager::terminalsDetached, this,
            &Konsole::MainWindow::terminalsDetached);
121

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
122
    setCentralWidget(_viewManager->widget());
123

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

129 130
    // create menus
    createGUI();
131

132
    // remember the original menu accelerators for later use
133 134
    rememberMenuAccelerators();

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

139
    setProfileList(new ProfileList(true, this));
140

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

Eike Hein's avatar
Eike Hein committed
147 148
void MainWindow::updateUseTransparency()
{
149 150 151 152 153
    if (!WindowSystemInfo::HAVE_TRANSPARENCY) {
        return;
    }

    bool useTranslucency = KWindowSystem::compositingActive();
Eike Hein's avatar
Eike Hein committed
154 155 156 157 158 159

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

160 161
void MainWindow::rememberMenuAccelerators()
{
162 163
    const QList<QAction *> actions = menuBar()->actions();
    for (QAction *menuItem : actions) {
164 165 166 167 168
        QString itemText = menuItem->text();
        menuItem->setData(itemText);
    }
}

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

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

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
203
ViewManager *MainWindow::viewManager() const
204 205 206 207
{
    return _viewManager;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
208
void MainWindow::disconnectController(SessionController *controller)
209
{
210 211 212 213
    disconnect(controller, &Konsole::SessionController::titleChanged,
               this, &Konsole::MainWindow::activeViewTitleChanged);
    disconnect(controller, &Konsole::SessionController::rawTitleChanged,
               this, &Konsole::MainWindow::updateWindowCaption);
214 215
    disconnect(controller, &Konsole::SessionController::iconChanged,
               this, &Konsole::MainWindow::updateWindowIcon);
Robert Knight's avatar
 
Robert Knight committed
216

217 218 219 220
    if (auto view = controller->view()) {
        view->removeEventFilter(this);
    }

221 222 223 224
    // 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
225
    if (controller->isValid()) {
226
        guiFactory()->removeClient(controller);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
227
    }
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
228 229 230 231

    if (_pluggedController == controller) {
        _pluggedController = nullptr;
    }
232 233
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
234
void MainWindow::activeViewChanged(SessionController *controller)
235
{
236 237 238
    if (!SessionManager::instance()->sessionProfile(controller->session())) {
        return;
    }
239
    // associate bookmark menu with current session
240
    bookmarkHandler()->setActiveView(controller);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
241
    disconnect(bookmarkHandler(), &Konsole::BookmarkHandler::openUrl, nullptr, nullptr);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
242 243
    connect(bookmarkHandler(), &Konsole::BookmarkHandler::openUrl, controller,
            &Konsole::SessionController::openUrl);
244

245
    if (!_pluggedController.isNull()) {
246
        disconnectController(_pluggedController);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
247
    }
248

Kurt Hindenburg's avatar
Kurt Hindenburg committed
249
    Q_ASSERT(controller);
250
    _pluggedController = controller;
251
    _pluggedController->view()->installEventFilter(this);
252

253
    setBlur(ViewManager::profileHasBlurEnabled(SessionManager::instance()->sessionProfile(_pluggedController->session())));
254

255
    // listen for title changes from the current session
Kurt Hindenburg's avatar
Kurt Hindenburg committed
256 257 258 259 260 261
    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);
262

Kurt Hindenburg's avatar
Kurt Hindenburg committed
263
    controller->setShowMenuAction(_toggleMenuBarAction);
264 265 266 267 268
    guiFactory()->addClient(controller);

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

269 270
    // Update window icon to newly activated session's icon
    updateWindowIcon();
271 272
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
273
void MainWindow::activeViewTitleChanged(ViewProperties *properties)
274
{
275
    Q_UNUSED(properties)
276 277 278 279 280
    updateWindowCaption();
}

void MainWindow::updateWindowCaption()
{
281
    if (_pluggedController.isNull()) {
282
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
283
    }
284

Kurt Hindenburg's avatar
Kurt Hindenburg committed
285 286
    const QString &title = _pluggedController->title();
    const QString &userTitle = _pluggedController->userTitle();
287 288 289 290

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

291 292 293 294 295 296
    // 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(" ");
297 298
    }

299
    setCaption(caption);
300 301
}

302 303
void MainWindow::updateWindowIcon()
{
304
    if ((!_pluggedController.isNull()) && !_pluggedController->icon().isNull()) {
305
        setWindowIcon(_pluggedController->icon());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
306
    }
307 308
}

309
void MainWindow::setupActions()
310
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
311
    KActionCollection *collection = actionCollection();
312

313
    // File Menu
Kurt Hindenburg's avatar
Kurt Hindenburg committed
314 315
    _newTabMenuAction = new KActionMenu(QIcon::fromTheme(QStringLiteral("tab-new")),
                                        i18nc("@action:inmenu", "&New Tab"), collection);
316
    collection->setDefaultShortcut(_newTabMenuAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_T);
317
    collection->setShortcutsConfigurable(_newTabMenuAction, true);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
318
    _newTabMenuAction->setAutoRepeat(false);
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
319
    connect(_newTabMenuAction, &KActionMenu::triggered, this, &MainWindow::newTab);
320
    collection->addAction(QStringLiteral("new-tab"), _newTabMenuAction);
321
    collection->setShortcutsConfigurable(_newTabMenuAction, true);
322

323
    QAction* menuAction = collection->addAction(QStringLiteral("clone-tab"));
Michal Humpula's avatar
Michal Humpula committed
324
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("tab-duplicate")));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
325
    menuAction->setText(i18nc("@action:inmenu", "&Clone Tab"));
326
    collection->setDefaultShortcut(menuAction, QKeySequence());
Kurt Hindenburg's avatar
Kurt Hindenburg committed
327
    menuAction->setAutoRepeat(false);
328
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::cloneTab);
329

330
    menuAction = collection->addAction(QStringLiteral("new-window"));
Michal Humpula's avatar
Michal Humpula committed
331
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
332
    menuAction->setText(i18nc("@action:inmenu", "New &Window"));
333
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_N);
334
    menuAction->setAutoRepeat(false);
335
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::newWindow);
336

337
    menuAction = collection->addAction(QStringLiteral("close-window"));
Michal Humpula's avatar
Michal Humpula committed
338
    menuAction->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
339
    menuAction->setText(i18nc("@action:inmenu", "Close Window"));
340
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_Q);
341
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::close);
342 343

    // Bookmark Menu
Kurt Hindenburg's avatar
Kurt Hindenburg committed
344
    KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), collection);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
345
    _bookmarkHandler = new BookmarkHandler(collection, bookmarkMenu->menu(), true, this);
346
    collection->addAction(QStringLiteral("bookmark"), bookmarkMenu);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
347 348
    connect(_bookmarkHandler, &Konsole::BookmarkHandler::openUrls, this,
            &Konsole::MainWindow::openUrls);
349

Jekyll Wu's avatar
Jekyll Wu committed
350
    // Settings Menu
351
    _toggleMenuBarAction = KStandardAction::showMenubar(menuBar(), &QMenuBar::setVisible, collection);
352
    collection->setDefaultShortcut(_toggleMenuBarAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_M);
353

354
    // Full Screen
355
    menuAction = KStandardAction::fullScreen(this, &MainWindow::viewFullScreen, this, collection);
356
    collection->setDefaultShortcut(menuAction, Qt::Key_F11);
357

358 359 360
    KStandardAction::configureNotifications(this, &MainWindow::configureNotifications, collection);
    KStandardAction::keyBindings(this, &MainWindow::showShortcutsDialog, collection);
    KStandardAction::preferences(this, &MainWindow::showSettingsDialog, collection);
361

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

367
    // Set up an shortcut-only action for activating menu bar.
368
    menuAction = collection->addAction(QStringLiteral("activate-menu"));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
369
    menuAction->setText(i18nc("@item", "Activate Menu"));
370
    collection->setDefaultShortcut(menuAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_F10);
371
    connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::activateMenuBar);
372 373
}

374
void MainWindow::viewFullScreen(bool fullScreen)
375
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
376
    if (fullScreen) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
377
        setWindowState(windowState() | Qt::WindowFullScreen);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
378
    } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
379
        setWindowState(windowState() & ~Qt::WindowFullScreen);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
380
    }
381
}
382

Kurt Hindenburg's avatar
Kurt Hindenburg committed
383
BookmarkHandler *MainWindow::bookmarkHandler() const
384 385 386 387
{
    return _bookmarkHandler;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
388
void MainWindow::setProfileList(ProfileList *list)
389
{
390
    profileListChanged(list->actions());
391

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
392 393
    connect(list, &Konsole::ProfileList::profileSelected, this, &MainWindow::newFromProfile);
    connect(list, &Konsole::ProfileList::actionsChanged, this, &Konsole::MainWindow::profileListChanged);
394 395
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
396
void MainWindow::profileListChanged(const QList<QAction *> &sessionActions)
397
{
398 399
    // If only 1 profile is to be shown in the menu, only display
    // it if it is the non-default profile.
400
    if (sessionActions.size() > 2) {
401
        // Update the 'New Tab' KActionMenu
402
        if (_newTabMenuAction->menu() != nullptr) {
403 404 405 406
            _newTabMenuAction->menu()->clear();
        } else {
            _newTabMenuAction->setMenu(new QMenu());
        }
407
        for (QAction *sessionAction : sessionActions) {
408
            _newTabMenuAction->menu()->addAction(sessionAction);
409

410
            // NOTE: defaultProfile seems to not work here, sigh.
411
            Profile::Ptr profile = ProfileManager::instance()->defaultProfile();
412
            if (profile && profile->name() == sessionAction->text().remove(QLatin1Char('&'))) {
413
                QIcon icon(KIconLoader::global()->loadIcon(profile->icon(), KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(QStringLiteral("emblem-favorite"))));
Michal Humpula's avatar
Michal Humpula committed
414
                sessionAction->setIcon(icon);
415
                _newTabMenuAction->menu()->setDefaultAction(sessionAction);
416 417 418
                QFont actionFont = sessionAction->font();
                actionFont.setBold(true);
                sessionAction->setFont(actionFont);
419
            }
420
        }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
421
    } else {
422
        if (_newTabMenuAction->menu() != nullptr) {
423 424
            _newTabMenuAction->menu()->clear();
        } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
425
            _newTabMenuAction->setMenu(new QMenu());
426
        }
427
        Profile::Ptr profile = ProfileManager::instance()->defaultProfile();
428

429
        // NOTE: Compare names w/o any '&'
Kurt Hindenburg's avatar
Kurt Hindenburg committed
430 431
        if (sessionActions.size() == 2
            && sessionActions[1]->text().remove(QLatin1Char('&')) != profile->name()) {
432
            _newTabMenuAction->menu()->addAction(sessionActions[1]);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
433
        } else {
434
            _newTabMenuAction->menu()->deleteLater();
435 436
        }
    }
437 438
}

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

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

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

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

471 472 473 474
void MainWindow::cloneTab()
{
    Q_ASSERT(_pluggedController);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
475
    Session *session = _pluggedController->session();
476
    Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
477
    if (profile) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
478
        createSession(profile, activeSessionDir());
479
    } else {
480 481
        // something must be wrong: every session should be associated with profile
        Q_ASSERT(false);
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
482
        newTab();
483 484 485
    }
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
486
Session *MainWindow::createSession(Profile::Ptr profile, const QString &directory)
487
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
488
    if (!profile) {
489
        profile = ProfileManager::instance()->defaultProfile();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
490
    }
491

492 493
    const QString newSessionDirectory = profile->startInCurrentSessionDir() ? directory : QString();
    Session *session = _viewManager->createSession(profile, newSessionDirectory);
494 495 496 497 498

    // 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
499 500
    auto newView = _viewManager->createView(session);
    _viewManager->activeContainer()->addView(newView);
501
    return session;
502 503
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
504
Session *MainWindow::createSSHSession(Profile::Ptr profile, const QUrl &url)
505
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
506
    if (!profile) {
507
        profile = ProfileManager::instance()->defaultProfile();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
508
    }
509

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

512
    QString sshCommand = QStringLiteral("ssh ");
513
    if (url.port() > -1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
514
        sshCommand += QStringLiteral("-p %1 ").arg(url.port());
515
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
516
    if (!url.userName().isEmpty()) {
517
        sshCommand += (url.userName() + QLatin1Char('@'));
518
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
519
    if (!url.host().isEmpty()) {
520 521 522
        sshCommand += url.host();
    }

523
    session->sendTextToTerminal(sshCommand, QLatin1Char('\r'));
524 525 526 527 528

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

534 535 536 537 538
void MainWindow::setFocus()
{
    _viewManager->activeView()->setFocus();
}

539
void MainWindow::newWindow()
540
{
541
    Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
542
    emit newWindowRequest(defaultProfile, activeSessionDir());
543 544
}

545 546
bool MainWindow::queryClose()
{
547 548 549
    // 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.
550
    if (qApp->isSavingSession()) {
551 552 553
        return true;
    }

554
    // Check what processes are running, excluding the shell
555
    QStringList processesRunning;
556 557 558 559 560 561 562
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
    // 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());
#else
    const QSet<Session*> uniqueSessions = QSet<Session*>::fromList(_viewManager->sessions());
#endif
563

564
    for (Session *session : uniqueSessions) {
565
        if ((session == nullptr) || !session->isForegroundProcessActive()) {
566
            continue;
567
        }
568

569 570
        const QString defaultProc = session->program().split(QLatin1Char('/')).last();
        const QString currentProc = session->foregroundProcessName().split(QLatin1Char('/')).last();
571

Kurt Hindenburg's avatar
Kurt Hindenburg committed
572
        if (currentProc.isEmpty()) {
573
            continue;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
574
        }
575

576 577 578 579
        if (defaultProc != currentProc) {
            processesRunning.append(currentProc);
        }
    }
580 581 582 583 584 585

    // 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) {
586
        return true;
587
    }
588

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

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

Laurent Montel's avatar
Laurent Montel committed
599
    if (!processesRunning.isEmpty()) {
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
        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"));
        }
640 641
    } else {
        result = KMessageBox::warningYesNoCancel(this,
Kurt Hindenburg's avatar
Kurt Hindenburg committed
642
                                                 i18nc("@info",
643
                                                       "There are %1 open terminals in this window. "
Kurt Hindenburg's avatar
Kurt Hindenburg committed
644 645 646 647 648 649 650 651 652
                                                       "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(),
653 654
                                                 // 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
655
                                                 QStringLiteral("CloseAllEmptyTabs"));
656
    }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
657 658

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

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

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
682
void MainWindow::readProperties(const KConfigGroup &group)
Robert Knight's avatar
 
Robert Knight committed
683 684 685 686
{
    _viewManager->restoreSessions(group);
}

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
692
void MainWindow::readGlobalProperties(KConfig *config)
Robert Knight's avatar
 
Robert Knight committed
693 694 695 696
{
    SessionManager::instance()->restoreSessions(config);
}

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

707
void MainWindow::showShortcutsDialog()
708
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
709 710
    KShortcutsDialog dialog(KShortcutsEditor::AllActions,
                            KShortcutsEditor::LetterShortcutsDisallowed, this);
711 712

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

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