ViewManager.cpp 43.9 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 "ViewManager.h"

23 24
#include <config-konsole.h>

25
// Qt
26 27
#include <QSignalMapper>
#include <QStringList>
28
#include <QAction>
29

30
// KDE
Robert Knight's avatar
Robert Knight committed
31
#include <KAcceleratorManager>
32
#include <KLocalizedString>
33
#include <KActionCollection>
Laurent Montel's avatar
Laurent Montel committed
34
#include <KConfigGroup>
35 36

// Konsole
37
#include <windowadaptor.h>
38

39
#include "ColorScheme.h"
40
#include "ColorSchemeManager.h"
41
#include "Session.h"
42
#include "TerminalDisplay.h"
43 44
#include "SessionController.h"
#include "SessionManager.h"
45
#include "ProfileManager.h"
46
#include "ViewSplitter.h"
47
#include "Enumeration.h"
48

49 50
using namespace Konsole;

51 52
int ViewManager::lastManagerId = 0;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
53 54
ViewManager::ViewManager(QObject *parent, KActionCollection *collection) :
    QObject(parent),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
55
    _viewSplitter(nullptr),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
56 57 58 59 60 61 62 63 64
    _actionCollection(collection),
    _containerSignalMapper(new QSignalMapper(this)),
    _navigationMethod(TabbedNavigation),
    _navigationVisibility(ViewContainer::AlwaysShowNavigation),
    _navigationPosition(ViewContainer::NavigationPositionTop),
    _showQuickButtons(false),
    _newTabBehavior(PutNewTabAtTheEnd),
    _navigationStyleSheet(QString()),
    _managerId(0)
65
{
66
    // create main view area
Kurt Hindenburg's avatar
Kurt Hindenburg committed
67
    _viewSplitter = new ViewSplitter(nullptr);
68
    KAcceleratorManager::setNoAccel(_viewSplitter);
Robert Knight's avatar
Robert Knight committed
69

70 71 72 73 74
    // the ViewSplitter class supports both recursive and non-recursive splitting,
    // in non-recursive mode, all containers are inserted into the same top-level splitter
    // widget, and all the divider lines between the containers have the same orientation
    //
    // the ViewManager class is not currently able to handle a ViewSplitter in recursive-splitting
Kurt Hindenburg's avatar
Kurt Hindenburg committed
75
    // mode
76
    _viewSplitter->setRecursiveSplitting(false);
77
    _viewSplitter->setFocusPolicy(Qt::NoFocus);
78

79
    // setup actions which are related to the views
80 81 82
    setupActions();

    // emit a signal when all of the views held by this view manager are destroyed
Kurt Hindenburg's avatar
Kurt Hindenburg committed
83 84 85 86
    connect(_viewSplitter.data(), &Konsole::ViewSplitter::allContainersEmpty,
            this, &Konsole::ViewManager::empty);
    connect(_viewSplitter.data(), &Konsole::ViewSplitter::empty, this,
            &Konsole::ViewManager::empty);
87 88

    // listen for addition or removal of views from associated containers
Kurt Hindenburg's avatar
Kurt Hindenburg committed
89 90 91
    connect(_containerSignalMapper,
            static_cast<void (QSignalMapper::*)(QObject *)>(&QSignalMapper::mapped),
            this, &Konsole::ViewManager::containerViewsChanged);
92 93

    // listen for profile changes
Kurt Hindenburg's avatar
Kurt Hindenburg committed
94 95 96 97
    connect(ProfileManager::instance(), &Konsole::ProfileManager::profileChanged,
            this, &Konsole::ViewManager::profileChanged);
    connect(SessionManager::instance(), &Konsole::SessionManager::sessionUpdated,
            this, &Konsole::ViewManager::updateViewsForSession);
98 99

    //prepare DBus communication
100
    new WindowAdaptor(this);
101

102
    _managerId = ++lastManagerId;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
103 104
    QDBusConnection::sessionBus().registerObject(QLatin1String("/Windows/")
                                                 + QString::number(_managerId), this);
105 106
}

107
ViewManager::~ViewManager() = default;
108 109 110 111 112 113

int ViewManager::managerId() const
{
    return _managerId;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
114
QWidget *ViewManager::activeView() const
115
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
116
    ViewContainer *container = _viewSplitter->activeContainer();
117
    if (container != nullptr) {
118
        return container->activeView();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
119
    } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
120
        return nullptr;
121 122
    }
}
123

