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

23
24
25
26
27
28
29
// KDE
#include <kdebug.h>
#include <KLocale>
#include <KToggleAction>
#include <KXMLGUIFactory>

// Konsole
30
31
#include "BookmarkHandler.h"
#include "MainWindow.h"
32
#include "Session.h"
33
#include "TerminalDisplay.h"
34
#include "schema.h"
35
36
37
38
39
40
#include "SessionController.h"
#include "SessionManager.h"
#include "ViewContainer.h"
#include "ViewSplitter.h"
#include "ViewManager.h"

41
42
using namespace Konsole;

43
ViewManager::ViewManager(MainWindow* mainWindow)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    : QObject(mainWindow)
    , _mainWindow(mainWindow)
    , _viewSplitter(0)
    , _pluggedController(0)
{
    // setup actions which relating to the view
    setupActions();

    // create main view area
    _viewSplitter = new ViewSplitter(_mainWindow);

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

ViewManager::~ViewManager()
{
}

64
65
66
67
68
QWidget* ViewManager::widget() const
{
    return _viewSplitter;
}

69
70
void ViewManager::setupActions()
{
71
72
    KActionCollection* collection = _mainWindow->actionCollection();

Robert Knight's avatar
Robert Knight committed
73
74
    _splitViewAction = new KToggleAction( KIcon("view-top-bottom"),i18n("&Split View") , this);
    _splitViewAction->setCheckedState( KGuiItem(i18n("&Remove Split") , KIcon("view-remove") ) );
75
    _splitViewAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_S) );
76
    collection->addAction("split-view",_splitViewAction);
77
    connect( _splitViewAction , SIGNAL(toggled(bool)) , this , SLOT(splitView(bool)));
78
79


80
    QAction* detachViewAction = collection->addAction("detach-view");
Robert Knight's avatar
Robert Knight committed
81
    detachViewAction->setIcon( KIcon("tab-breakoff") );
82
    detachViewAction->setText( i18n("&Detach View") );
83
84
85
    // 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_T) );
86
87
88

    connect( detachViewAction , SIGNAL(triggered()) , this , SLOT(detachActiveView()) );

89
90
91
    QAction* mergeAction = collection->addAction("merge-windows");
    mergeAction->setText( i18n("&Merge Windows") );
        
92
    connect( mergeAction , SIGNAL(triggered()) , _mainWindow , SLOT(mergeWindows()) );
Robert Knight's avatar
   
Robert Knight committed
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134


    QAction* nextViewAction = collection->addAction("next-view");
    nextViewAction->setText( i18n("Next View") );
    nextViewAction->setShortcut( QKeySequence(Qt::SHIFT+Qt::Key_Right) );
    connect( nextViewAction, SIGNAL(triggered()) , this , SLOT(nextView()) );
    _mainWindow->addAction(nextViewAction);

    QAction* previousViewAction = collection->addAction("previous-view");
    previousViewAction->setText( i18n("Previous View") );
    previousViewAction->setShortcut( QKeySequence(Qt::SHIFT+Qt::Key_Left) );
    connect( previousViewAction, SIGNAL(triggered()) , this , SLOT(previousView()) );
    _mainWindow->addAction(previousViewAction);

    QAction* nextContainerAction = collection->addAction("next-container");
    nextContainerAction->setText( i18n("Next View Container") );
    nextContainerAction->setShortcut( QKeySequence(Qt::SHIFT+Qt::Key_Tab) );
    connect( nextContainerAction , SIGNAL(triggered()) , this , SLOT(nextContainer()) );
    _mainWindow->addAction(nextContainerAction);
}

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();
135
136
137
138
139
140
}

void ViewManager::detachActiveView()
{
    // find the currently active view and remove it from its container 
    ViewContainer* container = _viewSplitter->activeContainer();
141
    TerminalDisplay* activeView = dynamic_cast<TerminalDisplay*>(container->activeView());
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

    if (!activeView)
        return;

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

    // remove the view from this window
    container->removeView(activeView);
    delete activeView;


    // 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 )
    {
        delete container;
162
163
164
165

        // this will need to be removed if Konsole is modified so the menu item to
        // split the view is no longer one toggle-able item
        _splitViewAction->setChecked(false);
166
167
168
169
    }

}

170
void ViewManager::sessionFinished( Session* session )
171
{
172
    QList<TerminalDisplay*> children = _viewSplitter->findChildren<TerminalDisplay*>();
173

174
    foreach ( TerminalDisplay* view , children )
175
176
177
178
179
180
    {
        if ( _sessionMap[view] == session )
        {
            _sessionMap.remove(view);
            delete view;
        }
Robert Knight's avatar
   
Robert Knight committed
181
182
183
184
185
186
187
    }

    focusActiveView(); 
}

