ViewManager.cpp 14.1 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
93
94
95
96
97
98
    connect( mergeAction , SIGNAL(triggered()) , _mainWindow , SLOT(mergeWindows()) );
}

void ViewManager::detachActiveView()
{
    // find the currently active view and remove it from its container 
    ViewContainer* container = _viewSplitter->activeContainer();
99
    TerminalDisplay* activeView = dynamic_cast<TerminalDisplay*>(container->activeView());
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

    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;
120
121
122
123

        // 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);
124
125
126
127
    }

}

128
void ViewManager::sessionFinished( Session* session )
129
{
130
    QList<TerminalDisplay*> children = _viewSplitter->findChildren<TerminalDisplay*>();
131

132
    foreach ( TerminalDisplay* view , children )
133
134
135
136
137
138
    {
        if ( _sessionMap[view] == session )
        {
            _sessionMap.remove(view);
            delete view;
        }
Robert Knight's avatar
   
Robert Knight committed
139
140
141
142
143
144
145
    }

    focusActiveView(); 
}

void ViewManager::focusActiveView()
{
Robert Knight's avatar
Robert Knight committed
146
147
148
149
150
151
    // 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
152
153
154
155
156
157
158
159
160
    ViewContainer* container = _viewSplitter->activeContainer(); 
    if ( container )
    {
        QWidget* activeView = container->activeView();
        if ( activeView )
        {
            activeView->setFocus(Qt::MouseFocusReason);
        }
    }
161
162
}

163

164
165
void ViewManager::viewActivated( QWidget* view )
{
166
167
168
169
170
171
    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);
172
173
}

174
175
176
177
178
179
180
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() );
}

181
182
void ViewManager::viewFocused( SessionController* controller )
{
Robert Knight's avatar
Robert Knight committed
183
184
185
186
    // 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

187
188
    if ( _pluggedController != controller )
    {
Robert Knight's avatar
Robert Knight committed
189
        // remove existing session specific menu items if there are any
190
        if ( _pluggedController )
191
        {
192
            _mainWindow->guiFactory()->removeClient(_pluggedController);
193
194
            disconnect( controller , SIGNAL(titleChanged(ViewProperties*)),
                        this , SLOT(activeViewTitleChanged(ViewProperties*)) );
195
            disconnect( _mainWindow->bookmarkHandler() , 0 , controller , 0 );
196
            _pluggedController->setSearchBar( 0 );
197
        }
198
199
        
        _pluggedController = controller;
200

201
202
        // update the menus in the main window to use the actions from the active
        // controller 
203
        _mainWindow->guiFactory()->addClient(controller);
204
205
206
      
        // make the bookmark menu operate on the currently focused session 
        _mainWindow->bookmarkHandler()->setController(controller);
207
208
        connect( _mainWindow->bookmarkHandler() , SIGNAL(openUrl(const KUrl&)) , controller , 
                SLOT(openUrl(const KUrl&)) );
209

210
211
212
        // associated main window's search bar with session
        controller->setSearchBar( _mainWindow->searchBar() );

213
        // update the caption of the main window to match that of the focused session
214
215
216
        activeViewTitleChanged(controller);

        // listen for future changes in the focused session's title
217
218
        connect( controller , SIGNAL(titleChanged(ViewProperties*)),
                 this       , SLOT(activeViewTitleChanged(ViewProperties*)) );        
219
220
        
    
221

222
        //kDebug() << "Plugged actions for " << controller->session()->displayTitle() << endl;
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    }
}

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())
        {
238
            Session* session = _sessionMap[(TerminalDisplay*)existingViewIter.next()];
239
            TerminalDisplay* display = createTerminalDisplay();
240
            loadViewSettings(display,session); 
Robert Knight's avatar
   
Robert Knight committed
241
            ViewProperties* properties = createController(session,display);
242
243
244

            _sessionMap[display] = session;

Robert Knight's avatar
   
Robert Knight committed
245
            container->addView(display,properties);
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
            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;
        }
    }
262
263
264

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

267
SessionController* ViewManager::createController(Session* session , TerminalDisplay* view)
268
{
Robert Knight's avatar
Robert Knight committed
269
270
    // create a new controller for the session, and ensure that this view manager
    // is notified when the view gains the focus
271
272
273
274
275
276
    SessionController* controller = new SessionController(session,view,this);
    connect( controller , SIGNAL(focused(SessionController*)) , this , SLOT(viewFocused(SessionController*)) );

    return controller;
}

