File indexing completed on 2018-06-12 20:16:30

0001 /*
0002     Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
0003     Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de>
0004 
0005     This program is free software; you can redistribute it and/or modify
0006     it under the terms of the GNU General Public License as published by
0007     the Free Software Foundation; either version 2 of the License, or
0008     (at your option) any later version.
0009 
0010     This program is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013     GNU General Public License for more details.
0014 
0015     You should have received a copy of the GNU General Public License
0016     along with this program; if not, write to the Free Software
0017     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0018     02110-1301  USA.
0019 */
0020 
0021 #ifndef SESSIONCONTROLLER_H
0022 #define SESSIONCONTROLLER_H
0023 
0024 // Qt
0025 #include <QList>
0026 #include <QSet>
0027 #include <QPointer>
0028 #include <QString>
0029 #include <QHash>
0030 #include <QRegularExpression>
0031 
0032 // KDE
0033 #include <KXMLGUIClient>
0034 
0035 // Konsole
0036 #include "ViewProperties.h"
0037 #include "Profile.h"
0038 #include "Enumeration.h"
0039 
0040 namespace KIO {
0041 class Job;
0042 }
0043 
0044 class QAction;
0045 class QTextCodec;
0046 class QKeyEvent;
0047 class QTimer;
0048 class QUrl;
0049 
0050 class KCodecAction;
0051 class KJob;
0052 class QAction;
0053 class KActionMenu;
0054 
0055 namespace Konsole {
0056 class Session;
0057 class SessionGroup;
0058 class ScreenWindow;
0059 class TerminalDisplay;
0060 class IncrementalSearchBar;
0061 class ProfileList;
0062 class RegExpFilter;
0063 class UrlFilter;
0064 class FileFilter;
0065 class EditProfileDialog;
0066 
0067 // SaveHistoryTask
0068 class TerminalCharacterDecoder;
0069 
0070 typedef QPointer<Session> SessionPtr;
0071 
0072 /**
0073  * Provides the menu actions to manipulate a single terminal session and view pair.
0074  * The actions provided by this class are defined in the sessionui.rc XML file.
0075  *
0076  * SessionController monitors the session and provides access to basic information
0077  * about the session such as title(), icon() and currentDir().  SessionController
0078  * provides notifications of activity in the session via the activity() signal.
0079  *
0080  * When the controlled view receives the focus, the focused() signal is emitted
0081  * with a pointer to the controller.  This can be used by main application window
0082  * which contains the view to plug the controller's actions into the menu when
0083  * the view is focused.
0084  */
0085 class KONSOLEPRIVATE_EXPORT SessionController : public ViewProperties, public KXMLGUIClient
0086 {
0087     Q_OBJECT
0088 
0089 public:
0090     enum CopyInputToEnum {
0091         /** Copy keyboard input to all the other tabs in current window */
0092         CopyInputToAllTabsMode = 0,
0093 
0094         /** Copy keyboard input to user selected tabs in current window */
0095         CopyInputToSelectedTabsMode = 1,
0096 
0097         /** Do not copy keyboard input to other tabs */
0098         CopyInputToNoneMode = 2
0099     };
0100 
0101     /**
0102      * Constructs a new SessionController which operates on @p session and @p view.
0103      */
0104     SessionController(Session *session, TerminalDisplay *view, QObject *parent);
0105     ~SessionController() Q_DECL_OVERRIDE;
0106 
0107     /** Returns the session associated with this controller */
0108     QPointer<Session> session()
0109     {
0110         return _session;
0111     }
0112 
0113     /** Returns the view associated with this controller */
0114     QPointer<TerminalDisplay> view()
0115     {
0116         return _view;
0117     }
0118 
0119     /**
0120      * Returns the "window title" of the associated session.
0121      */
0122     QString userTitle() const;
0123 
0124     /**
0125      * Returns true if the controller is valid.
0126      * A valid controller is one which has a non-null session() and view().
0127      *
0128      * Equivalent to "!session().isNull() && !view().isNull()"
0129      */
0130     bool isValid() const;
0131 
0132     /** Set the start line from which the next search will be done **/
0133     void setSearchStartTo(int line);
0134 
0135     /** set start line to the first or last line (depending on the reverse search
0136      * setting) in the terminal display **/
0137     void setSearchStartToWindowCurrentLine();
0138 
0139     /**
0140      * Sets the widget used for searches through the session's output.
0141      *
0142      * When the user clicks on the "Search Output" menu action the @p searchBar 's
0143      * show() method will be called.  The SessionController will then connect to the search
0144      * bar's signals to update the search when the widget's controls are pressed.
0145      */
0146     void setSearchBar(IncrementalSearchBar *searchBar);
0147     /**
0148      * see setSearchBar()
0149      */
0150     IncrementalSearchBar *searchBar() const;
0151 
0152     /**
0153      * Sets the action displayed in the session's context menu to hide or
0154      * show the menu bar.
0155      */
0156     void setShowMenuAction(QAction *action);
0157 
0158     EditProfileDialog *profileDialogPointer();
0159 
0160     // reimplemented
0161     QUrl url() const Q_DECL_OVERRIDE;
0162     QString currentDir() const Q_DECL_OVERRIDE;
0163     void rename() Q_DECL_OVERRIDE;
0164     bool confirmClose() const Q_DECL_OVERRIDE;
0165     virtual bool confirmForceClose() const;
0166 
0167     // Reimplemented to watch for events happening to the view
0168     bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE;
0169 
0170     /** Returns the set of all controllers that exist. */
0171     static QSet<SessionController *> allControllers()
0172     {
0173         return _allControllers;
0174     }
0175 
0176     /* Returns true if called within a KPart; false if called within Konsole. */
0177     bool isKonsolePart() const;
0178     bool isReadOnly() const;
0179 
0180 Q_SIGNALS:
0181     /**
0182      * Emitted when the view associated with the controller is focused.
0183      * This can be used by other classes to plug the controller's actions into a window's
0184      * menus.
0185      */
0186     void focused(SessionController *controller);
0187 
0188     void rawTitleChanged();
0189 
0190     /**
0191      * Emitted when the current working directory of the session associated with
0192      * the controller is changed.
0193      */
0194     void currentDirectoryChanged(const QString &dir);
0195 
0196     /**
0197      * Emitted when the user changes the tab title.
0198      */
0199     void tabRenamedByUser(bool renamed) const;
0200 
0201 public Q_SLOTS:
0202     /**
0203      * Issues a command to the session to navigate to the specified URL.
0204      * This may not succeed if the foreground program does not understand
0205      * the command sent to it ( 'cd path' for local URLs ) or is not
0206      * responding to input.
0207      *
0208      * openUrl() currently supports urls for local paths and those
0209      * using the 'ssh' protocol ( eg. "ssh://joebloggs@hostname" )
0210      */
0211     void openUrl(const QUrl &url);
0212 
0213     /**
0214      * update actions which are meaningful only when primary screen is in use.
0215      */
0216     void setupPrimaryScreenSpecificActions(bool use);
0217 
0218     /**
0219      * update actions which are closely related with the selected text.
0220      */
0221     void selectionChanged(const QString &selectedText);
0222 
0223     /**
0224      * close the associated session. This might involve user interaction for
0225      * confirmation.
0226      */
0227     void closeSession();
0228 
0229     /**  Increase font size */
0230     void increaseFontSize();
0231 
0232     /**  Decrease font size */
0233     void decreaseFontSize();
0234 
0235 private Q_SLOTS:
0236     // menu item handlers
0237     void openBrowser();
0238     void copy();
0239     void paste();
0240     void selectAll();
0241     void selectLine();
0242     void pasteFromX11Selection(); // shortcut only
0243     void copyInputActionsTriggered(QAction *action);
0244     void copyInputToAllTabs();
0245     void copyInputToSelectedTabs();
0246     void copyInputToNone();
0247     void editCurrentProfile();
0248     void changeCodec(QTextCodec *codec);
0249     void enableSearchBar(bool showSearchBar);
0250     void searchHistory(bool showSearchBar);
0251     void searchBarEvent();
0252     void searchFrom();
0253     void findNextInHistory();
0254     void findPreviousInHistory();
0255     void changeSearchMatch();
0256     void print_screen();
0257     void saveHistory();
0258     void showHistoryOptions();
0259     void clearHistory();
0260     void clearHistoryAndReset();
0261     void monitorActivity(bool monitor);
0262     void monitorSilence(bool monitor);
0263     void renameSession();
0264     void switchProfile(Profile::Ptr profile);
0265     void handleWebShortcutAction();
0266     void configureWebShortcuts();
0267     void sendSignal(QAction *action);
0268     void sendBackgroundColor();
0269     void toggleReadOnly();
0270 
0271     // other
0272     void prepareSwitchProfileMenu();
0273     void updateCodecAction();
0274     void showDisplayContextMenu(const QPoint &position);
0275     void movementKeyFromSearchBarReceived(QKeyEvent *event);
0276     void sessionStateChanged(int state);
0277     void sessionAttributeChanged();
0278     void sessionReadOnlyChanged();
0279     void searchTextChanged(const QString &text);
0280     void searchCompleted(bool success);
0281     void searchClosed(); // called when the user clicks on the
0282     // history search bar's close button
0283 
0284     void updateFilterList(Profile::Ptr profile); // Called when the profile has changed, so we might need to change the list of filters
0285 
0286     void interactionHandler();
0287     void snapshot(); // called periodically as the user types
0288     // to take a snapshot of the state of the
0289     // foreground process in the terminal
0290 
0291     void highlightMatches(bool highlight);
0292     void scrollBackOptionsChanged(int mode, int lines);
0293     void sessionResizeRequest(const QSize &size);
0294     void trackOutput(QKeyEvent *event);  // move view to end of current output
0295     // when a key press occurs in the
0296     // display area
0297 
0298     void updateSearchFilter();
0299 
0300     void zmodemDownload();
0301     void zmodemUpload();
0302 
0303     // update actions related with selected text
0304     void updateCopyAction(const QString &selectedText);
0305     void updateWebSearchMenu();
0306 
0307 private:
0308     Q_DISABLE_COPY(SessionController)
0309 
0310     // begins the search
0311     // text - pattern to search for
0312     // direction - value from SearchHistoryTask::SearchDirection enum to specify
0313     //             the search direction
0314     void beginSearch(const QString &text, Enum::SearchDirection direction);
0315     QRegularExpression regexpFromSearchBarOptions() const;
0316     bool reverseSearchChecked() const;
0317     void setupCommonActions();
0318     void setupExtraActions();
0319     void removeSearchFilter(); // remove and delete the current search filter if set
0320     void setFindNextPrevEnabled(bool enabled);
0321     void listenForScreenWindowUpdates();
0322 
0323 private:
0324     void updateSessionIcon();
0325     void updateReadOnlyActionStates();
0326 
0327     QPointer<Session> _session;
0328     QPointer<TerminalDisplay> _view;
0329     SessionGroup *_copyToGroup;
0330 
0331     ProfileList *_profileList;
0332 
0333     QIcon _sessionIcon;
0334     QString _sessionIconName;
0335     int _previousState;
0336 
0337     RegExpFilter *_searchFilter;
0338     UrlFilter *_urlFilter;
0339     FileFilter *_fileFilter;
0340 
0341     QAction *_copyInputToAllTabsAction;
0342 
0343     QAction *_findAction;
0344     QAction *_findNextAction;
0345     QAction *_findPreviousAction;
0346 
0347     QTimer *_interactionTimer;
0348 
0349     int _searchStartLine;
0350     int _prevSearchResultLine;
0351     QPointer<IncrementalSearchBar> _searchBar;
0352 
0353     KCodecAction *_codecAction;
0354 
0355     KActionMenu *_switchProfileMenu;
0356     KActionMenu *_webSearchMenu;
0357 
0358     bool _listenForScreenWindowUpdates;
0359     bool _preventClose;
0360 
0361     bool _keepIconUntilInteraction;
0362 
0363     QString _selectedText;
0364 
0365     QAction *_showMenuAction;
0366 
0367     static QSet<SessionController *> _allControllers;
0368     static int _lastControllerId;
0369 
0370     QStringList _bookmarkValidProgramsToClear;
0371 
0372     bool _isSearchBarEnabled;
0373     QPointer<EditProfileDialog> _editProfileDialog;
0374 
0375     QString _searchText;
0376 };
0377 inline bool SessionController::isValid() const
0378 {
0379     return !_session.isNull() && !_view.isNull();
0380 }
0381 
0382 /**
0383  * Abstract class representing a task which can be performed on a group of sessions.
0384  *
0385  * Create a new instance of the appropriate sub-class for the task you want to perform and
0386  * call the addSession() method to add each session which needs to be processed.
0387  *
0388  * Finally, call the execute() method to perform the sub-class specific action on each
0389  * of the sessions.
0390  */
0391 class SessionTask : public QObject
0392 {
0393     Q_OBJECT
0394 
0395 public:
0396     explicit SessionTask(QObject *parent = nullptr);
0397 
0398     /**
0399      * Sets whether the task automatically deletes itself when the task has been finished.
0400      * Depending on whether the task operates synchronously or asynchronously, the deletion
0401      * may be scheduled immediately after execute() returns or it may happen some time later.
0402      */
0403     void setAutoDelete(bool enable);
0404     /** Returns true if the task automatically deletes itself.  See setAutoDelete() */
0405     bool autoDelete() const;
0406 
0407     /** Adds a new session to the group */
0408     void addSession(Session *session);
0409 
0410     /**
0411      * Executes the task on each of the sessions in the group.
0412      * The completed() signal is emitted when the task is finished, depending on the specific sub-class
0413      * execute() may be synchronous or asynchronous
0414      */
0415     virtual void execute() = 0;
0416 
0417 Q_SIGNALS:
0418     /**
0419      * Emitted when the task has completed.
0420      * Depending on the task this may occur just before execute() returns, or it
0421      * may occur later
0422      *
0423      * @param success Indicates whether the task completed successfully or not
0424      */
0425     void completed(bool success);
0426 
0427 protected:
0428     /** Returns a list of sessions in the group */
0429     QList< SessionPtr > sessions() const;
0430 
0431 private:
0432     bool _autoDelete;
0433     QList< SessionPtr > _sessions;
0434 };
0435 
0436 /**
0437  * A task which prompts for a URL for each session and saves that session's output
0438  * to the given URL
0439  */
0440 class SaveHistoryTask : public SessionTask
0441 {
0442     Q_OBJECT
0443 
0444 public:
0445     /** Constructs a new task to save session output to URLs */
0446     explicit SaveHistoryTask(QObject *parent = nullptr);
0447     ~SaveHistoryTask() Q_DECL_OVERRIDE;
0448 
0449     /**
0450      * Opens a save file dialog for each session in the group and begins saving
0451      * each session's history to the given URL.
0452      *
0453      * The data transfer is performed asynchronously and will continue after execute() returns.
0454      */
0455     void execute() Q_DECL_OVERRIDE;
0456 
0457 private Q_SLOTS:
0458     void jobDataRequested(KIO::Job *job, QByteArray &data);
0459     void jobResult(KJob *job);
0460 
0461 private:
0462     class SaveJob // structure to keep information needed to process
0463         // incoming data requests from jobs
0464     {
0465     public:
0466         SessionPtr session; // the session associated with a history save job
0467         int lastLineFetched; // the last line processed in the previous data request
0468         // set this to -1 at the start of the save job
0469 
0470         TerminalCharacterDecoder *decoder;  // decoder used to convert terminal characters
0471         // into output
0472     };
0473 
0474     QHash<KJob *, SaveJob> _jobSession;
0475 };
0476 
0477 //class SearchHistoryThread;
0478 /**
0479  * A task which searches through the output of sessions for matches for a given regular expression.
0480  * SearchHistoryTask operates on ScreenWindow instances rather than sessions added by addSession().
0481  * A screen window can be added to the list to search using addScreenWindow()
0482  *
0483  * When execute() is called, the search begins in the direction specified by searchDirection(),
0484  * starting at the position of the current selection.
0485  *
0486  * FIXME - This is not a proper implementation of SessionTask, in that it ignores sessions specified
0487  * with addSession()
0488  *
0489  * TODO - Implementation requirements:
0490  *          May provide progress feedback to the user when searching very large output logs.
0491  */
0492 class SearchHistoryTask : public SessionTask
0493 {
0494     Q_OBJECT
0495 
0496 public:
0497     /**
0498      * Constructs a new search task.
0499      */
0500     explicit SearchHistoryTask(QObject *parent = nullptr);
0501 
0502     /** Adds a screen window to the list to search when execute() is called. */
0503     void addScreenWindow(Session *session, ScreenWindow *searchWindow);
0504 
0505     /** Sets the regular expression which is searched for when execute() is called */
0506     void setRegExp(const QRegularExpression &expression);
0507     /** Returns the regular expression which is searched for when execute() is called */
0508     QRegularExpression regExp() const;
0509 
0510     /** Specifies the direction to search in when execute() is called. */
0511     void setSearchDirection(Enum::SearchDirection direction);
0512     /** Returns the current search direction.  See setSearchDirection(). */
0513     Enum::SearchDirection searchDirection() const;
0514 
0515     /** The line from which the search will be done **/
0516     void setStartLine(int line);
0517 
0518     /**
0519      * Performs a search through the session's history, starting at the position
0520      * of the current selection, in the direction specified by setSearchDirection().
0521      *
0522      * If it finds a match, the ScreenWindow specified in the constructor is
0523      * scrolled to the position where the match occurred and the selection
0524      * is set to the matching text.  execute() then returns immediately.
0525      *
0526      * To continue the search looking for further matches, call execute() again.
0527      */
0528     void execute() Q_DECL_OVERRIDE;
0529 
0530 private:
0531     typedef QPointer<ScreenWindow> ScreenWindowPtr;
0532 
0533     void executeOnScreenWindow(SessionPtr session, ScreenWindowPtr window);
0534     void highlightResult(ScreenWindowPtr window, int position);
0535 
0536     QMap< SessionPtr, ScreenWindowPtr > _windows;
0537     QRegularExpression _regExp;
0538     Enum::SearchDirection _direction;
0539     int _startLine;
0540 
0541     //static QPointer<SearchHistoryThread> _thread;
0542 };
0543 }
0544 
0545 #endif //SESSIONCONTROLLER_H