ViewManager.cpp 33 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 38 39
#include <KLocale>
#include <KToggleAction>
#include <KXMLGUIFactory>

// Konsole
40
#include "ColorScheme.h"
41
#include "ProfileList.h"
42
#include "Session.h"
43
#include "TerminalDisplay.h"
44 45 46 47 48
#include "SessionController.h"
#include "SessionManager.h"
#include "ViewContainer.h"
#include "ViewSplitter.h"

49 50
using namespace Konsole;

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

63 64 65 66 67 68 69
    // 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);
70
    _viewSplitter->setFocusPolicy(Qt::NoFocus);
71

72 73 74 75 76 77
    // 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()) );
78 79 80 81

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

    // listen for profile changes
84 85
    connect( SessionManager::instance() , SIGNAL(profileChanged(Profile::Ptr)) , this,
            SLOT(profileChanged(Profile::Ptr)) );
86 87
    connect( SessionManager::instance() , SIGNAL(sessionUpdated(Session*)) , this,
            SLOT(updateViewsForSession(Session*)) );
88 89 90 91
}

ViewManager::~ViewManager()
{
92 93 94 95 96 97 98 99 100 101 102 103 104 105
    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;
106
}
107 108 109 110 111 112 113 114 115 116 117 118
QWidget* ViewManager::activeView() const
{
    ViewContainer* container = _viewSplitter->activeContainer();
    if ( container )
    {
        return container->activeView();
    }
    else
    {
        return 0;
    }
}
119

120 121 122 123 124
QWidget* ViewManager::widget() const
{
    return _viewSplitter;
}

125 126
void ViewManager::setupActions()
{
127
    KActionCollection* collection = _actionCollection;
128

129 130
    KAction* nextViewAction = new KAction( i18n("Next View") , this );
    KAction* previousViewAction = new KAction( i18n("Previous View") , this );
131
    KAction* nextContainerAction = new KAction( i18n("Next View Container") , this);
132
  
133 134
    KAction* moveViewLeftAction = new KAction( i18n("Move View Left") , this );
    KAction* moveViewRightAction = new KAction( i18n("Move View Right") , this );
135

136 137 138 139 140
    // list of actions that should only be enabled when there are multiple view
    // containers open
    QList<QAction*> multiViewOnlyActions;
    multiViewOnlyActions << nextContainerAction;

141 142
    if ( collection )
    {
143
        KAction* splitLeftRightAction = new KAction( KIcon("view-split-left-right"),
Thomas Reitelbach's avatar
Thomas Reitelbach committed
144
                                                      i18nc("@action:inmenu", "Split View Left/Right"),
145 146 147 148 149
                                                      this );
        splitLeftRightAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_L) );
        collection->addAction("split-view-left-right",splitLeftRightAction);
        connect( splitLeftRightAction , SIGNAL(triggered()) , this , SLOT(splitLeftRight()) );

150
        KAction* splitTopBottomAction = new KAction( KIcon("view-split-top-bottom") , 
Thomas Reitelbach's avatar
Thomas Reitelbach committed
151
                                             i18nc("@action:inmenu", "Split View Top/Bottom"),this);
152 153 154 155
        splitTopBottomAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_T) );
        collection->addAction("split-view-top-bottom",splitTopBottomAction);
        connect( splitTopBottomAction , SIGNAL(triggered()) , this , SLOT(splitTopBottom()));

Thomas Reitelbach's avatar
Thomas Reitelbach committed
156
        KAction* closeActiveAction = new KAction( i18nc("@action:inmenu Close Active View", "Close Active") , this );
Pino Toscano's avatar
Pino Toscano committed
157
        closeActiveAction->setIcon(KIcon("view-close"));
158
        closeActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_S) );
159
        closeActiveAction->setEnabled(false);
160 161
        collection->addAction("close-active-view",closeActiveAction);
        connect( closeActiveAction , SIGNAL(triggered()) , this , SLOT(closeActiveView()) );
162 163 164
      
        multiViewOnlyActions << closeActiveAction; 

Thomas Reitelbach's avatar
Thomas Reitelbach committed
165
        KAction* closeOtherAction = new KAction( i18nc("@action:inmenu Close Other Views", "Close Others") , this );