Kurt Hindenburg's avatar
Kurt Hindenburg committed
124
QWidget *ViewManager::widget() const
125 126 127 128
{
    return _viewSplitter;
}

129 130
void ViewManager::setupActions()
{
131
    Q_ASSERT(_actionCollection);
132
    if (_actionCollection == nullptr) {
133 134 135
        return;
    }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
136
    KActionCollection *collection = _actionCollection;
137

Kurt Hindenburg's avatar
Kurt Hindenburg committed
138 139 140 141 142 143
    QAction *nextViewAction = new QAction(i18nc("@action Shortcut entry", "Next Tab"), this);
    QAction *previousViewAction = new QAction(i18nc("@action Shortcut entry", "Previous Tab"), this);
    QAction *lastViewAction = new QAction(i18nc("@action Shortcut entry",
                                                "Switch to Last Tab"), this);
    QAction *nextContainerAction = new QAction(i18nc("@action Shortcut entry",
                                                     "Next View Container"), this);
144

Kurt Hindenburg's avatar
Kurt Hindenburg committed
145 146 147
    QAction *moveViewLeftAction = new QAction(i18nc("@action Shortcut entry", "Move Tab Left"), this);
    QAction *moveViewRightAction = new QAction(i18nc("@action Shortcut entry",
                                                     "Move Tab Right"), this);
148

149 150
    // list of actions that should only be enabled when there are multiple view
    // containers open
Kurt Hindenburg's avatar
Kurt Hindenburg committed
151
    QList<QAction *> multiViewOnlyActions;
152 153
    multiViewOnlyActions << nextContainerAction;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
154 155 156
    QAction *splitLeftRightAction = new QAction(QIcon::fromTheme(QStringLiteral("view-split-left-right")),
                                                i18nc("@action:inmenu", "Split View Left/Right"),
                                                this);
157
    collection->setDefaultShortcut(splitLeftRightAction, Konsole::ACCEL + Qt::Key_ParenLeft);
158
    collection->addAction(QStringLiteral("split-view-left-right"), splitLeftRightAction);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
159
    connect(splitLeftRightAction, &QAction::triggered, this, &Konsole::ViewManager::splitLeftRight);
160

Kurt Hindenburg's avatar
Kurt Hindenburg committed
161 162 163
    QAction *splitTopBottomAction = new QAction(QIcon::fromTheme(QStringLiteral("view-split-top-bottom")),
                                                i18nc("@action:inmenu",
                                                "Split View Top/Bottom"), this);
164
    collection->setDefaultShortcut(splitTopBottomAction, Konsole::ACCEL + Qt::Key_ParenRight);
165
    collection->addAction(QStringLiteral("split-view-top-bottom"), splitTopBottomAction);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
166
    connect(splitTopBottomAction, &QAction::triggered, this, &Konsole::ViewManager::splitTopBottom);
167

Kurt Hindenburg's avatar
Kurt Hindenburg committed
168
    QAction *closeActiveAction = new QAction(i18nc("@action:inmenu Close Active View", "Close Active"), this);
169 170 171
    closeActiveAction->setIcon(QIcon::fromTheme(QStringLiteral("view-close")));
    collection->setDefaultShortcut(closeActiveAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_X);
    closeActiveAction->setEnabled(false);
172
    collection->addAction(QStringLiteral("close-active-view"), closeActiveAction);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
173 174
    connect(closeActiveAction, &QAction::triggered, this,
            &Konsole::ViewManager::closeActiveContainer);
175 176 177

    multiViewOnlyActions << closeActiveAction;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
178 179
    QAction *closeOtherAction = new QAction(i18nc("@action:inmenu Close Other Views",
                                                  "Close Others"), this);
180 181
    collection->setDefaultShortcut(closeOtherAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_O);
    closeOtherAction->setEnabled(false);
182
    collection->addAction(QStringLiteral("close-other-views"), closeOtherAction);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
183 184
    connect(closeOtherAction, &QAction::triggered, this,
            &Konsole::ViewManager::closeOtherContainers);
185 186 187 188

    multiViewOnlyActions << closeOtherAction;

    // Expand & Shrink Active View
Kurt Hindenburg's avatar
Kurt Hindenburg committed
189 190 191
    QAction *expandActiveAction = new QAction(i18nc("@action:inmenu", "Expand View"), this);
    collection->setDefaultShortcut(expandActiveAction,
                                   Konsole::ACCEL + Qt::SHIFT + Qt::Key_BracketRight);
192
    expandActiveAction->setEnabled(false);
193
    collection->addAction(QStringLiteral("expand-active-view"), expandActiveAction);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
194 195
    connect(expandActiveAction, &QAction::triggered, this,
            &Konsole::ViewManager::expandActiveContainer);
196 197 198

    multiViewOnlyActions << expandActiveAction;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
199 200 201
    QAction *shrinkActiveAction = new QAction(i18nc("@action:inmenu", "Shrink View"), this);
    collection->setDefaultShortcut(shrinkActiveAction,
                                   Konsole::ACCEL + Qt::SHIFT + Qt::Key_BracketLeft);
202
    shrinkActiveAction->setEnabled(false);
203
    collection->addAction(QStringLiteral("shrink-active-view"), shrinkActiveAction);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
204 205
    connect(shrinkActiveAction, &QAction::triggered, this,
            &Konsole::ViewManager::shrinkActiveContainer);
206 207

    multiViewOnlyActions << shrinkActiveAction;
208

209
#if defined(ENABLE_DETACHING)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
210
    QAction *detachViewAction = collection->addAction(QStringLiteral("detach-view"));
211 212 213 214 215 216
    detachViewAction->setIcon(QIcon::fromTheme(QStringLiteral("tab-detach")));
    detachViewAction->setText(i18nc("@action:inmenu", "D&etach Current Tab"));
    // Ctrl+Shift+D is not used as a shortcut by default because it is too close
    // to Ctrl+D - which will terminate the session in many cases
    collection->setDefaultShortcut(detachViewAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_H);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
217 218 219
    connect(this, &Konsole::ViewManager::splitViewToggle, this,
            &Konsole::ViewManager::updateDetachViewState);
    connect(detachViewAction, &QAction::triggered, this, &Konsole::ViewManager::detachActiveView);
220
#endif
221

222
    // Next / Previous View , Next Container
223 224 225 226 227 228
    collection->addAction(QStringLiteral("next-view"), nextViewAction);
    collection->addAction(QStringLiteral("previous-view"), previousViewAction);
    collection->addAction(QStringLiteral("last-tab"), lastViewAction);
    collection->addAction(QStringLiteral("next-container"), nextContainerAction);
    collection->addAction(QStringLiteral("move-view-left"), moveViewLeftAction);
    collection->addAction(QStringLiteral("move-view-right"), moveViewRightAction);
229 230 231 232

    // Switch to tab N shortcuts
    const int SWITCH_TO_TAB_COUNT = 19;
    auto switchToTabMapper = new QSignalMapper(this);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
233 234
    connect(switchToTabMapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped),
            this, &Konsole::ViewManager::switchToView);