void ViewManager::focusActiveView()
{
Robert Knight's avatar
Robert Knight committed
188
189
190
191
192
193
    // 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
194
195
196
197
198
199
200
201
202
    ViewContainer* container = _viewSplitter->activeContainer(); 
    if ( container )
    {
        QWidget* activeView = container->activeView();
        if ( activeView )
        {
            activeView->setFocus(Qt::MouseFocusReason);
        }
    }
203
204
}

205

206
207
void ViewManager::viewActivated( QWidget* view )
{
208
209
210
211
212
213
    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);
214
215
}

216
217
218
219
220
221
222
void ViewManager::activeViewTitleChanged(ViewProperties* properties)
{
    // set a plain caption (ie. without the automatic addition of " - AppName" at the end)
    // to make the taskbar entry cleaner and easier to read
    _mainWindow->setPlainCaption( properties->title() );
}

223
224
void ViewManager::viewFocused( SessionController* controller )
{
Robert Knight's avatar
Robert Knight committed
225
226
227
228
    // if a view is given the focus which is different to the one for which menu items
    // are currently being shown then unplug the current session-specific menu items
    // and plug in the ones for the newly focused session

229
230
    if ( _pluggedController != controller )
    {
Robert Knight's avatar
Robert Knight committed
231
        // remove existing session specific menu items if there are any
232
        if ( _pluggedController )
233
        {
234
            _mainWindow->guiFactory()->removeClient(_pluggedController);
235
236
            disconnect( controller , SIGNAL(titleChanged(ViewProperties*)),
                        this , SLOT(activeViewTitleChanged(ViewProperties*)) );
237
            disconnect( _mainWindow->bookmarkHandler() , 0 , controller , 0 );
238
            _pluggedController->setSearchBar( 0 );
239
        }
240
241
        
        _pluggedController = controller;
242

243
244
        // update the menus in the main window to use the actions from the active
        // controller 
245
        _mainWindow->guiFactory()->addClient(controller);
246
247
248
      
        // make the bookmark menu operate on the currently focused session 
        _mainWindow->bookmarkHandler()->setController(controller);
249
250
        connect( _mainWindow->bookmarkHandler() , SIGNAL(openUrl(const KUrl&)) , controller , 
                SLOT(openUrl(const KUrl&)) );
251

252
253
254
        // associated main window's search bar with session
        controller->setSearchBar( _mainWindow->searchBar() );

255
        // update the caption of the main window to match that of the focused session
256
257
258
        activeViewTitleChanged(controller);

        // listen for future changes in the focused session's title
259
260
        connect( controller , SIGNAL(titleChanged(ViewProperties*)),
                 this       , SLOT(activeViewTitleChanged(ViewProperties*)) );        
261
262
        
    
263

264
        //kDebug() << "Plugged actions for " << controller->session()->displayTitle() << endl;
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
    }
}

void ViewManager::splitView(bool splitView)
{
    if (splitView)
    {
        // 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());
        
        ViewContainer* container = createContainer(); 

        while (existingViewIter.hasNext())
        {
280
            Session* session = _sessionMap[(TerminalDisplay*)existingViewIter.next()];
281
            TerminalDisplay* display = createTerminalDisplay();
282
            loadViewSettings(display,session); 
Robert Knight's avatar
   
Robert Knight committed
283
            ViewProperties* properties = createController(session,display);
284
285
286

            _sessionMap[display] = session;

Robert Knight's avatar
   
Robert Knight committed
287
            container->addView(display,properties);
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
            session->addView( display );
        }

        _viewSplitter->addContainer(container,Qt::Vertical);
    }
    else
    {
        // delete the active container when unsplitting the view unless it is the last
        // one
        if ( _viewSplitter->containers().count() > 1 )
        {
            ViewContainer* container = _viewSplitter->activeContainer();
        
            delete container;
        }
    }
304
305
306

    // ensure that the active view is focused after the split / unsplit
    _viewSplitter->activeContainer()->activeView()->setFocus(Qt::OtherFocusReason);
307
308
}

309
SessionController* ViewManager::createController(Session* session , TerminalDisplay* view)
310
{
Robert Knight's avatar
Robert Knight committed
311
312
    // create a new controller for the session, and ensure that this view manager
    // is notified when the view gains the focus
313
314
315
316
317
318
    SessionController* controller = new SessionController(session,view,this);
    connect( controller , SIGNAL(focused(SessionController*)) , this , SLOT(viewFocused(SessionController*)) );

    return controller;
}

