00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "SessionController.h"
00022
00023
00024 #include <QtGui/QApplication>
00025 #include <QMenu>
00026
00027
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
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
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
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
00116 connect( _session , SIGNAL(resizeRequest(const QSize&)) , this ,
00117 SLOT(sessionResizeRequest(const QSize&)) );
00118
00119
00120 connect( _view , SIGNAL(configureRequest(QPoint)) , this,
00121 SLOT(showDisplayContextMenu(QPoint)) );
00122
00123
00124 connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , this ,
00125 SLOT(trackOutput(QKeyEvent*)) );
00126
00127
00128 connect( _session , SIGNAL(stateChanged(int)) , this ,
00129 SLOT(sessionStateChanged(int) ));
00130
00131 connect( _session , SIGNAL(titleChanged()) , this , SLOT(sessionTitleChanged()) );
00132
00133
00134 connect( _session , SIGNAL(changeBackgroundColorRequest(QColor)) , _view , SLOT(setBackgroundColor(QColor)) );
00135 connect( _session , SIGNAL(changeForegroundColorRequest(QColor)) , _view , SLOT(setForegroundColor(QColor)) );
00136
00137
00138 connect( _session , SIGNAL(started()) , this , SLOT(snapshot()) );
00139
00140
00141 connect( _session->emulation() , SIGNAL(outputChanged()) , this ,
00142 SLOT(fireActivity()) );
00143
00144
00145 connect( _session , SIGNAL(flowControlEnabledChanged(bool)) , _view ,
00146 SLOT(setFlowControlWarningEnabled(bool)) );
00147 _view->setFlowControlWarningEnabled(_session->flowControlEnabled());
00148
00149
00150
00151
00152
00153
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
00183
00184
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
00198
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
00210 if (_copyToGroup && _copyToGroup->sessions().count() > 1)
00211 title.append('*');
00212
00213
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
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
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
00267
00268 emit focused(this);
00269
00270
00271
00272
00273
00274 disconnect( _session , SIGNAL(bellRequest(const QString&)) , 0 , 0 );
00275
00276 connect( _session , SIGNAL(bellRequest(const QString&)) ,
00277 _view , SLOT(bell(const QString&)) );
00278 }
00279
00280
00281
00282
00283
00284
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
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
00322 if ( _searchBar )
00323 {
00324 disconnect( this , 0 , _searchBar , 0 );
00325 disconnect( _searchBar , 0 , this , 0 );
00326 }
00327
00328
00329 removeSearchFilter();
00330
00331
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
00341
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
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
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
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
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
00405 action = collection->addAction("copy-input-to");
00406 action->setText(i18n("Copy Input To..."));
00407 connect( action , SIGNAL(triggered()) , this , SLOT(copyInputTo()) );
00408
00409
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
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
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
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
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
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
00547
00548
00549
00550
00551
00552 _session->setTabTitleFormat(Session::LocalTabTitle,text);
00553 _session->setTabTitleFormat(Session::RemoteTabTitle,text);
00554
00555
00556 snapshot();
00557 }
00558 }
00559 void SessionController::saveSession()
00560 {
00561 Q_ASSERT(0);
00562
00563
00564
00565 }
00566 bool SessionController::confirmClose() const
00567 {
00568 if (_session->isChildActive())
00569 {
00570 QString title = _session->childName();
00571
00572
00573
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
00700
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
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
00753
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
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
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
00914
00915 title.replace("%w",_session->userTitle());
00916
00917
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
00929
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
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
00954
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
00980
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
01046
01047
01048
01049
01050
01051 KFileDialog* dialog = new KFileDialog( QString(":konsole") ,
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
01062
01063
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 {
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,
01085
01086
01087
01088
01089 KIO::Overwrite | (url.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags)
01090
01091
01092
01093
01094
01095 );
01096
01097
01098 SaveJob jobInfo;
01099 jobInfo.session = session;
01100 jobInfo.lastLineFetched = -1;
01101
01102
01103
01104
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
01124
01125
01126 const int LINES_PER_REQUEST = 500;
01127
01128 SaveJob& info = _jobSession[job];
01129
01130
01131
01132 if ( info.session )
01133 {
01134
01135
01136
01137 int sessionLines = info.session->emulation()->lineCount();
01138
01139 if ( sessionLines-1 == info.lastLineFetched )
01140 return;
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
01151
01152
01153
01154
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
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
01219 QTextStream searchStream(&string);
01220
01221 PlainTextDecoder decoder;
01222 decoder.setRecordLinePositions(true);
01223
01224
01225 int line = startLine;
01226
01227
01228
01229
01230
01231 const int maxDelta = qMin(window->lineCount(),10000);
01232 int delta = forwards ? maxDelta : -maxDelta;
01233
01234 int endLine = line;
01235 bool hasWrapped = false;
01236
01237
01238
01239
01240 do
01241 {
01242
01243
01244 QApplication::processEvents();
01245
01246
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
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
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
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
01310 string.clear();
01311 line = endLine;
01312
01313 } while ( startLine != endLine );
01314
01315
01316 window->clearSelection();
01317 window->notifyOutputChanged();
01318 }
01319
01320 emit completed(false);
01321 }
01322 void SearchHistoryTask::highlightResult(ScreenWindowPtr window , int findPos)
01323 {
01324
01325
01326
01327 kDebug(1211) << "Found result at line " << findPos;
01328
01329
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
01364
01365
01366
01367
01368
01369