Filter.h 13.8 KB
Newer Older
1
/*
2
    Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301  USA.
*/

#ifndef FILTER_H
#define FILTER_H

// Qt
24 25 26 27 28 29 30
#include <QFileInfo>
#include <QList>
#include <QObject>
#include <QPointer>
#include <QStringList>
#include <QRegularExpression>
#include <QMultiHash>
31

Jekyll Wu's avatar
Jekyll Wu committed
32
// Konsole
Robert Knight's avatar
 
Robert Knight committed
33 34
#include "Character.h"

35 36
class QAction;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
37
namespace Konsole {
38 39
class Session;

40 41 42 43 44 45 46 47 48 49
/**
 * A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list)
 * and marks the areas which match the filter's patterns as 'hotspots'.
 *
 * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ),
 * and an action.  When the user performs some activity such as a mouse-click in a hotspot area ( the exact
 * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's
 * activate() method should be called.  Depending on the type of hotspot this will trigger a suitable response.
 *
 * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser.
Jekyll Wu's avatar
Jekyll Wu committed
50
 * Hotspots may have more than one action, in which case the list of actions can be obtained using the
51 52 53 54 55 56 57
 * actions() method.
 *
 * Different subclasses of filter will return different types of hotspot.
 * Subclasses must reimplement the process() method to examine a block of text and identify sections of interest.
 * When processing the text they should create instances of Filter::HotSpot subclasses for sections of interest
 * and add them to the filter's list of hotspots using addHotSpot()
 */
58 59 60
class Filter
{
public:
61 62 63 64 65 66 67 68 69
    /**
    * Represents an area of text which matched the pattern a particular filter has been looking for.
    *
    * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ),
    * and an action.  When the user performs some activity such as a mouse-click in a hotspot area ( the exact
    * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's
    * activate() method should be called.  Depending on the type of hotspot this will trigger a suitable response.
    *
    * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser.
Jekyll Wu's avatar
Jekyll Wu committed
70 71
    * Hotspots may have more than one action, in which case the list of actions can be obtained using the
    * actions() method.  These actions may then be displayed in a popup menu or toolbar for example.
72
    */
73 74 75
    class HotSpot
    {
    public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
76 77 78 79
        /**
         * Constructs a new hotspot which covers the area from (@p startLine,@p startColumn) to (@p endLine,@p endColumn)
         * in a block of text.
         */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
80
        HotSpot(int startLine, int startColumn, int endLine, int endColumn);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
81 82 83
        virtual ~HotSpot();

        enum Type {
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
Kurt Hindenburg's avatar
Kurt Hindenburg committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
        };

        /** Returns the line when the hotspot area starts */
        int startLine() const;
        /** Returns the line where the hotspot area ends */
        int endLine() const;
        /** Returns the column on startLine() where the hotspot area starts */
        int startColumn() const;
        /** Returns the column on endLine() where the hotspot area ends */
        int endColumn() const;
        /**
         * Returns the type of the hotspot.  This is usually used as a hint for views on how to represent
         * the hotspot graphically.  eg.  Link hotspots are typically underlined when the user mouses over them
         */
        Type type() const;
        /**
         * Causes the action associated with a hotspot to be triggered.
         *
         * @param object The object which caused the hotspot to be triggered.  This is
         * typically null ( in which case the default action should be performed ) or
         * one of the objects from the actions() list.  In which case the associated
         * action should be performed.
         */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
113
        virtual void activate(QObject *object = 0) = 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
114 115 116 117
        /**
         * Returns a list of actions associated with the hotspot which can be used in a
         * menu or toolbar
         */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
118
        virtual QList<QAction *> actions();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
119

120
    protected:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
121 122
        /** Sets the type of a hotspot.  This should only be set once */
        void setType(Type type);
123 124

    private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
125 126 127 128
        int _startLine;
        int _startColumn;
        int _endLine;
        int _endColumn;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
129
        Type _type;
130 131
    };

132 133
    /** Constructs a new filter. */
    Filter();
134 135
    virtual ~Filter();

136
    /** Causes the filter to process the block of text currently in its internal buffer */
137 138
    virtual void process() = 0;

Jekyll Wu's avatar
Jekyll Wu committed
139
    /**
140
     * Empties the filters internal buffer and resets the line count back to 0.
Jekyll Wu's avatar
Jekyll Wu committed
141
     * All hotspots are deleted.
142
     */
143
    void reset();
144 145

    /** Adds a new line of text to the filter and increments the line count */
146
    //void addLine(const QString& string);
147

148
    /** Returns the hotspot which covers the given @p line and @p column, or 0 if no hotspot covers that area */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
149
    HotSpot *hotSpotAt(int line, int column) const;
150 151

    /** Returns the list of hotspots identified by the filter */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
152
    QList<HotSpot *> hotSpots() const;
153 154