166
        closeOtherAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_O) );
167
        closeOtherAction->setEnabled(false);
168 169
        collection->addAction("close-other-views",closeOtherAction);
        connect( closeOtherAction , SIGNAL(triggered()) , this , SLOT(closeOtherViews()) );
170 171

        multiViewOnlyActions << closeOtherAction;
172

173
        KAction* detachViewAction = collection->addAction("detach-view");
Pino Toscano's avatar
Pino Toscano committed
174
        detachViewAction->setIcon( KIcon("tab-detach") );
175 176 177 178
        detachViewAction->setText( i18n("&Detach View") );
        // 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
        detachViewAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_H) );
179
       
180
          connect( this , SIGNAL(splitViewToggle(bool)) , this , SLOT(updateDetachViewState()) ); 
181
        connect( detachViewAction , SIGNAL(triggered()) , this , SLOT(detachActiveView()) );
182 183
   
        // Expand & Shrink Active View
Thomas Reitelbach's avatar
Thomas Reitelbach committed
184
        KAction* expandActiveAction = new KAction( i18nc("@action:inmenu", "Expand View") , this );
185 186 187 188
        expandActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_BracketRight) );
        collection->addAction("expand-active-view",expandActiveAction);
        connect( expandActiveAction , SIGNAL(triggered()) , this , SLOT(expandActiveView()) );

189 190
        multiViewOnlyActions << expandActiveAction;

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

196 197
        multiViewOnlyActions << shrinkActiveAction;

198
        // Next / Previous View , Next Container
199 200 201
        collection->addAction("next-view",nextViewAction);
        collection->addAction("previous-view",previousViewAction);
        collection->addAction("next-container",nextContainerAction);
202 203
        collection->addAction("move-view-left",moveViewLeftAction);
        collection->addAction("move-view-right",moveViewRightAction);
204 205 206 207 208 209 210 211 212 213 214 215 216

        // 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);
        }
217
    }
Robert Knight's avatar
 
Robert Knight committed
218

219 220 221 222 223 224
    QListIterator<QAction*> iter(multiViewOnlyActions);
    while ( iter.hasNext() )
    {
        connect( this , SIGNAL(splitViewToggle(bool)) , iter.next() , SLOT(setEnabled(bool)) );
    }

225
    // keyboard shortcut only actions
226
    KShortcut nextViewShortcut = nextViewAction->shortcut();
227 228
    nextViewShortcut.setPrimary( QKeySequence(Qt::SHIFT+Qt::Key_Right) );
    nextViewShortcut.setAlternate( QKeySequence(Qt::CTRL+Qt::Key_PageUp) );
229
    nextViewAction->setShortcut(nextViewShortcut); 
Robert Knight's avatar
 
Robert Knight committed
230
    connect( nextViewAction, SIGNAL(triggered()) , this , SLOT(nextView()) );
231
    _viewSplitter->addAction(nextViewAction);
Robert Knight's avatar
 
Robert Knight committed
232

233 234 235 236
    KShortcut previousViewShortcut = previousViewAction->shortcut();
    previousViewShortcut.setPrimary( QKeySequence(Qt::SHIFT+Qt::Key_Left) );
    previousViewShortcut.setAlternate( QKeySequence(Qt::CTRL+Qt::Key_PageDown) );
    previousViewAction->setShortcut(previousViewShortcut);
Robert Knight's avatar
 
Robert Knight committed
237
    connect( previousViewAction, SIGNAL(triggered()) , this , SLOT(previousView()) );
238
    _viewSplitter->addAction(previousViewAction);
Robert Knight's avatar
 
Robert Knight committed
239 240 241

    nextContainerAction->setShortcut( QKeySequence(Qt::SHIFT+Qt::Key_Tab) );
    connect( nextContainerAction , SIGNAL(triggered()) , this , SLOT(nextContainer()) );
242
    _viewSplitter->addAction(nextContainerAction);
243 244 245 246 247 248 249

    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
