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

    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

30 31
// KDE
#include <kdebug.h>
Robert Knight's avatar
Robert Knight committed
32
#include <KAcceleratorManager>
33
#include <KGlobal>
34 35 36 37 38
#include <KLocale>
#include <KToggleAction>
#include <KXMLGUIFactory>

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

47 48
using namespace Konsole;

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

60 61 62 63 64 65 66
    // 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);
67
    _viewSplitter->setFocusPolicy(Qt::NoFocus);
68

69 70 71 72 73 74
    // 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()) );
75 76 77 78

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

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

ViewManager::~ViewManager()
{
}
90 91 92 93 94 95 96 97 98 99 100 101
QWidget* ViewManager::activeView() const
{
    ViewContainer* container = _viewSplitter->activeContainer();
    if ( container )
    {
        return container->activeView();
    }
    else
    {
        return 0;
    }
}
102

103 104 105 106 107
QWidget* ViewManager::widget() const
{
    return _viewSplitter;
}

108 109
void ViewManager::setupActions()
{
110
    KActionCollection* collection = _actionCollection;
111

112 113 114
    KAction* nextViewAction = new KAction( i18n("Next View") , this );
    KAction* previousViewAction = new KAction( i18n("Previous View") , this );
    QAction* nextContainerAction = new QAction( i18n("Next View Container") , this);
115 116 117 118
  
    QAction* moveViewLeftAction = new QAction( i18n("Move View Left") , this );
    QAction* moveViewRightAction = new QAction( i18n("Move View Right") , this );

119 120 121 122 123
    // list of actions that should only be enabled when there are multiple view
    // containers open
    QList<QAction*> multiViewOnlyActions;
    multiViewOnlyActions << nextContainerAction;

124 125
    if ( collection )
    {
126
        KAction* splitLeftRightAction = new KAction( KIcon("view-split-left-right"),
Thomas Reitelbach's avatar
Thomas Reitelbach committed
127
                                                      i18nc("@action:inmenu", "Split View Left/Right"),
128 129 130 131 132
                                                      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()) );

133
        KAction* splitTopBottomAction = new KAction( KIcon("view-split-top-bottom") , 
Thomas Reitelbach's avatar
Thomas Reitelbach committed
134
                                             i18nc("@action:inmenu", "Split View Top/Bottom"),this);
135 136 137 138
        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
139
        KAction* closeActiveAction = new KAction( i18nc("@action:inmenu Close Active View", "Close Active") , this );
Pino Toscano's avatar
Pino Toscano committed
140
        closeActiveAction->setIcon(KIcon("view-close"));
141
        closeActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_S) );
142
        closeActiveAction->setEnabled(false);
143 144
        collection->addAction("close-active-view",closeActiveAction);
        connect( closeActiveAction , SIGNAL(triggered()) , this , SLOT(closeActiveView()) );
145 146 147
      
        multiViewOnlyActions << closeActiveAction; 

Thomas Reitelbach's avatar
Thomas Reitelbach committed
148
        KAction* closeOtherAction = new KAction( i18nc("@action:inmenu Close Other Views", "Close Others") , this );
149
        closeOtherAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_O) );
150
        closeOtherAction->setEnabled(false);
151 152
        collection->addAction("close-other-views",closeOtherAction);
        connect( closeOtherAction , SIGNAL(triggered()) , this , SLOT(closeOtherViews()) );
153 154

        multiViewOnlyActions << closeOtherAction;
155

156
        KAction* detachViewAction = collection->addAction("detach-view");
Pino Toscano's avatar
Pino Toscano committed
157
        detachViewAction->setIcon( KIcon("tab-detach") );
158 159 160 161
        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) );
162 163
       
  		connect( this , SIGNAL(splitViewToggle(bool)) , this , SLOT(updateDetachViewState()) ); 
164
        connect( detachViewAction , SIGNAL(triggered()) , this , SLOT(detachActiveView()) );
165 166
   
        // Expand & Shrink Active View
Thomas Reitelbach's avatar
Thomas Reitelbach committed
167
        KAction* expandActiveAction = new KAction( i18nc("@action:inmenu", "Expand View") , this );
168 169 170 171
        expandActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_BracketRight) );
        collection->addAction("expand-active-view",expandActiveAction);
        connect( expandActiveAction , SIGNAL(triggered()) , this , SLOT(expandActiveView()) );

172 173
        multiViewOnlyActions << expandActiveAction;

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

179 180
        multiViewOnlyActions << shrinkActiveAction;

181
        // Next / Previous View , Next Container
