1/*
2 Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
3 Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301 USA.
19*/
20
21#ifndef SESSIONCONTROLLER_H
22#define SESSIONCONTROLLER_H
23
24// Qt
25#include <QtCore/QList>
26#include <QtCore/QSet>
27#include <QtCore/QPointer>
28#include <QtCore/QString>
29#include <QtCore/QHash>
30
31// KDE
32#include <KIcon>
33#include <KXMLGUIClient>
34
35// Konsole
36#include "ViewProperties.h"
37#include "Profile.h"
38
39namespace KIO
40{
41class Job;
42}
43
44class QAction;
45class QTextCodec;
46class QKeyEvent;
47class QTimer;
48
49class KCodecAction;
50class KUrl;
51class KJob;
52class KAction;
53class KActionMenu;
54
55namespace Konsole
56{
57class Session;
58class SessionGroup;
59class ScreenWindow;
60class TerminalDisplay;
61class IncrementalSearchBar;
62class ProfileList;
63class UrlFilter;
64class RegExpFilter;
65class EditProfileDialog;
66
67// SaveHistoryTask
68class TerminalCharacterDecoder;
69
70typedef QPointer<Session> SessionPtr;
71
72/**
73 * Provides the menu actions to manipulate a single terminal session and view pair.
74 * The actions provided by this class are defined in the sessionui.rc XML file.
75 *
76 * SessionController monitors the session and provides access to basic information
77 * about the session such as title(), icon() and currentDir(). SessionController
78 * provides notifications of activity in the session via the activity() signal.
79 *
80 * When the controlled view receives the focus, the focused() signal is emitted
81 * with a pointer to the controller. This can be used by main application window
82 * which contains the view to plug the controller's actions into the menu when
83 * the view is focused.
84 */
85class KONSOLEPRIVATE_EXPORT SessionController : public ViewProperties , public KXMLGUIClient
86{
87 Q_OBJECT
88
89public:
90 enum CopyInputToEnum {
91 /** Copy keyboard input to all the other tabs in current window */
92 CopyInputToAllTabsMode = 0 ,
93
94 /** Copy keyboard input to user selected tabs in current window */
95 CopyInputToSelectedTabsMode = 1 ,
96
97 /** Do not copy keyboard input to other tabs */
98 CopyInputToNoneMode = 2
99 };
100
101 /**
102 * Constructs a new SessionController which operates on @p session and @p view.
103 */
104 SessionController(Session* session , TerminalDisplay* view, QObject* parent);
105 ~SessionController();
106
107 /** Returns the session associated with this controller */
108 QPointer<Session> session() {
109 return _session;
110 }
111 /** Returns the view associated with this controller */
112 QPointer<TerminalDisplay> view() {
113 return _view;
114 }
115
116 /**
117 * Returns the "window title" of the associated session.
118 */
119 QString userTitle() const;
120
121 /**
122 * Returns true if the controller is valid.
123 * A valid controller is one which has a non-null session() and view().
124 *
125 * Equivalent to "!session().isNull() && !view().isNull()"
126 */
127 bool isValid() const;
128
129 /** Set the start line from which the next search will be done **/
130 void setSearchStartTo(int line);
131
132 /** set start line to the first or last line (depending on the reverse search
133 * setting) in the terminal display **/
134 void setSearchStartToWindowCurrentLine();
135
136 /**
137 * Sets the widget used for searches through the session's output.
138 *
139 * When the user clicks on the "Search Output" menu action the @p searchBar 's
140 * show() method will be called. The SessionController will then connect to the search
141 * bar's signals to update the search when the widget's controls are pressed.
142 */
143 void setSearchBar(IncrementalSearchBar* searchBar);
144 /**
145 * see setSearchBar()
146 */
147 IncrementalSearchBar* searchBar() const;
148
149 /**
150 * Sets the action displayed in the session's context menu to hide or
151 * show the menu bar.
152 */
153 void setShowMenuAction(QAction* action);
154
155 EditProfileDialog* profileDialogPointer();
156
157 // reimplemented
158 virtual KUrl url() const;
159 virtual QString currentDir() const;
160 virtual void rename();
161 virtual bool confirmClose() const;
162 virtual bool confirmForceClose() const;
163
164 // Reimplemented to watch for events happening to the view
165 virtual bool eventFilter(QObject* watched , QEvent* event);
166
167 /** Returns the set of all controllers that exist. */
168 static QSet<SessionController*> allControllers() {
169 return _allControllers;
170 }
171
172signals:
173 /**
174 * Emitted when the view associated with the controller is focused.
175 * This can be used by other classes to plug the controller's actions into a window's
176 * menus.
177 */
178 void focused(SessionController* controller);
179
180 void rawTitleChanged();
181
182 /**
183 * Emitted when the current working directory of the session associated with
184 * the controller is changed.
185 */
186 void currentDirectoryChanged(const QString& dir);
187
188public slots:
189 /**
190 * Issues a command to the session to navigate to the specified URL.
191 * This may not succeed if the foreground program does not understand
192 * the command sent to it ( 'cd path' for local URLs ) or is not
193 * responding to input.
194 *
195 * openUrl() currently supports urls for local paths and those
196 * using the 'ssh' protocol ( eg. "ssh://joebloggs@hostname" )
197 */
198 void openUrl(const KUrl& url);
199
200 /**
201 * update actions which are meaningful only when primary screen is in use.
202 */
203 void setupPrimaryScreenSpecificActions(bool use);
204
205 /**
206 * update actions which are closely related with the selected text.
207 */
208 void selectionChanged(const QString& selectedText);
209
210 /**
211 * close the associated session. This might involve user interaction for
212 * confirmation.
213 */
214 void closeSession();
215
216 /** Increase font size */
217 void increaseFontSize();
218
219 /** Decrease font size */
220 void decreaseFontSize();
221
222private slots:
223 // menu item handlers
224 void openBrowser();
225 void copy();
226 void paste();
227 void selectAll();
228 void selectLine();
229 void pasteFromX11Selection(); // shortcut only
230 void copyInputActionsTriggered(QAction* action);
231 void copyInputToAllTabs();
232 void copyInputToSelectedTabs();
233 void copyInputToNone();
234 void editCurrentProfile();
235 void changeCodec(QTextCodec* codec);
236 void enableSearchBar(bool showSearchBar);
237 void searchHistory(bool showSearchBar);
238 void searchBarEvent();
239 void searchFrom();
240 void findNextInHistory();
241 void findPreviousInHistory();
242 void changeSearchMatch();
243 void print_screen();
244 void saveHistory();
245 void showHistoryOptions();
246 void clearHistory();
247 void clearHistoryAndReset();
248 void monitorActivity(bool monitor);
249 void monitorSilence(bool monitor);
250 void renameSession();
251 void switchProfile(Profile::Ptr profile);
252 void handleWebShortcutAction();
253 void configureWebShortcuts();
254 void sendSignal(QAction* action);
255
256 // other
257 void prepareSwitchProfileMenu();
258 void updateCodecAction();
259 void showDisplayContextMenu(const QPoint& position);
260 void movementKeyFromSearchBarReceived(QKeyEvent *event);
261 void sessionStateChanged(int state);
262 void sessionTitleChanged();
263 void searchTextChanged(const QString& text);
264 void searchCompleted(bool success);
265 void searchClosed(); // called when the user clicks on the
266 // history search bar's close button
267
268 void interactionHandler();
269 void snapshot(); // called periodically as the user types
270 // to take a snapshot of the state of the
271 // foreground process in the terminal
272
273 void requireUrlFilterUpdate();
274 void highlightMatches(bool highlight);
275 void scrollBackOptionsChanged(int mode , int lines);
276 void sessionResizeRequest(const QSize& size);
277 void trackOutput(QKeyEvent* event); // move view to end of current output
278 // when a key press occurs in the
279 // display area
280
281 void updateSearchFilter();
282
283 void zmodemDownload();
284 void zmodemUpload();
285
286 /* Returns true if called within a KPart; false if called within Konsole. */
287 bool isKonsolePart() const;
288
289 // update actions related with selected text
290 void updateCopyAction(const QString& selectedText);
291 void updateWebSearchMenu();
292
293private:
294 // begins the search
295 // text - pattern to search for
296 // direction - value from SearchHistoryTask::SearchDirection enum to specify
297 // the search direction
298 void beginSearch(const QString& text , int direction);
299 QRegExp regexpFromSearchBarOptions();
300 bool reverseSearchChecked() const;
301 void setupCommonActions();
302 void setupExtraActions();
303 void removeSearchFilter(); // remove and delete the current search filter if set
304 void setFindNextPrevEnabled(bool enabled);
305 void listenForScreenWindowUpdates();
306
307private:
308 void updateSessionIcon();
309
310 QPointer<Session> _session;
311 QPointer<TerminalDisplay> _view;
312 SessionGroup* _copyToGroup;
313
314 ProfileList* _profileList;
315
316 KIcon _sessionIcon;
317 QString _sessionIconName;
318 int _previousState;
319
320 UrlFilter* _viewUrlFilter;
321 RegExpFilter* _searchFilter;
322
323 KAction* _copyInputToAllTabsAction;
324
325 KAction* _findAction;
326 KAction* _findNextAction;
327 KAction* _findPreviousAction;
328
329 QTimer* _interactionTimer;
330
331 bool _urlFilterUpdateRequired;
332
333 int _searchStartLine;
334 int _prevSearchResultLine;
335 QPointer<IncrementalSearchBar> _searchBar;
336
337 KCodecAction* _codecAction;
338
339 KActionMenu* _switchProfileMenu;
340 KActionMenu* _webSearchMenu;
341
342 bool _listenForScreenWindowUpdates;
343 bool _preventClose;
344
345 bool _keepIconUntilInteraction;
346
347 QString _selectedText;
348
349 QAction* _showMenuAction;
350
351 static QSet<SessionController*> _allControllers;
352 static int _lastControllerId;
353 static const KIcon _activityIcon;
354 static const KIcon _silenceIcon;
355 static const KIcon _broadcastIcon;
356
357 QStringList _bookmarkValidProgramsToClear;
358
359 bool _isSearchBarEnabled;
360 QWeakPointer<EditProfileDialog> _editProfileDialog;
361
362 QString _searchText;
363};
364inline bool SessionController::isValid() const
365{
366 return !_session.isNull() && !_view.isNull();
367}
368
369/**
370 * Abstract class representing a task which can be performed on a group of sessions.
371 *
372 * Create a new instance of the appropriate sub-class for the task you want to perform and
373 * call the addSession() method to add each session which needs to be processed.
374 *
375 * Finally, call the execute() method to perform the sub-class specific action on each
376 * of the sessions.
377 */
378class SessionTask : public QObject
379{
380 Q_OBJECT
381
382public:
383 explicit SessionTask(QObject* parent = 0);
384
385 /**
386 * Sets whether the task automatically deletes itself when the task has been finished.
387 * Depending on whether the task operates synchronously or asynchronously, the deletion
388 * may be scheduled immediately after execute() returns or it may happen some time later.
389 */
390 void setAutoDelete(bool enable);
391 /** Returns true if the task automatically deletes itself. See setAutoDelete() */
392 bool autoDelete() const;
393
394 /** Adds a new session to the group */
395 void addSession(Session* session);
396
397 /**
398 * Executes the task on each of the sessions in the group.
399 * The completed() signal is emitted when the task is finished, depending on the specific sub-class
400 * execute() may be synchronous or asynchronous
401 */
402 virtual void execute() = 0;
403
404signals:
405 /**
406 * Emitted when the task has completed.
407 * Depending on the task this may occur just before execute() returns, or it
408 * may occur later
409 *
410 * @param success Indicates whether the task completed successfully or not
411 */
412 void completed(bool success);
413
414protected:
415 /** Returns a list of sessions in the group */
416 QList< SessionPtr > sessions() const;
417
418private:
419 bool _autoDelete;
420 QList< SessionPtr > _sessions;
421};
422
423/**
424 * A task which prompts for a URL for each session and saves that session's output
425 * to the given URL
426 */
427class SaveHistoryTask : public SessionTask
428{
429 Q_OBJECT
430
431public:
432 /** Constructs a new task to save session output to URLs */
433 explicit SaveHistoryTask(QObject* parent = 0);
434 virtual ~SaveHistoryTask();
435
436 /**
437 * Opens a save file dialog for each session in the group and begins saving
438 * each session's history to the given URL.
439 *
440 * The data transfer is performed asynchronously and will continue after execute() returns.
441 */
442 virtual void execute();
443
444private slots:
445 void jobDataRequested(KIO::Job* job , QByteArray& data);
446 void jobResult(KJob* job);
447
448private:
449 class SaveJob // structure to keep information needed to process
450 // incoming data requests from jobs
451 {
452 public:
453 SessionPtr session; // the session associated with a history save job
454 int lastLineFetched; // the last line processed in the previous data request
455 // set this to -1 at the start of the save job
456
457 TerminalCharacterDecoder* decoder; // decoder used to convert terminal characters
458 // into output
459 };
460
461 QHash<KJob*, SaveJob> _jobSession;
462};
463
464//class SearchHistoryThread;
465/**
466 * A task which searches through the output of sessions for matches for a given regular expression.
467 * SearchHistoryTask operates on ScreenWindow instances rather than sessions added by addSession().
468 * A screen window can be added to the list to search using addScreenWindow()
469 *
470 * When execute() is called, the search begins in the direction specified by searchDirection(),
471 * starting at the position of the current selection.
472 *
473 * FIXME - This is not a proper implementation of SessionTask, in that it ignores sessions specified
474 * with addSession()
475 *
476 * TODO - Implementation requirements:
477 * May provide progress feedback to the user when searching very large output logs.
478 */
479class SearchHistoryTask : public SessionTask
480{
481 Q_OBJECT
482
483public:
484 /**
485 * This enum describes the strategies available for searching through the
486 * session's output.
487 */
488 enum SearchDirection {
489 /** Searches forwards through the output, starting at the current selection. */
490 ForwardsSearch,
491 /** Searches backwards through the output, starting at the current selection. */
492 BackwardsSearch
493 };
494
495 /**
496 * Constructs a new search task.
497 */
498 explicit SearchHistoryTask(QObject* parent = 0);
499
500 /** Adds a screen window to the list to search when execute() is called. */
501 void addScreenWindow(Session* session , ScreenWindow* searchWindow);
502
503 /** Sets the regular expression which is searched for when execute() is called */
504 void setRegExp(const QRegExp& regExp);
505 /** Returns the regular expression which is searched for when execute() is called */
506 QRegExp regExp() const;
507
508 /** Specifies the direction to search in when execute() is called. */
509 void setSearchDirection(SearchDirection direction);
510 /** Returns the current search direction. See setSearchDirection(). */
511 SearchDirection searchDirection() const;
512
513 /** The line from which the search will be done **/
514 void setStartLine(int startLine);
515
516 /**
517 * Performs a search through the session's history, starting at the position
518 * of the current selection, in the direction specified by setSearchDirection().
519 *
520 * If it finds a match, the ScreenWindow specified in the constructor is
521 * scrolled to the position where the match occurred and the selection
522 * is set to the matching text. execute() then returns immediately.
523 *
524 * To continue the search looking for further matches, call execute() again.
525 */
526 virtual void execute();
527
528private:
529 typedef QPointer<ScreenWindow> ScreenWindowPtr;
530
531 void executeOnScreenWindow(SessionPtr session , ScreenWindowPtr window);
532 void highlightResult(ScreenWindowPtr window , int position);
533
534 QMap< SessionPtr , ScreenWindowPtr > _windows;
535 QRegExp _regExp;
536 SearchDirection _direction;
537 int _startLine;
538
539 //static QPointer<SearchHistoryThread> _thread;
540};
541}
542
543#endif //SESSIONCONTROLLER_H
544
545