SessionController.cpp 47.6 KB
Newer Older
1
/*
2
    Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
3
    Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

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

// Own
#include "SessionController.h"
23

24
// Qt
25
#include <QtGui/QApplication>
26 27
#include <QMenu>

28 29
// KDE
#include <KAction>
Thomas Zander's avatar
Thomas Zander committed
30
#include <KDebug>
Robert Knight's avatar
 
Robert Knight committed
31
#include <KIcon>
32
#include <KInputDialog>
33
#include <KLocale>
34
#include <KMenu>
35
#include <KMessageBox>
36
#include <KRun>
37
#include <kshell.h>
38
#include <KStandardDirs>
Robert Knight's avatar
 
Robert Knight committed
39
#include <KToggleAction>
40
#include <KUrl>
41
#include <KXMLGUIFactory>
42
#include <KXMLGUIBuilder>
43
#include <kdebug.h>
44
#include <kcodecaction.h>
45
#include <kdeversion.h>
46

47
// Konsole
48
#include "EditProfileDialog.h"
49
#include "CopyInputDialog.h"
50
#include "Emulation.h"
51
#include "Filter.h"
52
#include "History.h"
53
#include "IncrementalSearchBar.h"
54
#include "ScreenWindow.h"
55
#include "Session.h"
56
#include "ProfileList.h"
57
#include "TerminalDisplay.h"
58
#include "SessionManager.h"
59

60 61
// for SaveHistoryTask
#include <KFileDialog>
62
#include <KIO/Job>
63 64 65 66 67
#include <KJob>
#include <KMessageBox>
#include "TerminalCharacterDecoder.h"


68
using namespace Konsole;
69

Robert Knight's avatar
 
Robert Knight committed
70 71
KIcon SessionController::_activityIcon;
KIcon SessionController::_silenceIcon;
72
QSet<SessionController*> SessionController::_allControllers;
73
QPointer<SearchHistoryThread> SearchHistoryTask::_thread;
74
int SessionController::_lastControllerId;
Robert Knight's avatar
 
Robert Knight committed
75

76
SessionController::SessionController(Session* session , TerminalDisplay* view, QObject* parent)
Robert Knight's avatar
 
Robert Knight committed
77
    : ViewProperties(parent)
78 79 80
    , KXMLGUIClient()
    , _session(session)
    , _view(view)
81
    , _copyToGroup(0)
82
    , _profileList(0)
Robert Knight's avatar
 
Robert Knight committed
83
    , _previousState(-1)
84
    , _viewUrlFilter(0)
85 86
    , _searchFilter(0)
    , _searchToggleAction(0)
87 88
    , _findNextAction(0)
    , _findPreviousAction(0)
89
    , _urlFilterUpdateRequired(false)
90
    , _codecAction(0)
91
    , _changeProfileMenu(0)
92 93
    , _listenForScreenWindowUpdates(false)
    , _preventClose(false)
94
{
95 96
    _allControllers.insert(this);

97 98 99
    Q_ASSERT( session );
    Q_ASSERT( view );

100
    // handle user interface related to session (menus etc.)
101 102 103 104
    if (isKonsolePart())
        setXMLFile("konsole/partui.rc");
    else
        setXMLFile("konsole/sessionui.rc");
105

106
    setupActions();
107 108 109
    actionCollection()->addAssociatedWidget(view);
    foreach (QAction* action, actionCollection()->actions())
        action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
110

111
    setIdentifier(++_lastControllerId);
Robert Knight's avatar
 
Robert Knight committed
112
    sessionTitleChanged();
113 114

    view->installEventFilter(this);
Robert Knight's avatar
 
Robert Knight committed
115

116 117 118 119
    // listen for session resize requests
    connect( _session , SIGNAL(resizeRequest(const QSize&)) , this ,
            SLOT(sessionResizeRequest(const QSize&)) );

120
    // listen for popup menu requests
121 122
    connect( _view , SIGNAL(configureRequest(QPoint)) , this,
            SLOT(showDisplayContextMenu(QPoint)) );
123

124
    // move view to newest output when keystrokes occur
125
    connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , this ,
126
            SLOT(trackOutput(QKeyEvent*)) );
127

Robert Knight's avatar
 
Robert Knight committed
128
    // listen to activity / silence notifications from session
129
    connect( _session , SIGNAL(stateChanged(int)) , this ,
130
            SLOT(sessionStateChanged(int) ));
131
    // listen to title and icon changes
132
    connect( _session , SIGNAL(titleChanged()) , this , SLOT(sessionTitleChanged()) );
133

134 135 136
    // listen for color changes
    connect( _session , SIGNAL(changeBackgroundColorRequest(QColor)) , _view , SLOT(setBackgroundColor(QColor)) );
    connect( _session , SIGNAL(changeForegroundColorRequest(QColor)) , _view , SLOT(setForegroundColor(QColor)) );
137

138 139
    // update the title when the session starts
    connect( _session , SIGNAL(started()) , this , SLOT(snapshot()) ); 
140

141
    // listen for output changes to set activity flag
142
    connect( _session->emulation() , SIGNAL(outputChanged()) , this ,
143
            SLOT(fireActivity()) );
144 145 146 147

    // listen for detection of ZModem transfer
    connect( _session , SIGNAL(zmodemDetected()) , this , SLOT(zmodemDownload()) ); 

Robert Knight's avatar
 
Robert Knight committed
148
    // listen for flow control status changes
Stephan Binner's avatar
Stephan Binner committed
149
    connect( _session , SIGNAL(flowControlEnabledChanged(bool)) , _view ,
150 151
        SLOT(setFlowControlWarningEnabled(bool)) );
    _view->setFlowControlWarningEnabled(_session->flowControlEnabled());
Robert Knight's avatar
 
Robert Knight committed
152

153 154
    // take a snapshot of the session state every so often when
    // user activity occurs
155 156 157 158
    //
    // the timer is owned by the session so that it will be destroyed along
    // with the session
    QTimer* activityTimer = new QTimer(_session);
159 160 161 162
    activityTimer->setSingleShot(true);
    activityTimer->setInterval(2000);
    connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , activityTimer , SLOT(start()) );
    connect( activityTimer , SIGNAL(timeout()) , this , SLOT(snapshot()) );
163 164
}

165 166
void SessionController::updateSearchFilter()
{
167 168 169
    if ( _searchFilter ) 
    {
        Q_ASSERT( searchBar() && searchBar()->isVisible() );
170

171 172
        _view->processFilters();
    }
173 174
}

175
SessionController::~SessionController()
176 177
{
   if ( _view )
178
      _view->setScreenWindow(0);
179 180

   _allControllers.remove(this);
181
}
182
void SessionController::trackOutput(QKeyEvent* event)
183 184 185
{
    Q_ASSERT( _view->screenWindow() );

186 187 188 189 190 191 192 193 194 195 196 197
    // jump to the end of the scrollback buffer unless the key pressed
    // is one of the three main modifiers, as these are used to select
    // the selection mode (eg. Ctrl+Alt+<Left Click> for column/block selection)
    switch (event->key())
    {
        case Qt::Key_Shift:
        case Qt::Key_Control:
        case Qt::Key_Alt:
            break;
        default:
            _view->screenWindow()->setTrackOutput(true);
    }
198
}
199 200 201 202
void SessionController::requireUrlFilterUpdate()
{
    // this method is called every time the screen window's output changes, so do not
    // do anything expensive here.
203

204 205
    _urlFilterUpdateRequired = true;
}
206 207
void SessionController::snapshot()
{
208
    Q_ASSERT( _session != 0 );
209

Robert Knight's avatar
 
Robert Knight committed
210 211
    QString title = _session->getDynamicTitle();    
    title         = title.simplified();
212

213 214
    // Visualize that the session is broadcasting to others
    if (_copyToGroup && _copyToGroup->sessions().count() > 1) {
215
        title.append('*');
216 217
    }
    updateSessionIcon();
218

219
    // apply new title
220
    if ( !title.isEmpty() )
221
        _session->setTitle(Session::DisplayedTitleRole,title);
222
    else
223
        _session->setTitle(Session::DisplayedTitleRole,_session->title(Session::NameRole));
224 225
}

226 227
QString SessionController::currentDir() const
{
Robert Knight's avatar
 
Robert Knight committed
228
    return _session->currentWorkingDirectory();
229 230
}

231 232
KUrl SessionController::url() const
{
Robert Knight's avatar
 
Robert Knight committed
233
    return _session->getUrl();
234 235
}

236 237
void SessionController::rename()
{
238
    renameSession();
239 240
}

241
void SessionController::openUrl( const KUrl& url )
242 243
{
    // handle local paths
244
    if ( url.isLocalFile() )
245
    {
246
        QString path = url.toLocalFile();
Dirk Mueller's avatar
Dirk Mueller committed
247
        _session->emulation()->sendText("cd " + KShell::quoteArg(path) + '\r');
248
    }
249 250 251 252
    else if ( url.protocol() == "ssh" )
    {
        _session->emulation()->sendText("ssh ");

253 254
        if ( url.port() > -1 )
            _session->emulation()->sendText("-p " + QString::number(url.port()) + ' ' );
255 256 257 258 259
        if ( url.hasUser() )
            _session->emulation()->sendText(url.user() + '@');
        if ( url.hasHost() )
            _session->emulation()->sendText(url.host() + '\r');
    }
260 261 262 263 264 265 266 267 268 269 270 271
    else if ( url.protocol() == "telnet" )
    {
        _session->emulation()->sendText("telnet ");

        if ( url.hasUser() )
            _session->emulation()->sendText("-l " + url.user() + ' ');
        if ( url.hasHost() )
            _session->emulation()->sendText(url.host() + ' ');
        if ( url.port() > -1 )
            _session->emulation()->sendText(QString::number(url.port()));
         _session->emulation()->sendText("\r");
    }
272 273 274
    else
    {
        //TODO Implement handling for other Url types
Thomas Zander's avatar
Thomas Zander committed
275
        kWarning(1211) << "Unable to open bookmark at url" << url << ", I do not know"
276
           << " how to handle the protocol " << url.protocol();
277 278 279
    }
}

280
bool SessionController::eventFilter(QObject* watched , QEvent* event)
281
{
282 283 284 285
    if ( watched == _view )
    {
        if ( event->type() == QEvent::FocusIn )
        {
286 287
            // notify the world that the view associated with this session has been focused
            // used by the view manager to update the title of the MainWindow widget containing the view
288
            emit focused(this);
Robert Knight's avatar
 
Robert Knight committed
289

290 291
            // when the view is focused, set bell events from the associated session to be delivered
            // by the focused view
292 293

            // first, disconnect any other views which are listening for bell signals from the session
294 295
            disconnect( _session , SIGNAL(bellRequest(const QString&)) , 0 , 0 );
            // second, connect the newly focused view to listen for the session's bell signal
Robert Knight's avatar
 
Robert Knight committed
296 297 298
            connect( _session , SIGNAL(bellRequest(const QString&)) ,
                    _view , SLOT(bell(const QString&)) );
        }
299 300 301
        // when a mouse move is received, create the URL filter and listen for output changes if
        // it has not already been created.  If it already exists, then update only if the output
        // has changed since the last update ( _urlFilterUpdateRequired == true )
302 303 304
        //
        // also check that no mouse buttons are pressed since the URL filter only applies when
        // the mouse is hovering over the view
305
        if ( event->type() == QEvent::MouseMove &&
306 307
            (!_viewUrlFilter || _urlFilterUpdateRequired) &&
            ((QMouseEvent*)event)->buttons() == Qt::NoButton )
308 309 310
        {
            if ( _view->screenWindow() && !_viewUrlFilter )
            {
311 312
                connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this ,
                        SLOT(requireUrlFilterUpdate()) );
313 314 315
                connect( _view->screenWindow() , SIGNAL(outputChanged()) , this ,
                         SLOT(requireUrlFilterUpdate()) );

316 317 318 319 320 321 322 323
                // install filter on the view to highlight URLs
                _viewUrlFilter = new UrlFilter();
                _view->filterChain()->addFilter( _viewUrlFilter );
            }

            _view->processFilters();
            _urlFilterUpdateRequired = false;
        }
324
    }
325

326
    return false;
327 328
}

329 330 331 332 333 334 335 336 337 338
void SessionController::removeSearchFilter()
{
    if (!_searchFilter)
        return;

    _view->filterChain()->removeFilter(_searchFilter);
    delete _searchFilter;
    _searchFilter = 0;
}

339
void SessionController::setSearchBar(IncrementalSearchBar* searchBar)
340
{
341
    // disconnect the existing search bar
342
    if ( _searchBar )
343 344
    {
        disconnect( this , 0 , _searchBar , 0 );
345
        disconnect( _searchBar , 0 , this , 0 );
346 347
    }

348
    // remove any existing search filter
349
    removeSearchFilter();
350

351 352 353 354 355 356
    // connect new search bar
    _searchBar = searchBar;
    if ( _searchBar )
    {
        connect( _searchBar , SIGNAL(closeClicked()) , this , SLOT(searchClosed()) );
        connect( _searchBar , SIGNAL(findNextClicked()) , this , SLOT(findNextInHistory()) );
357 358
        connect( _searchBar , SIGNAL(findPreviousClicked()) , this , SLOT(findPreviousInHistory()) );
        connect( _searchBar , SIGNAL(highlightMatchesToggled(bool)) , this , SLOT(highlightMatches(bool)) );
359

360 361 362 363
        // if the search bar was previously active
        // then re-enter search mode
        searchHistory( _searchToggleAction->isChecked() );
    }
364 365 366 367 368 369
}
IncrementalSearchBar* SessionController::searchBar() const
{
    return _searchBar;
}

370 371 372 373 374
void SessionController::setShowMenuAction(QAction* action)
{
    actionCollection()->addAction("show-menubar",action);
}

375
void SessionController::setupActions()
376
{
377
    KAction* action = 0;
378
    KToggleAction* toggleAction = 0;
379
    KActionCollection* collection = actionCollection();
380

381
    // Close Session
382
    action = collection->addAction("close-session");
383
    action->setIcon( KIcon("tab-close") );
384
    action->setText( i18n("&Close Tab") );
385
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_W) );
386
    connect( action , SIGNAL(triggered()) , this , SLOT(closeSession()) );
387 388 389

    // Open Browser
    action = collection->addAction("open-browser");
390
    action->setText( i18n("Open File Manager") );
391
    action->setIcon( KIcon("system-file-manager") );
392
    connect( action, SIGNAL(triggered()), this, SLOT(openBrowser()) );
393

394
    // Copy and Paste
395
    action = collection->addAction("copy");
Robert Knight's avatar
Robert Knight committed
396
    action->setIcon( KIcon("edit-copy") );
397
    action->setText( i18n("&Copy") );
398
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_C) );
399
    connect( action , SIGNAL(triggered()) , this , SLOT(copy()) );
400

401
    KAction* pasteAction = new KAction( i18n("&Paste") , this );
402 403 404 405 406 407 408 409 410 411
    pasteAction->setIcon( KIcon("edit-paste") );

    KShortcut pasteShortcut = pasteAction->shortcut();
    pasteShortcut.setPrimary( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_V) );
    pasteShortcut.setAlternate( QKeySequence(Qt::SHIFT+Qt::Key_Insert) );
    pasteAction->setShortcut(pasteShortcut);

    collection->addAction("paste",pasteAction);

    connect( pasteAction , SIGNAL(triggered()) , this , SLOT(paste()) );
412

413 414 415 416
    action = collection->addAction("paste-selection");
    action->setText( i18n("Paste Selection") );
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Insert) );
    connect( action , SIGNAL(triggered()) , this , SLOT(pasteSelection()) );
417

418 419
    // Rename Session
    action = collection->addAction("rename-session");
Stephan Binner's avatar
Stephan Binner committed
420
    action->setText( i18n("&Rename Tab...") );
421 422 423
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_S) );
    connect( action , SIGNAL(triggered()) , this , SLOT(renameSession()) );

424 425 426 427
    // Copy Input To
    action = collection->addAction("copy-input-to");
    action->setText(i18n("Copy Input To..."));
    connect( action , SIGNAL(triggered()) , this , SLOT(copyInputTo()) );
428

429
    // Clear+Reset
430
    action = collection->addAction("clear-and-reset");
Stephan Binner's avatar
Stephan Binner committed
431
    action->setText( i18n("Clear && Reset") );
Pino Toscano's avatar
Pino Toscano committed
432
    action->setIcon( KIcon("edit-clear-history") );
433 434
    connect( action , SIGNAL(triggered()) , this , SLOT(clearAndReset()) );

435 436 437 438 439 440
    action = collection->addAction("zmodem-upload");
    action->setText( i18n( "&ZModem Upload..." ) );
    action->setIcon( KIcon("document-open") );
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_U) );
    connect( action , SIGNAL(triggered()) , this , SLOT(zmodemUpload()) );
    
Robert Knight's avatar
 
Robert Knight committed
441
    // Monitor
442
    toggleAction = new KToggleAction(i18n("Monitor for &Activity"),this);
443
    toggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_A) );
444
    action = collection->addAction("monitor-activity",toggleAction);
Robert Knight's avatar
 
Robert Knight committed
445 446
    connect( action , SIGNAL(toggled(bool)) , this , SLOT(monitorActivity(bool)) );

447
    toggleAction = new KToggleAction(i18n("Monitor for &Silence"),this);
448
    toggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_I) );
449
    action = collection->addAction("monitor-silence",toggleAction);
Robert Knight's avatar
 
Robert Knight committed
450
    connect( action , SIGNAL(toggled(bool)) , this , SLOT(monitorSilence(bool)) );
451

452
    // Character Encoding
453
    _codecAction = new KCodecAction(i18n("Character Encoding"),this);
454 455 456 457
    collection->addAction("character-encoding",_codecAction);
    connect( _codecAction->menu() , SIGNAL(aboutToShow()) , this , SLOT(updateCodecAction()) );
    connect( _codecAction , SIGNAL(triggered(QTextCodec*)) , this , SLOT(changeCodec(QTextCodec*)) );

458 459 460 461 462 463 464 465 466 467
    // Text Size
    action = collection->addAction("increase-text-size");
    action->setText( i18n("Increase Text Size") );
    action->setIcon( KIcon("zoom-in") );
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_Plus) );
    connect( action , SIGNAL(triggered()) , this , SLOT(increaseTextSize()) );

    action = collection->addAction("decrease-text-size");
    action->setText( i18n("Decrease Text Size") );
    action->setIcon( KIcon("zoom-out") );
468
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_Minus) );
469 470
    connect( action , SIGNAL(triggered()) , this , SLOT(decreaseTextSize()) );

471
    // Scrollback
Stephan Binner's avatar
Stephan Binner committed
472
    _searchToggleAction = new KAction(i18n("Search Output..."),this);
473
    _searchToggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_F) );
Robert Knight's avatar
Robert Knight committed
474
    _searchToggleAction->setIcon( KIcon("edit-find") );
475
    _searchToggleAction->setCheckable(true);
476
    action = collection->addAction("search-history" , _searchToggleAction);
477 478
    connect( action , SIGNAL(toggled(bool)) , this , SLOT(searchHistory(bool)) );

479
    _findNextAction = collection->addAction("find-next");
480
    _findNextAction->setIcon( KIcon("go-down-search") );
481 482 483 484 485 486
    _findNextAction->setText( i18n("Find Next") );
    _findNextAction->setShortcut( QKeySequence(Qt::Key_F3) );
    _findNextAction->setEnabled(false);
    connect( _findNextAction , SIGNAL(triggered()) , this , SLOT(findNextInHistory()) );

    _findPreviousAction = collection->addAction("find-previous");
487
    _findPreviousAction->setIcon( KIcon("go-up-search") );
488 489 490 491
    _findPreviousAction->setText( i18n("Find Previous") );
    _findPreviousAction->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_F3) );
    _findPreviousAction->setEnabled(false);
    connect( _findPreviousAction , SIGNAL(triggered()) , this , SLOT(findPreviousInHistory()) );
492

493
    action = collection->addAction("save-history");
494 495
    action->setText( i18n("Save Output...") );
    action->setIcon( KIcon("document-save-as") );
496
    connect( action , SIGNAL(triggered()) , this , SLOT(saveHistory()) );
497

498
    action = collection->addAction("history-options");
Stephan Binner's avatar
Stephan Binner committed
499
    action->setText( i18n("Scrollback Options") );
Robert Knight's avatar
Robert Knight committed
500
    action->setIcon( KIcon("configure") );
501
    connect( action , SIGNAL(triggered()) , this , SLOT(showHistoryOptions()) );
502

503
    action = collection->addAction("clear-history-and-reset");
504
    action->setText( i18n("Clear Scrollback && Reset") );
505
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_X) );
506 507
    connect( action , SIGNAL(triggered()) , this , SLOT(clearHistoryAndReset()) );

508
    // Profile Options
509 510
    action = collection->addAction("edit-current-profile");
    action->setText( i18n("Edit Current Profile...") );
511
    action->setIcon( KIcon("document-properties") );
512
    connect( action , SIGNAL(triggered()) , this , SLOT(editCurrentProfile()) );
513

514
    _changeProfileMenu = new KMenu(i18n("Change Profile"),_view);
515
    collection->addAction("change-profile",_changeProfileMenu->menuAction());
516
    connect( _changeProfileMenu , SIGNAL(aboutToShow()) , this , SLOT(prepareChangeProfileMenu()) );
517
}
518
void SessionController::changeProfile(Profile::Ptr profile)
519
{
520
    SessionManager::instance()->setSessionProfile(_session,profile);    
521 522 523 524 525
}
void SessionController::prepareChangeProfileMenu()
{
    if ( _changeProfileMenu->isEmpty() )
    {
526
        _profileList = new ProfileList(false,this);
527 528
        connect( _profileList , SIGNAL(profileSelected(Profile::Ptr)) ,
                this , SLOT(changeProfile(Profile::Ptr)) );
529
    }
530 531 532

    _changeProfileMenu->clear();
    _changeProfileMenu->addActions(_profileList->actions());
533
}
534 535 536 537 538 539
void SessionController::updateCodecAction()
{
    _codecAction->setCurrentCodec( QString(_session->emulation()->codec()->name()) );
}
void SessionController::changeCodec(QTextCodec* codec)
{
540
    _session->setCodec(codec);
541
}
542

543
void SessionController::editCurrentProfile()
544
{
545
    EditProfileDialog* dialog = new EditProfileDialog( QApplication::activeWindow() );
546

547
    dialog->setProfile(SessionManager::instance()->sessionProfile(_session));
548
    dialog->show();
549 550 551
}
void SessionController::renameSession()
{
552
    QPointer<Session> guard(_session);
553 554 555
    bool ok = false;
    const QString& text = KInputDialog::getText( i18n("Rename Tab") ,
                                                 i18n("Enter new tab text:") ,
556
                                                 _session->tabTitleFormat(Session::LocalTabTitle) ,
Craig Drummond's avatar
Craig Drummond committed
557
                                                 &ok, QApplication::activeWindow() );
558
    if (!guard)
559
        return;
560

561
    if ( ok )
562
    {
563 564 565 566 567 568
        // renaming changes both the local and remote tab title formats, to save confusion over
        // the tab title not changing if renaming the tab whilst the remote tab title format is 
        // being displayed
        //
        // The downside of this approach is that after renaming a tab manually, the ability to 
        // have separate formats for local and remote activities is lost
569
        _session->setTabTitleFormat(Session::LocalTabTitle,text);
570
        _session->setTabTitleFormat(Session::RemoteTabTitle,text);
571

572 573 574
        // trigger an update of the tab text
        snapshot();
    }
575
}
576 577
void SessionController::saveSession()
{
578 579
    Q_ASSERT(0); // not implemented yet

580 581 582
    //SaveSessionDialog dialog(_view);
    //int result = dialog.exec();
}
583 584
bool SessionController::confirmClose() const
{
585
    if (_session->isForegroundProcessActive())
586
    {
587
        QString title = _session->foregroundProcessName();
588 589 590 591
      
        // hard coded for now.  In future make it possible for the user to specify which programs
        // are ignored when considering whether to display a confirmation
        QStringList ignoreList; 
592
        ignoreList << QString(qgetenv("SHELL")).section('/',-1);
593 594 595 596
        if (ignoreList.contains(title))
            return true;

        QString question;
Robert Knight's avatar
 
Robert Knight committed
597
        if (title.isEmpty())
598 599
            question = i18n("A program is currently running in this session."
                            "  Are you sure you want to close it?");
Robert Knight's avatar
 
Robert Knight committed
600 601 602
        else
            question = i18n("The program '%1' is currently running in this session."  
                            "  Are you sure you want to close it?",title);
603 604 605 606 607 608

        int result = KMessageBox::warningYesNo(_view->window(),question,i18n("Confirm Close"));
        return (result == KMessageBox::Yes) ? true : false; 
    }
    return true;
}
609
void SessionController::closeSession()
610
{
611 612
    if (_preventClose)
        return;
613

614 615
    if (confirmClose())
        _session->close();
616
}
617

618 619 620 621 622
void SessionController::openBrowser()
{
    new KRun(url(), QApplication::activeWindow());
}

623
void SessionController::copy()
624
{
625
    _view->copyClipboard();
626
}
627 628

void SessionController::paste()
629
{
630
    _view->pasteClipboard();
631
}
632 633
void SessionController::pasteSelection()
{
634
    _view->pasteSelection();
635
}
636 637
void SessionController::copyInputTo()
{
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
    if (!_copyToGroup)
    {
        _copyToGroup = new SessionGroup(this);
        _copyToGroup->addSession(_session);
        _copyToGroup->setMasterStatus(_session,true);
        _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll);
    }

    CopyInputDialog* dialog = new CopyInputDialog(_view);
    dialog->setMasterSession(_session);
    
    QSet<Session*> currentGroup = QSet<Session*>::fromList(_copyToGroup->sessions());
    currentGroup.remove(_session);
    
    dialog->setChosenSessions(currentGroup);

    QPointer<Session> guard(_session);
    int result = dialog->exec();
    if (!guard)
        return;

    if (result)
    {
        QSet<Session*> newGroup = dialog->chosenSessions();
        newGroup.remove(_session);
    
        QSet<Session*> completeGroup = newGroup | currentGroup;
        foreach(Session* session, completeGroup)
        {
            if (newGroup.contains(session) && !currentGroup.contains(session))
                _copyToGroup->addSession(session);
            else if (!newGroup.contains(session) && currentGroup.contains(session))
                _copyToGroup->removeSession(session);
        }

        snapshot();
    }

    delete dialog;
677
}
678
void SessionController::clear()
679
{
680
    Emulation* emulation = _session->emulation();
681

682
    emulation->clearEntireScreen();
683
}
684
void SessionController::clearAndReset()
685
{
686
    Emulation* emulation = _session->emulation();
687

688
    emulation->reset();
689
    _session->refresh();
690
}
691 692
void SessionController::searchClosed()
{
693
    _searchToggleAction->toggle();
694 695
}

696
#if 0
697 698 699
void SessionController::searchHistory()
{
    searchHistory(true);
700
}
701
#endif
702

703 704
void SessionController::listenForScreenWindowUpdates()
{
705 706
    if (_listenForScreenWindowUpdates)
        return;
707

708 709 710 711
    connect( _view->screenWindow() , SIGNAL(outputChanged()) , this , 
            SLOT(updateSearchFilter()) );
    connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this , 
            SLOT(updateSearchFilter()) );
712

713
    _listenForScreenWindowUpdates = true;
714 715
}

716
// searchHistory() may be called either as a result of clicking a menu item or
717
// as a result of changing the search bar widget
718
void SessionController::searchHistory(bool showSearchBar)
719
{
720 721 722
    if ( _searchBar )
    {
        _searchBar->setVisible(showSearchBar);
723

724
        if (showSearchBar)
725
        {
726
            removeSearchFilter();
727

728 729
            listenForScreenWindowUpdates();
            
730 731
            _searchFilter = new RegExpFilter();
            _view->filterChain()->addFilter(_searchFilter);
732 733 734 735
            connect( _searchBar , SIGNAL(searchChanged(const QString&)) , this ,
                    SLOT(searchTextChanged(const QString&)) );

            // invoke search for matches for the current search text
736 737 738 739 740 741
            const QString& currentSearchText = _searchBar->searchText();
            if (!currentSearchText.isEmpty())
            {
                searchTextChanged(currentSearchText);
            }