182 183 184
        collection->addAction("next-view",nextViewAction);
        collection->addAction("previous-view",previousViewAction);
        collection->addAction("next-container",nextContainerAction);
185 186
        collection->addAction("move-view-left",moveViewLeftAction);
        collection->addAction("move-view-right",moveViewRightAction);
187
    }
Robert Knight's avatar
 
Robert Knight committed
188

189 190 191 192 193 194
    QListIterator<QAction*> iter(multiViewOnlyActions);
    while ( iter.hasNext() )
    {
        connect( this , SIGNAL(splitViewToggle(bool)) , iter.next() , SLOT(setEnabled(bool)) );
    }

195
    // keyboard shortcut only actions
196
    KShortcut nextViewShortcut = nextViewAction->shortcut();
197 198
    nextViewShortcut.setPrimary( QKeySequence(Qt::SHIFT+Qt::Key_Right) );
    nextViewShortcut.setAlternate( QKeySequence(Qt::CTRL+Qt::Key_PageUp) );
199
    nextViewAction->setShortcut(nextViewShortcut); 
Robert Knight's avatar
 
Robert Knight committed
200
    connect( nextViewAction, SIGNAL(triggered()) , this , SLOT(nextView()) );
201
    _viewSplitter->addAction(nextViewAction);
Robert Knight's avatar
 
Robert Knight committed
202

203 204 205 206
    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
207
    connect( previousViewAction, SIGNAL(triggered()) , this , SLOT(previousView()) );
208
    _viewSplitter->addAction(previousViewAction);
Robert Knight's avatar
 
Robert Knight committed
209 210 211

    nextContainerAction->setShortcut( QKeySequence(Qt::SHIFT+Qt::Key_Tab) );
    connect( nextContainerAction , SIGNAL(triggered()) , this , SLOT(nextContainer()) );
212
    _viewSplitter->addAction(nextContainerAction);
213 214 215 216 217 218 219

    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
220 221
}

222 223 224 225 226
void ViewManager::updateDetachViewState()
{
	if (!_actionCollection)
		return;

227

228 229 230 231 232 233 234 235
	bool splitView = _viewSplitter->containers().count() >= 2;
	bool shouldEnable = splitView || _viewSplitter->activeContainer()->views().count() >= 2;

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

	if ( detachAction && shouldEnable != detachAction->isEnabled() )
		detachAction->setEnabled(shouldEnable);
}
236 237 238 239 240 241 242 243 244 245 246 247
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
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
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();
269 270 271 272 273
}
void ViewManager::detachActiveView()
{
    // find the currently active view and remove it from its container 
    ViewContainer* container = _viewSplitter->activeContainer();
274
    TerminalDisplay* activeView = dynamic_cast<TerminalDisplay*>(container->activeView());
275 276 277 278 279 280 281 282 283 284

    if (!activeView)
        return;

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

    // remove the view from this window
    container->removeView(activeView);
285
    activeView->deleteLater();
286 287 288 289 290 291 292

    // 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 )
    {
293
        removeContainer(container);
294 295 296 297
    }

}

298
void ViewManager::sessionFinished()
299
{
300 301 302 303 304
	// if this slot is called after the view manager's main widget
	// has been destroyed, do nothing
	if (!_viewSplitter)
		return;

305 306
    Session* session = qobject_cast<Session*>(sender());

307 308 309 310 311 312 313 314
    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();
    }

315 316
    Q_ASSERT(session);

317
    // close attached views
318
    QList<TerminalDisplay*> children = _viewSplitter->findChildren<TerminalDisplay*>();
319

320
    foreach ( TerminalDisplay* view , children )
321 322 323 324
    {
        if ( _sessionMap[view] == session )
        {
            _sessionMap.remove(view);
325
            view->deleteLater();
326
        }
Robert Knight's avatar
 
Robert Knight committed
327
    }
328

Robert Knight's avatar
 
Robert Knight committed
329 330 331 332
}

void ViewManager::focusActiveView()
{
Robert Knight's avatar
Robert Knight committed
333 334 335 336 337 338
    // 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
339 340 341 342 343 344 345 346 347
    ViewContainer* container = _viewSplitter->activeContainer(); 
    if ( container )
    {
        QWidget* activeView = container->activeView();
        if ( activeView )
        {
            activeView->setFocus(Qt::MouseFocusReason);
        }
    }
348 349
}

350

351 352
void ViewManager::viewActivated( QWidget* view )
{
353 354 355 356 357 358
    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);
359 360
}

361
void ViewManager::splitLeftRight()
362
{
363
    splitView(Qt::Horizontal);
364
}
365
void ViewManager::splitTopBottom()
366
{
367
    splitView(Qt::Vertical);
368 369
}

