SessionController.cpp 45.2 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 20 21

    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"
22

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

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

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

57 58
// for SaveHistoryTask
#include <KFileDialog>
59
#include <KIO/Job>
60 61 62 63 64
#include <KJob>
#include <KMessageBox>
#include "TerminalCharacterDecoder.h"


65
using namespace Konsole;
66

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

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

94 95 96
    Q_ASSERT( session );
    Q_ASSERT( view );

97
    // handle user interface related to session (menus etc.)
98

Stephan Binner's avatar
Stephan Binner committed
99
#ifdef KONSOLE_PART
100 101
    setXMLFile("konsole/partui.rc");
#else
102
    setXMLFile("konsole/sessionui.rc");
103 104
#endif

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

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

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

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

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

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

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

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

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

140
    // listen for output changes to set activity flag
141
    connect( _session->emulation() , SIGNAL(outputChanged()) , this ,
142
            SLOT(fireActivity()) );
143
    
Robert Knight's avatar
 
Robert Knight committed
144
    // listen for flow control status changes
Stephan Binner's avatar
Stephan Binner committed
145
    connect( _session , SIGNAL(flowControlEnabledChanged(bool)) , _view ,
146 147
        SLOT(setFlowControlWarningEnabled(bool)) );
    _view->setFlowControlWarningEnabled(_session->flowControlEnabled());
Robert Knight's avatar
 
Robert Knight committed
148

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

161 162
void SessionController::updateSearchFilter()
{
163 164 165
    if ( _searchFilter ) 
    {
        Q_ASSERT( searchBar() && searchBar()->isVisible() );
166

167 168
        _view->processFilters();
    }
169 170
}

171
SessionController::~SessionController()
172 173
{
   if ( _view )
174
      _view->setScreenWindow(0);
175 176

   _allControllers.remove(this);
177
}
178
void SessionController::trackOutput(QKeyEvent* event)
179 180 181
{
    Q_ASSERT( _view->screenWindow() );

182 183 184 185 186 187 188 189 190 191 192 193
    // 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);
    }
194
}
195 196 197 198
void SessionController::requireUrlFilterUpdate()
{
    // this method is called every time the screen window's output changes, so do not
    // do anything expensive here.
199

200 201
    _urlFilterUpdateRequired = true;
}
202 203
void SessionController::snapshot()
{
204
    Q_ASSERT( _session != 0 );
205

Robert Knight's avatar
 
Robert Knight committed
206 207
    QString title = _session->getDynamicTitle();    
    title         = title.simplified();
208

209 210 211
    // crude indicator when the session is broadcasting to others
    if (_copyToGroup && _copyToGroup->sessions().count() > 1)
        title.append('*');
212

213
    // apply new title
214
    if ( !title.isEmpty() )
215
        _session->setTitle(Session::DisplayedTitleRole,title);
216
    else
217
        _session->setTitle(Session::DisplayedTitleRole,_session->title(Session::NameRole));
218 219
}

220 221
QString SessionController::currentDir() const
{
Robert Knight's avatar
 
Robert Knight committed
222
    return _session->currentWorkingDirectory();
223 224
}

225 226
KUrl SessionController::url() const
{
Robert Knight's avatar
 
Robert Knight committed
227
    return _session->getUrl();
228 229
}

230 231
void SessionController::rename()
{
232
    renameSession();
233 234
}

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

247 248
        if ( url.port() > -1 )
            _session->emulation()->sendText("-p " + QString::number(url.port()) + ' ' );
249 250 251 252 253
        if ( url.hasUser() )
            _session->emulation()->sendText(url.user() + '@');
        if ( url.hasHost() )
            _session->emulation()->sendText(url.host() + '\r');
    }
254 255 256 257 258 259 260 261 262 263 264 265
    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");
    }
266 267 268
    else
    {
        //TODO Implement handling for other Url types
Thomas Zander's avatar
Thomas Zander committed
269
        kWarning(1211) << "Unable to open bookmark at url" << url << ", I do not know"
270
           << " how to handle the protocol " << url.protocol();
271 272 273
    }
}

274
bool SessionController::eventFilter(QObject* watched , QEvent* event)
275
{
276 277 278 279
    if ( watched == _view )
    {
        if ( event->type() == QEvent::FocusIn )
        {
280 281
            // 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
282
            emit focused(this);
Robert Knight's avatar
 
Robert Knight committed
283

284 285
            // when the view is focused, set bell events from the associated session to be delivered
            // by the focused view
286 287

            // first, disconnect any other views which are listening for bell signals from the session
288 289
            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
290 291 292
            connect( _session , SIGNAL(bellRequest(const QString&)) ,
                    _view , SLOT(bell(const QString&)) );
        }
293 294 295
        // 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 )
296 297 298
        //
        // also check that no mouse buttons are pressed since the URL filter only applies when
        // the mouse is hovering over the view
299
        if ( event->type() == QEvent::MouseMove &&
300 301
            (!_viewUrlFilter || _urlFilterUpdateRequired) &&
            ((QMouseEvent*)event)->buttons() == Qt::NoButton )
302 303 304
        {
            if ( _view->screenWindow() && !_viewUrlFilter )
            {
305 306
                connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this ,
                        SLOT(requireUrlFilterUpdate()) );
307 308 309
                connect( _view->screenWindow() , SIGNAL(outputChanged()) , this ,
                         SLOT(requireUrlFilterUpdate()) );

310 311 312 313 314 315 316 317
                // install filter on the view to highlight URLs
                _viewUrlFilter = new UrlFilter();
                _view->filterChain()->addFilter( _viewUrlFilter );
            }

            _view->processFilters();
            _urlFilterUpdateRequired = false;
        }
318
    }
