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:
*/