    /** Returns the list of hotspots identified by the filter which occur on a given line */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
155
    QList<HotSpot *> hotSpotsAtLine(int line) const;
156

Jekyll Wu's avatar
Jekyll Wu committed
157
    /**
158 159
     * TODO: Document me
     */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
160
    void setBuffer(const QString *buffer, const QList<int> *linePositions);
161

162
protected:
163
    /** Adds a new hotspot to the list */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
164
    void addHotSpot(HotSpot *);
165
    /** Returns the internal buffer */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
166
    const QString *buffer();
167
    /** Converts a character position within buffer() to a line and column */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
168
    void getLineColumn(int position, int &startLine, int &startColumn);
169 170

private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
171 172
    QMultiHash<int, HotSpot *> _hotspots;
    QList<HotSpot *> _hotspotList;
173

Kurt Hindenburg's avatar
Kurt Hindenburg committed
174 175
    const QList<int> *_linePositions;
    const QString *_buffer;
176 177
};

Jekyll Wu's avatar
Jekyll Wu committed
178 179
/**
 * A filter which searches for sections of text matching a regular expression and creates a new RegExpFilter::HotSpot
180 181 182
 * instance for them.
 *
 * Subclasses can reimplement newHotSpot() to return custom hotspot types when matches for the regular expression
Jekyll Wu's avatar
Jekyll Wu committed
183
 * are found.
184
 */
185 186 187
class RegExpFilter : public Filter
{
public:
Jekyll Wu's avatar
Jekyll Wu committed
188
    /**
189 190 191
     * Type of hotspot created by RegExpFilter.  The capturedTexts() method can be used to find the text
     * matched by the filter's regular expression.
     */
192 193 194
    class HotSpot : public Filter::HotSpot
    {
    public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
195 196 197
        HotSpot(int startLine, int startColumn, int endLine, int endColumn,
                const QStringList &capturedTexts);
        void activate(QObject *object = 0) Q_DECL_OVERRIDE;
198

199
        /** Returns the texts found by the filter when matching the filter's regular expression */
200 201 202 203 204
        QStringList capturedTexts() const;
    private:
        QStringList _capturedTexts;
    };

205
    /** Constructs a new regular expression filter */
206 207
    RegExpFilter();

Jekyll Wu's avatar
Jekyll Wu committed
208 209
    /**
     * Sets the regular expression which the filter searches for in blocks of text.
210 211
     *
     * Regular expressions which match the empty string are treated as not matching
Jekyll Wu's avatar
Jekyll Wu committed
212
     * anything.
213
     */
214
    void setRegExp(const QRegularExpression &text);
215
    /** Returns the regular expression which the filter searches for in blocks of text */
216
    QRegularExpression regExp() const;
217

Jekyll Wu's avatar
Jekyll Wu committed
218 219
    /**
     * Reimplemented to search the filter's text buffer for text matching regExp()
220 221
     *
     * If regexp matches the empty string, then process() will return immediately
Jekyll Wu's avatar
Jekyll Wu committed
222
     * without finding results.
223
     */
224
    void process() Q_DECL_OVERRIDE;
225 226

protected:
Jekyll Wu's avatar
Jekyll Wu committed
227
    /**
228 229 230
     * Called when a match for the regular expression is encountered.  Subclasses should reimplement this
     * to return custom hotspot types
     */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
231 232
    virtual RegExpFilter::HotSpot *newHotSpot(int startLine, int startColumn, int endLine,
                                              int endColumn, const QStringList &capturedTexts);
233 234

private:
235
    QRegularExpression _searchText;
236 237 238
};

class FilterObject;
239 240

/** A filter which matches URLs in blocks of text */
Jekyll Wu's avatar
Jekyll Wu committed
241
class UrlFilter : public RegExpFilter
242 243
{
public:
Jekyll Wu's avatar
Jekyll Wu committed
244 245
    /**
     * Hotspot type created by UrlFilter instances.  The activate() method opens a web browser
246 247
     * at the given URL when called.
     */
Jekyll Wu's avatar
Jekyll Wu committed
248
    class HotSpot : public RegExpFilter::HotSpot
249 250
    {
    public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
251 252
        HotSpot(int startLine, int startColumn, int endLine, int endColumn,
                const QStringList &capturedTexts);
253
        ~HotSpot() Q_DECL_OVERRIDE;
254

Kurt Hindenburg's avatar
Kurt Hindenburg committed
255
        QList<QAction *> actions() Q_DECL_OVERRIDE;
256

Jekyll Wu's avatar
Jekyll Wu committed
257
        /**
258 259 260
         * Open a web browser at the current URL.  The url itself can be determined using
         * the capturedTexts() method.
         */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
261
        void activate(QObject *object = 0) Q_DECL_OVERRIDE;
262

263
    private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
264
        enum UrlType {
265 266 267 268 269 270
            StandardUrl,
            Email,
            Unknown
        };
        UrlType urlType() const;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
271
        FilterObject *_urlObject;
272 273 274 275 276
    };

    UrlFilter();

protected:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
277
    RegExpFilter::HotSpot *newHotSpot(int, int, int, int, const QStringList &) Q_DECL_OVERRIDE;
278 279

private:
280 281
    static const QRegularExpression FullUrlRegExp;
    static const QRegularExpression EmailAddressRegExp;
282 283

    // combined OR of FullUrlRegExp and EmailAddressRegExp
284
    static const QRegularExpression CompleteUrlRegExp;
285 286
};

287 288 289 290 291 292 293 294 295 296 297 298 299
/**
 * A filter which matches files according to POSIX Portable Filename Character Set
 * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267
 */
class FileFilter : public RegExpFilter
{
public:
    /**
     * Hotspot type created by FileFilter instances.
     */
    class HotSpot : public RegExpFilter::HotSpot
    {
    public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
300 301
        HotSpot(int startLine, int startColumn, int endLine, int endColumn,
                const QStringList &capturedTexts, const QFileInfo &file);
302
        ~HotSpot() Q_DECL_OVERRIDE;
303

Kurt Hindenburg's avatar
Kurt Hindenburg committed
304
        QList<QAction *> actions() Q_DECL_OVERRIDE;
305 306 307 308