235
    for (int i = 0; i < SWITCH_TO_TAB_COUNT; i++) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
236
        QAction *switchToTabAction = new QAction(i18nc("@action Shortcut entry", "Switch to Tab %1", i + 1), this);
237
        switchToTabMapper->setMapping(switchToTabAction, i);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
238 239
        connect(switchToTabAction, &QAction::triggered, switchToTabMapper,
                static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
240
        collection->addAction(QStringLiteral("switch-to-tab-%1").arg(i), switchToTabAction);
241
    }
Robert Knight's avatar
 
Robert Knight committed
242

Kurt Hindenburg's avatar
Kurt Hindenburg committed
243 244
    foreach (QAction *action, multiViewOnlyActions) {
        connect(this, &Konsole::ViewManager::splitViewToggle, action, &QAction::setEnabled);
245 246
    }

247
    // keyboard shortcut only actions
248 249
    const QList<QKeySequence> nextViewActionKeys{Qt::SHIFT + Qt::Key_Right, Qt::CTRL + Qt::Key_PageDown};
    collection->setDefaultShortcuts(nextViewAction, nextViewActionKeys);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
250
    connect(nextViewAction, &QAction::triggered, this, &Konsole::ViewManager::nextView);
251
    _viewSplitter->addAction(nextViewAction);
Robert Knight's avatar
 
Robert Knight committed
252

253 254
    const QList<QKeySequence> previousViewActionKeys{Qt::SHIFT + Qt::Key_Left, Qt::CTRL + Qt::Key_PageUp};
    collection->setDefaultShortcuts(previousViewAction, previousViewActionKeys);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
255
    connect(previousViewAction, &QAction::triggered, this, &Konsole::ViewManager::previousView);
