ViewManager.cpp 38.2 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 25
// System
#include <assert.h>

26
// Qt
27
#include <QtCore/QDateTime>
Dirk Mueller's avatar
Dirk Mueller committed
28
#include <QtCore/QSignalMapper>
29
#include <QtGui/QMenu>
30

31 32
// KDE
#include <kdebug.h>
Robert Knight's avatar
Robert Knight committed
33
#include <KAcceleratorManager>
34
#include <KGlobal>
35 36 37
#include <KLocale>
#include <KToggleAction>
#include <KXMLGUIFactory>
38
#include <QStringList>
Laurent Montel's avatar
Laurent Montel committed
39
#include <KConfigGroup>
40 41

// Konsole
42
#include "ColorScheme.h"
43
#include "ProfileList.h"
44
#include "Session.h"
45
#include "TerminalDisplay.h"
46 47 48 49
#include "SessionController.h"
#include "SessionManager.h"
#include "ViewContainer.h"
#include "ViewSplitter.h"
50 51
#include "konsoleadaptor.h"
#include "Profile.h"
52

53 54
using namespace Konsole;

55 56
ViewManager::ViewManager(QObject* parent , KActionCollection* collection)
    : QObject(parent)
57
    , _viewSplitter(0)
58 59
    , _actionCollection(collection)
    , _containerSignalMapper(new QSignalMapper(this))
60
    , _navigationMethod(TabbedNavigation)
61
    , _newViewMenu(0)
62
{
63
    // create main view area
Robert Knight's avatar
Robert Knight committed
64
    _viewSplitter = new ViewSplitter(0);  
65
    KAcceleratorManager::setNoAccel(_viewSplitter);
Robert Knight's avatar
Robert Knight committed
66

67 68 69 70 71 72 73
    // 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
    // mode 
    _viewSplitter->setRecursiveSplitting(false);
74
    _viewSplitter->setFocusPolicy(Qt::NoFocus);
75

76 77 78 79 80 81
    // setup actions which relating to the view
    setupActions();

    // emit a signal when all of the views held by this view manager are destroyed
    connect( _viewSplitter , SIGNAL(allContainersEmpty()) , this , SIGNAL(empty()) );
    connect( _viewSplitter , SIGNAL(empty(ViewSplitter*)) , this , SIGNAL(empty()) );
82 83 84 85

    // listen for addition or removal of views from associated containers
    connect( _containerSignalMapper , SIGNAL(mapped(QObject*)) , this , 
            SLOT(containerViewsChanged(QObject*)) ); 
86 87

    // listen for profile changes
88 89
    connect( SessionManager::instance() , SIGNAL(profileChanged(Profile::Ptr)) , this,
            SLOT(profileChanged(Profile::Ptr)) );
90 91
    connect( SessionManager::instance() , SIGNAL(sessionUpdated(Session*)) , this,
            SLOT(updateViewsForSession(Session*)) );
92 93 94 95 96

    //prepare DBus communication
    new KonsoleAdaptor(this);
    QDBusConnection::sessionBus().registerObject(QLatin1String("/Konsole"), this);

97 98 99 100
}

ViewManager::~ViewManager()
{
101 102 103 104 105 106 107 108 109 110 111 112 113 114
    delete _newViewMenu;
}
QMenu* ViewManager::createNewViewMenu() 
{
    if (_newViewMenu)
        return _newViewMenu;

    _newViewMenu = new QMenu(0);
    ProfileList* newViewProfiles = new ProfileList(false,_newViewMenu);
    newViewProfiles->syncWidgetActions(_newViewMenu,true);
    connect(newViewProfiles,SIGNAL(profileSelected(Profile::Ptr)),this,
        SIGNAL(newViewRequest(Profile::Ptr)));

    return _newViewMenu;
115
}
116 117 118 119 120 121 122 123 124 125 126 127
QWidget* ViewManager::activeView() const
{
    ViewContainer* container = _viewSplitter->activeContainer();
    if ( container )
    {
        return container->activeView();
    }
    else
    {
        return 0;
    }
}
128

129 130 131 132 133
QWidget* ViewManager::widget() const
{
    return _viewSplitter;
}

