1/*
2 Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301 USA.
18*/
19
20#ifndef FILTER_H
21#define FILTER_H
22
23// Qt
24#include <QAction>
25#include <QList>
26#include <QObject>
27#include <QStringList>
28#include <QHash>
29#include <QRegExp>
30
31// Local
32
33namespace Konsole
34{
35
36typedef unsigned char LineProperty;
37class Character;
38
39/**
40 * A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list)
41 * and marks the areas which match the filter's patterns as 'hotspots'.
42 *
43 * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ),
44 * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact
45 * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's
46 * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response.
47 *
48 * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser.
49 * Hotspots may have more than one action, in which case the list of actions can be obtained using the
50 * actions() method.
51 *
52 * Different subclasses of filter will return different types of hotspot.
53 * Subclasses must reimplement the process() method to examine a block of text and identify sections of interest.
54 * When processing the text they should create instances of Filter::HotSpot subclasses for sections of interest
55 * and add them to the filter's list of hotspots using addHotSpot()
56 */
57class Filter : public QObject
58{
59public:
60 /**
61 * Represents an area of text which matched the pattern a particular filter has been looking for.
62 *
63 * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ),
64 * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact
65 * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's
66 * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response.
67 *
68 * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser.
69 * Hotspots may have more than one action, in which case the list of actions can be obtained using the
70 * actions() method. These actions may then be displayed in a popup menu or toolbar for example.
71 */
72 class HotSpot
73 {
74 public:
75 /**
76 * Constructs a new hotspot which covers the area from (@p startLine,@p startColumn) to (@p endLine,@p endColumn)
77 * in a block of text.
78 */
79 HotSpot(int startLine , int startColumn , int endLine , int endColumn);
80 virtual ~HotSpot();
81
82 enum Type
83 {
84 // the type of the hotspot is not specified
85 NotSpecified,
86 // this hotspot represents a clickable link
87 Link,
88 // this hotspot represents a marker
89 Marker
90 };
91
92 /** Returns the line when the hotspot area starts */
93 int startLine() const;
94 /** Returns the line where the hotspot area ends */
95 int endLine() const;
96 /** Returns the column on startLine() where the hotspot area starts */
97 int startColumn() const;
98 /** Returns the column on endLine() where the hotspot area ends */
99 int endColumn() const;
100 /**
101 * Returns the type of the hotspot. This is usually used as a hint for views on how to represent
102 * the hotspot graphically. eg. Link hotspots are typically underlined when the user mouses over them
103 */
104 Type type() const;
105 /**
106 * Causes the an action associated with a hotspot to be triggered.
107 *
108 * @param action The action to trigger. This is
109 * typically empty ( in which case the default action should be performed ) or
110 * one of the object names from the actions() list. In which case the associated
111 * action should be performed.
112 */
113 virtual void activate(const QString& action = QString()) = 0;
114 /**
115 * Returns a list of actions associated with the hotspot which can be used in a
116 * menu or toolbar
117 */
118 virtual QList<QAction*> actions();
119
120 protected:
121 /** Sets the type of a hotspot. This should only be set once */
122 void setType(Type type);
123
124 private:
125 int _startLine;
126 int _startColumn;
127 int _endLine;
128 int _endColumn;
129 Type _type;
130
131 };
132
133 /** Constructs a new filter. */
134 Filter();
135 virtual ~Filter();
136
137 /** Causes the filter to process the block of text currently in its internal buffer */
138 virtual void process() = 0;
139
140 /**
141 * Empties the filters internal buffer and resets the line count back to 0.
142 * All hotspots are deleted.
143 */
144 void reset();
145
146 /** Adds a new line of text to the filter and increments the line count */
147 //void addLine(const QString& string);
148
149 /** Returns the hotspot which covers the given @p line and @p column, or 0 if no hotspot covers that area */
150 HotSpot* hotSpotAt(int line , int column) const;
151
152 /** Returns the list of hotspots identified by the filter */
153 QList<HotSpot*> hotSpots() const;
154
155 /** Returns the list of hotspots identified by the filter which occur on a given line */
156 QList<HotSpot*> hotSpotsAtLine(int line) const;
157
158 /**
159 * TODO: Document me
160 */
161 void setBuffer(const QString* buffer , const QList<int>* linePositions);
162
163protected:
164 /** Adds a new hotspot to the list */
165 void addHotSpot(HotSpot*);
166 /** Returns the internal buffer */
167 const QString* buffer();
168 /** Converts a character position within buffer() to a line and column */
169 void getLineColumn(int position , int& startLine , int& startColumn);
170
171private:
172 QMultiHash<int,HotSpot*> _hotspots;
173 QList<HotSpot*> _hotspotList;
174
175 const QList<int>* _linePositions;
176 const QString* _buffer;
177};
178
179/**
180 * A filter which searches for sections of text matching a regular expression and creates a new RegExpFilter::HotSpot
181 * instance for them.
182 *
183 * Subclasses can reimplement newHotSpot() to return custom hotspot types when matches for the regular expression
184 * are found.
185 */
186class RegExpFilter : public Filter
187{
188public:
189 /**
190 * Type of hotspot created by RegExpFilter. The capturedTexts() method can be used to find the text
191 * matched by the filter's regular expression.
192 */
193 class HotSpot : public Filter::HotSpot
194 {
195 public:
196 HotSpot(int startLine, int startColumn, int endLine , int endColumn);
197 virtual void activate(const QString& action = QString());
198
199 /** Sets the captured texts associated with this hotspot */
200 void setCapturedTexts(const QStringList& texts);
201 /** Returns the texts found by the filter when matching the filter's regular expression */
202 QStringList capturedTexts() const;
203 private:
204 QStringList _capturedTexts;
205 };
206
207 /** Constructs a new regular expression filter */
208 RegExpFilter();
209
210 /**
211 * Sets the regular expression which the filter searches for in blocks of text.
212 *
213 * Regular expressions which match the empty string are treated as not matching
214 * anything.
215 */
216 void setRegExp(const QRegExp& text);
217 /** Returns the regular expression which the filter searches for in blocks of text */
218 QRegExp regExp() const;
219
220 /**
221 * Reimplemented to search the filter's text buffer for text matching regExp()
222 *
223 * If regexp matches the empty string, then process() will return immediately
224 * without finding results.
225 */
226 virtual void process();
227
228protected:
229 /**
230 * Called when a match for the regular expression is encountered. Subclasses should reimplement this
231 * to return custom hotspot types
232 */
233 virtual RegExpFilter::HotSpot* newHotSpot(int startLine,int startColumn,
234 int endLine,int endColumn);
235
236private:
237 QRegExp _searchText;
238};
239
240class FilterObject;
241
242/** A filter which matches URLs in blocks of text */
243class UrlFilter : public RegExpFilter
244{
245 Q_OBJECT
246public:
247 /**
248 * Hotspot type created by UrlFilter instances. The activate() method opens a web browser
249 * at the given URL when called.
250 */
251 class HotSpot : public RegExpFilter::HotSpot
252 {
253 public:
254 HotSpot(int startLine,int startColumn,int endLine,int endColumn);
255 virtual ~HotSpot();
256
257 FilterObject* getUrlObject() const;
258
259 virtual QList<QAction*> actions();
260
261 /**
262 * Open a web browser at the current URL. The url itself can be determined using
263 * the capturedTexts() method.
264 */
265 virtual void activate(const QString& action = QString());
266
267 private:
268 enum UrlType
269 {
270 StandardUrl,
271 Email,
272 Unknown
273 };
274 UrlType urlType() const;
275
276 FilterObject* _urlObject;
277 };
278
279 UrlFilter();
280
281protected:
282 virtual RegExpFilter::HotSpot* newHotSpot(int,int,int,int);
283
284private:
285
286 static const QRegExp FullUrlRegExp;
287 static const QRegExp EmailAddressRegExp;
288
289 // combined OR of FullUrlRegExp and EmailAddressRegExp
290 static const QRegExp CompleteUrlRegExp;
291signals:
292 void activated(const QUrl& url, bool fromContextMenu);
293};
294
295class FilterObject : public QObject
296{
297 Q_OBJECT
298public:
299 FilterObject(Filter::HotSpot* filter) : _filter(filter) {}
300
301 void emitActivated(const QUrl& url, bool fromContextMenu);
302public slots:
303 void activate();
304private:
305 Filter::HotSpot* _filter;
306signals:
307 void activated(const QUrl& url, bool fromContextMenu);
308};
309
310/**
311 * A chain which allows a group of filters to be processed as one.
312 * The chain owns the filters added to it and deletes them when the chain itself is destroyed.
313 *
314 * Use addFilter() to add a new filter to the chain.
315 * When new text to be filtered arrives, use addLine() to add each additional
316 * line of text which needs to be processed and then after adding the last line, use
317 * process() to cause each filter in the chain to process the text.
318 *
319 * After processing a block of text, the reset() method can be used to set the filter chain's
320 * internal cursor back to the first line.
321 *
322 * The hotSpotAt() method will return the first hotspot which covers a given position.
323 *
324 * The hotSpots() and hotSpotsAtLine() method return all of the hotspots in the text and on
325 * a given line respectively.
326 */
327class FilterChain : protected QList<Filter*>
328{
329public:
330 virtual ~FilterChain();
331
332 /** Adds a new filter to the chain. The chain will delete this filter when it is destroyed */
333 void addFilter(Filter* filter);
334 /** Removes a filter from the chain. The chain will no longer delete the filter when destroyed */
335 void removeFilter(Filter* filter);
336 /** Returns true if the chain contains @p filter */
337 bool containsFilter(Filter* filter);
338 /** Removes all filters from the chain */
339 void clear();
340
341 /** Resets each filter in the chain */
342 void reset();
343 /**
344 * Processes each filter in the chain
345 */
346 void process();
347
348 /** Sets the buffer for each filter in the chain to process. */
349 void setBuffer(const QString* buffer , const QList<int>* linePositions);
350
351 /** Returns the first hotspot which occurs at @p line, @p column or 0 if no hotspot was found */
352 Filter::HotSpot* hotSpotAt(int line , int column) const;
353 /** Returns a list of all the hotspots in all the chain's filters */
354 QList<Filter::HotSpot*> hotSpots() const;
355 /** Returns a list of all hotspots at the given line in all the chain's filters */
356 QList<Filter::HotSpot> hotSpotsAtLine(int line) const;
357
358};
359
360/** A filter chain which processes character images from terminal displays */
361class TerminalImageFilterChain : public FilterChain
362{
363public:
364 TerminalImageFilterChain();
365 virtual ~TerminalImageFilterChain();
366
367 /**
368 * Set the current terminal image to @p image.
369 *
370 * @param image The terminal image
371 * @param lines The number of lines in the terminal image
372 * @param columns The number of columns in the terminal image
373 * @param lineProperties The line properties to set for image
374 */
375 void setImage(const Character* const image , int lines , int columns,
376 const QVector<LineProperty>& lineProperties);
377
378private:
379 QString* _buffer;
380 QList<int>* _linePositions;
381};
382
383}
384
385typedef Konsole::Filter Filter;
386
387#endif //FILTER_H
388