250
}
251 252 253 254 255 256 257 258 259 260
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));
}
261 262
void ViewManager::updateDetachViewState()
{
263 264
    if (!_actionCollection)
        return;
265

266

267 268
    bool splitView = _viewSplitter->containers().count() >= 2;
    bool shouldEnable = splitView || _viewSplitter->activeContainer()->views().count() >= 2;
269

270
    QAction* detachAction = _actionCollection->action("detach-view");
271

272 273
    if ( detachAction && shouldEnable != detachAction->isEnabled() )
        detachAction->setEnabled(shouldEnable);
274
}
275 276 277 278 279 280 281 282 283 284 285 286
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
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
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();
308 309 310 311 312
}
void ViewManager::detachActiveView()
{
    // find the currently active view and remove it from its container 
    ViewContainer* container = _viewSplitter->activeContainer();
313
    TerminalDisplay* activeView = dynamic_cast<TerminalDisplay*>(container->activeView());
314 315 316 317 318 319 320 321 322 323

    if (!activeView)
        return;

    emit viewDetached(_sessionMap[activeView]);
    
    _sessionMap.remove(activeView);

    // remove the view from this window
    container->removeView(activeView);
324
    activeView->deleteLater();
325 326 327 328 329 330 331

    // 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 )
    {
332
        removeContainer(container);
333 334 335 336
    }

}

337
void ViewManager::sessionFinished()
338
{
339 340 341 342
    // if this slot is called after the view manager's main widget
    // has been destroyed, do nothing
    if (!_viewSplitter)
        return;
343

344 345
    Session* session = qobject_cast<Session*>(sender());

346 347 348 349 350 351 352 353
    if ( _sessionMap[qobject_cast<TerminalDisplay*>(activeView())] == session )
    {
        // switch to the previous view before deleting the session views to prevent flicker 
        // occurring as a result of an interval between removing the active view and switching
        // to the previous view
        previousView();
    }

354 355
    Q_ASSERT(session);

356
    // close attached views
357
    QList<TerminalDisplay*> children = _viewSplitter->findChildren<TerminalDisplay*>();
358

359
    foreach ( TerminalDisplay* view , children )
360 361 362 363
    {
        if ( _sessionMap[view] == session )
        {
            _sessionMap.remove(view);
364
            view->deleteLater();
365
        }
Robert Knight's avatar
 
Robert Knight committed
366
    }
367

Robert Knight's avatar
 
Robert Knight committed
368 369 370 371
}

void ViewManager::focusActiveView()
{
Robert Knight's avatar
Robert Knight committed
372 373 374 375 376 377
    // 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
378 379 380 381 382 383 384 385 386
    ViewContainer* container = _viewSplitter->activeContainer(); 
    if ( container )
    {
        QWidget* activeView = container->activeView();
        if ( activeView )
        {
            activeView->setFocus(Qt::MouseFocusReason);
        }
    }
387 388
}

389

390 391
void ViewManager::viewActivated( QWidget* view )
{
392 393 394 395 396 397
    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);
398 399
}

400
void ViewManager::splitLeftRight()
401
{
402
    splitView(Qt::Horizontal);
403
}
404
void ViewManager::splitTopBottom()
405
{
406
    splitView(Qt::Vertical);
407 408
}

409
void ViewManager::splitView(Qt::Orientation orientation)
410
{
411 412 413 414
    // 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());
    
415
    ViewContainer* container = 0; 
416 417

    while (existingViewIter.hasNext())
418
    {
419
        Session* session = _sessionMap[(TerminalDisplay*)existingViewIter.next()];
420
        TerminalDisplay* display = createTerminalDisplay(session);
421
        applyProfile(display,SessionManager::instance()->sessionProfile(session),false);
422
        ViewProperties* properties = createController(session,display);
423

424
        _sessionMap[display] = session;
425

426 427 428
        // create a container using settings from the first 
        // session in the previous container
        if ( !container )
429
            container = createContainer(SessionManager::instance()->sessionProfile(session));
430

431 432 433
        container->addView(display,properties);
        session->addView( display );
    }
434

435 436
    _viewSplitter->addContainer(container,orientation);
    emit splitViewToggle(_viewSplitter->containers().count() > 0);
437

438 439
    // focus the new container
    container->containerWidget()->setFocus();
440 441

    // ensure that the active view is focused after the split / unsplit
442 443 444 445 446
    ViewContainer* activeContainer = _viewSplitter->activeContainer();
    QWidget* activeView = activeContainer ? activeContainer->activeView() : 0;

    if ( activeView )
        activeView->setFocus(Qt::OtherFocusReason);
447
}
448
void ViewManager::removeContainer(ViewContainer* container)
449
{
450 451 452 453 454 455 456 457
    // 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);
    } 