370
void ViewManager::splitView(Qt::Orientation orientation)
371
{
372 373 374 375
    // 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());
    
376
    ViewContainer* container = 0; 
377 378

    while (existingViewIter.hasNext())
379
    {
380
        Session* session = _sessionMap[(TerminalDisplay*)existingViewIter.next()];
381
        TerminalDisplay* display = createTerminalDisplay(session);
382
        applyProfile(display,SessionManager::instance()->sessionProfile(session));
383
        ViewProperties* properties = createController(session,display);
384

385
        _sessionMap[display] = session;
386

387 388 389
        // create a container using settings from the first 
        // session in the previous container
        if ( !container )
390
            container = createContainer(SessionManager::instance()->sessionProfile(session));
391

392 393 394
        container->addView(display,properties);
        session->addView( display );
    }
395

396 397
    _viewSplitter->addContainer(container,orientation);
    emit splitViewToggle(_viewSplitter->containers().count() > 0);
398

399 400
    // focus the new container
    container->containerWidget()->setFocus();
401 402

    // ensure that the active view is focused after the split / unsplit
403 404 405 406 407
    ViewContainer* activeContainer = _viewSplitter->activeContainer();
    QWidget* activeView = activeContainer ? activeContainer->activeView() : 0;

    if ( activeView )
        activeView->setFocus(Qt::OtherFocusReason);
408
}
409
void ViewManager::removeContainer(ViewContainer* container)
410
{
411 412 413 414 415 416 417 418
    // 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);
    } 

419
	_viewSplitter->removeContainer(container);
420
    container->deleteLater();
421 422

    emit splitViewToggle( _viewSplitter->containers().count() > 1 );
423
}
424 425 426 427 428 429 430 431
void ViewManager::expandActiveView()
{
    _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(),10);
}
void ViewManager::shrinkActiveView()
{
    _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(),-10);
}
432 433 434 435 436 437 438 439
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);
440

441 442 443 444
        // focus next container so that user can continue typing 
        // without having to manually focus it themselves
        nextContainer();
    }
445 446 447 448 449 450 451 452 453 454
}
void ViewManager::closeOtherViews()
{
    ViewContainer* active = _viewSplitter->activeContainer();

    QListIterator<ViewContainer*> iter(_viewSplitter->containers());
    while ( iter.hasNext() )
    {
        ViewContainer* next = iter.next();
        if ( next != active )
455
            removeContainer(next);
456 457 458
    }
}

459
SessionController* ViewManager::createController(Session* session , TerminalDisplay* view)
460
{
Robert Knight's avatar
Robert Knight committed
461 462
    // create a new controller for the session, and ensure that this view manager
    // is notified when the view gains the focus
463
    SessionController* controller = new SessionController(session,view,this);
464
    connect( controller , SIGNAL(focused(SessionController*)) , this , SLOT(controllerChanged(SessionController*)) );
465 466
    connect( session , SIGNAL(destroyed()) , controller , SLOT(deleteLater()) );
    connect( view , SIGNAL(destroyed()) , controller , SLOT(deleteLater()) );
467

468 469
	// if this is the first controller created then set it as the active controller
	if (!_pluggedController)
470
		controllerChanged(controller);
471

472 473 474
    return controller;
}

475 476
void ViewManager::controllerChanged(SessionController* controller)
{
477 478 479
	if ( controller == _pluggedController )
		return;

480 481
	_viewSplitter->setFocusProxy(controller->view());

482 483 484 485 486 487 488 489
	_pluggedController = controller;
	emit activeViewChanged(controller);
}

