File indexing completed on 2024-05-19 05:28:15

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