256
    _viewSplitter->addAction(previousViewAction);
Robert Knight's avatar
 
Robert Knight committed
257

258
    collection->setDefaultShortcut(nextContainerAction, Qt::SHIFT + Qt::Key_Tab);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
259
    connect(nextContainerAction, &QAction::triggered, this, &Konsole::ViewManager::nextContainer);
260
    _viewSplitter->addAction(nextContainerAction);
261

262
#ifdef Q_OS_MACOS
Kurt Hindenburg's avatar
Kurt Hindenburg committed
263 264
    collection->setDefaultShortcut(moveViewLeftAction,
                                   Konsole::ACCEL + Qt::SHIFT + Qt::Key_BracketLeft);
265 266 267
#else
    collection->setDefaultShortcut(moveViewLeftAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_Left);
#endif
Kurt Hindenburg's avatar
Kurt Hindenburg committed
268 269
    connect(moveViewLeftAction, &QAction::triggered, this,
            &Konsole::ViewManager::moveActiveViewLeft);
270
    _viewSplitter->addAction(moveViewLeftAction);
271

272
#ifdef Q_OS_MACOS
Kurt Hindenburg's avatar
Kurt Hindenburg committed
273 274
    collection->setDefaultShortcut(moveViewRightAction,
                                   Konsole::ACCEL + Qt::SHIFT + Qt::Key_BracketRight);
275 276 277
#else
    collection->setDefaultShortcut(moveViewRightAction, Konsole::ACCEL + Qt::SHIFT + Qt::Key_Right);
#endif
Kurt Hindenburg's avatar
Kurt Hindenburg committed
278 279
    connect(moveViewRightAction, &QAction::triggered, this,
            &Konsole::ViewManager::moveActiveViewRight);
280
    _viewSplitter->addAction(moveViewRightAction);
281

Kurt Hindenburg's avatar
Kurt Hindenburg committed
282
    connect(lastViewAction, &QAction::triggered, this, &Konsole::ViewManager::lastView);
283
    _viewSplitter->addAction(lastViewAction);
Robert Knight's avatar
 
Robert Knight committed
284
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
285

286 287 288
void ViewManager::switchToView(int index)
{
    Q_ASSERT(index >= 0);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
289
    ViewContainer *container = _viewSplitter->activeContainer();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
290
    Q_ASSERT(container);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
291 292
    QList<QWidget *> containerViews = container->views();
    if (index >= containerViews.count()) {
293
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
294
    }
295 296
    container->setActiveView(containerViews.at(index));
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
297

298 299
void ViewManager::updateDetachViewState()
{
300
    Q_ASSERT(_actionCollection);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
301
    if (_actionCollection == nullptr) {
302
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
303
    }
304

Jekyll Wu's avatar
Jekyll Wu committed
305
    const bool splitView = _viewSplitter->containers().count() >= 2;
306
    auto activeContainer = _viewSplitter->activeContainer();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
307 308 309
    const bool shouldEnable = splitView
                              || ((activeContainer != nullptr)
                                  && activeContainer->views().count() >= 2);
310

Kurt Hindenburg's avatar
Kurt Hindenburg committed
311
    QAction *detachAction = _actionCollection->action(QStringLiteral("detach-view"));
312

Kurt Hindenburg's avatar
Kurt Hindenburg committed
313
    if ((detachAction != nullptr) && shouldEnable != detachAction->isEnabled()) {
314
        detachAction->setEnabled(shouldEnable);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
315
    }
316
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
317

318 319
void ViewManager::moveActiveViewLeft()
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
320
    ViewContainer *container = _viewSplitter->activeContainer();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
321 322
    Q_ASSERT(container);
    container->moveActiveView(ViewContainer::MoveViewLeft);
323
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
324

325 326
void ViewManager::moveActiveViewRight()
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
327
    ViewContainer *container = _viewSplitter->activeContainer();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
328 329
    Q_ASSERT(container);
    container->moveActiveView(ViewContainer::MoveViewRight);
330
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
331

Robert Knight's avatar
 
Robert Knight committed
332 333 334 335 336 337 338
void ViewManager::nextContainer()
{
    _viewSplitter->activateNextContainer();
}

void ViewManager::nextView()
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
339
    ViewContainer *container = _viewSplitter->activeContainer();
Robert Knight's avatar
 
Robert Knight committed
340

Kurt Hindenburg's avatar
Kurt Hindenburg committed
341
    Q_ASSERT(container);
Robert Knight's avatar
 
Robert Knight committed
342 343 344 345 346 347

    container->activateNextView();
}

void ViewManager::previousView()
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
348
    ViewContainer *container = _viewSplitter->activeContainer();
Robert Knight's avatar
 
Robert Knight committed
349

Kurt Hindenburg's avatar
Kurt Hindenburg committed
350
    Q_ASSERT(container);
Robert Knight's avatar
 
Robert Knight committed
351 352

    container->activatePreviousView();
353
}
354

