File indexing completed on 2018-01-16 12:10:17

0001 /*
0002     Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
0003 
0004     This program is free software; you can redistribute it and/or modify
0005     it under the terms of the GNU General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or
0007     (at your option) any later version.
0008 
0009     This program is distributed in the hope that it will be useful,
0010     but WITHOUT ANY WARRANTY; without even the implied warranty of
0011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012     GNU General Public License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program; if not, write to the Free Software
0016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017     02110-1301  USA.
0018 */
0019 
0020 #ifndef FILTER_H
0021 #define FILTER_H
0022 
0023 // Qt
0024 #include <QList>
0025 #include <QSet>
0026 #include <QObject>
0027 #include <QPointer>
0028 #include <QStringList>
0029 #include <QRegularExpression>
0030 #include <QMultiHash>
0031 
0032 // Konsole
0033 #include "Character.h"
0034 
0035 class QAction;
0036 
0037 namespace Konsole {
0038 class Session;
0039 
0040 /**
0041  * A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list)
0042  * and marks the areas which match the filter's patterns as 'hotspots'.
0043  *
0044  * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ),
0045  * and an action.  When the user performs some activity such as a mouse-click in a hotspot area ( the exact
0046  * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's
0047  * activate() method should be called.  Depending on the type of hotspot this will trigger a suitable response.
0048  *
0049  * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser.
0050  * Hotspots may have more than one action, in which case the list of actions can be obtained using the
0051  * actions() method.
0052  *
0053  * Different subclasses of filter will return different types of hotspot.
0054  * Subclasses must reimplement the process() method to examine a block of text and identify sections of interest.
0055  * When processing the text they should create instances of Filter::HotSpot subclasses for sections of interest
0056  * and add them to the filter's list of hotspots using addHotSpot()
0057  */
0058 class Filter
0059 {
0060 public:
0061     /**
0062     * Represents an area of text which matched the pattern a particular filter has been looking for.
0063     *
0064     * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ),
0065     * and an action.  When the user performs some activity such as a mouse-click in a hotspot area ( the exact
0066     * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's
0067     * activate() method should be called.  Depending on the type of hotspot this will trigger a suitable response.
0068     *
0069     * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser.
0070     * Hotspots may have more than one action, in which case the list of actions can be obtained using the
0071     * actions() method.  These actions may then be displayed in a popup menu or toolbar for example.
0072     */
0073     class HotSpot
0074     {
0075     public:
0076         /**
0077          * Constructs a new hotspot which covers the area from (@p startLine,@p startColumn) to (@p endLine,@p endColumn)
0078          * in a block of text.
0079          */
0080         HotSpot(int startLine, int startColumn, int endLine, int endColumn);
0081         virtual ~HotSpot();
0082 
0083         enum Type {
0084             // the type of the hotspot is not specified
0085             NotSpecified,
0086             // this hotspot represents a clickable link
0087             Link,
0088             // this hotspot represents a marker
0089             Marker
0090         };
0091 
0092         /** Returns the line when the hotspot area starts */
0093         int startLine() const;
0094         /** Returns the line where the hotspot area ends */
0095         int endLine() const;
0096         /** Returns the column on startLine() where the hotspot area starts */
0097         int startColumn() const;
0098         /** Returns the column on endLine() where the hotspot area ends */
0099         int endColumn() const;
0100         /**
0101          * Returns the type of the hotspot.  This is usually used as a hint for views on how to represent
0102          * the hotspot graphically.  eg.  Link hotspots are typically underlined when the user mouses over them
0103          */
0104         Type type() const;
0105         /**
0106          * Causes the action associated with a hotspot to be triggered.
0107          *
0108          * @param object The object which caused the hotspot to be triggered.  This is
0109          * typically null ( in which case the default action should be performed ) or
0110          * one of the objects from the actions() list.  In which case the associated
0111          * action should be performed.
0112          */
0113         virtual void activate(QObject *object = nullptr) = 0;
0114         /**
0115          * Returns a list of actions associated with the hotspot which can be used in a
0116          * menu or toolbar
0117          */
0118         virtual QList<QAction *> actions();
0119 
0120     protected:
0121         /** Sets the type of a hotspot.  This should only be set once */
0122         void setType(Type type);
0123 
0124     private:
0125         int _startLine;
0126         int _startColumn;
0127         int _endLine;
0128         int _endColumn;
0129         Type _type;
0130     };
0131 
0132     /** Constructs a new filter. */
0133     Filter();
0134     virtual ~Filter();
0135 
0136     /** Causes the filter to process the block of text currently in its internal buffer */
0137     virtual void process() = 0;
0138 
0139     /**
0140      * Empties the filters internal buffer and resets the line count back to 0.
0141      * All hotspots are deleted.
0142      */
0143     void reset();
0144 
0145     /** Returns the hotspot which covers the given @p line and @p column, or 0 if no hotspot covers that area */
0146     HotSpot *hotSpotAt(int line, int column) const;
0147 
0148     /** Returns the list of hotspots identified by the filter */
0149     QList<HotSpot *> hotSpots() const;
0150 
0151     /** Returns the list of hotspots identified by the filter which occur on a given line */
0152     QList<HotSpot *> hotSpotsAtLine(int line) const;
0153 
0154     /**
0155      * TODO: Document me
0156      */
0157     void setBuffer(const QString *buffer, const QList<int> *linePositions);
0158 
0159 protected:
0160     /** Adds a new hotspot to the list */
0161     void addHotSpot(HotSpot *);
0162     /** Returns the internal buffer */
0163     const QString *buffer();
0164     /** Converts a character position within buffer() to a line and column */
0165     void getLineColumn(int position, int &startLine, int &startColumn);
0166 
0167 private:
0168     Q_DISABLE_COPY(Filter)
0169 
0170     QMultiHash<int, HotSpot *> _hotspots;
0171     QList<HotSpot *> _hotspotList;
0172 
0173     const QList<int> *_linePositions;
0174     const QString *_buffer;
0175 };
0176 
0177 /**
0178  * A filter which searches for sections of text matching a regular expression and creates a new RegExpFilter::HotSpot
0179  * instance for them.
0180  *
0181  * Subclasses can reimplement newHotSpot() to return custom hotspot types when matches for the regular expression
0182  * are found.
0183  */
0184 class RegExpFilter : public Filter
0185 {
0186 public:
0187     /**
0188      * Type of hotspot created by RegExpFilter.  The capturedTexts() method can be used to find the text
0189      * matched by the filter's regular expression.
0190      */
0191     class HotSpot : public Filter::HotSpot
0192     {
0193     public:
0194         HotSpot(int startLine, int startColumn, int endLine, int endColumn,
0195                 const QStringList &capturedTexts);
0196         void activate(QObject *object = nullptr) Q_DECL_OVERRIDE;
0197 
0198         /** Returns the texts found by the filter when matching the filter's regular expression */
0199         QStringList capturedTexts() const;
0200     private:
0201         QStringList _capturedTexts;
0202     };
0203 
0204     /** Constructs a new regular expression filter */
0205     RegExpFilter();
0206 
0207     /**
0208      * Sets the regular expression which the filter searches for in blocks of text.
0209      *
0210      * Regular expressions which match the empty string are treated as not matching
0211      * anything.
0212      */
0213     void setRegExp(const QRegularExpression &regExp);
0214     /** Returns the regular expression which the filter searches for in blocks of text */
0215     QRegularExpression regExp() const;
0216 
0217     /**
0218      * Reimplemented to search the filter's text buffer for text matching regExp()
0219      *
0220      * If regexp matches the empty string, then process() will return immediately
0221      * without finding results.
0222      */
0223     void process() Q_DECL_OVERRIDE;
0224 
0225 protected:
0226     /**
0227      * Called when a match for the regular expression is encountered.  Subclasses should reimplement this
0228      * to return custom hotspot types
0229      */
0230     virtual RegExpFilter::HotSpot *newHotSpot(int startLine, int startColumn, int endLine,
0231                                               int endColumn, const QStringList &capturedTexts);
0232 
0233 private:
0234     QRegularExpression _searchText;
0235 };
0236 
0237 class FilterObject;
0238 
0239 /** A filter which matches URLs in blocks of text */
0240 class UrlFilter : public RegExpFilter
0241 {
0242 public:
0243     /**
0244      * Hotspot type created by UrlFilter instances.  The activate() method opens a web browser
0245      * at the given URL when called.
0246      */
0247     class HotSpot : public RegExpFilter::HotSpot
0248     {
0249     public:
0250         HotSpot(int startLine, int startColumn, int endLine, int endColumn,
0251                 const QStringList &capturedTexts);
0252         ~HotSpot() Q_DECL_OVERRIDE;
0253 
0254         QList<QAction *> actions() Q_DECL_OVERRIDE;
0255 
0256         /**
0257          * Open a web browser at the current URL.  The url itself can be determined using
0258          * the capturedTexts() method.
0259          */
0260         void activate(QObject *object = nullptr) Q_DECL_OVERRIDE;
0261 
0262     private:
0263         enum UrlType {
0264             StandardUrl,
0265             Email,
0266             Unknown
0267         };
0268         UrlType urlType() const;
0269 
0270         FilterObject *_urlObject;
0271     };
0272 
0273     UrlFilter();
0274 
0275 protected:
0276     RegExpFilter::HotSpot *newHotSpot(int, int, int, int, const QStringList &) Q_DECL_OVERRIDE;
0277 
0278 private:
0279     static const QRegularExpression FullUrlRegExp;
0280     static const QRegularExpression EmailAddressRegExp;
0281 
0282     // combined OR of FullUrlRegExp and EmailAddressRegExp
0283     static const QRegularExpression CompleteUrlRegExp;
0284 };
0285 
0286 /**
0287  * A filter which matches files according to POSIX Portable Filename Character Set
0288  * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267
0289  */
0290 class FileFilter : public RegExpFilter
0291 {
0292 public:
0293     /**
0294      * Hotspot type created by FileFilter instances.
0295      */
0296     class HotSpot : public RegExpFilter::HotSpot
0297     {
0298     public:
0299         HotSpot(int startLine, int startColumn, int endLine, int endColumn,
0300                 const QStringList &capturedTexts, const QString &filePath);
0301         ~HotSpot() Q_DECL_OVERRIDE;
0302 
0303         QList<QAction *> actions() Q_DECL_OVERRIDE;
0304 
0305         /**
0306          * Opens kate for editing the file.
0307          */
0308         void activate(QObject *object = nullptr) Q_DECL_OVERRIDE;
0309 
0310     private:
0311         FilterObject *_fileObject;
0312         QString _filePath;
0313     };
0314 
0315     explicit FileFilter(Session *session);
0316 
0317     void process() Q_DECL_OVERRIDE;
0318 
0319 protected:
0320     RegExpFilter::HotSpot *newHotSpot(int, int, int, int, const QStringList &) Q_DECL_OVERRIDE;
0321 
0322 private:
0323     QPointer<Session> _session;
0324     QString _dirPath;
0325     QSet<QString> _currentFiles;
0326 };
0327 
0328 class FilterObject : public QObject
0329 {
0330     Q_OBJECT
0331 public:
0332     explicit FilterObject(Filter::HotSpot *filter) : _filter(filter)
0333     {
0334     }
0335 
0336 public Q_SLOTS:
0337     void activated();
0338 private:
0339     Q_DISABLE_COPY(FilterObject)
0340 
0341     Filter::HotSpot *_filter;
0342 };
0343 
0344 /**
0345  * A chain which allows a group of filters to be processed as one.
0346  * The chain owns the filters added to it and deletes them when the chain itself is destroyed.
0347  *
0348  * Use addFilter() to add a new filter to the chain.
0349  * When new text to be filtered arrives, use addLine() to add each additional
0350  * line of text which needs to be processed and then after adding the last line, use
0351  * process() to cause each filter in the chain to process the text.
0352  *
0353  * After processing a block of text, the reset() method can be used to set the filter chain's
0354  * internal cursor back to the first line.
0355  *
0356  * The hotSpotAt() method will return the first hotspot which covers a given position.
0357  *
0358  * The hotSpots() and hotSpotsAtLine() method return all of the hotspots in the text and on
0359  * a given line respectively.
0360  */
0361 class FilterChain : protected QList<Filter *>
0362 {
0363 public:
0364     virtual ~FilterChain();
0365 
0366     /** Adds a new filter to the chain.  The chain will delete this filter when it is destroyed */
0367     void addFilter(Filter *filter);
0368     /** Removes a filter from the chain.  The chain will no longer delete the filter when destroyed */
0369     void removeFilter(Filter *filter);
0370     /** Removes all filters from the chain */
0371     void clear();
0372 
0373     /** Resets each filter in the chain */
0374     void reset();
0375     /**
0376      * Processes each filter in the chain
0377      */
0378     void process();
0379 
0380     /** Sets the buffer for each filter in the chain to process. */
0381     void setBuffer(const QString *buffer, const QList<int> *linePositions);
0382 
0383     /** Returns the first hotspot which occurs at @p line, @p column or 0 if no hotspot was found */
0384     Filter::HotSpot *hotSpotAt(int line, int column) const;
0385     /** Returns a list of all the hotspots in all the chain's filters */
0386     QList<Filter::HotSpot *> hotSpots() const;
0387     /** Returns a list of all hotspots at the given line in all the chain's filters */
0388     QList<Filter::HotSpot> hotSpotsAtLine(int line) const;
0389 };
0390 
0391 /** A filter chain which processes character images from terminal displays */
0392 class TerminalImageFilterChain : public FilterChain
0393 {
0394 public:
0395     TerminalImageFilterChain();
0396     ~TerminalImageFilterChain() Q_DECL_OVERRIDE;
0397 
0398     /**
0399      * Set the current terminal image to @p image.
0400      *
0401      * @param image The terminal image
0402      * @param lines The number of lines in the terminal image
0403      * @param columns The number of columns in the terminal image
0404      * @param lineProperties The line properties to set for image
0405      */
0406     void setImage(const Character * const image, int lines, int columns,
0407                   const QVector<LineProperty> &lineProperties);
0408 
0409 private:
0410     Q_DISABLE_COPY(TerminalImageFilterChain)
0411 
0412     QString *_buffer;
0413     QList<int> *_linePositions;
0414 };
0415 }
0416 #endif //FILTER_H