458
    _viewSplitter->removeContainer(container);
459
    container->deleteLater();
460 461

    emit splitViewToggle( _viewSplitter->containers().count() > 1 );
462
}
463 464 465 466 467 468 469 470
void ViewManager::expandActiveView()
{
    _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(),10);
}
void ViewManager::shrinkActiveView()
{
    _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(),-10);
}
471 472 473 474 475 476 477 478
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);
479

480 481 482 483
        // focus next container so that user can continue typing 
        // without having to manually focus it themselves
        nextContainer();
    }
484 485 486 487 488 489 490 491 492 493
}
void ViewManager::closeOtherViews()
{
    ViewContainer* active = _viewSplitter->activeContainer();

    QListIterator<ViewContainer*> iter(_viewSplitter->containers());
    while ( iter.hasNext() )
    {
        ViewContainer* next = iter.next();
        if ( next != active )
494
            removeContainer(next);
495 496 497
    }
}

498
SessionController* ViewManager::createController(Session* session , TerminalDisplay* view)
499
{
Robert Knight's avatar
Robert Knight committed
500 501
    // create a new controller for the session, and ensure that this view manager
    // is notified when the view gains the focus
502
    SessionController* controller = new SessionController(session,view,this);
503
    connect( controller , SIGNAL(focused(SessionController*)) , this , SLOT(controllerChanged(SessionController*)) );
504 505
    connect( session , SIGNAL(destroyed()) , controller , SLOT(deleteLater()) );
    connect( view , SIGNAL(destroyed()) , controller , SLOT(deleteLater()) );
506

507 508 509
    // if this is the first controller created then set it as the active controller
    if (!_pluggedController)
        controllerChanged(controller);
510

511 512 513
    return controller;
}

514 515
void ViewManager::controllerChanged(SessionController* controller)
{
516 517
    if ( controller == _pluggedController )
        return;
518

519
    _viewSplitter->setFocusProxy(controller->view());
520

521 522
    _pluggedController = controller;
    emit activeViewChanged(controller);
523 524 525 526
}

SessionController* ViewManager::activeViewController() const
{
527
    return _pluggedController;
528
}
529 530 531 532 533 534

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

535 536
void ViewManager::createView(Session* session, ViewContainer* container, int index)
{
537
    // notify this view manager when the session finishes so that its view
538
    // can be deleted
539 540 541
    //
    // TODO - Find a more efficient a way to avoid multiple connections
    disconnect( session , SIGNAL(finished()) , this , SLOT(sessionFinished()) );
542 543
    connect( session , SIGNAL(finished()) , this , SLOT(sessionFinished()) );

544
     bool isFirst = _sessionMap.isEmpty();
545
     TerminalDisplay* display = createTerminalDisplay(session);
546
     applyProfile(display,SessionManager::instance()->sessionProfile(session),isFirst);
547 548 549 550 551 552 553 554 555 556 557
     
     // 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
558
     const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
559 560 561 562 563 564 565
     session->setDarkBackground( colorSchemeForProfile(profile)->hasDarkBackground() );

     if ( container == _viewSplitter->activeContainer() ) 
     {
         container->setActiveView(display);
         display->setFocus( Qt::OtherFocusReason );
     }
566 567
    
     updateDetachViewState();
568
}
569

570
void ViewManager::createView(Session* session)
571
{
572 573 574
    // create the default container
    if (_viewSplitter->containers().count() == 0)
    {
575
        _viewSplitter->addContainer( createContainer(SessionManager::instance()->sessionProfile(session)) , 
576
                                     Qt::Vertical );
577
        emit splitViewToggle(false);
578 579
    }

580
       
Robert Knight's avatar
Robert Knight committed
581 582 583
    // 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 
584 585 586 587 588
    QListIterator<ViewContainer*> containerIter(_viewSplitter->containers());

    while ( containerIter.hasNext() )
    {
        ViewContainer* container = containerIter.next();
589
        createView(session,container,-1);
590
    }
591

592 593
}