355 356
void ViewManager::lastView()
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
357
    ViewContainer *container = _viewSplitter->activeContainer();
358

Kurt Hindenburg's avatar
Kurt Hindenburg committed
359
    Q_ASSERT(container);
360 361 362 363

    container->activateLastView();
}

364 365
void ViewManager::detachActiveView()
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
366
    // find the currently active view and remove it from its container
Kurt Hindenburg's avatar
Kurt Hindenburg committed
367
    ViewContainer *container = _viewSplitter->activeContainer();
368

369 370 371
    detachView(container, container->activeView());
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
372
void ViewManager::detachView(ViewContainer *container, QWidget *widgetView)
373
{
374 375 376 377
#if !defined(ENABLE_DETACHING)
    return;
#endif

Kurt Hindenburg's avatar
Kurt Hindenburg committed
378
    TerminalDisplay *viewToDetach = qobject_cast<TerminalDisplay *>(widgetView);
379

Kurt Hindenburg's avatar
Kurt Hindenburg committed
380
    if (viewToDetach == nullptr) {
381
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
382
    }
383

384
    emit viewDetached(_sessionMap[viewToDetach]);
385

386
    _sessionMap.remove(viewToDetach);
387 388

    // remove the view from this window
389 390
    container->removeView(viewToDetach);
    viewToDetach->deleteLater();
391 392 393 394

    // if the container from which the view was removed is now empty then it can be deleted,
    // unless it is the only container in the window, in which case it is left empty
    // so that there is always an active container
Kurt Hindenburg's avatar
Kurt Hindenburg committed
395 396
    if (_viewSplitter->containers().count() > 1
        && container->views().count() == 0) {
397
        removeContainer(container);
398 399 400
    }
}

401
void ViewManager::sessionFinished()
402
{
403 404
    // if this slot is called after the view manager's main widget
    // has been destroyed, do nothing
Kurt Hindenburg's avatar
Kurt Hindenburg committed
405
    if (_viewSplitter == nullptr) {
406
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
407
    }
408

Kurt Hindenburg's avatar
Kurt Hindenburg committed
409
    Session *session = qobject_cast<Session *>(sender());
410 411
    Q_ASSERT(session);

412
    // close attached views
Kurt Hindenburg's avatar
Kurt Hindenburg committed
413
    QList<TerminalDisplay *> children = _viewSplitter->findChildren<TerminalDisplay *>();
414

Kurt Hindenburg's avatar
Kurt Hindenburg committed
415
    foreach (TerminalDisplay *view, children) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
416
        if (_sessionMap[view] == session) {
417
            _sessionMap.remove(view);
418
            view->deleteLater();
419
        }
Robert Knight's avatar
 
Robert Knight committed
420
    }
421

Kurt Hindenburg's avatar
Kurt Hindenburg committed
422
    // This is needed to remove this controller from factory() in
423
    // order to prevent BUG: 185466 - disappearing menu popup
Kurt Hindenburg's avatar
Kurt Hindenburg committed
424
    if (_pluggedController != nullptr) {
425
        emit unplugController(_pluggedController);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
426
    }
Robert Knight's avatar
 
Robert Knight committed
427 428
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
429
void ViewManager::viewActivated(QWidget *view)
430
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
431
    Q_ASSERT(view != nullptr);
432 433 434 435 436

    // focus the activated view, this will cause the SessionController
    // to notify the world that the view has been focused and the appropriate UI
    // actions will be plugged in.
    view->setFocus(Qt::OtherFocusReason);
437 438
}