SessionController* ViewManager::activeViewController() const
{
	return _pluggedController;
}
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
void ViewManager::createView(Session* session, ViewContainer* container, int index)
{
	// notify this view manager when the session finishes so that its view
    // can be deleted
	//
	// TODO - Find a more efficient a way to avoid multiple connections
	disconnect( session , SIGNAL(finished()) , this , SLOT(sessionFinished()) );
    connect( session , SIGNAL(finished()) , this , SLOT(sessionFinished()) );

     TerminalDisplay* display = createTerminalDisplay(session);
     applyProfile(display,SessionManager::instance()->sessionProfile(session));
     
     // 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
 	const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
     session->setDarkBackground( colorSchemeForProfile(profile)->hasDarkBackground() );

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

524
void ViewManager::createView(Session* session)
525
{
526 527 528
    // create the default container
    if (_viewSplitter->containers().count() == 0)
    {
529
        _viewSplitter->addContainer( createContainer(SessionManager::instance()->sessionProfile(session)) , 
530
                                     Qt::Vertical );
531
        emit splitViewToggle(false);
532 533
    }

534
       
Robert Knight's avatar
Robert Knight committed
535 536 537
    // 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 
538 539 540 541 542
    QListIterator<ViewContainer*> containerIter(_viewSplitter->containers());

    while ( containerIter.hasNext() )
    {
        ViewContainer* container = containerIter.next();
543
		createView(session,container,-1);
544
    }
545

546 547
}

548
ViewContainer* ViewManager::createContainer(const Profile::Ptr info)
549
{
550 551
    Q_ASSERT( info );

552
    const int tabPosition = info->property<int>(Profile::TabBarPosition);
553 554 555 556 557

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

558 559 560 561 562 563 564 565 566 567 568
    ViewContainer* container = 0;

    switch ( _navigationMethod )
    {
        case TabbedNavigation:    
            container = new TabbedViewContainerV2(position,_viewSplitter);
            break;
        case NoNavigation:
        default:
            container = new StackedViewContainer(_viewSplitter);
    }
569

570
    // connect signals and slots
571 572 573 574 575 576
    connect( container , SIGNAL(viewAdded(QWidget*,ViewProperties*)) , _containerSignalMapper ,
           SLOT(map()) );
    connect( container , SIGNAL(viewRemoved(QWidget*)) , _containerSignalMapper ,
           SLOT(map()) ); 
    _containerSignalMapper->setMapping(container,container);

577
    connect( container, SIGNAL(newViewRequest()), this, SIGNAL(newViewRequest()) );
578 579
    connect( container, SIGNAL(moveViewRequest(int,int,bool&)), 
	this , SLOT(containerMoveViewRequest(int,int,bool&)) );
580
    connect( container , SIGNAL(viewRemoved(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
581
    connect( container , SIGNAL(closeRequest(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
582
    connect( container , SIGNAL(activeViewChanged(QWidget*)) , this , SLOT(viewActivated(QWidget*)));
583
    
584
    return container;
585
}
586 587 588 589
void ViewManager::containerMoveViewRequest(int index, int id, bool& moved)
{
	ViewContainer* container = qobject_cast<ViewContainer*>(sender());
	SessionController* controller = qobject_cast<SessionController*>(ViewProperties::propertiesById(id));
590

591 592 593 594 595 596
	if (!controller)
		return;

	createView(controller->session(),container,index);
	moved = true;
}
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
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 );
612 613 614 615 616 617

        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 );
618 619 620

        action = collection->action( "rename-session" );
        if ( action ) action->setEnabled( _navigationMethod != NoNavigation );
621 622 623
    }
}

624 625
ViewManager::NavigationMethod ViewManager::navigationMethod() const { return _navigationMethod; }

626 627 628 629 630 631 632 633
void ViewManager::containerViewsChanged(QObject* container)
{
    if ( container == _viewSplitter->activeContainer() )
    {
        emit viewPropertiesChanged( viewProperties() );
    } 
}

634 635
void ViewManager::viewCloseRequest(QWidget* view)
{
636 637 638 639 640
    //FIXME Check that this cast is actually legal
    TerminalDisplay* display = (TerminalDisplay*)view;
  
    Q_ASSERT(display);

641 642
    // 1. detach view from session
    // 2. if the session has no views left, close it
643
    Session* session = _sessionMap[ display ];
644
    _sessionMap.remove(display);
645 646
    if ( session )
    {
647
        display->deleteLater();
648

649
        if ( session->views().count() == 0 )
650
            session->close();
651
    }
652
        
653
    focusActiveView();
654
	updateDetachViewState();
655 656
}

657
TerminalDisplay* ViewManager::createTerminalDisplay(Session* session)
658
{
659
   TerminalDisplay* display = new TerminalDisplay(0);
660 661

   //TODO Temporary settings used here
662 663
   display->setBellMode(TerminalDisplay::NotifyBell);
   display->setTerminalSizeHint(true);
664
   display->setTripleClickMode(TerminalDisplay::SelectWholeLine);
665
   display->setTerminalSizeStartup(true);
666
   display->setScrollBarPosition(TerminalDisplay::ScrollBarRight);
667 668
   display->setRandomSeed(session->sessionId() * 31);

669 670 671
   return display;
}

672
const ColorScheme* ViewManager::colorSchemeForProfile(const Profile::Ptr info) const
673
{
674 675
    const ColorScheme* colorScheme = ColorSchemeManager::instance()->
                                            findColorScheme(info->colorScheme());
676 677
    if ( !colorScheme )
       colorScheme = ColorSchemeManager::instance()->defaultColorScheme(); 
678 679
    Q_ASSERT( colorScheme );

680 681 682
    return colorScheme;
}

683
void ViewManager::applyProfile(TerminalDisplay* view , const Profile::Ptr info ) 
684 685 686
{
    Q_ASSERT( info );
    
687
    const ColorScheme* colorScheme = colorSchemeForProfile(info);
688

689
    // menu bar visibility
690
    emit setMenuBarVisibleRequest( info->property<bool>(Profile::ShowMenuBar) );
691 692 693

    // tab bar visibility
    ViewContainer* container = _viewSplitter->activeContainer();
694 695
    int tabBarMode = info->property<int>(Profile::TabBarMode);
    int tabBarPosition = info->property<int>(Profile::TabBarPosition);
696

697 698 699 700 701 702 703
    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);

704 705
    ViewContainer::NavigationPosition position = container->navigationPosition();

706
    if ( tabBarPosition == Profile::TabBarTop )
707
        position = ViewContainer::NavigationPositionTop;
708
    else if ( tabBarPosition == Profile::TabBarBottom )
709 710 711 712
        position = ViewContainer::NavigationPositionBottom; 

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

714
    // load colour scheme
715 716 717 718
    ColorEntry table[TABLE_COLORS];
    
    colorScheme->getColorTable(table , view->randomSeed() );
    view->setColorTable(table);
719
    view->setOpacity(colorScheme->opacity());
720
  
721
    // load font 
722
    view->setAntialias(info->property<bool>(Profile::AntiAliasFonts));
723
    view->setVTFont(info->font());
724 725

    // set scroll-bar position
726
    int scrollBarPosition = info->property<int>(Profile::ScrollBarPosition);
727 728

    if ( scrollBarPosition == Profile::ScrollBarHidden )
729
       view->setScrollBarPosition(TerminalDisplay::NoScrollBar);
730
    else if ( scrollBarPosition == Profile::ScrollBarLeft )
731
       view->setScrollBarPosition(TerminalDisplay::ScrollBarLeft);
732
    else if ( scrollBarPosition == Profile::ScrollBarRight )
733
       view->setScrollBarPosition(TerminalDisplay::ScrollBarRight);
734 735

    // terminal features
736
    bool blinkingCursor = info->property<bool>(Profile::BlinkingCursorEnabled);
737 738
    view->setBlinkingCursor(blinkingCursor);  

739 740 741
    bool bidiEnabled = info->property<bool>(Profile::BidiRenderingEnabled);
    view->setBidiEnabled(bidiEnabled);

742
    // cursor shape
743
    int cursorShape = info->property<int>(Profile::CursorShape);
744 745 746 747 748 749 750 751 752

    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
753 754
    bool useCustomColor = info->property<bool>(Profile::UseCustomCursorColor);
    const QColor& cursorColor = info->property<QColor>(Profile::CustomCursorColor);
755 756 757 758
        
    view->setKeyboardCursorColor(!useCustomColor,cursorColor);

    // word characters
759
    view->setWordCharacters( info->property<QString>(Profile::WordCharacters) );
760 761
}

762 763
void ViewManager::updateViewsForSession(Session* session)
{
764
    QListIterator<TerminalDisplay*> iter(_sessionMap.keys(session));
765 766
    while ( iter.hasNext() )
    {
767
        applyProfile(iter.next(),SessionManager::instance()->sessionProfile(session));
768 769 770
    }
}

771
void ViewManager::profileChanged(Profile::Ptr profile)
772
{
773
    QHashIterator<TerminalDisplay*,Session*> iter(_sessionMap);
774 775 776 777 778 779

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

        // if session uses this profile, update the display
780 781 782
        if ( iter.key() != 0 && 
			 iter.value() != 0 && 
			 SessionManager::instance()->sessionProfile(iter.value()) == profile ) 
783
        {
784
            applyProfile(iter.key(),profile);
785 786
        }
    }
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
}

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

    ViewContainer* container = _viewSplitter->activeContainer();

    Q_ASSERT( container );

    QListIterator<QWidget*> viewIter(container->views());
    while ( viewIter.hasNext() )
    {
        ViewProperties* properties = container->viewProperties(viewIter.next());        Q_ASSERT( properties );
        list << properties; 
    } 
803

804
    return list;
805 806
}

807 808 809 810 811
uint qHash(QPointer<TerminalDisplay> display)
{
    return qHash((TerminalDisplay*)display);
}

812
#include "ViewManager.moc"