594
ViewContainer* ViewManager::createContainer(const Profile::Ptr info)
595
{
596 597
    Q_ASSERT( info );

598
    const int tabPosition = info->property<int>(Profile::TabBarPosition);
599 600 601 602 603

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

604 605 606 607 608
    ViewContainer* container = 0;

    switch ( _navigationMethod )
    {
        case TabbedNavigation:    
609
            container = new TabbedViewContainer(position,_viewSplitter);
610 611 612 613 614
            break;
        case NoNavigation:
        default:
            container = new StackedViewContainer(_viewSplitter);
    }
615

616
    // connect signals and slots
617 618 619 620 621 622
    connect( container , SIGNAL(viewAdded(QWidget*,ViewProperties*)) , _containerSignalMapper ,
           SLOT(map()) );
    connect( container , SIGNAL(viewRemoved(QWidget*)) , _containerSignalMapper ,
           SLOT(map()) ); 
    _containerSignalMapper->setMapping(container,container);

623
    connect( container, SIGNAL(newViewRequest()), this, SIGNAL(newViewRequest()) );
624
    connect( container, SIGNAL(moveViewRequest(int,int,bool&)), 
625
    this , SLOT(containerMoveViewRequest(int,int,bool&)) );
626
    connect( container , SIGNAL(viewRemoved(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
627
    connect( container , SIGNAL(closeRequest(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
628
    connect( container , SIGNAL(activeViewChanged(QWidget*)) , this , SLOT(viewActivated(QWidget*)));
629
    
630
    return container;
631
}
632 633
void ViewManager::containerMoveViewRequest(int index, int id, bool& moved)
{
634 635
    ViewContainer* container = qobject_cast<ViewContainer*>(sender());
    SessionController* controller = qobject_cast<SessionController*>(ViewProperties::propertiesById(id));
636

637 638
    if (!controller)
        return;
639

640 641
    createView(controller->session(),container,index);
    moved = true;
642
}
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
void ViewManager::setNavigationMethod(NavigationMethod method)
{
    _navigationMethod = method;

    KActionCollection* collection = _actionCollection;

    if ( collection )
    {
        QAction* action;

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

        action = collection->action( "previous-view" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );
658 659 660 661 662 663

        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 );
664 665 666

        action = collection->action( "rename-session" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );
667 668 669
    }
}

670 671
ViewManager::NavigationMethod ViewManager::navigationMethod() const { return _navigationMethod; }

672 673
void ViewManager::containerViewsChanged(QObject* container)
{
674
    if (_viewSplitter && container == _viewSplitter->activeContainer() )
675 676 677 678 679
    {
        emit viewPropertiesChanged( viewProperties() );
    } 
}

680 681
void ViewManager::viewCloseRequest(QWidget* view)
{
682 683 684 685 686
    //FIXME Check that this cast is actually legal
    TerminalDisplay* display = (TerminalDisplay*)view;
  
    Q_ASSERT(display);

687 688
    // 1. detach view from session
    // 2. if the session has no views left, close it
689
    Session* session = _sessionMap[ display ];
690
    _sessionMap.remove(display);
691 692
    if ( session )
    {
693
        display->deleteLater();
694

695
        if ( session->views().count() == 0 )
696
            session->close();
697
    }
698 699 700 701 702
    //we only update the focus if the splitter is still alive
    if (_viewSplitter) {
        focusActiveView();
        updateDetachViewState();
    }
703 704
}

705
TerminalDisplay* ViewManager::createTerminalDisplay(Session* session)
706
{
707
   TerminalDisplay* display = new TerminalDisplay(0);
708 709

   //TODO Temporary settings used here
710 711
   display->setBellMode(TerminalDisplay::NotifyBell);
   display->setTerminalSizeHint(true);
712
   display->setTripleClickMode(TerminalDisplay::SelectWholeLine);
713
   display->setTerminalSizeStartup(true);
714
   display->setScrollBarPosition(TerminalDisplay::ScrollBarRight);
715 716
   display->setRandomSeed(session->sessionId() * 31);

717 718 719
   return display;
}

720
const ColorScheme* ViewManager::colorSchemeForProfile(const Profile::Ptr info) const
721
{
722 723
    const ColorScheme* colorScheme = ColorSchemeManager::instance()->
                                            findColorScheme(info->colorScheme());
724 725
    if ( !colorScheme )
       colorScheme = ColorSchemeManager::instance()->defaultColorScheme(); 
726 727
    Q_ASSERT( colorScheme );

728 729 730
    return colorScheme;
}

731 732
void ViewManager::applyProfile(TerminalDisplay* view , const Profile::Ptr info, 
                               bool applyContainerSettings) 
733 734 735
{
    Q_ASSERT( info );
    
736
    const ColorScheme* colorScheme = colorSchemeForProfile(info);
737

738
    // menu bar visibility
739
    emit setMenuBarVisibleRequest( info->property<bool>(Profile::ShowMenuBar) );
740 741

    // tab bar visibility
742 743 744 745 746
    if (applyContainerSettings)
    {
        ViewContainer* container = _viewSplitter->activeContainer();
        int tabBarMode = info->property<int>(Profile::TabBarMode);
        int tabBarPosition = info->property<int>(Profile::TabBarPosition);
Robert Knight's avatar
 
Robert Knight committed
747
        bool showNewCloseButtons = info->property<bool>(Profile::ShowNewAndCloseTabButtons);
748

749 750 751 752 753 754
        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);
755

756
        ViewContainer::NavigationPosition position = container->navigationPosition();
757

758 759 760 761
        if ( tabBarPosition == Profile::TabBarTop )
            position = ViewContainer::NavigationPositionTop;
        else if ( tabBarPosition == Profile::TabBarBottom )
            position = ViewContainer::NavigationPositionBottom; 
762

763 764
        if ( container->supportedNavigationPositions().contains(position) )
            container->setNavigationPosition(position);
765
       
Robert Knight's avatar
 
Robert Knight committed
766
        if (showNewCloseButtons)
767
        {
Robert Knight's avatar
 
Robert Knight committed
768 769
            container->setFeatures(container->features() 
                               | ViewContainer::QuickNewView | ViewContainer::QuickCloseView);
770 771 772
            container->setNewViewMenu(createNewViewMenu());
        }
        else
Robert Knight's avatar
 
Robert Knight committed
773 774
            container->setFeatures(container->features() 
                            & ~ViewContainer::QuickNewView & ~ViewContainer::QuickCloseView);
775
    }
776

777
    // load colour scheme
778 779 780 781
    ColorEntry table[TABLE_COLORS];
    
    colorScheme->getColorTable(table , view->randomSeed() );
    view->setColorTable(table);
782
    view->setOpacity(colorScheme->opacity());
783
  
784
    // load font 
785
    view->setAntialias(info->property<bool>(Profile::AntiAliasFonts));
786
    view->setVTFont(info->font());
787 788

    // set scroll-bar position
789
    int scrollBarPosition = info->property<int>(Profile::ScrollBarPosition);
790 791

    if ( scrollBarPosition == Profile::ScrollBarHidden )
792
       view->setScrollBarPosition(TerminalDisplay::NoScrollBar);
793
    else if ( scrollBarPosition == Profile::ScrollBarLeft )
794
       view->setScrollBarPosition(TerminalDisplay::ScrollBarLeft);
795
    else if ( scrollBarPosition == Profile::ScrollBarRight )
796
       view->setScrollBarPosition(TerminalDisplay::ScrollBarRight);
797 798

    // terminal features
799
    bool blinkingCursor = info->property<bool>(Profile::BlinkingCursorEnabled);
800 801
    view->setBlinkingCursor(blinkingCursor);  

802 803 804
    bool blinkingText = info->property<bool>(Profile::BlinkingTextEnabled);
    view->setBlinkingTextEnabled(blinkingText);

805 806 807
    bool bidiEnabled = info->property<bool>(Profile::BidiRenderingEnabled);
    view->setBidiEnabled(bidiEnabled);

808
    // cursor shape
809
    int cursorShape = info->property<int>(Profile::CursorShape);
810 811 812 813 814 815 816 817 818

    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
819 820
    bool useCustomColor = info->property<bool>(Profile::UseCustomCursorColor);
    const QColor& cursorColor = info->property<QColor>(Profile::CustomCursorColor);
821 822 823 824
        
    view->setKeyboardCursorColor(!useCustomColor,cursorColor);

    // word characters
825
    view->setWordCharacters( info->property<QString>(Profile::WordCharacters) );
826 827
}

828 829
void ViewManager::updateViewsForSession(Session* session)
{
830
    QListIterator<TerminalDisplay*> iter(_sessionMap.keys(session));
831 832
    while ( iter.hasNext() )
    {
833
        applyProfile(iter.next(),SessionManager::instance()->sessionProfile(session),false);
834 835 836
    }
}

837
void ViewManager::profileChanged(Profile::Ptr profile)
838
{
839
    QHashIterator<TerminalDisplay*,Session*> iter(_sessionMap);
840 841 842 843 844 845

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

        // if session uses this profile, update the display
846
        if ( iter.key() != 0 && 
847 848
             iter.value() != 0 && 
             SessionManager::instance()->sessionProfile(iter.value()) == profile ) 
849
        {
850
            applyProfile(iter.key(),profile,true);
851 852
        }
    }
853 854 855 856 857 858 859 860 861 862 863 864 865
}

QList<ViewProperties*> ViewManager::viewProperties() const
{
    QList<ViewProperties*> list;

    ViewContainer* container = _viewSplitter->activeContainer();

    Q_ASSERT( container );

    QListIterator<QWidget*> viewIter(container->views());
    while ( viewIter.hasNext() )
    {
Robert Knight's avatar
Robert Knight committed
866 867
        ViewProperties* properties = container->viewProperties(viewIter.next()); 
        Q_ASSERT( properties );
868 869
        list << properties; 
    } 
870

871
    return list;
872 873
}

Robert Knight's avatar
 
Robert Knight committed
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 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
void ViewManager::saveSessions(KConfigGroup& group)
{
    // find all unique session restore IDs
    QList<int> ids;
    QHash<Session*,int> unique;

    // first: sessions in the active container, preserving the order
    ViewContainer* container = _viewSplitter->activeContainer();
    Q_ASSERT(container);
    TerminalDisplay* activeview = dynamic_cast<TerminalDisplay*>(container->activeView());

    QListIterator<QWidget*> viewIter(container->views());
    int tab = 1;
    while (viewIter.hasNext())
    {
        TerminalDisplay *view = dynamic_cast<TerminalDisplay*>(viewIter.next());
        Q_ASSERT(view);
        Session *session = _sessionMap[view];
        ids << SessionManager::instance()->getRestoreId(session);
        if (view == activeview) group.writeEntry("Active", tab);
        unique.insert(session, 1);
        tab++;
    }

    // second: all other sessions, in random order
    // we don't want to have sessions restored that are not connected
    foreach(Session* session, _sessionMap)
        if (!unique.contains(session))
        {
            ids << SessionManager::instance()->getRestoreId(session);
            unique.insert(session, 1);
        }

    group.writeEntry("Sessions", ids);
}

void ViewManager::restoreSessions(const KConfigGroup& group)
{
    QList<int> ids = group.readEntry("Sessions", QList<int>());
    int activeTab  = group.readEntry("Active", 0);
    TerminalDisplay *display = 0;

    int tab = 1;
    foreach(int id, ids)
    {
        Session *session = SessionManager::instance()->idToSession(id);
        createView(session);
        if (!session->isRunning())
            session->run();
        if (tab++ == activeTab)
            display = dynamic_cast<TerminalDisplay*>(activeView());
    }

    if (display)
    {
        _viewSplitter->activeContainer()->setActiveView(display);
        display->setFocus(Qt::OtherFocusReason);
    }
}

934 935 936 937 938
uint qHash(QPointer<TerminalDisplay> display)
{
    return qHash((TerminalDisplay*)display);
}

939
#include "ViewManager.moc"
Robert Knight's avatar
 
Robert Knight committed
940 941 942 943 944 945 946 947 948

/*
  Local Variables:
  mode: c++
  c-file-style: "stroustrup"
  indent-tabs-mode: nil
  tab-width: 4
  End:
*/