439
void ViewManager::splitLeftRight()
440
{
441
    splitView(Qt::Horizontal);
442
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
443

444
void ViewManager::splitTopBottom()
445
{
446
    splitView(Qt::Vertical);
447 448
}

449
void ViewManager::splitView(Qt::Orientation orientation)
450
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
451
    ViewContainer *container = createContainer();
452

453
    // iterate over each session which has a view in the current active
Kurt Hindenburg's avatar
Kurt Hindenburg committed
454
    // container and create a new view for that session in a new container
Kurt Hindenburg's avatar
Kurt Hindenburg committed
455 456 457
    foreach (QWidget *view, _viewSplitter->activeContainer()->views()) {
        Session *session = _sessionMap[qobject_cast<TerminalDisplay *>(view)];
        TerminalDisplay *display = createTerminalDisplay(session);
Jekyll Wu's avatar
Jekyll Wu committed
458 459
        const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
        applyProfileToView(display, profile);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
460
        ViewProperties *properties = createController(session, display);
461

462
        _sessionMap[display] = session;
463

Kurt Hindenburg's avatar
Kurt Hindenburg committed
464 465
        container->addView(display, properties);
        session->addView(display);
466
    }
467

Kurt Hindenburg's avatar
Kurt Hindenburg committed
468
    _viewSplitter->addContainer(container, orientation);
469
    emit splitViewToggle(_viewSplitter->containers().count() > 0);
470

471 472
    // focus the new container
    container->containerWidget()->setFocus();
473 474

    // ensure that the active view is focused after the split / unsplit
Kurt Hindenburg's avatar
Kurt Hindenburg committed
475
    ViewContainer *activeContainer = _viewSplitter->activeContainer();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
476
    QWidget *activeView = activeContainer != nullptr ? activeContainer->activeView() : nullptr;
477

Kurt Hindenburg's avatar
Kurt Hindenburg committed
478
    if (activeView != nullptr) {
479
        activeView->setFocus(Qt::OtherFocusReason);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
480
    }
481
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
482 483

void ViewManager::removeContainer(ViewContainer *container)
484
{
485
    // remove session map entries for views in this container
Kurt Hindenburg's avatar
Kurt Hindenburg committed
486 487
    foreach (QWidget *view, container->views()) {
        TerminalDisplay *display = qobject_cast<TerminalDisplay *>(view);
488 489
        Q_ASSERT(display);
        _sessionMap.remove(display);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
490
    }
491

492
    _viewSplitter->removeContainer(container);
493
    container->deleteLater();
494

Kurt Hindenburg's avatar
Kurt Hindenburg committed
495
    emit splitViewToggle(_viewSplitter->containers().count() > 1);
496
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
497

498
void ViewManager::expandActiveContainer()
499
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
500
    _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(), 10);
501
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
502

503
void ViewManager::shrinkActiveContainer()
504
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
505
    _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(), -10);
506
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
507

