• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Konsole

SessionController.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
00003 
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301  USA.
00018 */
00019 
00020 // Own
00021 #include "SessionController.h"
00022 
00023 // Qt
00024 #include <QtGui/QApplication>
00025 #include <QMenu>
00026 
00027 // KDE
00028 #include <KAction>
00029 #include <KDebug>
00030 #include <KIcon>
00031 #include <KInputDialog>
00032 #include <KLocale>
00033 #include <KMenu>
00034 #include <KRun>
00035 #include <kshell.h>
00036 #include <KToggleAction>
00037 #include <KUrl>
00038 #include <KXMLGUIFactory>
00039 #include <KXMLGUIBuilder>
00040 #include <kdebug.h>
00041 #include <kcodecaction.h>
00042 #include <kdeversion.h>
00043 
00044 // Konsole
00045 #include "EditProfileDialog.h"
00046 #include "CopyInputDialog.h"
00047 #include "Emulation.h"
00048 #include "Filter.h"
00049 #include "History.h"
00050 #include "IncrementalSearchBar.h"
00051 #include "ScreenWindow.h"
00052 #include "Session.h"
00053 #include "ProfileList.h"
00054 #include "TerminalDisplay.h"
00055 #include "SessionManager.h"
00056 
00057 // for SaveHistoryTask
00058 #include <KFileDialog>
00059 #include <KIO/Job>
00060 #include <KJob>
00061 #include <KMessageBox>
00062 #include "TerminalCharacterDecoder.h"
00063 
00064 
00065 using namespace Konsole;
00066 
00067 KIcon SessionController::_activityIcon;
00068 KIcon SessionController::_silenceIcon;
00069 QSet<SessionController*> SessionController::_allControllers;
00070 QPointer<SearchHistoryThread> SearchHistoryTask::_thread;
00071 int SessionController::_lastControllerId;
00072 
00073 SessionController::SessionController(Session* session , TerminalDisplay* view, QObject* parent)
00074     : ViewProperties(parent)
00075     , KXMLGUIClient()
00076     , _session(session)
00077     , _view(view)
00078     , _copyToGroup(0)
00079     , _profileList(0)
00080     , _previousState(-1)
00081     , _viewUrlFilter(0)
00082     , _searchFilter(0)
00083     , _searchToggleAction(0)
00084     , _findNextAction(0)
00085     , _findPreviousAction(0)
00086     , _urlFilterUpdateRequired(false)
00087     , _codecAction(0)
00088     , _changeProfileMenu(0)
00089     , _listenForScreenWindowUpdates(false)
00090     , _preventClose(false)
00091 {
00092     _allControllers.insert(this);
00093 
00094     Q_ASSERT( session );
00095     Q_ASSERT( view );
00096 
00097     // handle user interface related to session (menus etc.)
00098 
00099 #ifdef KONSOLE_PART
00100     setXMLFile("konsole/partui.rc");
00101 #else
00102     setXMLFile("konsole/sessionui.rc");
00103 #endif
00104 
00105     setupActions();
00106     actionCollection()->addAssociatedWidget(view);
00107     foreach (QAction* action, actionCollection()->actions())
00108         action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00109 
00110     setIdentifier(++_lastControllerId);
00111     sessionTitleChanged();
00112 
00113     view->installEventFilter(this);
00114 
00115     // listen for session resize requests
00116     connect( _session , SIGNAL(resizeRequest(const QSize&)) , this ,
00117             SLOT(sessionResizeRequest(const QSize&)) );
00118 
00119     // listen for popup menu requests
00120     connect( _view , SIGNAL(configureRequest(QPoint)) , this,
00121             SLOT(showDisplayContextMenu(QPoint)) );
00122 
00123     // move view to newest output when keystrokes occur
00124     connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , this ,
00125             SLOT(trackOutput(QKeyEvent*)) );
00126 
00127     // listen to activity / silence notifications from session
00128     connect( _session , SIGNAL(stateChanged(int)) , this ,
00129             SLOT(sessionStateChanged(int) ));
00130     // listen to title and icon changes
00131     connect( _session , SIGNAL(titleChanged()) , this , SLOT(sessionTitleChanged()) );
00132 
00133     // listen for color changes
00134     connect( _session , SIGNAL(changeBackgroundColorRequest(QColor)) , _view , SLOT(setBackgroundColor(QColor)) );
00135     connect( _session , SIGNAL(changeForegroundColorRequest(QColor)) , _view , SLOT(setForegroundColor(QColor)) );
00136 
00137     // update the title when the session starts
00138     connect( _session , SIGNAL(started()) , this , SLOT(snapshot()) ); 
00139 
00140     // listen for output changes to set activity flag
00141     connect( _session->emulation() , SIGNAL(outputChanged()) , this ,
00142             SLOT(fireActivity()) );
00143     
00144     // listen for flow control status changes
00145     connect( _session , SIGNAL(flowControlEnabledChanged(bool)) , _view ,
00146         SLOT(setFlowControlWarningEnabled(bool)) );
00147     _view->setFlowControlWarningEnabled(_session->flowControlEnabled());
00148 
00149     // take a snapshot of the session state every so often when
00150     // user activity occurs
00151     //
00152     // the timer is owned by the session so that it will be destroyed along
00153     // with the session
00154     QTimer* activityTimer = new QTimer(_session);
00155     activityTimer->setSingleShot(true);
00156     activityTimer->setInterval(2000);
00157     connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , activityTimer , SLOT(start()) );
00158     connect( activityTimer , SIGNAL(timeout()) , this , SLOT(snapshot()) );
00159 }
00160 
00161 void SessionController::updateSearchFilter()
00162 {
00163     if ( _searchFilter ) 
00164     {
00165         Q_ASSERT( searchBar() && searchBar()->isVisible() );
00166 
00167         _view->processFilters();
00168     }
00169 }
00170 
00171 SessionController::~SessionController()
00172 {
00173    if ( _view )
00174       _view->setScreenWindow(0);
00175 
00176    _allControllers.remove(this);
00177 }
00178 void SessionController::trackOutput(QKeyEvent* event)
00179 {
00180     Q_ASSERT( _view->screenWindow() );
00181 
00182     // jump to the end of the scrollback buffer unless the key pressed
00183     // is one of the three main modifiers, as these are used to select
00184     // the selection mode (eg. Ctrl+Alt+<Left Click> for column/block selection)
00185     switch (event->key())
00186     {
00187         case Qt::Key_Shift:
00188         case Qt::Key_Control:
00189         case Qt::Key_Alt:
00190             break;
00191         default:
00192             _view->screenWindow()->setTrackOutput(true);
00193     }
00194 }
00195 void SessionController::requireUrlFilterUpdate()
00196 {
00197     // this method is called every time the screen window's output changes, so do not
00198     // do anything expensive here.
00199 
00200     _urlFilterUpdateRequired = true;
00201 }
00202 void SessionController::snapshot()
00203 {
00204     Q_ASSERT( _session != 0 );
00205 
00206     QString title = _session->getDynamicTitle();    
00207     title         = title.simplified();
00208 
00209     // crude indicator when the session is broadcasting to others
00210     if (_copyToGroup && _copyToGroup->sessions().count() > 1)
00211         title.append('*');
00212 
00213     // apply new title
00214     if ( !title.isEmpty() )
00215         _session->setTitle(Session::DisplayedTitleRole,title);
00216     else
00217         _session->setTitle(Session::DisplayedTitleRole,_session->title(Session::NameRole));
00218 }
00219 
00220 QString SessionController::currentDir() const
00221 {
00222     return _session->currentWorkingDirectory();
00223 }
00224 
00225 KUrl SessionController::url() const
00226 {
00227     return _session->getUrl();
00228 }
00229 
00230 void SessionController::rename()
00231 {
00232     renameSession();
00233 }
00234 
00235 void SessionController::openUrl( const KUrl& url )
00236 {
00237     // handle local paths
00238     if ( url.isLocalFile() )
00239     {
00240         QString path = url.toLocalFile();
00241         _session->emulation()->sendText("cd " + KShell::quoteArg(path) + '\r');
00242     }
00243     else if ( url.protocol() == "ssh" )
00244     {
00245         _session->emulation()->sendText("ssh ");
00246 
00247         if ( url.hasUser() )
00248             _session->emulation()->sendText(url.user() + '@');
00249         if ( url.hasHost() )
00250             _session->emulation()->sendText(url.host() + '\r');
00251     }
00252     else
00253     {
00254         //TODO Implement handling for other Url types
00255         kWarning(1211) << "Unable to open bookmark at url" << url << ", I do not know"
00256            << " how to handle the protocol " << url.protocol();
00257     }
00258 }
00259 
00260 bool SessionController::eventFilter(QObject* watched , QEvent* event)
00261 {
00262     if ( watched == _view )
00263     {
00264         if ( event->type() == QEvent::FocusIn )
00265         {
00266             // notify the world that the view associated with this session has been focused
00267             // used by the view manager to update the title of the MainWindow widget containing the view
00268             emit focused(this);
00269 
00270             // when the view is focused, set bell events from the associated session to be delivered
00271             // by the focused view
00272 
00273             // first, disconnect any other views which are listening for bell signals from the session
00274             disconnect( _session , SIGNAL(bellRequest(const QString&)) , 0 , 0 );
00275             // second, connect the newly focused view to listen for the session's bell signal
00276             connect( _session , SIGNAL(bellRequest(const QString&)) ,
00277                     _view , SLOT(bell(const QString&)) );
00278         }
00279         // when a mouse move is received, create the URL filter and listen for output changes if
00280         // it has not already been created.  If it already exists, then update only if the output
00281         // has changed since the last update ( _urlFilterUpdateRequired == true )
00282         //
00283         // also check that no mouse buttons are pressed since the URL filter only applies when
00284         // the mouse is hovering over the view
00285         if ( event->type() == QEvent::MouseMove &&
00286             (!_viewUrlFilter || _urlFilterUpdateRequired) &&
00287             ((QMouseEvent*)event)->buttons() == Qt::NoButton )
00288         {
00289             if ( _view->screenWindow() && !_viewUrlFilter )
00290             {
00291                 connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this ,
00292                         SLOT(requireUrlFilterUpdate()) );
00293                 connect( _view->screenWindow() , SIGNAL(outputChanged()) , this ,
00294                          SLOT(requireUrlFilterUpdate()) );
00295 
00296                 // install filter on the view to highlight URLs
00297                 _viewUrlFilter = new UrlFilter();
00298                 _view->filterChain()->addFilter( _viewUrlFilter );
00299             }
00300 
00301             _view->processFilters();
00302             _urlFilterUpdateRequired = false;
00303         }
00304     }
00305 
00306     return false;
00307 }
00308 
00309 void SessionController::removeSearchFilter()
00310 {
00311     if (!_searchFilter)
00312         return;
00313 
00314     _view->filterChain()->removeFilter(_searchFilter);
00315     delete _searchFilter;
00316     _searchFilter = 0;
00317 }
00318 
00319 void SessionController::setSearchBar(IncrementalSearchBar* searchBar)
00320 {
00321     // disconnect the existing search bar
00322     if ( _searchBar )
00323     {
00324         disconnect( this , 0 , _searchBar , 0 );
00325         disconnect( _searchBar , 0 , this , 0 );
00326     }
00327 
00328     // remove any existing search filter
00329     removeSearchFilter();
00330 
00331     // connect new search bar
00332     _searchBar = searchBar;
00333     if ( _searchBar )
00334     {
00335         connect( _searchBar , SIGNAL(closeClicked()) , this , SLOT(searchClosed()) );
00336         connect( _searchBar , SIGNAL(findNextClicked()) , this , SLOT(findNextInHistory()) );
00337         connect( _searchBar , SIGNAL(findPreviousClicked()) , this , SLOT(findPreviousInHistory()) );
00338         connect( _searchBar , SIGNAL(highlightMatchesToggled(bool)) , this , SLOT(highlightMatches(bool)) );
00339 
00340         // if the search bar was previously active
00341         // then re-enter search mode
00342         searchHistory( _searchToggleAction->isChecked() );
00343     }
00344 }
00345 IncrementalSearchBar* SessionController::searchBar() const
00346 {
00347     return _searchBar;
00348 }
00349 
00350 void SessionController::setShowMenuAction(QAction* action)
00351 {
00352     actionCollection()->addAction("show-menubar",action);
00353 }
00354 
00355 void SessionController::setupActions()
00356 {
00357     KAction* action = 0;
00358     KToggleAction* toggleAction = 0;
00359     KActionCollection* collection = actionCollection();
00360 
00361     // Close Session
00362     action = collection->addAction("close-session");
00363     action->setIcon( KIcon("tab-close") );
00364     action->setText( i18n("&Close Tab") );
00365     action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_W) );
00366     connect( action , SIGNAL(triggered()) , this , SLOT(closeSession()) );
00367 
00368     // Open Browser
00369     action = collection->addAction("open-browser");
00370     action->setText( i18n("Open Browser Here") );
00371     action->setIcon( KIcon("system-file-manager") );
00372     connect( action, SIGNAL(triggered()), this, SLOT(openBrowser()) );
00373 
00374     // Copy and Paste
00375     action = collection->addAction("copy");
00376     action->setIcon( KIcon("edit-copy") );
00377     action->setText( i18n("&Copy") );
00378     action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_C) );
00379     connect( action , SIGNAL(triggered()) , this , SLOT(copy()) );
00380 
00381     KAction* pasteAction = new KAction( i18n("&Paste") , this );
00382     pasteAction->setIcon( KIcon("edit-paste") );
00383 
00384     KShortcut pasteShortcut = pasteAction->shortcut();
00385     pasteShortcut.setPrimary( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_V) );
00386     pasteShortcut.setAlternate( QKeySequence(Qt::SHIFT+Qt::Key_Insert) );
00387     pasteAction->setShortcut(pasteShortcut);
00388 
00389     collection->addAction("paste",pasteAction);
00390 
00391     connect( pasteAction , SIGNAL(triggered()) , this , SLOT(paste()) );
00392 
00393     action = collection->addAction("paste-selection");
00394     action->setText( i18n("Paste Selection") );
00395     action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Insert) );
00396     connect( action , SIGNAL(triggered()) , this , SLOT(pasteSelection()) );
00397 
00398     // Rename Session
00399     action = collection->addAction("rename-session");
00400     action->setText( i18n("&Rename Tab...") );
00401     action->setShortcut( QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_S) );
00402     connect( action , SIGNAL(triggered()) , this , SLOT(renameSession()) );
00403 
00404     // Copy Input To
00405     action = collection->addAction("copy-input-to");
00406     action->setText(i18n("Copy Input To..."));
00407     connect( action , SIGNAL(triggered()) , this , SLOT(copyInputTo()) );
00408 
00409     // Clear and Clear+Reset
00410     action = collection->addAction("clear");
00411     action->setText( i18n("C&lear Display") );
00412     action->setIcon( KIcon("edit-clear") );
00413     connect( action , SIGNAL(triggered()) , this , SLOT(clear()) );
00414 
00415     action = collection->addAction("clear-and-reset");
00416     action->setText( i18n("Clear && Reset") );
00417     action->setIcon( KIcon("edit-clear-history") );
00418     connect( action , SIGNAL(triggered()) , this , SLOT(clearAndReset()) );
00419 
00420     // Monitor
00421     toggleAction = new KToggleAction(i18n("Monitor for &Activity"),this);
00422     toggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_A) );
00423     action = collection->addAction("monitor-activity",toggleAction);
00424     connect( action , SIGNAL(toggled(bool)) , this , SLOT(monitorActivity(bool)) );
00425 
00426     toggleAction = new KToggleAction(i18n("Monitor for &Silence"),this);
00427     toggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_I) );
00428     action = collection->addAction("monitor-silence",toggleAction);
00429     connect( action , SIGNAL(toggled(bool)) , this , SLOT(monitorSilence(bool)) );
00430 
00431     // Character Encoding
00432     _codecAction = new KCodecAction(i18n("Character Encoding"),this);
00433     collection->addAction("character-encoding",_codecAction);
00434     connect( _codecAction->menu() , SIGNAL(aboutToShow()) , this , SLOT(updateCodecAction()) );
00435     connect( _codecAction , SIGNAL(triggered(QTextCodec*)) , this , SLOT(changeCodec(QTextCodec*)) );
00436 
00437     // Text Size
00438     action = collection->addAction("increase-text-size");
00439     action->setText( i18n("Increase Text Size") );
00440     action->setIcon( KIcon("zoom-in") );
00441     action->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_Plus) );
00442     connect( action , SIGNAL(triggered()) , this , SLOT(increaseTextSize()) );
00443 
00444     action = collection->addAction("decrease-text-size");
00445     action->setText( i18n("Decrease Text Size") );
00446     action->setIcon( KIcon("zoom-out") );
00447     action->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_Minus) );
00448     connect( action , SIGNAL(triggered()) , this , SLOT(decreaseTextSize()) );
00449 
00450     // Scrollback
00451     _searchToggleAction = new KAction(i18n("Search Output..."),this);
00452     _searchToggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_F) );
00453     _searchToggleAction->setIcon( KIcon("edit-find") );
00454     _searchToggleAction->setCheckable(true);
00455     action = collection->addAction("search-history" , _searchToggleAction);
00456     connect( action , SIGNAL(toggled(bool)) , this , SLOT(searchHistory(bool)) );
00457 
00458     _findNextAction = collection->addAction("find-next");
00459     _findNextAction->setIcon( KIcon("go-down-search") );
00460     _findNextAction->setText( i18n("Find Next") );
00461     _findNextAction->setShortcut( QKeySequence(Qt::Key_F3) );
00462     _findNextAction->setEnabled(false);
00463     connect( _findNextAction , SIGNAL(triggered()) , this , SLOT(findNextInHistory()) );
00464 
00465     _findPreviousAction = collection->addAction("find-previous");
00466     _findPreviousAction->setIcon( KIcon("go-up-search") );
00467     _findPreviousAction->setText( i18n("Find Previous") );
00468     _findPreviousAction->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_F3) );
00469     _findPreviousAction->setEnabled(false);
00470     connect( _findPreviousAction , SIGNAL(triggered()) , this , SLOT(findPreviousInHistory()) );
00471 
00472     action = collection->addAction("save-history");
00473     action->setText( i18n("Save Output...") );
00474     action->setIcon( KIcon("document-save-as") );
00475     connect( action , SIGNAL(triggered()) , this , SLOT(saveHistory()) );
00476 
00477     action = collection->addAction("history-options");
00478     action->setText( i18n("Scrollback Options") );
00479     action->setIcon( KIcon("configure") );
00480     connect( action , SIGNAL(triggered()) , this , SLOT(showHistoryOptions()) );
00481 
00482     action = collection->addAction("clear-history");
00483     action->setText( i18n("Clear Scrollback") );
00484     connect( action , SIGNAL(triggered()) , this , SLOT(clearHistory()) );
00485 
00486     action = collection->addAction("clear-history-and-reset");
00487     action->setText( i18n("Clear Scrollback && Reset") );
00488     action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_X) );
00489     connect( action , SIGNAL(triggered()) , this , SLOT(clearHistoryAndReset()) );
00490 
00491     // Profile Options
00492     action = collection->addAction("edit-current-profile");
00493     action->setText( i18n("Edit Current Profile...") );
00494     action->setIcon( KIcon("document-properties") );
00495     connect( action , SIGNAL(triggered()) , this , SLOT(editCurrentProfile()) );
00496 
00497     _changeProfileMenu = new KMenu(i18n("Change Profile"),_view);
00498     collection->addAction("change-profile",_changeProfileMenu->menuAction());
00499     connect( _changeProfileMenu , SIGNAL(aboutToShow()) , this , SLOT(prepareChangeProfileMenu()) );
00500 }
00501 void SessionController::changeProfile(Profile::Ptr profile)
00502 {
00503     SessionManager::instance()->setSessionProfile(_session,profile);    
00504 }
00505 void SessionController::prepareChangeProfileMenu()
00506 {
00507     if ( _changeProfileMenu->isEmpty() )
00508     {
00509         _profileList = new ProfileList(false,this);
00510         connect( _profileList , SIGNAL(profileSelected(Profile::Ptr)) ,
00511                 this , SLOT(changeProfile(Profile::Ptr)) );
00512     }
00513 
00514     _changeProfileMenu->clear();
00515     _changeProfileMenu->addActions(_profileList->actions());
00516 }
00517 void SessionController::updateCodecAction()
00518 {
00519     _codecAction->setCurrentCodec( QString(_session->emulation()->codec()->name()) );
00520 }
00521 void SessionController::changeCodec(QTextCodec* codec)
00522 {
00523     _session->setCodec(codec);
00524 }
00525 
00526 void SessionController::editCurrentProfile()
00527 {
00528     EditProfileDialog* dialog = new EditProfileDialog( QApplication::activeWindow() );
00529 
00530     dialog->setProfile(SessionManager::instance()->sessionProfile(_session));
00531     dialog->show();
00532 }
00533 void SessionController::renameSession()
00534 {
00535     QPointer<Session> guard(_session);
00536     bool ok = false;
00537     const QString& text = KInputDialog::getText( i18n("Rename Tab") ,
00538                                                  i18n("Enter new tab text:") ,
00539                                                  _session->tabTitleFormat(Session::LocalTabTitle) ,
00540                                                  &ok, QApplication::activeWindow() );
00541     if (!guard)
00542         return;
00543 
00544     if ( ok )
00545     {
00546         // renaming changes both the local and remote tab title formats, to save confusion over
00547         // the tab title not changing if renaming the tab whilst the remote tab title format is 
00548         // being displayed
00549         //
00550         // The downside of this approach is that after renaming a tab manually, the ability to 
00551         // have separate formats for local and remote activities is lost
00552         _session->setTabTitleFormat(Session::LocalTabTitle,text);
00553         _session->setTabTitleFormat(Session::RemoteTabTitle,text);
00554 
00555         // trigger an update of the tab text
00556         snapshot();
00557     }
00558 }
00559 void SessionController::saveSession()
00560 {
00561     Q_ASSERT(0); // not implemented yet
00562 
00563     //SaveSessionDialog dialog(_view);
00564     //int result = dialog.exec();
00565 }
00566 bool SessionController::confirmClose() const
00567 {
00568     if (_session->isChildActive())
00569     {
00570         QString title = _session->childName();
00571       
00572         // hard coded for now.  In future make it possible for the user to specify which programs
00573         // are ignored when considering whether to display a confirmation
00574         QStringList ignoreList; 
00575         ignoreList << QString(qgetenv("SHELL")).section('/',-1);
00576         if (ignoreList.contains(title))
00577             return true;
00578 
00579         QString question;
00580         if (title.isEmpty())
00581             question = i18n("A program is currently running in this session."
00582                             "  Are you sure you want to close it?");
00583         else
00584             question = i18n("The program '%1' is currently running in this session."  
00585                             "  Are you sure you want to close it?",title);
00586 
00587         int result = KMessageBox::warningYesNo(_view->window(),question,i18n("Confirm Close"));
00588         return (result == KMessageBox::Yes) ? true : false; 
00589     }
00590     return true;
00591 }
00592 void SessionController::closeSession()
00593 {
00594     if (_preventClose)
00595         return;
00596 
00597     if (confirmClose())
00598         _session->close();
00599 }
00600 
00601 void SessionController::openBrowser()
00602 {
00603     new KRun(url(), QApplication::activeWindow());
00604 }
00605 
00606 void SessionController::copy()
00607 {
00608     _view->copyClipboard();
00609 }
00610 
00611 void SessionController::paste()
00612 {
00613     _view->pasteClipboard();
00614 }
00615 void SessionController::pasteSelection()
00616 {
00617     _view->pasteSelection();
00618 }
00619 void SessionController::copyInputTo()
00620 {
00621     if (!_copyToGroup)
00622     {
00623         _copyToGroup = new SessionGroup(this);
00624         _copyToGroup->addSession(_session);
00625         _copyToGroup->setMasterStatus(_session,true);
00626         _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll);
00627     }
00628 
00629     CopyInputDialog* dialog = new CopyInputDialog(_view);
00630     dialog->setMasterSession(_session);
00631     
00632     QSet<Session*> currentGroup = QSet<Session*>::fromList(_copyToGroup->sessions());
00633     currentGroup.remove(_session);
00634     
00635     dialog->setChosenSessions(currentGroup);
00636 
00637     QPointer<Session> guard(_session);
00638     int result = dialog->exec();
00639     if (!guard)
00640         return;
00641 
00642     if (result)
00643     {
00644         QSet<Session*> newGroup = dialog->chosenSessions();
00645         newGroup.remove(_session);
00646     
00647         QSet<Session*> completeGroup = newGroup | currentGroup;
00648         foreach(Session* session, completeGroup)
00649         {
00650             if (newGroup.contains(session) && !currentGroup.contains(session))
00651                 _copyToGroup->addSession(session);
00652             else if (!newGroup.contains(session) && currentGroup.contains(session))
00653                 _copyToGroup->removeSession(session);
00654         }
00655 
00656         snapshot();
00657     }
00658 
00659     delete dialog;
00660 }
00661 void SessionController::clear()
00662 {
00663     Emulation* emulation = _session->emulation();
00664 
00665     emulation->clearEntireScreen();
00666 }
00667 void SessionController::clearAndReset()
00668 {
00669     Emulation* emulation = _session->emulation();
00670 
00671     emulation->reset();
00672     _session->refresh();
00673 }
00674 void SessionController::searchClosed()
00675 {
00676     _searchToggleAction->toggle();
00677 }
00678 
00679 #if 0
00680 void SessionController::searchHistory()
00681 {
00682     searchHistory(true);
00683 }
00684 #endif
00685 
00686 void SessionController::listenForScreenWindowUpdates()
00687 {
00688     if (_listenForScreenWindowUpdates)
00689         return;
00690 
00691     connect( _view->screenWindow() , SIGNAL(outputChanged()) , this , 
00692             SLOT(updateSearchFilter()) );
00693     connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this , 
00694             SLOT(updateSearchFilter()) );
00695 
00696     _listenForScreenWindowUpdates = true;
00697 }
00698 
00699 // searchHistory() may be called either as a result of clicking a menu item or
00700 // as a result of changing the search bar widget
00701 void SessionController::searchHistory(bool showSearchBar)
00702 {
00703     if ( _searchBar )
00704     {
00705         _searchBar->setVisible(showSearchBar);
00706 
00707         if (showSearchBar)
00708         {
00709             removeSearchFilter();
00710 
00711             listenForScreenWindowUpdates();
00712             
00713             _searchFilter = new RegExpFilter();
00714             _view->filterChain()->addFilter(_searchFilter);
00715             connect( _searchBar , SIGNAL(searchChanged(const QString&)) , this ,
00716                     SLOT(searchTextChanged(const QString&)) );
00717 
00718             // invoke search for matches for the current search text
00719             const QString& currentSearchText = _searchBar->searchText();
00720             if (!currentSearchText.isEmpty())
00721             {
00722                 searchTextChanged(currentSearchText);
00723             }
00724 
00725             setFindNextPrevEnabled(true);
00726         }
00727         else
00728         {
00729             setFindNextPrevEnabled(false);
00730 
00731             disconnect( _searchBar , SIGNAL(searchChanged(const QString&)) , this ,
00732                     SLOT(searchTextChanged(const QString&)) );
00733 
00734             removeSearchFilter();
00735 
00736             _view->setFocus( Qt::ActiveWindowFocusReason );
00737         }
00738     }
00739 }
00740 void SessionController::setFindNextPrevEnabled(bool enabled)
00741 {
00742     _findNextAction->setEnabled(enabled);
00743     _findPreviousAction->setEnabled(enabled);
00744 }
00745 void SessionController::searchTextChanged(const QString& text)
00746 {
00747     Q_ASSERT( _view->screenWindow() );
00748 
00749     if ( text.isEmpty() )
00750         _view->screenWindow()->clearSelection();
00751 
00752     // update search.  this is called even when the text is
00753     // empty to clear the view's filters
00754     beginSearch(text , SearchHistoryTask::ForwardsSearch);
00755 }
00756 void SessionController::searchCompleted(bool success)
00757 {
00758     if ( _searchBar )
00759         _searchBar->setFoundMatch(success);
00760 }
00761 
00762 void SessionController::beginSearch(const QString& text , int direction)
00763 {
00764     Q_ASSERT( _searchBar );
00765     Q_ASSERT( _searchFilter );
00766 
00767     Qt::CaseSensitivity caseHandling = _searchBar->matchCase() ? Qt::CaseSensitive : Qt::CaseInsensitive;
00768     QRegExp::PatternSyntax syntax = _searchBar->matchRegExp() ? QRegExp::RegExp : QRegExp::FixedString;
00769 
00770     QRegExp regExp( text.trimmed() ,  caseHandling , syntax );
00771     _searchFilter->setRegExp(regExp);
00772 
00773     if ( !regExp.isEmpty() )
00774     {
00775         SearchHistoryTask* task = new SearchHistoryTask(this);
00776 
00777         connect( task , SIGNAL(completed(bool)) , this , SLOT(searchCompleted(bool)) );
00778 
00779         task->setRegExp(regExp);
00780         task->setSearchDirection( (SearchHistoryTask::SearchDirection)direction );
00781         task->setAutoDelete(true);
00782         task->addScreenWindow( _session , _view->screenWindow() );
00783         task->execute();
00784     }
00785 
00786     _view->processFilters();
00787 }
00788 void SessionController::highlightMatches(bool highlight)
00789 {
00790     if ( highlight )
00791     {
00792         _view->filterChain()->addFilter(_searchFilter);
00793         _view->processFilters();
00794     }
00795     else
00796     {
00797         _view->filterChain()->removeFilter(_searchFilter);
00798     }
00799 
00800     _view->update();
00801 }
00802 void SessionController::findNextInHistory()
00803 {
00804     Q_ASSERT( _searchBar );
00805     Q_ASSERT( _searchFilter );
00806 
00807     beginSearch(_searchBar->searchText(),SearchHistoryTask::ForwardsSearch);
00808 }
00809 void SessionController::findPreviousInHistory()
00810 {
00811     Q_ASSERT( _searchBar );
00812     Q_ASSERT( _searchFilter );
00813 
00814     beginSearch(_searchBar->searchText(),SearchHistoryTask::BackwardsSearch);
00815 }
00816 void SessionController::showHistoryOptions()
00817 {
00818     HistorySizeDialog* dialog = new HistorySizeDialog( QApplication::activeWindow() );
00819     const HistoryType& currentHistory = _session->historyType();
00820 
00821     if ( currentHistory.isEnabled() )
00822     {
00823         if ( currentHistory.isUnlimited() )
00824             dialog->setMode( HistorySizeDialog::UnlimitedHistory );
00825         else
00826         {
00827             dialog->setMode( HistorySizeDialog::FixedSizeHistory );
00828             dialog->setLineCount( currentHistory.maximumLineCount() );
00829         }
00830     }
00831     else
00832         dialog->setMode( HistorySizeDialog::NoHistory );
00833 
00834     connect( dialog , SIGNAL(optionsChanged(int,int)) ,
00835              this , SLOT(scrollBackOptionsChanged(int,int)) );
00836 
00837     dialog->show();
00838 }
00839 void SessionController::sessionResizeRequest(const QSize& size)
00840 {
00841     kDebug(1211) << "View resize requested to " << size;
00842     _view->setSize(size.width(),size.height());
00843 }
00844 void SessionController::scrollBackOptionsChanged( int mode , int lines )
00845 {
00846     switch (mode)
00847     {
00848         case HistorySizeDialog::NoHistory:
00849             _session->setHistoryType( HistoryTypeNone() );
00850             break;
00851          case HistorySizeDialog::FixedSizeHistory:
00852             _session->setHistoryType( HistoryTypeBuffer(lines) );
00853             break;
00854          case HistorySizeDialog::UnlimitedHistory:
00855              _session->setHistoryType( HistoryTypeFile() );
00856             break;
00857     }
00858 }
00859 
00860 void SessionController::saveHistory()
00861 {
00862     SessionTask* task = new SaveHistoryTask(this);
00863     task->setAutoDelete(true);
00864     task->addSession( _session );
00865     task->execute();
00866 }
00867 void SessionController::clearHistory()
00868 {
00869     _session->clearHistory();
00870 }
00871 void SessionController::clearHistoryAndReset()
00872 {
00873     clearAndReset();
00874     clearHistory();
00875 }
00876 void SessionController::increaseTextSize()
00877 {
00878     QFont font = _view->getVTFont();
00879     font.setPointSize(font.pointSize()+1);
00880     _view->setVTFont(font);
00881 
00882     //TODO - Save this setting as a session default
00883 }
00884 void SessionController::decreaseTextSize()
00885 {
00886     static const int MinimumFontSize = 6;
00887 
00888     QFont font = _view->getVTFont();
00889     font.setPointSize( qMax(font.pointSize()-1,MinimumFontSize) );
00890     _view->setVTFont(font);
00891 
00892     //TODO - Save this setting as a session default
00893 }
00894 void SessionController::monitorActivity(bool monitor)
00895 {
00896     _session->setMonitorActivity(monitor);
00897 }
00898 void SessionController::monitorSilence(bool monitor)
00899 {
00900     _session->setMonitorSilence(monitor);
00901 }
00902 void SessionController::sessionTitleChanged()
00903 {
00904         if ( _sessionIconName != _session->iconName() )
00905         {
00906             _sessionIconName = _session->iconName();
00907             _sessionIcon = KIcon( _sessionIconName );
00908             setIcon( _sessionIcon );
00909         }
00910 
00911         QString title = _session->title(Session::DisplayedTitleRole);
00912 
00913         // special handling for the "%w" marker which is replaced with the
00914         // window title set by the shell
00915         title.replace("%w",_session->userTitle());
00916         // special handling for the "%#" marker which is replaced with the 
00917         // number of the shell
00918         title.replace("%#",QString::number(_session->sessionId()));
00919 
00920        if ( title.isEmpty() )
00921           title = _session->title(Session::NameRole);
00922 
00923        setTitle( title );
00924 }
00925 
00926 void SessionController::showDisplayContextMenu(const QPoint& position)
00927 {
00928     // needed to make sure the popup menu is available, even if a hosting
00929     // application did not merge our GUI.
00930     if (!factory()) 
00931     {
00932         if (!clientBuilder()) 
00933             setClientBuilder(new KXMLGUIBuilder(_view));
00934         
00935         KXMLGUIFactory* factory = new KXMLGUIFactory(clientBuilder(), this);
00936         factory->addClient(this);
00937     }
00938 
00939     QMenu* popup = qobject_cast<QMenu*>(factory()->container("session-popup-menu",this));
00940     if (popup)
00941     {
00942         // prepend content-specific actions such as "Open Link", "Copy Email Address" etc.
00943         QList<QAction*> contentActions = _view->filterActions(position);
00944         QAction* contentSeparator = new QAction(popup);
00945         contentSeparator->setSeparator(true);
00946         contentActions << contentSeparator;
00947 
00948         _preventClose = true;
00949 
00950         popup->insertActions(popup->actions().value(0,0),contentActions);
00951         QAction* chosen = popup->exec( _view->mapToGlobal(position) );
00952 
00953         // remove content-specific actions, unless the close action was chosen
00954         // in which case the popup menu will be partially destroyed at this point
00955            foreach(QAction* action,contentActions)
00956             popup->removeAction(action);
00957         delete contentSeparator;
00958 
00959         _preventClose = false;
00960 
00961         if (chosen && chosen->objectName() == "close-session")
00962             chosen->trigger();
00963     }
00964     else
00965     {
00966         kWarning() << "Unable to display popup menu for session"
00967                    << _session->title(Session::NameRole)
00968                    << ", no GUI factory available to build the popup.";
00969     }
00970 }
00971 
00972 void SessionController::sessionStateChanged(int state)
00973 {
00974     if ( state == _previousState )
00975         return;
00976 
00977     _previousState = state;
00978 
00979     // TODO - Replace the icon choices below when suitable icons for silence and activity
00980     // are available
00981     if ( state == NOTIFYACTIVITY )
00982     {
00983         if (_activityIcon.isNull())
00984         {
00985             _activityIcon = KIcon("dialog-information");
00986         }
00987 
00988         setIcon(_activityIcon);
00989     }
00990     else if ( state == NOTIFYSILENCE )
00991     {
00992         if (_silenceIcon.isNull())
00993         {
00994             _silenceIcon = KIcon("dialog-information");
00995         }
00996 
00997         setIcon(_silenceIcon);
00998     }
00999     else if ( state == NOTIFYNORMAL )
01000     {
01001         if ( _sessionIconName != _session->iconName() )
01002         {
01003             _sessionIconName = _session->iconName();
01004             _sessionIcon = KIcon( _sessionIconName );
01005         }
01006 
01007         setIcon( _sessionIcon );
01008     }
01009 }
01010 
01011 SessionTask::SessionTask(QObject* parent)
01012     :  QObject(parent)
01013     ,  _autoDelete(false)
01014 {
01015 }
01016 void SessionTask::setAutoDelete(bool enable)
01017 {
01018     _autoDelete = enable;
01019 }
01020 bool SessionTask::autoDelete() const
01021 {
01022     return _autoDelete;
01023 }
01024 void SessionTask::addSession(Session* session)
01025 {
01026     _sessions << session;
01027 }
01028 QList<SessionPtr> SessionTask::sessions() const
01029 {
01030     return _sessions;
01031 }
01032 
01033 SaveHistoryTask::SaveHistoryTask(QObject* parent)
01034     : SessionTask(parent)
01035 {
01036 }
01037 SaveHistoryTask::~SaveHistoryTask()
01038 {
01039 }
01040 
01041 void SaveHistoryTask::execute()
01042 {
01043     QListIterator<SessionPtr> iter(sessions());
01044 
01045     // TODO - think about the UI when saving multiple history sessions, if there are more than two or
01046     //        three then providing a URL for each one will be tedious
01047 
01048     // TODO - show a warning ( preferably passive ) if saving the history output fails
01049     //
01050 
01051      KFileDialog* dialog = new KFileDialog( QString(":konsole") /* check this */,
01052                                                QString(), QApplication::activeWindow() );
01053      dialog->setOperationMode(KFileDialog::Saving);
01054      dialog->setConfirmOverwrite(true);
01055 
01056      QStringList mimeTypes;
01057      mimeTypes << "text/plain";
01058      mimeTypes << "text/html";
01059      dialog->setMimeFilter(mimeTypes,"text/plain");
01060 
01061      // iterate over each session in the task and display a dialog to allow the user to choose where
01062      // to save that session's history.
01063      // then start a KIO job to transfer the data from the history to the chosen URL
01064     while ( iter.hasNext() )
01065     {
01066         SessionPtr session = iter.next();
01067 
01068         dialog->setCaption( i18n("Save Output From %1",session->title(Session::NameRole)) );
01069 
01070         int result = dialog->exec();
01071 
01072         if ( result != QDialog::Accepted )
01073             continue;
01074 
01075         KUrl url = dialog->selectedUrl();
01076 
01077         if ( !url.isValid() )
01078         { // UI:  Can we make this friendlier?
01079             KMessageBox::sorry( 0 , i18n("%1 is an invalid URL, the output could not be saved.",url.url()) );
01080             continue;
01081         }
01082 
01083         KIO::TransferJob* job = KIO::put( url,
01084                                           -1,   // no special permissions
01085                                           // overwrite existing files
01086                                           // do not resume an existing transfer
01087                                           // show progress information only for remote
01088                                           // URLs
01089                                           KIO::Overwrite | (url.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags)
01090                                                              // a better solution would be to show progress
01091                                                              // information after a certain period of time
01092                                                              // instead, since the overall speed of transfer
01093                                                              // depends on factors other than just the protocol
01094                                                              // used
01095                                         );
01096 
01097 
01098         SaveJob jobInfo;
01099         jobInfo.session = session;
01100         jobInfo.lastLineFetched = -1;  // when each request for data comes in from the KIO subsystem
01101                                        // lastLineFetched is used to keep track of how much of the history
01102                                        // has already been sent, and where the next request should continue
01103                                        // from.
01104                                        // this is set to -1 to indicate the job has just been started
01105 
01106         if ( dialog->currentMimeFilter() == "text/html" )
01107            jobInfo.decoder = new HTMLDecoder();
01108         else
01109            jobInfo.decoder = new PlainTextDecoder();
01110 
01111         _jobSession.insert(job,jobInfo);
01112 
01113         connect( job , SIGNAL(dataReq(KIO::Job*,QByteArray&)),
01114                  this, SLOT(jobDataRequested(KIO::Job*,QByteArray&)) );
01115         connect( job , SIGNAL(result(KJob*)),
01116                  this, SLOT(jobResult(KJob*)) );
01117     }
01118 
01119     dialog->deleteLater();
01120 }
01121 void SaveHistoryTask::jobDataRequested(KIO::Job* job , QByteArray& data)
01122 {
01123     // TODO - Report progress information for the job
01124 
01125     // PERFORMANCE:  Do some tests and tweak this value to get faster saving
01126     const int LINES_PER_REQUEST = 500;
01127 
01128     SaveJob& info = _jobSession[job];
01129 
01130     // transfer LINES_PER_REQUEST lines from the session's history
01131     // to the save location
01132     if ( info.session )
01133     {
01134         // note:  when retrieving lines from the emulation,
01135         // the first line is at index 0.
01136 
01137         int sessionLines = info.session->emulation()->lineCount();
01138 
01139         if ( sessionLines-1 == info.lastLineFetched )
01140             return; // if there is no more data to transfer then stop the job
01141 
01142         int copyUpToLine = qMin( info.lastLineFetched + LINES_PER_REQUEST ,
01143                                  sessionLines-1 );
01144 
01145         QTextStream stream(&data,QIODevice::ReadWrite);
01146         info.decoder->begin(&stream);
01147         info.session->emulation()->writeToStream( info.decoder , info.lastLineFetched+1 , copyUpToLine );
01148         info.decoder->end();
01149 
01150         // if there are still more lines to process after this request
01151         // then insert a new line character
01152         // to ensure that the next block of lines begins on a new line
01153         //
01154         // FIXME - There is still an extra new-line at the end of the save data.
01155         if ( copyUpToLine <= sessionLines-1 )
01156         {
01157             stream << '\n';
01158         }
01159 
01160 
01161         info.lastLineFetched = copyUpToLine;
01162     }
01163 }
01164 void SaveHistoryTask::jobResult(KJob* job)
01165 {
01166     if ( job->error() )
01167     {
01168         KMessageBox::sorry( 0 , i18n("A problem occurred when saving the output.\n%1",job->errorString()) );
01169     }
01170 
01171     SaveJob& info = _jobSession[job];
01172 
01173     _jobSession.remove(job);
01174 
01175     delete info.decoder;
01176 
01177     // notify the world that the task is done
01178     emit completed(true);
01179 
01180     if ( autoDelete() )
01181         deleteLater();
01182 }
01183 void SearchHistoryTask::addScreenWindow( Session* session , ScreenWindow* searchWindow )
01184 {
01185    _windows.insert(session,searchWindow);
01186 }
01187 void SearchHistoryTask::execute()
01188 {
01189     QMapIterator< SessionPtr , ScreenWindowPtr > iter(_windows);
01190 
01191     while ( iter.hasNext() )
01192     {
01193         iter.next();
01194         executeOnScreenWindow( iter.key() , iter.value() );
01195     }
01196 }
01197 
01198 void SearchHistoryTask::executeOnScreenWindow( SessionPtr session , ScreenWindowPtr window )
01199 {
01200     Q_ASSERT( session );
01201     Q_ASSERT( window );
01202 
01203     Emulation* emulation = session->emulation();
01204 
01205     int selectionColumn = 0;
01206     int selectionLine = 0;
01207 
01208     window->getSelectionEnd(selectionColumn , selectionLine);
01209 
01210     if ( !_regExp.isEmpty() )
01211     {
01212         int pos = -1;
01213         const bool forwards = ( _direction == ForwardsSearch );
01214         const int startLine = selectionLine + window->currentLine() + ( forwards ? 1 : -1 );
01215         const int lastLine = window->lineCount() - 1;
01216         QString string;
01217 
01218         //text stream to read history into string for pattern or regular expression searching
01219         QTextStream searchStream(&string);
01220 
01221         PlainTextDecoder decoder;
01222         decoder.setRecordLinePositions(true);
01223 
01224         //setup first and last lines depending on search direction
01225         int line = startLine;
01226 
01227         //read through and search history in blocks of 10K lines.
01228         //this balances the need to retrieve lots of data from the history each time
01229         //(for efficient searching)
01230         //without using silly amounts of memory if the history is very large.
01231         const int maxDelta = qMin(window->lineCount(),10000);
01232         int delta = forwards ? maxDelta : -maxDelta;
01233 
01234         int endLine = line;
01235         bool hasWrapped = false;  // set to true when we reach the top/bottom
01236                                   // of the output and continue from the other
01237                                   // end
01238 
01239         //loop through history in blocks of <delta> lines.
01240         do
01241         {
01242             // ensure that application does not appear to hang
01243             // if searching through a lengthy output
01244             QApplication::processEvents();
01245 
01246             // calculate lines to search in this iteration
01247             if ( hasWrapped )
01248             {
01249                 if ( endLine == lastLine )
01250                     line = 0;
01251                 else if ( endLine == 0 )
01252                     line = lastLine;
01253 
01254                 endLine += delta;
01255 
01256                 if ( forwards )
01257                    endLine = qMin( startLine , endLine );
01258                 else
01259                    endLine = qMax( startLine , endLine );
01260             }
01261             else
01262             {
01263                 endLine += delta;
01264 
01265                 if ( endLine > lastLine )
01266                 {
01267                     hasWrapped = true;
01268                     endLine = lastLine;
01269                 } else if ( endLine < 0 )
01270                 {
01271                     hasWrapped = true;
01272                     endLine = 0;
01273                 }
01274             }
01275 
01276             decoder.begin(&searchStream);
01277             emulation->writeToStream(&decoder, qMin(endLine,line) , qMax(endLine,line) );
01278             decoder.end();
01279 
01280             // line number search below assumes that the buffer ends with a new-line 
01281             string.append('\n');
01282 
01283             pos = -1;
01284             if (forwards)
01285                 pos = string.indexOf(_regExp);
01286             else
01287                 pos = string.lastIndexOf(_regExp);
01288 
01289             //if a match is found, position the cursor on that line and update the screen
01290             if ( pos != -1 )
01291             {
01292                 int newLines = 0;
01293                 QList<int> linePositions = decoder.linePositions();
01294                 while (newLines < linePositions.count() && linePositions[newLines] <= pos)
01295                     newLines++;
01296 
01297                 // ignore the new line at the start of the buffer
01298                 newLines--;
01299 
01300                 int findPos = qMin(line,endLine) + newLines;
01301 
01302                 highlightResult(window,findPos);
01303 
01304                 emit completed(true);
01305 
01306                 return;
01307             }
01308 
01309             //clear the current block of text and move to the next one
01310             string.clear();
01311             line = endLine;
01312 
01313         } while ( startLine != endLine );
01314 
01315         // if no match was found, clear selection to indicate this
01316         window->clearSelection();
01317         window->notifyOutputChanged();
01318     }
01319 
01320     emit completed(false);
01321 }
01322 void SearchHistoryTask::highlightResult(ScreenWindowPtr window , int findPos)
01323 {
01324      //work out how many lines into the current block of text the search result was found
01325      //- looks a little painful, but it only has to be done once per search.
01326 
01327      kDebug(1211) << "Found result at line " << findPos;
01328 
01329      //update display to show area of history containing selection
01330      window->scrollTo(findPos);
01331      window->setSelectionStart( 0 , findPos - window->currentLine() , false );
01332      window->setSelectionEnd( window->columnCount() , findPos - window->currentLine() );
01333      window->setTrackOutput(false);
01334      window->notifyOutputChanged();
01335 }
01336 
01337 SearchHistoryTask::SearchHistoryTask(QObject* parent)
01338     : SessionTask(parent)
01339     , _direction(ForwardsSearch)
01340 {
01341 
01342 }
01343 void SearchHistoryTask::setSearchDirection( SearchDirection direction )
01344 {
01345     _direction = direction;
01346 }
01347 SearchHistoryTask::SearchDirection SearchHistoryTask::searchDirection() const
01348 {
01349     return _direction;
01350 }
01351 void SearchHistoryTask::setRegExp(const QRegExp& expression)
01352 {
01353     _regExp = expression;
01354 }
01355 QRegExp SearchHistoryTask::regExp() const
01356 {
01357     return _regExp;
01358 }
01359 
01360 #include "SessionController.moc"
01361 
01362 /*
01363   Local Variables:
01364   mode: c++
01365   c-file-style: "stroustrup"
01366   indent-tabs-mode: nil
01367   tab-width: 4
01368   End:
01369 */

Konsole

Skip menu "Konsole"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • Konsole
  • Libraries
  •   libkonq
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal