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