1 #pragma once
2 
3 #include "providers/colors/ColorProvider.hpp"
4 #include "util/RapidJsonSerializeQString.hpp"
5 #include "util/RapidjsonHelpers.hpp"
6 
7 #include <QColor>
8 #include <QRegularExpression>
9 #include <QString>
10 #include <QUrl>
11 #include <pajlada/serialize.hpp>
12 
13 #include <memory>
14 
15 namespace chatterino {
16 
17 class HighlightPhrase
18 {
19 public:
20     bool operator==(const HighlightPhrase &other) const;
21 
22     /**
23      * @brief Create a new HighlightPhrase.
24      *
25      * Use this constructor when creating a new HighlightPhrase.
26      */
27     HighlightPhrase(const QString &pattern, bool showInMentions, bool hasAlert,
28                     bool hasSound, bool isRegex, bool isCaseSensitive,
29                     const QString &soundUrl, QColor color);
30 
31     /**
32      * @brief Create a new HighlightPhrase.
33      *
34      * Use this constructor when updating an existing HighlightPhrase's color.
35      */
36     HighlightPhrase(const QString &pattern, bool showInMentions, bool hasAlert,
37                     bool hasSound, bool isRegex, bool isCaseSensitive,
38                     const QString &soundUrl, std::shared_ptr<QColor> color);
39 
40     const QString &getPattern() const;
41     bool showInMentions() const;
42     bool hasAlert() const;
43 
44     /**
45      * @brief Check if this highlight phrase should play a sound when
46      *        triggered.
47      *
48      * In distinction from `HighlightPhrase::hasCustomSound`, this method only
49      * checks whether or not ANY sound should be played when the phrase is
50      * triggered.
51      *
52      * To check whether a custom sound is set, use
53      * `HighlightPhrase::hasCustomSound` instead.
54      *
55      * @return true, if this highlight phrase should play a sound when
56      *         triggered, false otherwise
57      */
58     bool hasSound() const;
59 
60     /**
61      * @brief Check if this highlight phrase has a custom sound set.
62      *
63      * Note that this method only checks whether the path to the custom sound
64      * is not empty. It does not check whether the file still exists, is a
65      * sound file, or anything else.
66      *
67      * @return true, if the custom sound file path is not empty, false otherwise
68      */
69     bool hasCustomSound() const;
70 
71     bool isRegex() const;
72     bool isValid() const;
73     bool isMatch(const QString &subject) const;
74     bool isCaseSensitive() const;
75     const QUrl &getSoundUrl() const;
76     const std::shared_ptr<QColor> getColor() const;
77 
78     /*
79      * XXX: Use the constexpr constructor here once we are building with
80      * Qt>=5.13.
81      */
82     static QColor FALLBACK_HIGHLIGHT_COLOR;
83     static QColor FALLBACK_REDEEMED_HIGHLIGHT_COLOR;
84     static QColor FALLBACK_SUB_COLOR;
85 
86 private:
87     QString pattern_;
88     bool showInMentions_;
89     bool hasAlert_;
90     bool hasSound_;
91     bool isRegex_;
92     bool isCaseSensitive_;
93     QUrl soundUrl_;
94     std::shared_ptr<QColor> color_;
95     QRegularExpression regex_;
96 };
97 
98 }  // namespace chatterino
99 
100 namespace pajlada {
101 
102 namespace {
constructError()103     chatterino::HighlightPhrase constructError()
104     {
105         return chatterino::HighlightPhrase(QString(), false, false, false,
106                                            false, false, QString(), QColor());
107     }
108 }  // namespace
109 
110 template <>
111 struct Serialize<chatterino::HighlightPhrase> {
getpajlada::Serialize112     static rapidjson::Value get(const chatterino::HighlightPhrase &value,
113                                 rapidjson::Document::AllocatorType &a)
114     {
115         rapidjson::Value ret(rapidjson::kObjectType);
116 
117         chatterino::rj::set(ret, "pattern", value.getPattern(), a);
118         chatterino::rj::set(ret, "showInMentions", value.showInMentions(), a);
119         chatterino::rj::set(ret, "alert", value.hasAlert(), a);
120         chatterino::rj::set(ret, "sound", value.hasSound(), a);
121         chatterino::rj::set(ret, "regex", value.isRegex(), a);
122         chatterino::rj::set(ret, "case", value.isCaseSensitive(), a);
123         chatterino::rj::set(ret, "soundUrl", value.getSoundUrl().toString(), a);
124         chatterino::rj::set(ret, "color",
125                             value.getColor()->name(QColor::HexArgb), a);
126 
127         return ret;
128     }
129 };
130 
131 template <>
132 struct Deserialize<chatterino::HighlightPhrase> {
getpajlada::Deserialize133     static chatterino::HighlightPhrase get(const rapidjson::Value &value,
134                                            bool *error = nullptr)
135     {
136         if (!value.IsObject())
137         {
138             PAJLADA_REPORT_ERROR(error)
139 
140             return constructError();
141         }
142 
143         QString _pattern;
144         bool _showInMentions = true;
145         bool _hasAlert = true;
146         bool _hasSound = false;
147         bool _isRegex = false;
148         bool _isCaseSensitive = false;
149         QString _soundUrl;
150         QString encodedColor;
151 
152         chatterino::rj::getSafe(value, "pattern", _pattern);
153         chatterino::rj::getSafe(value, "showInMentions", _showInMentions);
154         chatterino::rj::getSafe(value, "alert", _hasAlert);
155         chatterino::rj::getSafe(value, "sound", _hasSound);
156         chatterino::rj::getSafe(value, "regex", _isRegex);
157         chatterino::rj::getSafe(value, "case", _isCaseSensitive);
158         chatterino::rj::getSafe(value, "soundUrl", _soundUrl);
159         chatterino::rj::getSafe(value, "color", encodedColor);
160 
161         auto _color = QColor(encodedColor);
162         if (!_color.isValid())
163             _color = chatterino::HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR;
164 
165         return chatterino::HighlightPhrase(_pattern, _showInMentions, _hasAlert,
166                                            _hasSound, _isRegex,
167                                            _isCaseSensitive, _soundUrl, _color);
168     }
169 };
170 
171 }  // namespace pajlada
172