134 135
void ViewManager::setupActions()
{
136
    KActionCollection* collection = _actionCollection;
137

138 139
    KAction* nextViewAction = new KAction( i18n("Next Tab") , this );
    KAction* previousViewAction = new KAction( i18n("Previous Tab") , this );
140
    KAction* nextContainerAction = new KAction( i18n("Next View Container") , this);
141
  
142 143
    KAction* moveViewLeftAction = new KAction( i18n("Move Tab Left") , this );
    KAction* moveViewRightAction = new KAction( i18n("Move Tab Right") , this );
144

145 146 147 148 149
    // list of actions that should only be enabled when there are multiple view
    // containers open
    QList<QAction*> multiViewOnlyActions;
    multiViewOnlyActions << nextContainerAction;

150 151
    if ( collection )
    {
152
        KAction* splitLeftRightAction = new KAction( KIcon("view-split-left-right"),
Thomas Reitelbach's avatar
Thomas Reitelbach committed
153
                                                      i18nc("@action:inmenu", "Split View Left/Right"),
154
                                                      this );
155
        splitLeftRightAction->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_ParenLeft) );
156 157 158
        collection->addAction("split-view-left-right",splitLeftRightAction);
        connect( splitLeftRightAction , SIGNAL(triggered()) , this , SLOT(splitLeftRight()) );

159
        KAction* splitTopBottomAction = new KAction( KIcon("view-split-top-bottom") , 
Thomas Reitelbach's avatar
Thomas Reitelbach committed
160
                                             i18nc("@action:inmenu", "Split View Top/Bottom"),this);
161
        splitTopBottomAction->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_ParenRight) );
162 163 164
        collection->addAction("split-view-top-bottom",splitTopBottomAction);
        connect( splitTopBottomAction , SIGNAL(triggered()) , this , SLOT(splitTopBottom()));

Thomas Reitelbach's avatar
Thomas Reitelbach committed
165
        KAction* closeActiveAction = new KAction( i18nc("@action:inmenu Close Active View", "Close Active") , this );
Pino Toscano's avatar
Pino Toscano committed
166
        closeActiveAction->setIcon(KIcon("view-close"));
167
        closeActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_S) );
168
        closeActiveAction->setEnabled(false);
169 170
        collection->addAction("close-active-view",closeActiveAction);
        connect( closeActiveAction , SIGNAL(triggered()) , this , SLOT(closeActiveView()) );
171 172

        multiViewOnlyActions << closeActiveAction;
173

Thomas Reitelbach's avatar
Thomas Reitelbach committed
174
        KAction* closeOtherAction = new KAction( i18nc("@action:inmenu Close Other Views", "Close Others") , this );
175
        closeOtherAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_O) );
176
        closeOtherAction->setEnabled(false);
177 178
        collection->addAction("close-other-views",closeOtherAction);
        connect( closeOtherAction , SIGNAL(triggered()) , this , SLOT(closeOtherViews()) );
179 180

        multiViewOnlyActions << closeOtherAction;
181

182
        KAction* detachViewAction = collection->addAction("detach-view");
183 184
        detachViewAction->setIcon(KIcon("tab-detach"));
        detachViewAction->setText(i18n("D&etach Current Tab"));
185 186
        // 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
187 188 189
        detachViewAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H));

        connect( this , SIGNAL(splitViewToggle(bool)) , this , SLOT(updateDetachViewState()) );
190
        connect( detachViewAction , SIGNAL(triggered()) , this , SLOT(detachActiveView()) );
191

192
        // Expand & Shrink Active View
Thomas Reitelbach's avatar
Thomas Reitelbach committed
193
        KAction* expandActiveAction = new KAction( i18nc("@action:inmenu", "Expand View") , this );
194 195 196 197
        expandActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_BracketRight) );
        collection->addAction("expand-active-view",expandActiveAction);
        connect( expandActiveAction , SIGNAL(triggered()) , this , SLOT(expandActiveView()) );

198 199
        multiViewOnlyActions << expandActiveAction;

Thomas Reitelbach's avatar
Thomas Reitelbach committed
200
        KAction* shrinkActiveAction = new KAction( i18nc("@action:inmenu", "Shrink View") , this );
201 202 203 204
        shrinkActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_BracketLeft) );
        collection->addAction("shrink-active-view",shrinkActiveAction);
        connect( shrinkActiveAction , SIGNAL(triggered()) , this , SLOT(shrinkActiveView()) );

205 206
        multiViewOnlyActions << shrinkActiveAction;

207
        // Next / Previous View , Next Container
208 209 210
        collection->addAction("next-view",nextViewAction);
        collection->addAction("previous-view",previousViewAction);
        collection->addAction("next-container",nextContainerAction);
211 212
        collection->addAction("move-view-left",moveViewLeftAction);
        collection->addAction("move-view-right",moveViewRightAction);
