File indexing completed on 2025-03-16 08:11:25
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