508
void ViewManager::closeActiveContainer()
509 510
{
    // only do something if there is more than one container active
Kurt Hindenburg's avatar
Kurt Hindenburg committed
511
    if (_viewSplitter->containers().count() > 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
512
        ViewContainer *container = _viewSplitter->activeContainer();
513 514

        removeContainer(container);
515

Kurt Hindenburg's avatar
Kurt Hindenburg committed
516
        // focus next container so that user can continue typing
517 518 519
        // without having to manually focus it themselves
        nextContainer();
    }
520
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
521

522
void ViewManager::closeOtherContainers()
523
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
524
    ViewContainer *active = _viewSplitter->activeContainer();
525

Kurt Hindenburg's avatar
Kurt Hindenburg committed
526 527
    foreach (ViewContainer *container, _viewSplitter->containers()) {
        if (container != active) {
528
            removeContainer(container);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
529
        }
530 531 532
    }
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
533
SessionController *ViewManager::createController(Session *session, TerminalDisplay *view)
534
{
Robert Knight's avatar
Robert Knight committed
535 536
    // create a new controller for the session, and ensure that this view manager
    // is notified when the view gains the focus
Kurt Hindenburg's avatar
Kurt Hindenburg committed
537
    auto controller = new SessionController(session, view, this);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
538 539 540 541 542 543 544 545 546 547
    connect(controller, &Konsole::SessionController::focused, this,
            &Konsole::ViewManager::controllerChanged);
    connect(session, &Konsole::Session::destroyed, controller,
            &Konsole::SessionController::deleteLater);
    connect(session, &Konsole::Session::primaryScreenInUse, controller,
            &Konsole::SessionController::setupPrimaryScreenSpecificActions);
    connect(session, &Konsole::Session::selectionChanged, controller,
            &Konsole::SessionController::selectionChanged);
    connect(view, &Konsole::TerminalDisplay::destroyed, controller,
            &Konsole::SessionController::deleteLater);
548

549
    // if this is the first controller created then set it as the active controller
Kurt Hindenburg's avatar
Kurt Hindenburg committed
550
    if (_pluggedController == nullptr) {
551
        controllerChanged(controller);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
552
    }
553

554 555 556
    return controller;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
557
void ViewManager::controllerChanged(SessionController *controller)
558
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
559
    if (controller == _pluggedController) {
560
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
561
    }
562

563
    _viewSplitter->setFocusProxy(controller->view());
564

565 566
    _pluggedController = controller;
    emit activeViewChanged(controller);
567 568
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
569
SessionController *ViewManager::activeViewController() const
570
{
571
    return _pluggedController;
572
}
573

Kurt Hindenburg's avatar
Kurt Hindenburg committed
574
IncrementalSearchBar *ViewManager::searchBar() const
575 576 577 578
{
    return _viewSplitter->activeSplitter()->activeContainer()->searchBar();
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
579
void ViewManager::createView(Session *session, ViewContainer *container, int index)
580
{
581
    // notify this view manager when the session finishes so that its view
582
    // can be deleted
583
    //
584
    // Use Qt::UniqueConnection to avoid duplicate connection
Kurt Hindenburg's avatar
Kurt Hindenburg committed
585 586
    connect(session, &Konsole::Session::finished, this, &Konsole::ViewManager::sessionFinished,
            Qt::UniqueConnection);
587

Kurt Hindenburg's avatar
Kurt Hindenburg committed
588
    TerminalDisplay *display = createTerminalDisplay(session);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
589 590
    const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
    applyProfileToView(display, profile);
591

Kurt Hindenburg's avatar
Kurt Hindenburg committed
592
    // set initial size
Kurt Hindenburg's avatar
Kurt Hindenburg committed
593
    const QSize &preferredSize = session->preferredSize();
594
    // FIXME: +1 is needed here for getting the expected rows
595 596 597 598
    // Note that the display shouldn't need to take into account the tabbar.
    // However, it appears that taking into account the tabbar is needed.
    // If tabbar is not visible, no +1 is needed here; however, depending on
    // settings/tabbar style, +2 might be needed.
599 600 601 602 603 604
    // 1st attempt at fixing the above:
    // Guess if tabbar will NOT be visible; ignore ShowNavigationAsNeeded
    int heightAdjustment = 0;
    if (_navigationVisibility != ViewContainer::AlwaysHideNavigation) {
        heightAdjustment = 2;
    }
605

606
    display->setSize(preferredSize.width(), preferredSize.height() + heightAdjustment);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
607
    ViewProperties *properties = createController(session, display);
608

Kurt Hindenburg's avatar
Kurt Hindenburg committed
609 610 611
    _sessionMap[display] = session;
    container->addView(display, properties, index);
    session->addView(display);
612

Kurt Hindenburg's avatar
Kurt Hindenburg committed
613 614
    // tell the session whether it has a light or dark background
    session->setDarkBackground(colorSchemeForProfile(profile)->hasDarkBackground());
615

Kurt Hindenburg's avatar
Kurt Hindenburg committed
616 617 618 619
    if (container == _viewSplitter->activeContainer()) {
        container->setActiveView(display);
        display->setFocus(Qt::OtherFocusReason);
    }
620

Kurt Hindenburg's avatar
Kurt Hindenburg committed
621
    updateDetachViewState();
622
}
623

Kurt Hindenburg's avatar
Kurt Hindenburg committed
624
void ViewManager::createView(Session *session)
625
{
626
    // create the default container
Kurt Hindenburg's avatar
Kurt Hindenburg committed
627
    if (_viewSplitter->containers().count() == 0) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
628
        ViewContainer *container = createContainer();
629
        _viewSplitter->addContainer(container, Qt::Vertical);
630
        emit splitViewToggle(false);
631 632
    }

633
    // new tab will be put at the end by default.
634 635
    int index = -1;

636
    if (_newTabBehavior == PutNewTabAfterCurrentTab) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
637
        QWidget *view = activeView();
638
        if (view != nullptr) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
639
            QList<QWidget *> views = _viewSplitter->activeContainer()->views();
640 641
            index = views.indexOf(view) + 1;
        }
642 643
    }

Robert Knight's avatar
Robert Knight committed
644 645
    // iterate over the view containers owned by this view manager
    // and create a new terminal display for the session in each of them, along with
Kurt Hindenburg's avatar
Kurt Hindenburg committed
646
    // a controller for the session/display pair
Kurt Hindenburg's avatar
Kurt Hindenburg committed
647
    foreach (ViewContainer *container, _viewSplitter->containers()) {
648
        createView(session, container, index);
649 650 651
    }
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
652
ViewContainer *ViewManager::createContainer()
653
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
654
    ViewContainer *container = nullptr;
655

Kurt Hindenburg's avatar
Kurt Hindenburg committed
656
    switch (_navigationMethod) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
657 658
    case TabbedNavigation:
    {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
659
        auto tabbedContainer = new TabbedViewContainer(_navigationPosition, this, _viewSplitter);
660
        container = tabbedContainer;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
661

Laurent Montel's avatar
Laurent Montel committed
662
        connect(tabbedContainer, &TabbedViewContainer::detachTab, this, &ViewManager::detachView);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
663 664 665
        connect(tabbedContainer, &TabbedViewContainer::closeTab, this,
                &ViewManager::closeTabFromContainer);
        break;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
666 667 668 669
    }
    case NoNavigation:
    default:
        container = new StackedViewContainer(_viewSplitter);
670
    }