213 214 215 216 217 218 219 220 221 222 223 224 225

        // Switch to tab N shortcuts
        const int SWITCH_TO_TAB_COUNT = 10;
        QSignalMapper* switchToTabMapper = new QSignalMapper(this);
        connect(switchToTabMapper,SIGNAL(mapped(int)),this,SLOT(switchToView(int)));
        for (int i=0;i < SWITCH_TO_TAB_COUNT;i++)
        {
            KAction* switchToTabAction = new KAction(i18n("Switch to Tab %1",i+1),this);
            switchToTabMapper->setMapping(switchToTabAction,i);
            connect(switchToTabAction,SIGNAL(triggered()),switchToTabMapper,
                    SLOT(map()));
            collection->addAction(QString("switch-to-tab-%1").arg(i),switchToTabAction);
        }
226
    }
Robert Knight's avatar
 
Robert Knight committed
227

228 229 230 231 232 233
    QListIterator<QAction*> iter(multiViewOnlyActions);
    while ( iter.hasNext() )
    {
        connect( this , SIGNAL(splitViewToggle(bool)) , iter.next() , SLOT(setEnabled(bool)) );
    }

234
    // keyboard shortcut only actions
235
    KShortcut nextViewShortcut = nextViewAction->shortcut();
236
    nextViewShortcut.setPrimary( QKeySequence(Qt::SHIFT+Qt::Key_Right) );
237
    nextViewShortcut.setAlternate( QKeySequence(Qt::CTRL+Qt::Key_PageDown) );
238
    nextViewAction->setShortcut(nextViewShortcut); 
Robert Knight's avatar
 
Robert Knight committed
239
    connect( nextViewAction, SIGNAL(triggered()) , this , SLOT(nextView()) );
240
    _viewSplitter->addAction(nextViewAction);
Robert Knight's avatar
 
Robert Knight committed
241

242 243
    KShortcut previousViewShortcut = previousViewAction->shortcut();
    previousViewShortcut.setPrimary( QKeySequence(Qt::SHIFT+Qt::Key_Left) );
244
    previousViewShortcut.setAlternate( QKeySequence(Qt::CTRL+Qt::Key_PageUp) );
245
    previousViewAction->setShortcut(previousViewShortcut);
Robert Knight's avatar
 
Robert Knight committed
246
    connect( previousViewAction, SIGNAL(triggered()) , this , SLOT(previousView()) );
247
    _viewSplitter->addAction(previousViewAction);
Robert Knight's avatar
 
Robert Knight committed
248 249 250

    nextContainerAction->setShortcut( QKeySequence(Qt::SHIFT+Qt::Key_Tab) );
    connect( nextContainerAction , SIGNAL(triggered()) , this , SLOT(nextContainer()) );
251
    _viewSplitter->addAction(nextContainerAction);
252 253 254 255 256 257 258

    moveViewLeftAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Left) );
    connect( moveViewLeftAction , SIGNAL(triggered()) , this , SLOT(moveActiveViewLeft()) );
    _viewSplitter->addAction(moveViewLeftAction);
    moveViewRightAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Right) );
    connect( moveViewRightAction , SIGNAL(triggered()) , this , SLOT(moveActiveViewRight()) );
    _viewSplitter->addAction(moveViewRightAction);
Robert Knight's avatar
 
Robert Knight committed
259
}
260 261 262 263 264 265 266 267 268 269
void ViewManager::switchToView(int index)
{
    Q_ASSERT(index >= 0);
    ViewContainer* container = _viewSplitter->activeContainer();
    Q_ASSERT( container );
    QList<QWidget*> containerViews = container->views();
    if (index >= containerViews.count())
        return;
    container->setActiveView(containerViews.at(index));
}
270 271
void ViewManager::updateDetachViewState()
{
272 273
    if (!_actionCollection)
        return;
274

275

276 277
    bool splitView = _viewSplitter->containers().count() >= 2;
    bool shouldEnable = splitView || _viewSplitter->activeContainer()->views().count() >= 2;
278

279
    QAction* detachAction = _actionCollection->action("detach-view");
280

281 282
    if ( detachAction && shouldEnable != detachAction->isEnabled() )
        detachAction->setEnabled(shouldEnable);
283
}
284 285 286 287 288 289 290 291 292 293 294 295
void ViewManager::moveActiveViewLeft()
{
    ViewContainer* container = _viewSplitter->activeContainer();
    Q_ASSERT( container );
    container->moveActiveView( ViewContainer::MoveViewLeft );
}
void ViewManager::moveActiveViewRight()
{
    ViewContainer* container = _viewSplitter->activeContainer();
    Q_ASSERT( container );
    container->moveActiveView( ViewContainer::MoveViewRight );
}
Robert Knight's avatar
 
