uisupport: Provide helpers for dealing with widget changes
[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 #include <utility>
26
27 #include "abstractmessageprocessor.h"
28 #include "expressionmatch.h"
29 #include "nickhighlightmatcher.h"
30
31 class QtUiMessageProcessor : public AbstractMessageProcessor
32 {
33     Q_OBJECT
34
35 public:
36     enum Mode {
37         TimerBased,
38         Concurrent
39     };
40
41     QtUiMessageProcessor(QObject *parent);
42
43     inline bool isProcessing() const { return _processing; }
44     inline Mode processMode() const { return _processMode; }
45
46     void reset() override;
47
48 public slots:
49     void process(Message &msg) override;
50     void process(QList<Message> &msgs) override;
51
52     /**
53      * Network removed from system
54      *
55      * Handles cleaning up cache from stale networks.
56      *
57      * @param id Network ID of removed network
58      */
59     void networkRemoved(NetworkId id) override;
60
61 private slots:
62     void processNextMessage();
63     void nicksCaseSensitiveChanged(const QVariant &variant);
64     void highlightListChanged(const QVariant &variant);
65     void highlightNickChanged(const QVariant &variant);
66
67 private:
68     /**
69      * Individual highlight rule (legacy client-side version)
70      */
71     class LegacyHighlightRule
72     {
73     public:
74         /**
75          * Construct an empty highlight rule
76          */
77         LegacyHighlightRule() = default;
78
79         /**
80          * Construct a highlight rule with the given parameters
81          *
82          * @param contents         String representing a message contents expression to match
83          * @param isRegEx          True if regular expression, otherwise false
84          * @param isCaseSensitive  True if case sensitive, otherwise false
85          * @param isEnabled        True if enabled, otherwise false
86          * @param chanName         String representing a channel name expression to match
87          */
88         LegacyHighlightRule(QString contents, bool isRegEx, bool isCaseSensitive, bool isEnabled,
89                       QString chanName)
90             : _contents(std::move(contents)), _isRegEx(isRegEx), _isCaseSensitive(isCaseSensitive),
91               _isEnabled(isEnabled), _chanName(std::move(chanName))
92         {
93             _cacheInvalid = true;
94             // Cache expression matches on construction
95             //
96             // This provides immediate feedback on errors when loading the rule.  If profiling shows
97             // this as a performance bottleneck, this can be removed in deference to caching on
98             // first use.
99             //
100             // Inversely, if needed for validity checks, caching can be done on every update below
101             // instead of on first use.
102             determineExpressions();
103         }
104
105         /**
106          * Gets the message contents this rule matches
107          *
108          * NOTE: Use HighlightRule::contentsMatcher() for performing matches
109          *
110          * CAUTION: For legacy reasons, "contents" doubles as the identifier for the ignore rule.
111          * Duplicate entries are not allowed.
112          *
113          * @return String representing a phrase or expression to match
114          */
115         inline QString contents() const {
116             return _contents;
117         }
118         /**
119          * Sets the message contents this rule matches
120          *
121          * @param contents String representing a phrase or expression to match
122          */
123         inline void setContents(const QString &contents) {
124             _contents = contents;
125             _cacheInvalid = true;
126         }
127
128         /**
129          * Gets if this is a regular expression rule
130          *
131          * @return True if regular expression, otherwise false
132          */
133         inline bool isRegEx() const {
134             return _isRegEx;
135         }
136         /**
137          * Sets if this rule is a regular expression rule
138          *
139          * @param isRegEx True if regular expression, otherwise false
140          */
141         inline void setIsRegEx(bool isRegEx) {
142             _isRegEx = isRegEx;
143             _cacheInvalid = true;
144         }
145
146         /**
147          * Gets if this rule is case sensitive
148          *
149          * @return True if case sensitive, otherwise false
150          */
151         inline bool isCaseSensitive() const {
152             return _isCaseSensitive;
153         }
154         /**
155          * Sets if this rule is case sensitive
156          *
157          * @param isCaseSensitive True if case sensitive, otherwise false
158          */
159         inline void setIsCaseSensitive(bool isCaseSensitive) {
160             _isCaseSensitive = isCaseSensitive;
161             _cacheInvalid = true;
162         }
163
164         /**
165          * Gets if this rule is enabled and active
166          *
167          * @return True if enabled, otherwise false
168          */
169         inline bool isEnabled() const {
170             return _isEnabled;
171         }
172         /**
173          * Sets if this rule is enabled and active
174          *
175          * @param isEnabled True if enabled, otherwise false
176          */
177         inline void setIsEnabled(bool isEnabled) {
178             _isEnabled = isEnabled;
179         }
180
181         /**
182          * Gets the channel name this rule matches
183          *
184          * NOTE: Use HighlightRule::chanNameMatcher() for performing matches
185          *
186          * @return String representing a phrase or expression to match
187          */
188         inline QString chanName() const {
189             return _chanName;
190         }
191         /**
192          * Sets the channel name this rule matches
193          *
194          * @param chanName String representing a phrase or expression to match
195          */
196         inline void setChanName(const QString &chanName) {
197             _chanName = chanName;
198             _cacheInvalid = true;
199         }
200
201         /**
202          * Gets the expression matcher for the message contents, caching if needed
203          *
204          * @return Expression matcher to compare with message contents
205          */
206         inline ExpressionMatch contentsMatcher() const {
207             if (_cacheInvalid) {
208                 determineExpressions();
209             }
210             return _contentsMatch;
211         }
212
213         /**
214          * Gets the expression matcher for the channel name, caching if needed
215          *
216          * @return Expression matcher to compare with channel name
217          */
218         inline ExpressionMatch chanNameMatcher() const {
219             if (_cacheInvalid) {
220                 determineExpressions();
221             }
222             return _chanNameMatch;
223         }
224
225         bool operator!=(const LegacyHighlightRule &other) const;
226
227     private:
228         /**
229          * Update internal cache of expression matching if needed
230          */
231         void determineExpressions() const;
232
233         QString _contents = {};
234         bool _isRegEx = false;
235         bool _isCaseSensitive = false;
236         bool _isEnabled = true;
237         QString _chanName = {};
238
239         // These represent internal cache and should be safe to mutate in 'const' functions
240         // See https://stackoverflow.com/questions/3141087/what-is-meant-with-const-at-end-of-function-declaration
241         mutable bool _cacheInvalid = true;           ///< If true, match cache needs redone
242         mutable ExpressionMatch _contentsMatch = {}; ///< Expression match cache for message content
243         mutable ExpressionMatch _chanNameMatch = {}; ///< Expression match cache for channel name
244     };
245
246     using LegacyHighlightRuleList = QList<LegacyHighlightRule>;
247
248     void checkForHighlight(Message &msg);
249     void startProcessing();
250
251     using HighlightNickType = NotificationSettings::HighlightNickType;
252
253     LegacyHighlightRuleList _highlightRuleList; ///< Custom highlight rule list
254     NickHighlightMatcher _nickMatcher = {};     ///< Nickname highlight matcher
255
256     /// Nickname highlighting mode
257     HighlightNickType _highlightNick = HighlightNickType::CurrentNick;
258     bool _nicksCaseSensitive = false; ///< If true, match nicknames with exact case
259
260     QList<QList<Message> > _processQueue;
261     QList<Message> _currentBatch;
262     QTimer _processTimer;
263     bool _processing;
264     Mode _processMode;
265 };
266
267
268 #endif