d8559d362f8de405a0bf8d1ea8420bd1a684125c
[quassel.git] / src / qtui / qtuimessageprocessor.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #ifndef QTUIMESSAGEPROCESSOR_H_
22 #define QTUIMESSAGEPROCESSOR_H_
23
24 #include <QTimer>
25
26 #include "abstractmessageprocessor.h"
27 #include "expressionmatch.h"
28
29 class QtUiMessageProcessor : public AbstractMessageProcessor
30 {
31     Q_OBJECT
32
33 public:
34     enum Mode {
35         TimerBased,
36         Concurrent
37     };
38
39     QtUiMessageProcessor(QObject *parent);
40
41     inline bool isProcessing() const { return _processing; }
42     inline Mode processMode() const { return _processMode; }
43
44     void reset();
45
46 public slots:
47     void process(Message &msg);
48     void process(QList<Message> &msgs);
49
50 private slots:
51     void processNextMessage();
52     void nicksCaseSensitiveChanged(const QVariant &variant);
53     void highlightListChanged(const QVariant &variant);
54     void highlightNickChanged(const QVariant &variant);
55
56 private:
57     /**
58      * Individual highlight rule (legacy client-side version)
59      */
60     class LegacyHighlightRule
61     {
62     public:
63         /**
64          * Construct an empty highlight rule
65          */
66         LegacyHighlightRule() {}
67
68         /**
69          * Construct a highlight rule with the given parameters
70          *
71          * @param contents         String representing a message contents expression to match
72          * @param isRegEx          True if regular expression, otherwise false
73          * @param isCaseSensitive  True if case sensitive, otherwise false
74          * @param isEnabled        True if enabled, otherwise false
75          * @param chanName         String representing a channel name expression to match
76          */
77         LegacyHighlightRule(QString contents, bool isRegEx, bool isCaseSensitive, bool isEnabled,
78                       QString chanName)
79             : _contents(contents), _isRegEx(isRegEx), _isCaseSensitive(isCaseSensitive),
80               _isEnabled(isEnabled), _chanName(chanName)
81         {
82             _cacheInvalid = true;
83             // Cache expression matches on construction
84             //
85             // This provides immediate feedback on errors when loading the rule.  If profiling shows
86             // this as a performance bottleneck, this can be removed in deference to caching on
87             // first use.
88             //
89             // Inversely, if needed for validity checks, caching can be done on every update below
90             // instead of on first use.
91             determineExpressions();
92         }
93
94         /**
95          * Gets the message contents this rule matches
96          *
97          * NOTE: Use HighlightRule::contentsMatcher() for performing matches
98          *
99          * CAUTION: For legacy reasons, "contents" doubles as the identifier for the ignore rule.
100          * Duplicate entries are not allowed.
101          *
102          * @return String representing a phrase or expression to match
103          */
104         inline QString contents() const {
105             return _contents;
106         }
107         /**
108          * Sets the message contents this rule matches
109          *
110          * @param contents String representing a phrase or expression to match
111          */
112         inline void setContents(const QString &contents) {
113             _contents = contents;
114             _cacheInvalid = true;
115         }
116
117         /**
118          * Gets if this is a regular expression rule
119          *
120          * @return True if regular expression, otherwise false
121          */
122         inline bool isRegEx() const {
123             return _isRegEx;
124         }
125         /**
126          * Sets if this rule is a regular expression rule
127          *
128          * @param isRegEx True if regular expression, otherwise false
129          */
130         inline void setIsRegEx(bool isRegEx) {
131             _isRegEx = isRegEx;
132             _cacheInvalid = true;
133         }
134
135         /**
136          * Gets if this rule is case sensitive
137          *
138          * @return True if case sensitive, otherwise false
139          */
140         inline bool isCaseSensitive() const {
141             return _isCaseSensitive;
142         }
143         /**
144          * Sets if this rule is case sensitive
145          *
146          * @param isCaseSensitive True if case sensitive, otherwise false
147          */
148         inline void setIsCaseSensitive(bool isCaseSensitive) {
149             _isCaseSensitive = isCaseSensitive;
150             _cacheInvalid = true;
151         }
152
153         /**
154          * Gets if this rule is enabled and active
155          *
156          * @return True if enabled, otherwise false
157          */
158         inline bool isEnabled() const {
159             return _isEnabled;
160         }
161         /**
162          * Sets if this rule is enabled and active
163          *
164          * @param isEnabled True if enabled, otherwise false
165          */
166         inline void setIsEnabled(bool isEnabled) {
167             _isEnabled = isEnabled;
168         }
169
170         /**
171          * Gets the channel name this rule matches
172          *
173          * NOTE: Use HighlightRule::chanNameMatcher() for performing matches
174          *
175          * @return String representing a phrase or expression to match
176          */
177         inline QString chanName() const {
178             return _chanName;
179         }
180         /**
181          * Sets the channel name this rule matches
182          *
183          * @param chanName String representing a phrase or expression to match
184          */
185         inline void setChanName(const QString &chanName) {
186             _chanName = chanName;
187             _cacheInvalid = true;
188         }
189
190         /**
191          * Gets the expression matcher for the message contents, caching if needed
192          *
193          * @return Expression matcher to compare with message contents
194          */
195         inline ExpressionMatch contentsMatcher() const {
196             if (_cacheInvalid) {
197                 determineExpressions();
198             }
199             return _contentsMatch;
200         }
201
202         /**
203          * Gets the expression matcher for the channel name, caching if needed
204          *
205          * @return Expression matcher to compare with channel name
206          */
207         inline ExpressionMatch chanNameMatcher() const {
208             if (_cacheInvalid) {
209                 determineExpressions();
210             }
211             return _chanNameMatch;
212         }
213
214         bool operator!=(const LegacyHighlightRule &other) const;
215
216     private:
217         /**
218          * Update internal cache of expression matching if needed
219          */
220         void determineExpressions() const;
221
222         QString _contents = {};
223         bool _isRegEx = false;
224         bool _isCaseSensitive = false;
225         bool _isEnabled = true;
226         QString _chanName = {};
227
228         // These represent internal cache and should be safe to mutate in 'const' functions
229         // See https://stackoverflow.com/questions/3141087/what-is-meant-with-const-at-end-of-function-declaration
230         mutable bool _cacheInvalid = true;           ///< If true, match cache needs redone
231         mutable ExpressionMatch _contentsMatch = {}; ///< Expression match cache for message content
232         mutable ExpressionMatch _chanNameMatch = {}; ///< Expression match cache for channel name
233     };
234
235     using LegacyHighlightRuleList = QList<LegacyHighlightRule>;
236
237     void checkForHighlight(Message &msg);
238     void startProcessing();
239
240     using HighlightNickType = NotificationSettings::HighlightNickType;
241
242     /**
243      * Update internal cache of expression matching if needed
244      */
245     void determineNickExpressions(const QString &currentNick,
246                                   const QStringList identityNicks) const;
247
248     /**
249      * Check if nickname matching cache is invalid
250      * @param currentNick
251      * @param identityNicks
252      * @return
253      */
254     bool cacheNickInvalid(const QString &currentNick, const QStringList identityNicks) const {
255         if (_cacheNickConfigInvalid) return true;
256         if (_cachedNickCurrent != currentNick) return true;
257         if (_cachedIdentityNicks != identityNicks) return true;
258     }
259
260     LegacyHighlightRuleList _highlightRuleList;
261     HighlightNickType _highlightNick = HighlightNickType::CurrentNick;
262     bool _nicksCaseSensitive = false;
263
264     // These represent internal cache and should be safe to mutate in 'const' functions
265     mutable bool _cacheNickConfigInvalid = true;     ///< If true, nick match cache needs redone
266     mutable QString _cachedNickCurrent = {};         ///< Last cached current nick
267     mutable QStringList _cachedIdentityNicks = {};   ///< Last cached identity nicks
268     mutable ExpressionMatch _cachedNickMatcher = {}; ///< Expression match cache for nicks
269
270     QList<QList<Message> > _processQueue;
271     QList<Message> _currentBatch;
272     QTimer _processTimer;
273     bool _processing;
274     Mode _processMode;
275 };
276
277
278 #endif