Robert Knight committed
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
void ViewManager::nextContainer()
{
    _viewSplitter->activateNextContainer();
}

void ViewManager::nextView()
{
    ViewContainer* container = _viewSplitter->activeContainer();

    Q_ASSERT( container );

    container->activateNextView();
}

void ViewManager::previousView()
{
    ViewContainer* container = _viewSplitter->activeContainer();

    Q_ASSERT( container );

    container->activatePreviousView();
317
}
318

319 320 321 322 323
void ViewManager::detachActiveView()
{
    // find the currently active view and remove it from its container 
    ViewContainer* container = _viewSplitter->activeContainer();

324 325 326 327 328 329 330 331 332
    detachView(container, container->activeView());
}

void ViewManager::detachView(ViewContainer* container, QWidget* widgetView)
{
    TerminalDisplay * viewToDetach = 
        dynamic_cast<TerminalDisplay*>(widgetView);

    if (!viewToDetach)
333 334
        return;

335
    emit viewDetached(_sessionMap[viewToDetach]);
336
    
337
    _sessionMap.remove(viewToDetach);
338 339

    // remove the view from this window
340 341
    container->removeView(viewToDetach);
    viewToDetach->deleteLater();
342 343 344 345 346 347 348

    // 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
    if ( _viewSplitter->containers().count() > 1 && 
         container->views().count() == 0 )
    {
349
        removeContainer(container);
350 351 352 353
    }

}

354
void ViewManager::sessionFinished()
355
{
356 357 358 359
    // if this slot is called after the view manager's main widget
    // has been destroyed, do nothing
    if (!_viewSplitter)
        return;
360

361 362
    Session* session = qobject_cast<Session*>(sender());

363 364
    // We're using setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab)
    // so no need to manually select next tab.
365

366 367
    Q_ASSERT(session);

368
    // close attached views
369
    QList<TerminalDisplay*> children = _viewSplitter->findChildren<TerminalDisplay*>();
370

371
    foreach ( TerminalDisplay* view , children )
372 373 374 375
    {
        if ( _sessionMap[view] == session )
        {
            _sessionMap.remove(view);
376
            view->deleteLater();
377
        }
Robert Knight's avatar
 
Robert Knight committed
378
    }
379

380 381 382 383
    // This is needed to remove this controller from factory() in 
    // order to prevent BUG: 185466 - disappearing menu popup
    if (_pluggedController)
        emit unplugController(_pluggedController);
Robert Knight's avatar
 
Robert Knight committed
384 385 386 387
}

void ViewManager::focusActiveView()
{
Robert Knight's avatar
Robert Knight committed
388 389 390 391 392 393
    // give the active view in a container the focus.  this ensures 
    // that controller associated with that view is activated and the session-specific
    // menu items are replaced with the ones for the newly focused view

    // see the viewFocused() method

Robert Knight's avatar
 
Robert Knight committed
394 395 396 397 398 399 400 401 402
    ViewContainer* container = _viewSplitter->activeContainer(); 
    if ( container )
    {
        QWidget* activeView = container->activeView();
        if ( activeView )
        {
            activeView->setFocus(Qt::MouseFocusReason);
        }
    }
403 404
}

405

406 407
void ViewManager::viewActivated( QWidget* view )
{
408 409 410 411 412 413
    Q_ASSERT( view != 0 );

    // 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);
414 415
}

416
void ViewManager::splitLeftRight()
417
{
418
    splitView(Qt::Horizontal);
419
}
420
void ViewManager::splitTopBottom()
421
{
422
    splitView(Qt::Vertical);
423 424
}