319

320
    return false;
321 322
}

323 324 325 326 327 328 329 330 331 332
void SessionController::removeSearchFilter()
{
    if (!_searchFilter)
        return;

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

333
void SessionController::setSearchBar(IncrementalSearchBar* searchBar)
334
{
335
    // disconnect the existing search bar
336
    if ( _searchBar )
337 338
    {
        disconnect( this , 0 , _searchBar , 0 );
339
        disconnect( _searchBar , 0 , this , 0 );
340 341
    }

342
    // remove any existing search filter
343
    removeSearchFilter();
344

345 346 347 348 349 350
    // connect new search bar
    _searchBar = searchBar;
    if ( _searchBar )
    {
        connect( _searchBar , SIGNAL(closeClicked()) , this , SLOT(searchClosed()) );
        connect( _searchBar , SIGNAL(findNextClicked()) , this , SLOT(findNextInHistory()) );
351 352
        connect( _searchBar , SIGNAL(findPreviousClicked()) , this , SLOT(findPreviousInHistory()) );
        connect( _searchBar , SIGNAL(highlightMatchesToggled(bool)) , this , SLOT(highlightMatches(bool)) );
353

354 355 356 357
        // if the search bar was previously active
        // then re-enter search mode
        searchHistory( _searchToggleAction->isChecked() );
    }
358 359 360 361 362 363
}
IncrementalSearchBar* SessionController::searchBar() const
{
    return _searchBar;
}

364 365 366 367 368
void SessionController::setShowMenuAction(QAction* action)
{
    actionCollection()->addAction("show-menubar",action);
}

369
void SessionController::setupActions()
370
{
371
    KAction* action = 0;
372
    KToggleAction* toggleAction = 0;
373
    KActionCollection* collection = actionCollection();
374

375
    // Close Session
376
    action = collection->addAction("close-session");
377
    action->setIcon( KIcon("tab-close") );
378
    action->setText( i18n("&Close Tab") );
379
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_W) );
380
    connect( action , SIGNAL(triggered()) , this , SLOT(closeSession()) );
381 382 383 384

    // Open Browser
    action = collection->addAction("open-browser");
    action->setText( i18n("Open Browser Here") );
385
    action->setIcon( KIcon("system-file-manager") );
386
    connect( action, SIGNAL(triggered()), this, SLOT(openBrowser()) );
387

388
    // Copy and Paste
389
    action = collection->addAction("copy");
Robert Knight's avatar
Robert Knight committed
390
    action->setIcon( KIcon("edit-copy") );
391
    action->setText( i18n("&Copy") );
392
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_C) );
393
    connect( action , SIGNAL(triggered()) , this , SLOT(copy()) );
394

395
    KAction* pasteAction = new KAction( i18n("&Paste") , this );
396 397 398 399 400 401 402 403 404 405
    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()) );
406

407 408 409 410
    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()) );
411

412 413
    // Rename Session
    action = collection->addAction("rename-session");
Stephan Binner's avatar
Stephan Binner committed
414
    action->setText( i18n("&Rename Tab...") );
415 416 417
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_S) );
    connect( action , SIGNAL(triggered()) , this , SLOT(renameSession()) );

418 419 420 421
    // Copy Input To
    action = collection->addAction("copy-input-to");
    action->setText(i18n("Copy Input To..."));
    connect( action , SIGNAL(triggered()) , this , SLOT(copyInputTo()) );
422

423
    // Clear and Clear+Reset
424 425
    action = collection->addAction("clear");
    action->setText( i18n("C&lear Display") );
426
    action->setIcon( KIcon("edit-clear") );
427
    connect( action , SIGNAL(triggered()) , this , SLOT(clear()) );
428

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

Robert Knight's avatar
 
Robert Knight committed
434
    // Monitor
435
    toggleAction = new KToggleAction(i18n("Monitor for &Activity"),this);
436
    toggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_A) );
437
    action = collection->addAction("monitor-activity",toggleAction);
Robert Knight's avatar
 
Robert Knight committed
438 439
    connect( action , SIGNAL(toggled(bool)) , this , SLOT(monitorActivity(bool)) );

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

445
    // Character Encoding
446
    _codecAction = new KCodecAction(i18n("Character Encoding"),this);
447 448 449 450
    collection->addAction("character-encoding",_codecAction);
    connect( _codecAction->menu() , SIGNAL(aboutToShow()) , this , SLOT(updateCodecAction()) );
    connect( _codecAction , SIGNAL(triggered(QTextCodec*)) , this , SLOT(changeCodec(QTextCodec*)) );

