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 "ProcessInfo.h"
00054 #include "ProfileList.h"
00055 #include "TerminalDisplay.h"
00056 #include "SessionManager.h"
00057
00058
00059 #include <KFileDialog>
00060 #include <KIO/Job>
00061 #include <KJob>
00062 #include <KMessageBox>
00063 #include "TerminalCharacterDecoder.h"
00064
00065
00066 using namespace Konsole;
00067
00068 KIcon SessionController::_activityIcon;
00069 KIcon SessionController::_silenceIcon;
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 Q_ASSERT( session );
00093 Q_ASSERT( view );
00094
00095
00096
00097 #ifdef KONSOLE_PART
00098 setXMLFile("konsole/partui.rc");
00099 #else
00100 setXMLFile("konsole/sessionui.rc");
00101 #endif
00102
00103 setupActions();
00104 actionCollection()->addAssociatedWidget(view);
00105 foreach (QAction* action, actionCollection()->actions())
00106 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00107
00108 setIdentifier(++_lastControllerId);
00109 sessionTitleChanged();
00110
00111 view->installEventFilter(this);
00112
00113
00114 connect( _session , SIGNAL(resizeRequest(const QSize&)) , this ,
00115 SLOT(sessionResizeRequest(const QSize&)) );
00116
00117
00118 connect( _view , SIGNAL(configureRequest(TerminalDisplay*,int,const QPoint&)) , this,
00119 SLOT(showDisplayContextMenu(TerminalDisplay*,int,const QPoint&)) );
00120
00121
00122 connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , this ,
00123 SLOT(trackOutput(QKeyEvent*)) );
00124
00125
00126 connect( _session , SIGNAL(stateChanged(int)) , this ,
00127 SLOT(sessionStateChanged(int) ));
00128
00129 connect( _session , SIGNAL(titleChanged()) , this , SLOT(sessionTitleChanged()) );
00130
00131
00132 connect( _session , SIGNAL(changeBackgroundColorRequest(QColor)) , _view , SLOT(setBackgroundColor(QColor)) );
00133 connect( _session , SIGNAL(changeForegroundColorRequest(QColor)) , _view , SLOT(setForegroundColor(QColor)) );
00134
00135
00136 connect( _session , SIGNAL(started()) , this , SLOT(snapshot()) );
00137
00138
00139 connect( _session->emulation() , SIGNAL(outputChanged()) , this ,
00140 SLOT(fireActivity()) );
00141
00142
00143 connect( _session , SIGNAL(flowControlEnabledChanged(bool)) , _view ,
00144 SLOT(setFlowControlWarningEnabled(bool)) );
00145 _view->setFlowControlWarningEnabled(_session->flowControlEnabled());
00146
00147
00148
00149
00150
00151
00152 QTimer* activityTimer = new QTimer(_session);
00153 activityTimer->setSingleShot(true);
00154 activityTimer->setInterval(2000);
00155 connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , activityTimer , SLOT(start()) );
00156 connect( activityTimer , SIGNAL(timeout()) , this , SLOT(snapshot()) );
00157 }
00158
00159 void SessionController::updateSearchFilter()
00160 {
00161 if ( _searchFilter )
00162 {
00163 Q_ASSERT( searchBar() && searchBar()->isVisible() );
00164
00165 _view->processFilters();
00166 }
00167 }
00168
00169 SessionController::~SessionController()
00170 {
00171 if ( _view )
00172 _view->setScreenWindow(0);
00173 }
00174 void SessionController::trackOutput(QKeyEvent* event)
00175 {
00176 Q_ASSERT( _view->screenWindow() );
00177
00178
00179
00180
00181 switch (event->key())
00182 {
00183 case Qt::Key_Shift:
00184 case Qt::Key_Control:
00185 case Qt::Key_Alt:
00186 break;
00187 default:
00188 _view->screenWindow()->setTrackOutput(true);
00189 }
00190 }
00191 void SessionController::requireUrlFilterUpdate()
00192 {
00193
00194
00195
00196 _urlFilterUpdateRequired = true;
00197 }
00198 void SessionController::snapshot()
00199 {
00200 Q_ASSERT( _session != 0 );
00201
00202 ProcessInfo* process = 0;
00203 ProcessInfo* snapshot = ProcessInfo::newInstance(_session->processId());
00204 snapshot->update();
00205
00206
00207
00208 int pid = _session->foregroundProcessId();
00209 if ( pid != 0 )
00210 {
00211 process = ProcessInfo::newInstance(pid);
00212 process->update();
00213 }
00214 else
00215 process = snapshot;
00216
00217 bool ok = false;
00218
00219
00220 QString title;
00221 if ( process->name(&ok) == "ssh" && ok )
00222 {
00223 SSHProcessInfo sshInfo(*process);
00224 title = sshInfo.format(_session->tabTitleFormat(Session::RemoteTabTitle));
00225 }
00226 else
00227 title = process->format(_session->tabTitleFormat(Session::LocalTabTitle) ) ;
00228
00229
00230 if ( snapshot != process )
00231 {
00232 delete snapshot;
00233 delete process;
00234 }
00235 else
00236 delete snapshot;
00237
00238 title = title.simplified();
00239
00240
00241 if (_copyToGroup && _copyToGroup->sessions().count() > 1)
00242 title.append('*');
00243
00244
00245 if ( !title.isEmpty() )
00246 _session->setTitle(Session::DisplayedTitleRole,title);
00247 else
00248 _session->setTitle(Session::DisplayedTitleRole,_session->title(Session::NameRole));
00249 }
00250
00251 QString SessionController::currentDir() const
00252 {
00253 ProcessInfo* info = ProcessInfo::newInstance(_session->processId());
00254 info->update();
00255
00256 bool ok = false;
00257 QString path = info->currentDir(&ok);
00258
00259 delete info;
00260
00261 if ( ok )
00262 return path;
00263 else
00264 return QString();
00265 }
00266
00267 KUrl SessionController::url() const
00268 {
00269 ProcessInfo* info = ProcessInfo::newInstance(_session->processId());
00270 info->update();
00271
00272 QString path;
00273 if ( info->isValid() )
00274 {
00275 bool ok = false;
00276
00277
00278 int pid = _session->foregroundProcessId();
00279 if ( pid != 0 )
00280 {
00281 ProcessInfo* foregroundInfo = ProcessInfo::newInstance(pid);
00282 foregroundInfo->update();
00283
00284
00285
00286 if ( foregroundInfo->name(&ok) == "ssh" && ok )
00287 {
00288 SSHProcessInfo sshInfo(*foregroundInfo);
00289 path = "ssh://" + sshInfo.userName() + '@' + sshInfo.host();
00290 }
00291 else
00292 {
00293 path = foregroundInfo->currentDir(&ok);
00294
00295 if (!ok)
00296 path.clear();
00297 }
00298
00299 delete foregroundInfo;
00300 }
00301 else
00302 {
00303 path = info->currentDir(&ok);
00304 if (!ok)
00305 path.clear();
00306 }
00307 }
00308
00309 delete info;
00310 return KUrl( path );
00311 }
00312
00313 void SessionController::rename()
00314 {
00315 renameSession();
00316 }
00317
00318 void SessionController::openUrl( const KUrl& url )
00319 {
00320
00321 if ( url.isLocalFile() )
00322 {
00323 QString path = url.toLocalFile();
00324 _session->emulation()->sendText("cd " + KShell::quoteArg(path) + '\r');
00325 }
00326 else if ( url.protocol() == "ssh" )
00327 {
00328 _session->emulation()->sendText("ssh ");
00329
00330 if ( url.hasUser() )
00331 _session->emulation()->sendText(url.user() + '@');
00332 if ( url.hasHost() )
00333 _session->emulation()->sendText(url.host() + '\r');
00334 }
00335 else
00336 {
00337
00338 kWarning(1211) << "Unable to open bookmark at url" << url << ", I do not know"
00339 << " how to handle the protocol " << url.protocol();
00340 }
00341 }
00342
00343 bool SessionController::eventFilter(QObject* watched , QEvent* event)
00344 {
00345 if ( watched == _view )
00346 {
00347 if ( event->type() == QEvent::FocusIn )
00348 {
00349
00350
00351 emit focused(this);
00352
00353
00354
00355
00356
00357 disconnect( _session , SIGNAL(bellRequest(const QString&)) , 0 , 0 );
00358
00359 connect( _session , SIGNAL(bellRequest(const QString&)) ,
00360 _view , SLOT(bell(const QString&)) );
00361 }
00362
00363
00364
00365
00366
00367
00368 if ( event->type() == QEvent::MouseMove &&
00369 (!_viewUrlFilter || _urlFilterUpdateRequired) &&
00370 ((QMouseEvent*)event)->buttons() == Qt::NoButton )
00371 {
00372 if ( _view->screenWindow() && !_viewUrlFilter )
00373 {
00374 connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this ,
00375 SLOT(requireUrlFilterUpdate()) );
00376 connect( _view->screenWindow() , SIGNAL(outputChanged()) , this ,
00377 SLOT(requireUrlFilterUpdate()) );
00378
00379
00380 _viewUrlFilter = new UrlFilter();
00381 _view->filterChain()->addFilter( _viewUrlFilter );
00382 }
00383
00384 _view->processFilters();
00385 _urlFilterUpdateRequired = false;
00386 }
00387 }
00388
00389 return false;
00390 }
00391
00392 void SessionController::removeSearchFilter()
00393 {
00394 if (!_searchFilter)
00395 return;
00396
00397 _view->filterChain()->removeFilter(_searchFilter);
00398 delete _searchFilter;
00399 _searchFilter = 0;
00400 }
00401
00402 void SessionController::setSearchBar(IncrementalSearchBar* searchBar)
00403 {
00404
00405 if ( _searchBar )
00406 {
00407 disconnect( this , 0 , _searchBar , 0 );
00408 disconnect( _searchBar , 0 , this , 0 );
00409 }
00410
00411
00412 removeSearchFilter();
00413
00414
00415 _searchBar = searchBar;
00416 if ( _searchBar )
00417 {
00418 connect( _searchBar , SIGNAL(closeClicked()) , this , SLOT(searchClosed()) );
00419 connect( _searchBar , SIGNAL(findNextClicked()) , this , SLOT(findNextInHistory()) );
00420 connect( _searchBar , SIGNAL(findPreviousClicked()) , this , SLOT(findPreviousInHistory()) );
00421 connect( _searchBar , SIGNAL(highlightMatchesToggled(bool)) , this , SLOT(highlightMatches(bool)) );
00422
00423
00424
00425 searchHistory( _searchToggleAction->isChecked() );
00426 }
00427 }
00428 IncrementalSearchBar* SessionController::searchBar() const
00429 {
00430 return _searchBar;
00431 }
00432
00433 void SessionController::setShowMenuAction(QAction* action)
00434 {
00435 actionCollection()->addAction("show-menubar",action);
00436 }
00437
00438 void SessionController::setupActions()
00439 {
00440 KAction* action = 0;
00441 KToggleAction* toggleAction = 0;
00442 KActionCollection* collection = actionCollection();
00443
00444
00445 action = collection->addAction("close-session");
00446 action->setIcon( KIcon("tab-close") );
00447 action->setText( i18n("&Close Tab") );
00448 action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_W) );
00449 connect( action , SIGNAL(triggered()) , this , SLOT(closeSession()) );
00450
00451
00452 action = collection->addAction("open-browser");
00453 action->setText( i18n("Open Browser Here") );
00454 action->setIcon( KIcon("system-file-manager") );
00455 connect( action, SIGNAL(triggered()), this, SLOT(openBrowser()) );
00456
00457
00458 action = collection->addAction("copy");
00459 action->setIcon( KIcon("edit-copy") );
00460 action->setText( i18n("&Copy") );
00461 action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_C) );
00462 connect( action , SIGNAL(triggered()) , this , SLOT(copy()) );
00463
00464 KAction* pasteAction = new KAction( i18n("&Paste") , this );
00465 pasteAction->setIcon( KIcon("edit-paste") );
00466
00467 KShortcut pasteShortcut = pasteAction->shortcut();
00468 pasteShortcut.setPrimary( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_V) );
00469 pasteShortcut.setAlternate( QKeySequence(Qt::SHIFT+Qt::Key_Insert) );
00470 pasteAction->setShortcut(pasteShortcut);
00471
00472 collection->addAction("paste",pasteAction);
00473
00474 connect( pasteAction , SIGNAL(triggered()) , this , SLOT(paste()) );
00475
00476 action = collection->addAction("paste-selection");
00477 action->setText( i18n("Paste Selection") );
00478 action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Insert) );
00479 connect( action , SIGNAL(triggered()) , this , SLOT(pasteSelection()) );
00480
00481
00482 action = collection->addAction("rename-session");
00483 action->setText( i18n("&Rename Tab...") );
00484 action->setShortcut( QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_S) );
00485 connect( action , SIGNAL(triggered()) , this , SLOT(renameSession()) );
00486
00487
00488 action = collection->addAction("copy-input-to");
00489 action->setText(i18n("Copy Input To..."));
00490 connect( action , SIGNAL(triggered()) , this , SLOT(copyInputTo()) );
00491
00492
00493 action = collection->addAction("clear");
00494 action->setText( i18n("C&lear Display") );
00495 action->setIcon( KIcon("edit-clear") );
00496 connect( action , SIGNAL(triggered()) , this , SLOT(clear()) );
00497
00498 action = collection->addAction("clear-and-reset");
00499 action->setText( i18n("Clear && Reset") );
00500 action->setIcon( KIcon("edit-clear-history") );
00501 connect( action , SIGNAL(triggered()) , this , SLOT(clearAndReset()) );
00502
00503
00504 toggleAction = new KToggleAction(i18n("Monitor for &Activity"),this);
00505 toggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_A) );
00506 action = collection->addAction("monitor-activity",toggleAction);
00507 connect( action , SIGNAL(toggled(bool)) , this , SLOT(monitorActivity(bool)) );
00508
00509 toggleAction = new KToggleAction(i18n("Monitor for &Silence"),this);
00510 toggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_I) );
00511 action = collection->addAction("monitor-silence",toggleAction);
00512 connect( action , SIGNAL(toggled(bool)) , this , SLOT(monitorSilence(bool)) );
00513
00514
00515 _codecAction = new KCodecAction(i18n("Character Encoding"),this);
00516 collection->addAction("character-encoding",_codecAction);
00517 connect( _codecAction->menu() , SIGNAL(aboutToShow()) , this , SLOT(updateCodecAction()) );
00518 connect( _codecAction , SIGNAL(triggered(QTextCodec*)) , this , SLOT(changeCodec(QTextCodec*)) );
00519
00520
00521 action = collection->addAction("increase-text-size");
00522 action->setText( i18n("Increase Text Size") );
00523 action->setIcon( KIcon("zoom-in") );
00524 action->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_Plus) );
00525 connect( action , SIGNAL(triggered()) , this , SLOT(increaseTextSize()) );
00526
00527 action = collection->addAction("decrease-text-size");
00528 action->setText( i18n("Decrease Text Size") );
00529 action->setIcon( KIcon("zoom-out") );
00530 action->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_Minus) );
00531 connect( action , SIGNAL(triggered()) , this , SLOT(decreaseTextSize()) );
00532
00533
00534 _searchToggleAction = new KAction(i18n("Search Output..."),this);
00535 _searchToggleAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_F) );
00536 _searchToggleAction->setIcon( KIcon("edit-find") );
00537 _searchToggleAction->setCheckable(true);
00538 action = collection->addAction("search-history" , _searchToggleAction);
00539 connect( action , SIGNAL(toggled(bool)) , this , SLOT(searchHistory(bool)) );
00540
00541 _findNextAction = collection->addAction("find-next");
00542 _findNextAction->setIcon( KIcon("go-down-search") );
00543 _findNextAction->setText( i18n("Find Next") );
00544 _findNextAction->setShortcut( QKeySequence(Qt::Key_F3) );
00545 _findNextAction->setEnabled(false);
00546 connect( _findNextAction , SIGNAL(triggered()) , this , SLOT(findNextInHistory()) );
00547
00548 _findPreviousAction = collection->addAction("find-previous");
00549 _findPreviousAction->setIcon( KIcon("go-up-search") );
00550 _findPreviousAction->setText( i18n("Find Previous") );
00551 _findPreviousAction->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_F3) );
00552 _findPreviousAction->setEnabled(false);
00553 connect( _findPreviousAction , SIGNAL(triggered()) , this , SLOT(findPreviousInHistory()) );
00554
00555 action = collection->addAction("save-history");
00556 action->setText( i18n("Save Output...") );
00557 action->setIcon( KIcon("document-save-as") );
00558 connect( action , SIGNAL(triggered()) , this , SLOT(saveHistory()) );
00559
00560 action = collection->addAction("history-options");
00561 action->setText( i18n("Scrollback Options") );
00562 action->setIcon( KIcon("configure") );
00563 connect( action , SIGNAL(triggered()) , this , SLOT(showHistoryOptions()) );
00564
00565 action = collection->addAction("clear-history");
00566 action->setText( i18n("Clear Scrollback") );
00567 connect( action , SIGNAL(triggered()) , this , SLOT(clearHistory()) );
00568
00569 action = collection->addAction("clear-history-and-reset");
00570 action->setText( i18n("Clear Scrollback && Reset") );
00571 action->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_X) );
00572 connect( action , SIGNAL(triggered()) , this , SLOT(clearHistoryAndReset()) );
00573
00574
00575 action = collection->addAction("edit-current-profile");
00576 action->setText( i18n("Edit Current Profile...") );
00577 action->setIcon( KIcon("document-properties") );
00578 connect( action , SIGNAL(triggered()) , this , SLOT(editCurrentProfile()) );
00579
00580 _changeProfileMenu = new KMenu(i18n("Change Profile"),_view);
00581 collection->addAction("change-profile",_changeProfileMenu->menuAction());
00582 connect( _changeProfileMenu , SIGNAL(aboutToShow()) , this , SLOT(prepareChangeProfileMenu()) );
00583
00584
00585
00586
00587
00588 }
00589 void SessionController::changeProfile(Profile::Ptr profile)
00590 {
00591 SessionManager::instance()->setSessionProfile(_session,profile);
00592 }
00593 void SessionController::prepareChangeProfileMenu()
00594 {
00595 if ( _changeProfileMenu->isEmpty() )
00596 {
00597 _profileList = new ProfileList(false,this);
00598 connect( _profileList , SIGNAL(profileSelected(Profile::Ptr)) ,
00599 this , SLOT(changeProfile(Profile::Ptr)) );
00600 }
00601
00602 _changeProfileMenu->clear();
00603 _changeProfileMenu->addActions(_profileList->actions());
00604 }
00605 void SessionController::updateCodecAction()
00606 {
00607 _codecAction->setCurrentCodec( QString(_session->emulation()->codec()->name()) );
00608 }
00609 void SessionController::changeCodec(QTextCodec* codec)
00610 {
00611 _session->setCodec(codec);
00612 }
00613 void SessionController::debugProcess()
00614 {
00615
00616
00617 ProcessInfo* sessionProcess = ProcessInfo::newInstance(_session->processId());
00618 sessionProcess->update();
00619
00620 bool ok = false;
00621 int fpid = sessionProcess->foregroundPid(&ok);
00622
00623 if ( ok )
00624 {
00625 ProcessInfo* fp = ProcessInfo::newInstance(fpid);
00626 fp->update();
00627
00628 QString name = fp->name(&ok);
00629
00630 if ( ok )
00631 {
00632 _session->setTitle(Session::DisplayedTitleRole,name);
00633 sessionTitleChanged();
00634 }
00635
00636 QString currentDir = fp->currentDir(&ok);
00637
00638 if ( ok )
00639 kDebug(1211) << currentDir;
00640 else
00641 kDebug(1211) << "could not read current dir of foreground process";
00642
00643 delete fp;
00644 }
00645 delete sessionProcess;
00646 }
00647
00648 void SessionController::editCurrentProfile()
00649 {
00650 EditProfileDialog* dialog = new EditProfileDialog( QApplication::activeWindow() );
00651
00652 dialog->setProfile(SessionManager::instance()->sessionProfile(_session));
00653 dialog->show();
00654 }
00655 void SessionController::renameSession()
00656 {
00657 QPointer<Session> guard(_session);
00658 bool ok = false;
00659 const QString& text = KInputDialog::getText( i18n("Rename Tab") ,
00660 i18n("Enter new tab text:") ,
00661 _session->tabTitleFormat(Session::LocalTabTitle) ,
00662 &ok, QApplication::activeWindow() );
00663 if (!guard)
00664 return;
00665
00666 if ( ok )
00667 {
00668
00669
00670
00671
00672
00673
00674 _session->setTabTitleFormat(Session::LocalTabTitle,text);
00675 _session->setTabTitleFormat(Session::RemoteTabTitle,text);
00676
00677
00678 snapshot();
00679 }
00680 }
00681 void SessionController::saveSession()
00682 {
00683 Q_ASSERT(0);
00684
00685
00686
00687 }
00688 bool SessionController::confirmClose() const
00689 {
00690 if (_session->foregroundProcessId() != _session->processId())
00691 {
00692 ProcessInfo* foregroundInfo = ProcessInfo::newInstance(_session->foregroundProcessId());
00693 foregroundInfo->update();
00694 bool ok = false;
00695 QString title = foregroundInfo->name(&ok);
00696 delete foregroundInfo;
00697
00698
00699
00700 QStringList ignoreList;
00701 ignoreList << QString(getenv("SHELL")).section('/',-1);
00702 if (ignoreList.contains(title))
00703 return true;
00704
00705 QString question;
00706 if (ok)
00707 question = i18n("The program '%1' is currently running in this session."
00708 " Are you sure you want to close it?",title);
00709 else
00710 question = i18n("A program is currently running in this session."
00711 " Are you sure you want to close it?");
00712
00713 int result = KMessageBox::warningYesNo(_view->window(),question,i18n("Confirm Close"));
00714 return (result == KMessageBox::Yes) ? true : false;
00715 }
00716 return true;
00717 }
00718 void SessionController::closeSession()
00719 {
00720 if (_preventClose)
00721 return;
00722
00723 _session->close();
00724 }
00725
00726 void SessionController::openBrowser()
00727 {
00728 new KRun(url(), QApplication::activeWindow());
00729 }
00730
00731 void SessionController::copy()
00732 {
00733 _view->copyClipboard();
00734 }
00735
00736 void SessionController::paste()
00737 {
00738 _view->pasteClipboard();
00739 }
00740 void SessionController::pasteSelection()
00741 {
00742 _view->pasteSelection();
00743 }
00744 void SessionController::copyInputTo()
00745 {
00746 if (!_copyToGroup)
00747 {
00748 _copyToGroup = new SessionGroup(this);
00749 _copyToGroup->addSession(_session);
00750 _copyToGroup->setMasterStatus(_session,true);
00751 _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll);
00752 }
00753
00754 CopyInputDialog* dialog = new CopyInputDialog(_view);
00755 dialog->setMasterSession(_session);
00756
00757 QSet<Session*> currentGroup = QSet<Session*>::fromList(_copyToGroup->sessions());
00758 currentGroup.remove(_session);
00759
00760 dialog->setChosenSessions(currentGroup);
00761
00762 QPointer<Session> guard(_session);
00763 int result = dialog->exec();
00764 if (!guard)
00765 return;
00766
00767 if (result)
00768 {
00769 QSet<Session*> newGroup = dialog->chosenSessions();
00770 newGroup.remove(_session);
00771
00772 QSet<Session*> completeGroup = newGroup | currentGroup;
00773 foreach(Session* session, completeGroup)
00774 {
00775 if (newGroup.contains(session) && !currentGroup.contains(session))
00776 _copyToGroup->addSession(session);
00777 else if (!newGroup.contains(session) && currentGroup.contains(session))
00778 _copyToGroup->removeSession(session);
00779 }
00780
00781 snapshot();
00782 }
00783
00784 delete dialog;
00785 }
00786 void SessionController::clear()
00787 {
00788 Emulation* emulation = _session->emulation();
00789
00790 emulation->clearEntireScreen();
00791 }
00792 void SessionController::clearAndReset()
00793 {
00794 Emulation* emulation = _session->emulation();
00795
00796 emulation->reset();
00797 _session->refresh();
00798 }
00799 void SessionController::searchClosed()
00800 {
00801 _searchToggleAction->toggle();
00802 }
00803
00804 #if 0
00805 void SessionController::searchHistory()
00806 {
00807 searchHistory(true);
00808 }
00809 #endif
00810
00811 void SessionController::listenForScreenWindowUpdates()
00812 {
00813 if (_listenForScreenWindowUpdates)
00814 return;
00815
00816 connect( _view->screenWindow() , SIGNAL(outputChanged()) , this ,
00817 SLOT(updateSearchFilter()) );
00818 connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this ,
00819 SLOT(updateSearchFilter()) );
00820
00821 _listenForScreenWindowUpdates = true;
00822 }
00823
00824
00825
00826 void SessionController::searchHistory(bool showSearchBar)
00827 {
00828 if ( _searchBar )
00829 {
00830 _searchBar->setVisible(showSearchBar);
00831
00832 if (showSearchBar)
00833 {
00834 removeSearchFilter();
00835
00836 listenForScreenWindowUpdates();
00837
00838 _searchFilter = new RegExpFilter();
00839 _view->filterChain()->addFilter(_searchFilter);
00840 connect( _searchBar , SIGNAL(searchChanged(const QString&)) , this ,
00841 SLOT(searchTextChanged(const QString&)) );
00842
00843
00844 const QString& currentSearchText = _searchBar->searchText();
00845 if (!currentSearchText.isEmpty())
00846 {
00847 searchTextChanged(currentSearchText);
00848 }
00849
00850 setFindNextPrevEnabled(true);
00851 }
00852 else
00853 {
00854 setFindNextPrevEnabled(false);
00855
00856 disconnect( _searchBar , SIGNAL(searchChanged(const QString&)) , this ,
00857 SLOT(searchTextChanged(const QString&)) );
00858
00859 removeSearchFilter();
00860
00861 _view->setFocus( Qt::ActiveWindowFocusReason );
00862 }
00863 }
00864 }
00865 void SessionController::setFindNextPrevEnabled(bool enabled)
00866 {
00867 _findNextAction->setEnabled(enabled);
00868 _findPreviousAction->setEnabled(enabled);
00869 }
00870 void SessionController::searchTextChanged(const QString& text)
00871 {
00872 Q_ASSERT( _view->screenWindow() );
00873
00874 if ( text.isEmpty() )
00875 _view->screenWindow()->clearSelection();
00876
00877
00878
00879 beginSearch(text , SearchHistoryTask::ForwardsSearch);
00880 }
00881 void SessionController::searchCompleted(bool success)
00882 {
00883 if ( _searchBar )
00884 _searchBar->setFoundMatch(success);
00885 }
00886
00887 void SessionController::beginSearch(const QString& text , int direction)
00888 {
00889 Q_ASSERT( _searchBar );
00890 Q_ASSERT( _searchFilter );
00891
00892 Qt::CaseSensitivity caseHandling = _searchBar->matchCase() ? Qt::CaseSensitive : Qt::CaseInsensitive;
00893 QRegExp::PatternSyntax syntax = _searchBar->matchRegExp() ? QRegExp::RegExp : QRegExp::FixedString;
00894
00895 QRegExp regExp( text.trimmed() , caseHandling , syntax );
00896 _searchFilter->setRegExp(regExp);
00897
00898 if ( !regExp.isEmpty() )
00899 {
00900 SearchHistoryTask* task = new SearchHistoryTask(this);
00901
00902 connect( task , SIGNAL(completed(bool)) , this , SLOT(searchCompleted(bool)) );
00903
00904 task->setRegExp(regExp);
00905 task->setSearchDirection( (SearchHistoryTask::SearchDirection)direction );
00906 task->setAutoDelete(true);
00907 task->addScreenWindow( _session , _view->screenWindow() );
00908 task->execute();
00909 }
00910
00911 _view->processFilters();
00912 }
00913 void SessionController::highlightMatches(bool highlight)
00914 {
00915 if ( highlight )
00916 {
00917 _view->filterChain()->addFilter(_searchFilter);
00918 _view->processFilters();
00919 }
00920 else
00921 {
00922 _view->filterChain()->removeFilter(_searchFilter);
00923 }
00924
00925 _view->update();
00926 }
00927 void SessionController::findNextInHistory()
00928 {
00929 Q_ASSERT( _searchBar );
00930 Q_ASSERT( _searchFilter );
00931
00932 beginSearch(_searchBar->searchText(),SearchHistoryTask::ForwardsSearch);
00933 }
00934 void SessionController::findPreviousInHistory()
00935 {
00936 Q_ASSERT( _searchBar );
00937 Q_ASSERT( _searchFilter );
00938
00939 beginSearch(_searchBar->searchText(),SearchHistoryTask::BackwardsSearch);
00940 }
00941 void SessionController::showHistoryOptions()
00942 {
00943 HistorySizeDialog* dialog = new HistorySizeDialog( QApplication::activeWindow() );
00944 const HistoryType& currentHistory = _session->historyType();
00945
00946 if ( currentHistory.isEnabled() )
00947 {
00948 if ( currentHistory.isUnlimited() )
00949 dialog->setMode( HistorySizeDialog::UnlimitedHistory );
00950 else
00951 {
00952 dialog->setMode( HistorySizeDialog::FixedSizeHistory );
00953 dialog->setLineCount( currentHistory.maximumLineCount() );
00954 }
00955 }
00956 else
00957 dialog->setMode( HistorySizeDialog::NoHistory );
00958
00959 connect( dialog , SIGNAL(optionsChanged(int,int)) ,
00960 this , SLOT(scrollBackOptionsChanged(int,int)) );
00961
00962 dialog->show();
00963 }
00964 void SessionController::sessionResizeRequest(const QSize& size)
00965 {
00966 kDebug(1211) << "View resize requested to " << size;
00967 _view->setSize(size.width(),size.height());
00968 }
00969 void SessionController::scrollBackOptionsChanged( int mode , int lines )
00970 {
00971 switch (mode)
00972 {
00973 case HistorySizeDialog::NoHistory:
00974 _session->setHistoryType( HistoryTypeNone() );
00975 break;
00976 case HistorySizeDialog::FixedSizeHistory:
00977 _session->setHistoryType( HistoryTypeBuffer(lines) );
00978 break;
00979 case HistorySizeDialog::UnlimitedHistory:
00980 _session->setHistoryType( HistoryTypeFile() );
00981 break;
00982 }
00983 }
00984
00985 void SessionController::saveHistory()
00986 {
00987 SessionTask* task = new SaveHistoryTask(this);
00988 task->setAutoDelete(true);
00989 task->addSession( _session );
00990 task->execute();
00991 }
00992 void SessionController::clearHistory()
00993 {
00994 _session->clearHistory();
00995 }
00996 void SessionController::clearHistoryAndReset()
00997 {
00998 clearAndReset();
00999 clearHistory();
01000 }
01001 void SessionController::increaseTextSize()
01002 {
01003 QFont font = _view->getVTFont();
01004 font.setPointSize(font.pointSize()+1);
01005 _view->setVTFont(font);
01006
01007
01008 }
01009 void SessionController::decreaseTextSize()
01010 {
01011 static const int MinimumFontSize = 6;
01012
01013 QFont font = _view->getVTFont();
01014 font.setPointSize( qMax(font.pointSize()-1,MinimumFontSize) );
01015 _view->setVTFont(font);
01016
01017
01018 }
01019 void SessionController::monitorActivity(bool monitor)
01020 {
01021 _session->setMonitorActivity(monitor);
01022 }
01023 void SessionController::monitorSilence(bool monitor)
01024 {
01025 _session->setMonitorSilence(monitor);
01026 }
01027 void SessionController::sessionTitleChanged()
01028 {
01029 if ( _sessionIconName != _session->iconName() )
01030 {
01031 _sessionIconName = _session->iconName();
01032 _sessionIcon = KIcon( _sessionIconName );
01033 setIcon( _sessionIcon );
01034 }
01035
01036 QString title = _session->title(Session::DisplayedTitleRole);
01037
01038
01039
01040 title.replace("%w",_session->userTitle());
01041
01042
01043 title.replace("%#",QString::number(_session->sessionId()));
01044
01045 if ( title.isEmpty() )
01046 title = _session->title(Session::NameRole);
01047
01048 setTitle( title );
01049 }
01050
01051 void SessionController::showDisplayContextMenu(TerminalDisplay* , int , const QPoint& position)
01052 {
01053 QMenu* popup = 0;
01054
01055
01056
01057 if (!factory()) {
01058 if (!clientBuilder()) {
01059 setClientBuilder(new KXMLGUIBuilder(_view));
01060 }
01061 KXMLGUIFactory* f = new KXMLGUIFactory(clientBuilder(), this);
01062 f->addClient(this);
01063 }
01064
01065 if ( factory() )
01066 popup = qobject_cast<QMenu*>(factory()->container("session-popup-menu",this));
01067
01068 if (popup)
01069 {
01070
01071 QList<QAction*> contentActions = _view->filterActions(position);
01072 QAction* contentSeparator = new QAction(popup);
01073 contentSeparator->setSeparator(true);
01074 contentActions << contentSeparator;
01075
01076 _preventClose = true;
01077
01078 popup->insertActions(popup->actions().value(0,0),contentActions);
01079 QAction* chosen = popup->exec( _view->mapToGlobal(position) );
01080
01081
01082
01083 foreach(QAction* action,contentActions)
01084 popup->removeAction(action);
01085 delete contentSeparator;
01086
01087 _preventClose = false;
01088
01089 if (chosen && chosen->objectName() == "close-session")
01090 chosen->trigger();
01091 }
01092 else
01093 {
01094 kWarning(1211) << "Unable to display popup menu for session"
01095 << _session->title(Session::NameRole)
01096 << ", no GUI factory available to build the popup.";
01097 }
01098 }
01099
01100 void SessionController::sessionStateChanged(int state)
01101 {
01102 if ( state == _previousState )
01103 return;
01104
01105 _previousState = state;
01106
01107
01108
01109 if ( state == NOTIFYACTIVITY )
01110 {
01111 if (_activityIcon.isNull())
01112 {
01113 _activityIcon = KIcon("dialog-information");
01114 }
01115
01116 setIcon(_activityIcon);
01117 }
01118 else if ( state == NOTIFYSILENCE )
01119 {
01120 if (_silenceIcon.isNull())
01121 {
01122 _silenceIcon = KIcon("dialog-information");
01123 }
01124
01125 setIcon(_silenceIcon);
01126 }
01127 else if ( state == NOTIFYNORMAL )
01128 {
01129 if ( _sessionIconName != _session->iconName() )
01130 {
01131 _sessionIconName = _session->iconName();
01132 _sessionIcon = KIcon( _sessionIconName );
01133 }
01134
01135 setIcon( _sessionIcon );
01136 }
01137 }
01138
01139 SessionTask::SessionTask(QObject* parent)
01140 : QObject(parent)
01141 , _autoDelete(false)
01142 {
01143 }
01144 void SessionTask::setAutoDelete(bool enable)
01145 {
01146 _autoDelete = enable;
01147 }
01148 bool SessionTask::autoDelete() const
01149 {
01150 return _autoDelete;
01151 }
01152 void SessionTask::addSession(Session* session)
01153 {
01154 _sessions << session;
01155 }
01156 QList<SessionPtr> SessionTask::sessions() const
01157 {
01158 return _sessions;
01159 }
01160
01161 SaveHistoryTask::SaveHistoryTask(QObject* parent)
01162 : SessionTask(parent)
01163 {
01164 }
01165 SaveHistoryTask::~SaveHistoryTask()
01166 {
01167 }
01168
01169 void SaveHistoryTask::execute()
01170 {
01171 QListIterator<SessionPtr> iter(sessions());
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182 KFileDialog* dialog = new KFileDialog( QString(":konsole") ,
01183 QString(), QApplication::activeWindow() );
01184 QStringList mimeTypes;
01185 mimeTypes << "text/plain";
01186 mimeTypes << "text/html";
01187 dialog->setMimeFilter(mimeTypes,"text/plain");
01188
01189
01190
01191
01192 while ( iter.hasNext() )
01193 {
01194 SessionPtr session = iter.next();
01195
01196 dialog->setCaption( i18n("Save Output From %1",session->title(Session::NameRole)) );
01197
01198 int result = dialog->exec();
01199
01200 if ( result != QDialog::Accepted )
01201 continue;
01202
01203 KUrl url = dialog->selectedUrl();
01204
01205 if ( !url.isValid() )
01206 {
01207 KMessageBox::sorry( 0 , i18n("%1 is an invalid URL, the output could not be saved.",url.url()) );
01208 continue;
01209 }
01210
01211 KIO::TransferJob* job = KIO::put( url,
01212 -1,
01213
01214
01215
01216
01217 KIO::Overwrite | (url.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags)
01218
01219
01220
01221
01222
01223 );
01224
01225
01226 SaveJob jobInfo;
01227 jobInfo.session = session;
01228 jobInfo.lastLineFetched = -1;
01229
01230
01231
01232
01233
01234 if ( dialog->currentMimeFilter() == "text/html" )
01235 jobInfo.decoder = new HTMLDecoder();
01236 else
01237 jobInfo.decoder = new PlainTextDecoder();
01238
01239 _jobSession.insert(job,jobInfo);
01240
01241 connect( job , SIGNAL(dataReq(KIO::Job*,QByteArray&)),
01242 this, SLOT(jobDataRequested(KIO::Job*,QByteArray&)) );
01243 connect( job , SIGNAL(result(KJob*)),
01244 this, SLOT(jobResult(KJob*)) );
01245 }
01246
01247 dialog->deleteLater();
01248 }
01249 void SaveHistoryTask::jobDataRequested(KIO::Job* job , QByteArray& data)
01250 {
01251
01252
01253
01254 const int LINES_PER_REQUEST = 500;
01255
01256 SaveJob& info = _jobSession[job];
01257
01258
01259
01260 if ( info.session )
01261 {
01262
01263
01264
01265 int sessionLines = info.session->emulation()->lineCount();
01266
01267 if ( sessionLines-1 == info.lastLineFetched )
01268 return;
01269
01270 int copyUpToLine = qMin( info.lastLineFetched + LINES_PER_REQUEST ,
01271 sessionLines-1 );
01272
01273 QTextStream stream(&data,QIODevice::ReadWrite);
01274 info.decoder->begin(&stream);
01275 info.session->emulation()->writeToStream( info.decoder , info.lastLineFetched+1 , copyUpToLine );
01276 info.decoder->end();
01277
01278
01279
01280
01281
01282
01283 if ( copyUpToLine <= sessionLines-1 )
01284 {
01285 stream << '\n';
01286 }
01287
01288
01289 info.lastLineFetched = copyUpToLine;
01290 }
01291 }
01292 void SaveHistoryTask::jobResult(KJob* job)
01293 {
01294 if ( job->error() )
01295 {
01296 KMessageBox::sorry( 0 , i18n("A problem occurred when saving the output.\n%1",job->errorString()) );
01297 }
01298
01299 SaveJob& info = _jobSession[job];
01300
01301 _jobSession.remove(job);
01302
01303 delete info.decoder;
01304
01305
01306 emit completed(true);
01307
01308 if ( autoDelete() )
01309 deleteLater();
01310 }
01311 void SearchHistoryTask::addScreenWindow( Session* session , ScreenWindow* searchWindow )
01312 {
01313 _windows.insert(session,searchWindow);
01314 }
01315 void SearchHistoryTask::execute()
01316 {
01317 QMapIterator< SessionPtr , ScreenWindowPtr > iter(_windows);
01318
01319 while ( iter.hasNext() )
01320 {
01321 iter.next();
01322 executeOnScreenWindow( iter.key() , iter.value() );
01323 }
01324 }
01325
01326 void SearchHistoryTask::executeOnScreenWindow( SessionPtr session , ScreenWindowPtr window )
01327 {
01328 Q_ASSERT( session );
01329 Q_ASSERT( window );
01330
01331 Emulation* emulation = session->emulation();
01332
01333 int selectionColumn = 0;
01334 int selectionLine = 0;
01335
01336 window->getSelectionEnd(selectionColumn , selectionLine);
01337
01338 if ( !_regExp.isEmpty() )
01339 {
01340 int pos = -1;
01341 const bool forwards = ( _direction == ForwardsSearch );
01342 const int startLine = selectionLine + window->currentLine() + ( forwards ? 1 : -1 );
01343 const int lastLine = window->lineCount() - 1;
01344 QString string;
01345
01346
01347 QTextStream searchStream(&string);
01348
01349 PlainTextDecoder decoder;
01350 decoder.setRecordLinePositions(true);
01351
01352
01353 int line = startLine;
01354
01355
01356
01357
01358
01359 const int maxDelta = qMin(window->lineCount(),10000);
01360 int delta = forwards ? maxDelta : -maxDelta;
01361
01362 int endLine = line;
01363 bool hasWrapped = false;
01364
01365
01366
01367
01368 do
01369 {
01370
01371
01372 QApplication::processEvents();
01373
01374
01375 if ( hasWrapped )
01376 {
01377 if ( endLine == lastLine )
01378 line = 0;
01379 else if ( endLine == 0 )
01380 line = lastLine;
01381
01382 endLine += delta;
01383
01384 if ( forwards )
01385 endLine = qMin( startLine , endLine );
01386 else
01387 endLine = qMax( startLine , endLine );
01388 }
01389 else
01390 {
01391 endLine += delta;
01392
01393 if ( endLine > lastLine )
01394 {
01395 hasWrapped = true;
01396 endLine = lastLine;
01397 } else if ( endLine < 0 )
01398 {
01399 hasWrapped = true;
01400 endLine = 0;
01401 }
01402 }
01403
01404 decoder.begin(&searchStream);
01405 emulation->writeToStream(&decoder, qMin(endLine,line) , qMax(endLine,line) );
01406 decoder.end();
01407
01408
01409 string.append('\n');
01410
01411 pos = -1;
01412 if (forwards)
01413 pos = string.indexOf(_regExp);
01414 else
01415 pos = string.lastIndexOf(_regExp);
01416
01417
01418 if ( pos != -1 )
01419 {
01420 int newLines = 0;
01421 QList<int> linePositions = decoder.linePositions();
01422 while (newLines < linePositions.count() && linePositions[newLines] <= pos)
01423 newLines++;
01424
01425
01426 newLines--;
01427
01428 int findPos = qMin(line,endLine) + newLines;
01429
01430 highlightResult(window,findPos);
01431
01432 emit completed(true);
01433
01434 return;
01435 }
01436
01437
01438 string.clear();
01439 line = endLine;
01440
01441 } while ( startLine != endLine );
01442
01443
01444 window->clearSelection();
01445 window->notifyOutputChanged();
01446 }
01447
01448 emit completed(false);
01449 }
01450 void SearchHistoryTask::highlightResult(ScreenWindowPtr window , int findPos)
01451 {
01452
01453
01454
01455 kDebug(1211) << "Found result at line " << findPos;
01456
01457
01458 window->scrollTo(findPos);
01459 window->setSelectionStart( 0 , findPos - window->currentLine() , false );
01460 window->setSelectionEnd( window->columnCount() , findPos - window->currentLine() );
01461 window->setTrackOutput(false);
01462 window->notifyOutputChanged();
01463 }
01464
01465 SearchHistoryTask::SearchHistoryTask(QObject* parent)
01466 : SessionTask(parent)
01467 , _direction(ForwardsSearch)
01468 {
01469
01470 }
01471 void SearchHistoryTask::setSearchDirection( SearchDirection direction )
01472 {
01473 _direction = direction;
01474 }
01475 SearchHistoryTask::SearchDirection SearchHistoryTask::searchDirection() const
01476 {
01477 return _direction;
01478 }
01479 void SearchHistoryTask::setRegExp(const QRegExp& expression)
01480 {
01481 _regExp = expression;
01482 }
01483 QRegExp SearchHistoryTask::regExp() const
01484 {
01485 return _regExp;
01486 }
01487
01488 #include "SessionController.moc"