277
void ViewManager::createView(Session* session)
278
{
279
280
281
282
283
284
    // create the default container
    if (_viewSplitter->containers().count() == 0)
    {
        _viewSplitter->addContainer( createContainer() , Qt::Vertical );
    }

Robert Knight's avatar
Robert Knight committed
285
286
    // notify this view manager when the session finishes so that its view
    // can be deleted
287
    connect( session , SIGNAL(done(Session*)) , this , SLOT(sessionFinished(Session*)) );
Robert Knight's avatar
Robert Knight committed
288
289
290
291
   
    // 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 
292
293
294
295
296
297
    ViewContainer* const activeContainer = _viewSplitter->activeContainer();
    QListIterator<ViewContainer*> containerIter(_viewSplitter->containers());

    while ( containerIter.hasNext() )
    {
        ViewContainer* container = containerIter.next();
298
        TerminalDisplay* display = createTerminalDisplay();
299
        loadViewSettings(display,session);
Robert Knight's avatar
   
Robert Knight committed
300
        ViewProperties* properties = createController(session,display);
301
302

        _sessionMap[display] = session; 
Robert Knight's avatar
   
Robert Knight committed
303
        container->addView(display,properties);
304
305
        session->addView(display);

306
307
        if ( container == activeContainer ) 
        {
308
            container->setActiveView(display);
309
310
            display->setFocus( Qt::OtherFocusReason );
        }
311
312
313
314
315
    }
}

ViewContainer* ViewManager::createContainer()
{
Robert Knight's avatar
Robert Knight committed
316
317
    ViewContainer* container = new TabbedViewContainer(_viewSplitter); 
/*
318
319
320
321
322
323
324
325
326
    if ( _mainWindow->factory() )
    {
        QMenu* menu = (QMenu*)_mainWindow->factory()->container("new-session-popup",_mainWindow);
        
        if ( menu )
            container->setNewSessionMenu(menu);
    }
    else
    {
327
       kDebug() << __FILE__ << __LINE__ << ": ViewManager attempted to create a view before" <<
328
329
          " the main window GUI was created - unable to create popup menus for container." << endl;  
    }
Robert Knight's avatar
Robert Knight committed
330
*/
331

332
333
    // connect signals and slots
    connect( container , SIGNAL(closeRequest(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
334
335

    connect( container , SIGNAL(activeViewChanged(QWidget*)) , this , SLOT(viewActivated(QWidget*)));
336
    return container;
337
338
}

339
340
341
342
void ViewManager::viewCloseRequest(QWidget* view)
{
    // 1. detach view from session
    // 2. if the session has no views left, close it
343
    
344
    TerminalDisplay* display = (TerminalDisplay*)view;
345
    Session* session = _sessionMap[ display ];
346
347
348
349
350
351
352
353
354
355
356
357
358
    if ( session )
    {
        delete display;
        
        if ( session->views().count() == 0 )
            session->closeSession();
    }
    else
    {
        kDebug() << __FILE__ << __LINE__ << ": received close request from unknown view." << endl;
    }
}

359
360
void ViewManager::merge(ViewManager* otherManager)
{
Robert Knight's avatar
Robert Knight committed
361
362
363
364
365
366
    // 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
    //
367
368
369
370
371
372
373
374
375
    ViewSplitter* otherSplitter = otherManager->_viewSplitter;
    ViewContainer* otherContainer = otherSplitter->activeContainer();

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

    ViewContainer* activeContainer = _viewSplitter->activeContainer();

    while ( otherViewIter.hasNext() )
    {
376
        TerminalDisplay* view = dynamic_cast<TerminalDisplay*>(otherViewIter.next());
377
378
379
        
        assert(view);

380
        takeView(otherManager,otherContainer,activeContainer,view);
381
382
383
    } 
}

384
385

void ViewManager::takeView(ViewManager* otherManager , ViewContainer* otherContainer, 
386
                           ViewContainer* newContainer, TerminalDisplay* view)
387
{
Robert Knight's avatar
Robert Knight committed
388
389
390
    // 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
391
392
393
394
395
396
397
398
399
400
    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);
}

401
TerminalDisplay* ViewManager::createTerminalDisplay()
402
{
403
   TerminalDisplay* display = new TerminalDisplay(0);
404
405
406
407
408
409
410
411

   //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);
412
   display->setScrollbarLocation(TerminalDisplay::SCROLLBAR_RIGHT);
413
414
415
416

   return display;
}

417
void ViewManager::loadViewSettings(TerminalDisplay* view , Session* session)
418
419
420
421
422
423
{
    // load colour scheme
    view->setColorTable( session->schema()->table() );

}

424
#include "ViewManager.moc"