451 452 453 454 455 456 457 458 459 460
    // 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") );
461
    action->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_Minus) );
462 463
    connect( action , SIGNAL(triggered()) , this , SLOT(decreaseTextSize()) );

464
    // Scrollback
Stephan Binner's avatar
Stephan Binner committed
465
    _searchToggleAction = new KAction(i18n("Search Output..."),this);
466
    _searchToggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_F) );
Robert Knight's avatar
Robert Knight committed
467
    _searchToggleAction->setIcon( KIcon("edit-find") );
468
    _searchToggleAction->setCheckable(true);
469
    action = collection->addAction("search-history" , _searchToggleAction);
470 471
    connect( action , SIGNAL(toggled(bool)) , this , SLOT(searchHistory(bool)) );

472
    _findNextAction = collection->addAction("find-next");
473
    _findNextAction->setIcon( KIcon("go-down-search") );
474 475 476 477 478 479
    _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");
480
    _findPreviousAction->setIcon( KIcon("go-up-search") );
481 482 483 484
    _findPreviousAction->setText( i18n("Find Previous") );
    _findPreviousAction->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_F3) );
    _findPreviousAction->setEnabled(false);
    connect( _findPreviousAction , SIGNAL(triggered()) , this , SLOT(findPreviousInHistory()) );
485

486
    action = collection->addAction("save-history");
487 488
    action->setText( i18n("Save Output...") );
    action->setIcon( KIcon("document-save-as") );
489
    connect( action , SIGNAL(triggered()) , this , SLOT(saveHistory()) );
490

491
    action = collection->addAction("history-options");
Stephan Binner's avatar
Stephan Binner committed
492
    action->setText( i18n("Scrollback Options") );
Robert Knight's avatar
Robert Knight committed
493
    action->setIcon( KIcon("configure") );
494
    connect( action , SIGNAL(triggered()) , this , SLOT(showHistoryOptions()) );
495

496
    action = collection->addAction("clear-history");
497
    action->setText( i18n("Clear Scrollback") );
498
    connect( action , SIGNAL(triggered()) , this , SLOT(clearHistory()) );
499

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

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

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

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

540
void SessionController::editCurrentProfile()
541
{
542
    EditProfileDialog* dialog = new EditProfileDialog( QApplication::activeWindow() );
543

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

558
    if ( ok )
559
    {
560 561 562 563 564 565
        // 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
566
        _session->setTabTitleFormat(Session::LocalTabTitle,text);
567
        _session->setTabTitleFormat(Session::RemoteTabTitle,text);
568

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

577 578 579
    //SaveSessionDialog dialog(_view);
    //int result = dialog.exec();
}
580 581
bool SessionController::confirmClose() const
{
582
    if (_session->isForegroundProcessActive())
583
    {
584
        QString title = _session->foregroundProcessName();
585 586 587 588
      
        // 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; 
589
        ignoreList << QString(qgetenv("SHELL")).section('/',-1);
590 591 592 593
        if (ignoreList.contains(title))
            return true;

        QString question;
Robert Knight's avatar
 
Robert Knight committed
594
        if (title.isEmpty())
595 596
            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
597 598 599
        else
            question = i18n("The program '%1' is currently running in this session."  
                            "  Are you sure you want to close it?",title);
600 601 602 603 604 605

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

611 612
    if (confirmClose())
        _session->close();
613
}
614

615 616 617 618 619
void SessionController::openBrowser()
{
    new KRun(url(), QApplication::activeWindow());
}

620
void SessionController::copy()
621
{
622
    _view->copyClipboard();
623
}
624 625

void SessionController::paste()
626
{
627
    _view->pasteClipboard();
628
}
629 630
void SessionController::pasteSelection()
{
631
    _view->pasteSelection();
632
}
633 634
void SessionController::copyInputTo()
{
635 636 637 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
    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;
674
}
675
void SessionController::clear()
676
{
677
    Emulation* emulation = _session->emulation();
678

679
    emulation->clearEntireScreen();
680
}
681
void SessionController::clearAndReset()
682
{
683
    Emulation* emulation = _session->emulation();
684

685
    emulation->reset();
686
    _session->refresh();
687
}
688 689
void SessionController::searchClosed()
{
690
    _searchToggleAction->toggle();
691 692
}

693
#if 0
694 695 696
void SessionController::searchHistory()
{
    searchHistory(true);
697
}
698
#endif
699

700 701
void SessionController::listenForScreenWindowUpdates()
{
702 703
    if (_listenForScreenWindowUpdates)
        return;
704

705 706 707 708
    connect( _view->screenWindow() , SIGNAL(outputChanged()) , this , 
            SLOT(updateSearchFilter()) );
    connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this , 
            SLOT(updateSearchFilter()) );
709

710
    _listenForScreenWindowUpdates = true;
711 712
}

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

721
        if (showSearchBar)
722
        {
723
            removeSearchFilter();
724

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

            // invoke search for matches for the current search text