425
void ViewManager::splitView(Qt::Orientation orientation)
426
{
427 428 429 430
    // iterate over each session which has a view in the current active
    // container and create a new view for that session in a new container 
    QListIterator<QWidget*> existingViewIter(_viewSplitter->activeContainer()->views());
    
431
    ViewContainer* container = 0; 
432 433

    while (existingViewIter.hasNext())
434
    {
435
        Session* session = _sessionMap[(TerminalDisplay*)existingViewIter.next()];
436
        TerminalDisplay* display = createTerminalDisplay(session);
437
        const Profile::Ptr info = SessionManager::instance()->sessionProfile(session);
438
        applyProfileToView(display, info);
439
        ViewProperties* properties = createController(session,display);
440

441
        _sessionMap[display] = session;
442

443 444 445
        // create a container using settings from the first 
        // session in the previous container
        if ( !container )
446
        {
447
            container = createContainer(info);
448 449
            applyProfileToContainer(container, info);
        }
450

451 452 453
        container->addView(display,properties);
        session->addView( display );
    }
454

455 456
    _viewSplitter->addContainer(container,orientation);
    emit splitViewToggle(_viewSplitter->containers().count() > 0);
457

458 459
    // focus the new container
    container->containerWidget()->setFocus();
460 461

    // ensure that the active view is focused after the split / unsplit
462 463 464 465 466
    ViewContainer* activeContainer = _viewSplitter->activeContainer();
    QWidget* activeView = activeContainer ? activeContainer->activeView() : 0;

    if ( activeView )
        activeView->setFocus(Qt::OtherFocusReason);
467
}
468
void ViewManager::removeContainer(ViewContainer* container)
469
{
470 471 472 473 474 475 476 477
    // remove session map entries for views in this container
    foreach( QWidget* view , container->views() )
    {
        TerminalDisplay* display = qobject_cast<TerminalDisplay*>(view);
        Q_ASSERT(display);
        _sessionMap.remove(display);
    } 

478
    _viewSplitter->removeContainer(container);
479
    container->deleteLater();
480 481

    emit splitViewToggle( _viewSplitter->containers().count() > 1 );
482
}
483 484 485 486 487 488 489 490
void ViewManager::expandActiveView()
{
    _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(),10);
}
void ViewManager::shrinkActiveView()
{
    _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(),-10);
}
491 492 493 494 495 496 497 498
void ViewManager::closeActiveView()
{
    // only do something if there is more than one container active
    if ( _viewSplitter->containers().count() > 1 )
    {
        ViewContainer* container = _viewSplitter->activeContainer();

        removeContainer(container);
499

500 501 502 503
        // focus next container so that user can continue typing 
        // without having to manually focus it themselves
        nextContainer();
    }
504 505 506 507 508 509 510 511 512 513
}
void ViewManager::closeOtherViews()
{
    ViewContainer* active = _viewSplitter->activeContainer();

    QListIterator<ViewContainer*> iter(_viewSplitter->containers());
    while ( iter.hasNext() )
    {
        ViewContainer* next = iter.next();
        if ( next != active )
514
            removeContainer(next);
515 516 517
    }
}

518
SessionController* ViewManager::createController(Session* session , TerminalDisplay* view)
519
{
Robert Knight's avatar
Robert Knight committed
520 521
    // create a new controller for the session, and ensure that this view manager
    // is notified when the view gains the focus
522
    SessionController* controller = new SessionController(session,view,this);
523
    connect( controller , SIGNAL(focused(SessionController*)) , this , SLOT(controllerChanged(SessionController*)) );
524 525
    connect( session , SIGNAL(destroyed()) , controller , SLOT(deleteLater()) );
    connect( view , SIGNAL(destroyed()) , controller , SLOT(deleteLater()) );
526

527 528 529
    // if this is the first controller created then set it as the active controller
    if (!_pluggedController)
        controllerChanged(controller);
530

531 532 533
    return controller;
}

534 535
void ViewManager::controllerChanged(SessionController* controller)
{
536 537
    if ( controller == _pluggedController )
        return;
538

539
    _viewSplitter->setFocusProxy(controller->view());
540

541 542
    _pluggedController = controller;
    emit activeViewChanged(controller);
543 544 545 546
}

SessionController* ViewManager::activeViewController() const
{
547
    return _pluggedController;
548
}
549 550 551 552 553 554

IncrementalSearchBar* ViewManager::searchBar() const
{
    return _viewSplitter->activeSplitter()->activeContainer()->searchBar();
}

555 556
void ViewManager::createView(Session* session, ViewContainer* container, int index)
{
557
    // notify this view manager when the session finishes so that its view
558
    // can be deleted
559 560 561
    //
    // TODO - Find a more efficient a way to avoid multiple connections
    disconnect( session , SIGNAL(finished()) , this , SLOT(sessionFinished()) );
562 563 564
    connect( session , SIGNAL(finished()) , this , SLOT(sessionFinished()) );

     TerminalDisplay* display = createTerminalDisplay(session);
565 566 567 568 569 570
     const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
     applyProfileToView(display, profile);

     bool isFirst = _sessionMap.isEmpty();
     if ( isFirst)
         applyProfileToContainer(container, profile);
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
     
     // set initial size
     display->setSize(80,40);

     ViewProperties* properties = createController(session,display);

     _sessionMap[display] = session; 
     container->addView(display,properties,index);
     session->addView(display);

     // tell the session whether it has a light or dark background
     session->setDarkBackground( colorSchemeForProfile(profile)->hasDarkBackground() );

     if ( container == _viewSplitter->activeContainer() ) 
     {
         container->setActiveView(display);
         display->setFocus( Qt::OtherFocusReason );
     }
589 590
    
     updateDetachViewState();
591
}
592