        /**
         * Opens kate for editing the file.
         */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
309
        void activate(QObject *object = 0) Q_DECL_OVERRIDE;
310 311

    private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
312
        FilterObject *_fileObject;
313 314 315
        QFileInfo _file;
    };

Kurt Hindenburg's avatar
Kurt Hindenburg committed
316
    explicit FileFilter(Session *session);
317 318

protected:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
319
    RegExpFilter::HotSpot *newHotSpot(int, int, int, int, const QStringList &) Q_DECL_OVERRIDE;
320 321 322 323 324

private:
    QPointer<Session> _session;
};

325 326
class FilterObject : public QObject
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
327
    Q_OBJECT
328
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
329 330 331 332
    explicit FilterObject(Filter::HotSpot *filter) : _filter(filter)
    {
    }

333
public Q_SLOTS:
334 335
    void activated();
private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
336
    Filter::HotSpot *_filter;
337 338
};

Jekyll Wu's avatar
Jekyll Wu committed
339 340
/**
 * A chain which allows a group of filters to be processed as one.
341 342
 * The chain owns the filters added to it and deletes them when the chain itself is destroyed.
 *
Jekyll Wu's avatar
Jekyll Wu committed
343
 * Use addFilter() to add a new filter to the chain.
344 345 346 347 348 349 350 351 352 353 354 355
 * When new text to be filtered arrives, use addLine() to add each additional
 * line of text which needs to be processed and then after adding the last line, use
 * process() to cause each filter in the chain to process the text.
 *
 * After processing a block of text, the reset() method can be used to set the filter chain's
 * internal cursor back to the first line.
 *
 * The hotSpotAt() method will return the first hotspot which covers a given position.
 *
 * The hotSpots() and hotSpotsAtLine() method return all of the hotspots in the text and on
 * a given line respectively.
 */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
356
class FilterChain : protected QList<Filter *>
357 358
{
public:
359 360 361
    virtual ~FilterChain();

    /** Adds a new filter to the chain.  The chain will delete this filter when it is destroyed */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
362
    void addFilter(Filter *filter);
363
    /** Removes a filter from the chain.  The chain will no longer delete the filter when destroyed */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
364
    void removeFilter(Filter *filter);
365
    /** Removes all filters from the chain */
366 367
    void clear();

368
    /** Resets each filter in the chain */
369
    void reset();
370
    /**
Jekyll Wu's avatar
Jekyll Wu committed
371
     * Processes each filter in the chain
372
     */
373
    void process();
374

375
    /** Sets the buffer for each filter in the chain to process. */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
376
    void setBuffer(const QString *buffer, const QList<int> *linePositions);
377

378
    /** Returns the first hotspot which occurs at @p line, @p column or 0 if no hotspot was found */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
379
    Filter::HotSpot *hotSpotAt(int line, int column) const;
380
    /** Returns a list of all the hotspots in all the chain's filters */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
381
    QList<Filter::HotSpot *> hotSpots() const;
382
    /** Returns a list of all hotspots at the given line in all the chain's filters */
383 384 385
    QList<Filter::HotSpot> hotSpotsAtLine(int line) const;
};

386
/** A filter chain which processes character images from terminal displays */
387 388 389
class TerminalImageFilterChain : public FilterChain
{
public:
390
    TerminalImageFilterChain();
391
    ~TerminalImageFilterChain() Q_DECL_OVERRIDE;
392

393 394 395 396 397 398
    /**
     * Set the current terminal image to @p image.
     *
     * @param image The terminal image
     * @param lines The number of lines in the terminal image
     * @param columns The number of columns in the terminal image
Kurt Hindenburg's avatar
Kurt Hindenburg committed
399
     * @param lineProperties The line properties to set for image
400
     */
Kurt Hindenburg's avatar
Kurt Hindenburg committed
401 402
    void setImage(const Character * const image, int lines, int columns,
                  const QVector<LineProperty> &lineProperties);
403 404

private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
405 406
    QString *_buffer;
    QList<int> *_linePositions;
407
};
Stephan Binner's avatar
Stephan Binner committed
408
}
409
#endif //FILTER_H