ViewManager.cpp 14 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
#include "KonsoleBookmarkHandler.h"
31
32
#include "KonsoleMainWindow.h"
#include "TESession.h"
33
#include "TerminalDisplay.h"
34
#include "schema.h"
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include "SessionController.h"
#include "SessionManager.h"
#include "ViewContainer.h"
#include "ViewSplitter.h"
#include "ViewManager.h"

ViewManager::ViewManager(KonsoleMainWindow* mainWindow)
    : 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()
{
}

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

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

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


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

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

87
88
89
    QAction* mergeAction = collection->addAction("merge-windows");
    mergeAction->setText( i18n("&Merge Windows") );
        
90
91
92
93
94
95
96
    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();
97
    TerminalDisplay* activeView = dynamic_cast<TerminalDisplay*>(container->activeView());
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

    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;
118
119
120
121

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

}

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

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

    focusActiveView(); 
}

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

161

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

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

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

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

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

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

211
        // update the caption of the main window to match that of the focused session
212
213
        connect( controller , SIGNAL(titleChanged(ViewProperties*)),
                 this       , SLOT(activeViewTitleChanged(ViewProperties*)) );        
214

215

216

217
        //kDebug() << "Plugged actions for " << controller->session()->displayTitle() << endl;
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
    }
}

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())
        {
233
234
            TESession* session = _sessionMap[(TerminalDisplay*)existingViewIter.next()];
            TerminalDisplay* display = createTerminalDisplay();
235
            loadViewSettings(display,session); 
Robert Knight's avatar
   
Robert Knight committed
236
            ViewProperties* properties = createController(session,display);
237
238
239

            _sessionMap[display] = session;

Robert Knight's avatar
   
Robert Knight committed
240
            container->addView(display,properties);
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
            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;
        }
    }
257
258
259

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

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

    return controller;
}

void ViewManager::createView(TESession* session)
{
274
275
276
277
278
279
    // create the default container
    if (_viewSplitter->containers().count() == 0)
    {
        _viewSplitter->addContainer( createContainer() , Qt::Vertical );
    }

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

    while ( containerIter.hasNext() )
    {
        ViewContainer* container = containerIter.next();
293
        TerminalDisplay* display = createTerminalDisplay();
294
        loadViewSettings(display,session);
Robert Knight's avatar
   
Robert Knight committed
295
        ViewProperties* properties = createController(session,display);
296
297

        _sessionMap[display] = session; 
Robert Knight's avatar
   
Robert Knight committed
298
        container->addView(display,properties);
299
300
        session->addView(display);

301
302
        if ( container == activeContainer ) 
        {
303
            container->setActiveView(display);
304
305
            display->setFocus( Qt::OtherFocusReason );
        }
306
307
308
309
310
    }
}

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

327
328
    // connect signals and slots
    connect( container , SIGNAL(closeRequest(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) );
329
330

    connect( container , SIGNAL(activeViewChanged(QWidget*)) , this , SLOT(viewActivated(QWidget*)));
331
    return container;
332
333
}

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

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

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

    ViewContainer* activeContainer = _viewSplitter->activeContainer();

    while ( otherViewIter.hasNext() )
    {
371
        TerminalDisplay* view = dynamic_cast<TerminalDisplay*>(otherViewIter.next());
372
373
374
        
        assert(view);

375
        takeView(otherManager,otherContainer,activeContainer,view);
376
377
378
    } 
}

379
380

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

396
TerminalDisplay* ViewManager::createTerminalDisplay()
397
{
398
   TerminalDisplay* display = new TerminalDisplay(0);
399
400
401
402
403
404
405
406

   //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);
407
   display->setScrollbarLocation(TerminalDisplay::SCROLLBAR_RIGHT);
408
409
410
411

   return display;
}

412
void ViewManager::loadViewSettings(TerminalDisplay* view , TESession* session)
413
414
415
416
417
418
{
    // load colour scheme
    view->setColorTable( session->schema()->table() );

}

419
#include "ViewManager.moc"