671

672 673 674 675 676 677
    // FIXME: these code feels duplicated
    container->setNavigationVisibility(_navigationVisibility);
    container->setNavigationPosition(_navigationPosition);
    container->setStyleSheet(_navigationStyleSheet);
    if (_showQuickButtons) {
        container->setFeatures(container->features()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
678 679
                               | ViewContainer::QuickNewView
                               | ViewContainer::QuickCloseView);
680 681
    } else {
        container->setFeatures(container->features()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
682 683
                               & ~ViewContainer::QuickNewView
                               & ~ViewContainer::QuickCloseView);
684
    }
685

686
    // connect signals and slots
Kurt Hindenburg's avatar
Kurt Hindenburg committed
687 688 689 690
    connect(container, &Konsole::ViewContainer::viewAdded, _containerSignalMapper,
            static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
    connect(container, &Konsole::ViewContainer::viewRemoved, _containerSignalMapper,
            static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
691 692
    _containerSignalMapper->setMapping(container, container);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
693 694 695 696 697 698 699 700 701 702 703 704 705
    connect(container,
            static_cast<void (ViewContainer::*)()>(&Konsole::ViewContainer::newViewRequest), this,
            static_cast<void (ViewManager::*)()>(&Konsole::ViewManager::newViewRequest));
    connect(container,
            static_cast<void (ViewContainer::*)(Profile::Ptr)>(&Konsole::ViewContainer::newViewRequest),
            this,
            static_cast<void (ViewManager::*)(Profile::Ptr)>(&Konsole::ViewManager::newViewRequest));
    connect(container, &Konsole::ViewContainer::moveViewRequest, this,
            &Konsole::ViewManager::containerMoveViewRequest);
    connect(container, &Konsole::ViewContainer::viewRemoved, this,
            &Konsole::ViewManager::viewDestroyed);
    connect(container, &Konsole::ViewContainer::activeViewChanged, this,
            &Konsole::ViewManager::viewActivated);
706

707
    return container;
708
}
709

Kurt Hindenburg's avatar
Kurt Hindenburg committed
710 711
void ViewManager::containerMoveViewRequest(int index, int id, bool &moved,
                                           TabbedViewContainer *sourceTabbedContainer)
712
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
713 714
    ViewContainer *container = qobject_cast<ViewContainer *>(sender());
    SessionController *controller = qobject_cast<SessionController *>(ViewProperties::propertiesById(id));
715

Kurt Hindenburg's avatar
Kurt Hindenburg committed
716
    if (controller == nullptr) {
717
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
718
    }
719

Kurt Hindenburg's avatar
Kurt Hindenburg committed
720
    // do not move the last tab in a split view.
721
    if (sourceTabbedContainer != nullptr) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
722
        QPointer<ViewContainer> sourceContainer = qobject_cast<ViewContainer *>(sourceTabbedContainer);
723 724 725 726

        if (_viewSplitter->containers().contains(sourceContainer)) {
            return;
        } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
727
            ViewManager *sourceViewManager = sourceTabbedContainer->connectedViewManager();
728 729

            // do not remove the last tab on the window
Kurt Hindenburg's avatar
Kurt Hindenburg committed
730
            if (qobject_cast<ViewSplitter *>(sourceViewManager->widget())->containers().size() > 1) {