ViewManager.cpp 43 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 55 56 57 58 59 60 61 62 63 64
ViewManager::ViewManager(QObject *parent, KActionCollection *collection) :
    QObject(parent),
    _viewSplitter(0),
    _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(0);
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 {
120 121 122
        return 0;
    }
}
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
    collection->setDefaultShortcut(nextViewAction, Qt::SHIFT + Qt::Key_Right);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
249
    connect(nextViewAction, &QAction::triggered, this, &Konsole::ViewManager::nextView);
250
    _viewSplitter->addAction(nextViewAction);
Robert Knight's avatar
 
Robert Knight committed
251

252
    collection->setDefaultShortcut(previousViewAction, Qt::SHIFT + Qt::Key_Left);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
253
    connect(previousViewAction, &QAction::triggered, this, &Konsole::ViewManager::previousView);
254
    _viewSplitter->addAction(previousViewAction);
Robert Knight's avatar
 
Robert Knight committed
255

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    container->activateNextView();
}

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
348
    Q_ASSERT(container);
Robert Knight's avatar
 
Robert Knight committed
349 350

    container->activatePreviousView();
351
}
352

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
357
    Q_ASSERT(container);
358 359 360 361

    container->activateLastView();
}

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

367 368 369
    detachView(container, container->activeView());
}

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

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

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

382
    emit viewDetached(_sessionMap[viewToDetach]);
383

384
    _sessionMap.remove(viewToDetach);
385 386

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

    // 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
393 394
    if (_viewSplitter->containers().count() > 1
        && container->views().count() == 0) {
395
        removeContainer(container);
396 397 398
    }
}

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

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

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

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

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

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

    // 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);
435 436
}

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

442
void ViewManager::splitTopBottom()
443
{
444
    splitView(Qt::Vertical);
445 446
}

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

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

460
        _sessionMap[display] = session;
461

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

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

469 470
    // focus the new container
    container->containerWidget()->setFocus();
471 472

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

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

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

490
    _viewSplitter->removeContainer(container);
491
    container->deleteLater();
492

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

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

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

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

        removeContainer(container);
513

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

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

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
531
SessionController *ViewManager::createController(Session *session, TerminalDisplay *view)
532
{
Robert Knight's avatar
Robert Knight committed
533 534
    // 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
535
    auto controller = new SessionController(session, view, this);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
536 537 538 539 540 541 542 543 544 545
    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);
546

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

552 553 554
    return controller;
}

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

561
    _viewSplitter->setFocusProxy(controller->view());
562

563 564
    _pluggedController = controller;
    emit activeViewChanged(controller);
565 566
}

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

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

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

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
590
    // set initial size
Kurt Hindenburg's avatar
Kurt Hindenburg committed
591
    const QSize &preferredSize = session->preferredSize();
592
    // FIXME: +1 is needed here for getting the expected rows
593 594 595 596
    // 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.
597 598 599 600 601 602
    // 1st attempt at fixing the above:
    // Guess if tabbar will NOT be visible; ignore ShowNavigationAsNeeded
    int heightAdjustment = 0;
    if (_navigationVisibility != ViewContainer::AlwaysHideNavigation) {
        heightAdjustment = 2;
    }
603

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

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

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

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
619
    updateDetachViewState();
620
}
621

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

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

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

Robert Knight's avatar
Robert Knight committed
642 643
    // 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
644
    // a controller for the session/display pair
Kurt Hindenburg's avatar
Kurt Hindenburg committed
645
    foreach (ViewContainer *container, _viewSplitter->containers()) {
646
        createView(session, container, index);
647 648 649
    }
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
650
ViewContainer *ViewManager::createContainer()
651
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
652
    ViewContainer *container = 0;
653

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

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

670 671 672 673 674 675
    // 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
676 677
                               | ViewContainer::QuickNewView
                               | ViewContainer::QuickCloseView);
678 679
    } else {
        container->setFeatures(container->features()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
680 681
                               & ~ViewContainer::QuickNewView
                               & ~ViewContainer::QuickCloseView);
682
    }
683

684
    // connect signals and slots
Kurt Hindenburg's avatar
Kurt Hindenburg committed
685 686 687 688
    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
689 690
    _containerSignalMapper->setMapping(container, container);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
691 692 693 694 695 696 697 698 699 700 701 702 703
    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);
704

705
    return container;
706
}
707

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

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

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

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

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
734
    createView(controller->session(), container, index);
735
    controller->session()->refresh();
Robert Knight's avatar