319
void ViewManager::createView(Session* session)
320
{
321
322
323
324
325
326
    // create the default container
    if (_viewSplitter->containers().count() == 0)
    {
        _viewSplitter->addContainer( createContainer() , Qt::Vertical );
    }

Robert Knight's avatar
Robert Knight committed
327
328
    // notify this view manager when the session finishes so that its view
    // can be deleted
329
    connect( session , SIGNAL(done(Session*)) , this , SLOT(sessionFinished(Session*)) );
Robert Knight's avatar
Robert Knight committed
330
331
332
333
   
    // 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 
334
335
336
337
338
339
    ViewContainer* const activeContainer = _viewSplitter->activeContainer();
    QListIterator<ViewContainer*> containerIter(_viewSplitter->containers());

    while ( containerIter.hasNext() )
    {
        ViewContainer* container = containerIter.next();
340
        TerminalDisplay* display = createTerminalDisplay();
341
        loadViewSettings(display,session);
Robert Knight's avatar
   
Robert Knight committed
342
        ViewProperties* properties = createController(session,display);
343
344

        _sessionMap[display] = session; 
Robert Knight's avatar
   
Robert Knight committed
345
        container->addView(display,properties);
346
347
        session->addView(display);

348
349
        if ( container == activeContainer ) 
        {
350
            container->setActiveView(display);
351
352
            display->setFocus( Qt::OtherFocusReason );
        }
353
354
355
356
357
    }
}

ViewContainer* ViewManager::createContainer()
{
Robert Knight's avatar
Robert Knight committed
358
359
    ViewContainer* container = new TabbedViewContainer(_viewSplitter); 
/*
360
361
362
363
364
365
366
367
368
    if ( _mainWindow->factory() )
    {
        QMenu* menu = (QMenu*)_mainWindow->factory()->container("new-session-popup",_mainWindow);
        
        if ( menu )
            container->setNewSessionMenu(menu);
    }
    else
    {
369
       kDebug() << __FILE__ << __LINE__ << ": ViewManager attempted to create a view before" <<
370
371
          " the main window GUI was created - unable to create popup menus for container." << endl;  
    }
Robert Knight's avatar
Robert Knight committed
372
*/
373

374
375
    // connect signals and slots
    connect( container , SIGNAL(closeRequest(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
376
377

    connect( container , SIGNAL(activeViewChanged(QWidget*)) , this , SLOT(viewActivated(QWidget*)));
378
    return container;
379
380
}

381
382
383
384
void ViewManager::viewCloseRequest(QWidget* view)
{
    // 1. detach view from session
    // 2. if the session has no views left, close it
385
    
386
    TerminalDisplay* display = (TerminalDisplay*)view;
387
    Session* session = _sessionMap[ display ];
388
389
390
391
392
393
394
395
396
397
398
399
400
    if ( session )
    {
        delete display;
        
        if ( session->views().count() == 0 )
            session->closeSession();
    }
    else
    {
        kDebug() << __FILE__ << __LINE__ << ": received close request from unknown view." << endl;
    }
}

401
402
void ViewManager::merge(ViewManager* otherManager)
{
Robert Knight's avatar
Robert Knight committed
403
404
405
406
407
408
    // iterate over the views in otherManager's active container and take them from that
    // manager and put them in the active container in this manager
    //
    // TODO - This currently does not consider views in containers other than
    //        the active one in the other manager
    //
409
410
411
412
413
414
415
416
417
    ViewSplitter* otherSplitter = otherManager->_viewSplitter;
    ViewContainer* otherContainer = otherSplitter->activeContainer();

    QListIterator<QWidget*> otherViewIter(otherContainer->views());

    ViewContainer* activeContainer = _viewSplitter->activeContainer();

    while ( otherViewIter.hasNext() )
    {
418
        TerminalDisplay* view = dynamic_cast<TerminalDisplay*>(otherViewIter.next());
419
420
421
        
        assert(view);

422
        takeView(otherManager,otherContainer,activeContainer,view);
423
424
425
    } 
}

426
427

void ViewManager::takeView(ViewManager* otherManager , ViewContainer* otherContainer, 
428
                           ViewContainer* newContainer, TerminalDisplay* view)
429
{
Robert Knight's avatar
Robert Knight committed
430
431
432
    // FIXME - the controller associated with the display which is being moved
    //         may have signals which are connected to otherManager.  they need
    //         to be redirected to slots in this view manager
433
434
435
436
437
438
439
440
441
442
    ViewProperties* properties = otherContainer->viewProperties(view);
    otherContainer->removeView(view);

    newContainer->addView(view,properties);

    // transfer the session map entries
    _sessionMap.insert(view,otherManager->_sessionMap[view]);
    otherManager->_sessionMap.remove(view);
}

443
TerminalDisplay* ViewManager::createTerminalDisplay()
444
{
445
   TerminalDisplay* display = new TerminalDisplay(0);
446
447
448
449
450
451
452
453

   //TODO Temporary settings used here
   display->setBellMode(0);
   display->setVTFont( QFont("Monospace") );
   display->setTerminalSizeHint(false);
   display->setCutToBeginningOfLine(true);
   display->setTerminalSizeStartup(false);
   display->setSize(80,40);
454
   display->setScrollbarLocation(TerminalDisplay::SCROLLBAR_RIGHT);
455
456
457
458

   return display;
}

459
void ViewManager::loadViewSettings(TerminalDisplay* view , Session* session)
460
461
462
463
464
465
{
    // load colour scheme
    view->setColorTable( session->schema()->table() );

}

466
#include "ViewManager.moc"