593
void ViewManager::createView(Session* session)
594
{
595 596 597
    // create the default container
    if (_viewSplitter->containers().count() == 0)
    {
598
        _viewSplitter->addContainer( createContainer(SessionManager::instance()->sessionProfile(session)) , 
599
                                     Qt::Vertical );
600
        emit splitViewToggle(false);
601 602
    }

603
       
Robert Knight's avatar
Robert Knight committed
604 605 606
    // 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
    // a controller for the session/display pair 
607 608 609 610 611
    QListIterator<ViewContainer*> containerIter(_viewSplitter->containers());

    while ( containerIter.hasNext() )
    {
        ViewContainer* container = containerIter.next();
612
        createView(session,container,-1);
613
    }
614

615 616
}

617
ViewContainer* ViewManager::createContainer(const Profile::Ptr info)
618
{
619 620
    Q_ASSERT( info );

621
    const int tabPosition = info->property<int>(Profile::TabBarPosition);
622 623 624 625 626

    ViewContainer::NavigationPosition position = ( tabPosition == Profile::TabBarTop ) ?
                                                   ViewContainer::NavigationPositionTop :
                                                   ViewContainer::NavigationPositionBottom;

627 628 629 630 631
    ViewContainer* container = 0;

    switch ( _navigationMethod )
    {
        case TabbedNavigation:    
632 633 634 635 636
            {
                container =
                    new TabbedViewContainer(position,_viewSplitter);

                connect(container,
Laurent Montel's avatar
Laurent Montel committed
637
                    SIGNAL(detachTab(ViewContainer*,QWidget*)),
638
                    this,
Laurent Montel's avatar
Laurent Montel committed
639
                    SLOT(detachView(ViewContainer*,QWidget*))
640
                    );
641
                connect(container,
Laurent Montel's avatar
Laurent Montel committed
642
                    SIGNAL(closeTab(ViewContainer*,QWidget*)),
643
                    this,
Laurent Montel's avatar
Laurent Montel committed
644
                    SLOT(closeTabFromContainer(ViewContainer*,QWidget*)));
645

646
            }
647 648 649 650 651
            break;
        case NoNavigation:
        default:
            container = new StackedViewContainer(_viewSplitter);
    }
652

653
    applyProfileToContainer(container, info);
654

655
    // connect signals and slots
656 657 658 659 660 661
    connect( container , SIGNAL(viewAdded(QWidget*,ViewProperties*)) , _containerSignalMapper ,
           SLOT(map()) );
    connect( container , SIGNAL(viewRemoved(QWidget*)) , _containerSignalMapper ,
           SLOT(map()) ); 
    _containerSignalMapper->setMapping(container,container);

662
    connect( container, SIGNAL(newViewRequest()), this, SIGNAL(newViewRequest()) );
663
    connect( container, SIGNAL(moveViewRequest(int,int,bool&)), 
664
    this , SLOT(containerMoveViewRequest(int,int,bool&)) );
665
    connect( container , SIGNAL(viewRemoved(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
666
    connect( container , SIGNAL(closeRequest(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
667
    connect( container , SIGNAL(activeViewChanged(QWidget*)) , this , SLOT(viewActivated(QWidget*)));
668
    
669
    return container;
670
}
671 672
void ViewManager::containerMoveViewRequest(int index, int id, bool& moved)
{
673 674
    ViewContainer* container = qobject_cast<ViewContainer*>(sender());
    SessionController* controller = qobject_cast<SessionController*>(ViewProperties::propertiesById(id));
675

676 677
    if (!controller)
        return;
678

679
    createView(controller->session(),container,index);
680
    controller->session()->refresh();
681
    moved = true;
682
}
683 684 685 686 687 688 689 690
void ViewManager::setNavigationMethod(NavigationMethod method)
{
    _navigationMethod = method;

    KActionCollection* collection = _actionCollection;

    if ( collection )
    {
691 692 693 694 695 696 697 698 699
        // FIXME: The following disables certain actions for the KPart that it
        // doesn't actually have a use for, to avoid polluting the action/shortcut
        // namespace of an application using the KPart (otherwise, a shortcut may
        // be in use twice, and the user gets to see an "ambiguous shortcut over-
        // load" error dialog). However, this approach sucks - it's the inverse of
        // what it should be. Rather than disabling actions not used by the KPart,
        // a method should be devised to only enable those that are used, perhaps
        // by using a separate action collection.

700 701 702 703 704 705 706
        QAction* action;

        action = collection->action( "next-view" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );

        action = collection->action( "previous-view" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );
707 708 709 710 711 712

        action = collection->action( "split-view-left-right" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );

        action = collection->action( "split-view-top-bottom" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );
713 714 715

        action = collection->action( "rename-session" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );
716 717 718 719 720 721

        action = collection->action( "move-view-left" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );

        action = collection->action( "move-view-right" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );
722 723 724
    }
}

725 726
ViewManager::NavigationMethod ViewManager::navigationMethod() const { return _navigationMethod; }

727 728
void ViewManager::containerViewsChanged(QObject* container)
{
729
    if (_viewSplitter && container == _viewSplitter->activeContainer() )
730 731 732 733 734
    {
        emit viewPropertiesChanged( viewProperties() );
    } 
}

735 736
void ViewManager::viewCloseRequest(QWidget* view)
{
737 738 739 740 741
    //FIXME Check that this cast is actually legal
    TerminalDisplay* display = (TerminalDisplay*)view;
  
    Q_ASSERT(display);

742 743
    // 1. detach view from session
    // 2. if the session has no views left, close it
744
    Session* session = _sessionMap[ display ];
745
    _sessionMap.remove(display);
746 747
    if ( session )
    {
748
        display->deleteLater();
749

750
        if ( session->views().count() == 0 )
751
            session->close();
752
    }
753 754 755 756 757
    //we only update the focus if the splitter is still alive
    if (_viewSplitter) {
        focusActiveView();
        updateDetachViewState();
    }
758 759 760 761
    // The below causes the menus  to be messed up
    // Only happenss when using the tab bar close button
//    if (_pluggedController)
//        emit unplugController(_pluggedController);
762 763
}

764
TerminalDisplay* ViewManager::createTerminalDisplay(Session* session)
765
{
766
   TerminalDisplay* display = new TerminalDisplay(0);
767 768

   //TODO Temporary settings used here
769 770
   display->setBellMode(TerminalDisplay::NotifyBell);
   display->setTerminalSizeHint(true);
771
   display->setTripleClickMode(TerminalDisplay::SelectWholeLine);
772
   display->setTerminalSizeStartup(true);
773
   display->setScrollBarPosition(TerminalDisplay::ScrollBarRight);
774 775
   display->setRandomSeed(session->sessionId() * 31);

776 777 778
   return display;
}

779
const ColorScheme* ViewManager::colorSchemeForProfile(const Profile::Ptr info) const
780
{
781 782
    const ColorScheme* colorScheme = ColorSchemeManager::instance()->
                                            findColorScheme(info->colorScheme());
783 784
    if ( !colorScheme )
       colorScheme = ColorSchemeManager::instance()->defaultColorScheme(); 
785 786
    Q_ASSERT( colorScheme );

787 788 789
    return colorScheme;
}

790
void ViewManager::applyProfileToView(TerminalDisplay* view , const Profile::Ptr info)
791 792 793
{
    Q_ASSERT( info );
    
794
    const ColorScheme* colorScheme = colorSchemeForProfile(info);
795

796
    // menu bar visibility
797
    emit setMenuBarVisibleRequest( info->property<bool>(Profile::ShowMenuBar) );
798

799 800
    emit setSaveGeometryOnExitRequest( info->property<bool>(Profile::SaveGeometryOnExit) );

801 802
    emit updateWindowIcon();

803
    // load colour scheme
804 805 806 807
    ColorEntry table[TABLE_COLORS];
    
    colorScheme->getColorTable(table , view->randomSeed() );
    view->setColorTable(table);
808
    view->setOpacity(colorScheme->opacity());
809
    view->setWallpaper(colorScheme->wallpaper());
810
  
811
    // load font 
812
    view->setAntialias(info->property<bool>(Profile::AntiAliasFonts));
813
    view->setBoldIntense(info->property<bool>(Profile::BoldIntense));
814
    view->setVTFont(info->font());
815 816

    // set scroll-bar position
817
    int scrollBarPosition = info->property<int>(Profile::ScrollBarPosition);
818 819

    if ( scrollBarPosition == Profile::ScrollBarHidden )
820
       view->setScrollBarPosition(TerminalDisplay::NoScrollBar);
821
    else if ( scrollBarPosition == Profile::ScrollBarLeft )
822
       view->setScrollBarPosition(TerminalDisplay::ScrollBarLeft);
823
    else if ( scrollBarPosition == Profile::ScrollBarRight )
824
       view->setScrollBarPosition(TerminalDisplay::ScrollBarRight);
825

826 827 828
    // set visibility of the size widget
    view->setSizeWidgetVisibility(info->property<bool>(Profile::ShowSizeWidget));

829
    // terminal features
830
    bool blinkingCursor = info->property<bool>(Profile::BlinkingCursorEnabled);
831 832
    view->setBlinkingCursor(blinkingCursor);  

833 834 835
    bool blinkingText = info->property<bool>(Profile::BlinkingTextEnabled);
    view->setBlinkingTextEnabled(blinkingText);

836 837
    bool tripleClickMode = info->property<bool>(Profile::TripleClickMode);
    view->setTripleClickMode(tripleClickMode ? TerminalDisplay::SelectForwardsFromCursor : TerminalDisplay::SelectWholeLine);
838 839
    
    view->setUnderlineLinks(info->property<bool>(Profile::UnderlineLinksEnabled));
840

841 842 843
    bool bidiEnabled = info->property<bool>(Profile::BidiRenderingEnabled);
    view->setBidiEnabled(bidiEnabled);

844
    // cursor shape
845
    int cursorShape = info->property<int>(Profile::CursorShape);
846 847 848 849 850 851 852 853 854

    if ( cursorShape == Profile::BlockCursor )
        view->setKeyboardCursorShape(TerminalDisplay::BlockCursor);  
    else if ( cursorShape == Profile::IBeamCursor )
        view->setKeyboardCursorShape(TerminalDisplay::IBeamCursor);
    else if ( cursorShape == Profile::UnderlineCursor )
        view->setKeyboardCursorShape(TerminalDisplay::UnderlineCursor);

    // cursor color
855 856
    bool useCustomColor = info->property<bool>(Profile::UseCustomCursorColor);
    const QColor& cursorColor = info->property<QColor>(Profile::CustomCursorColor);
857 858 859 860
        
    view->setKeyboardCursorColor(!useCustomColor,cursorColor);

    // word characters
861
    view->setWordCharacters( info->property<QString>(Profile::WordCharacters) );
862 863
}

864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
void ViewManager::applyProfileToContainer(ViewContainer* container , const Profile::Ptr info)
{
    int tabBarMode = info->property<int>(Profile::TabBarMode);
    int tabBarPosition = info->property<int>(Profile::TabBarPosition);
    bool showNewCloseButtons = info->property<bool>(Profile::ShowNewAndCloseTabButtons);

    if ( tabBarMode == Profile::AlwaysHideTabBar )
        container->setNavigationDisplayMode(ViewContainer::AlwaysHideNavigation);
    else if ( tabBarMode == Profile::AlwaysShowTabBar )
        container->setNavigationDisplayMode(ViewContainer::AlwaysShowNavigation);
    else if ( tabBarMode == Profile::ShowTabBarAsNeeded )
        container->setNavigationDisplayMode(ViewContainer::ShowNavigationAsNeeded);

    ViewContainer::NavigationPosition position = container->navigationPosition();
    if ( tabBarPosition == Profile::TabBarTop )
        position = ViewContainer::NavigationPositionTop;
    else if ( tabBarPosition == Profile::TabBarBottom )
        position = ViewContainer::NavigationPositionBottom;

    if ( container->supportedNavigationPositions().contains(position) )
        container->setNavigationPosition(position);

    if (showNewCloseButtons)
    {
        container->setFeatures(container->features()
                | ViewContainer::QuickNewView | ViewContainer::QuickCloseView);
        container->setNewViewMenu(createNewViewMenu());
    }
    else
    {
        container->setFeatures(container->features()
                & ~ViewContainer::QuickNewView & ~ViewContainer::QuickCloseView);
    }
}

899 900
void ViewManager::updateViewsForSession(Session* session)
{
901
    QListIterator<TerminalDisplay*> iter(_sessionMap.keys(session));
902 903
    while ( iter.hasNext() )
    {
904
        applyProfileToView(iter.next(),SessionManager::instance()->sessionProfile(session));
905 906 907
    }
}

908
void ViewManager::profileChanged(Profile::Ptr profile)
909
{
910
    QHashIterator<TerminalDisplay*,Session*> iter(_sessionMap);
911 912 913 914 915 916

    while ( iter.hasNext() )
    {
        iter.next();

        // if session uses this profile, update the display
917
        if ( iter.key() != 0 &&