1 #include "ChannelFilterEditorDialog.hpp"
2
3 #include "controllers/filters/parser/FilterParser.hpp"
4
5 #include <QLabel>
6 #include <QPushButton>
7
8 namespace chatterino {
9
10 namespace {
11 const QStringList friendlyBinaryOps = {
12 "and", "or", "+", "-", "*", "/",
13 "%", "equals", "not equals", "<", ">", "<=",
14 ">=", "contains", "starts with", "ends with", "(nothing)"};
15 const QStringList realBinaryOps = {
16 "&&", "||", "+", "-", "*", "/",
17 "%", "==", "!=", "<", ">", "<=",
18 ">=", "contains", "startswith", "endswith", ""};
19 } // namespace
20
ChannelFilterEditorDialog(QWidget * parent)21 ChannelFilterEditorDialog::ChannelFilterEditorDialog(QWidget *parent)
22 : QDialog(parent)
23 {
24 auto vbox = new QVBoxLayout(this);
25 auto filterVbox = new QVBoxLayout;
26 auto buttonBox = new QHBoxLayout;
27 auto okButton = new QPushButton("Ok");
28 auto cancelButton = new QPushButton("Cancel");
29
30 okButton->setDefault(true);
31 cancelButton->setDefault(false);
32
33 auto helpLabel =
34 new QLabel(QString("<a href='%1'><span "
35 "style='color:#99f'>variable help</span></a>")
36 .arg("https://wiki.chatterino.com/Filters/#variables"));
37 helpLabel->setOpenExternalLinks(true);
38
39 buttonBox->addWidget(helpLabel);
40 buttonBox->addStretch(1);
41 buttonBox->addWidget(okButton);
42 buttonBox->addWidget(cancelButton);
43
44 QObject::connect(okButton, &QAbstractButton::clicked, [this] {
45 this->accept();
46 this->close();
47 });
48 QObject::connect(cancelButton, &QAbstractButton::clicked, [this] {
49 this->reject();
50 this->close();
51 });
52
53 this->setWindowFlags(
54 (this->windowFlags() & ~(Qt::WindowContextHelpButtonHint)) |
55 Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
56 this->setWindowTitle("Channel Filter Creator");
57
58 auto titleInput = new QLineEdit;
59 titleInput->setPlaceholderText("Filter name");
60 titleInput->setText("My filter");
61
62 this->titleInput_ = titleInput;
63 filterVbox->addWidget(titleInput);
64
65 auto left = new ChannelFilterEditorDialog::ValueSpecifier;
66 auto right = new ChannelFilterEditorDialog::ValueSpecifier;
67 auto exp =
68 new ChannelFilterEditorDialog::BinaryOperationSpecifier(left, right);
69
70 this->expressionSpecifier_ = exp;
71 filterVbox->addLayout(exp->layout());
72 vbox->addLayout(filterVbox);
73 vbox->addLayout(buttonBox);
74
75 // setup default values
76 left->setType("Variable");
77 left->setValue("message.content");
78 exp->setOperation("contains");
79 right->setType("Text");
80 right->setValue("hello");
81 }
82
getFilter() const83 const QString ChannelFilterEditorDialog::getFilter() const
84 {
85 return this->expressionSpecifier_->expressionText();
86 }
87
getTitle() const88 const QString ChannelFilterEditorDialog::getTitle() const
89 {
90 return this->titleInput_->text();
91 }
92
ValueSpecifier()93 ChannelFilterEditorDialog::ValueSpecifier::ValueSpecifier()
94 {
95 this->typeCombo_ = new QComboBox;
96 this->varCombo_ = new QComboBox;
97 this->valueInput_ = new QLineEdit;
98 this->layout_ = new QHBoxLayout;
99
100 this->typeCombo_->insertItems(
101 0, {"Constant Text", "Constant Number", "Variable"});
102 this->varCombo_->insertItems(0, filterparser::validIdentifiersMap.values());
103
104 this->layout_->addWidget(this->typeCombo_);
105 this->layout_->addWidget(this->varCombo_, 1);
106 this->layout_->addWidget(this->valueInput_, 1);
107 this->layout_->setContentsMargins(5, 5, 5, 5);
108
109 QObject::connect(
110 this->typeCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
111 [this](int index) {
112 const auto isNumber = (index == 1);
113 const auto isVariable = (index == 2);
114
115 this->valueInput_->setVisible(!isVariable);
116 this->varCombo_->setVisible(isVariable);
117 this->valueInput_->setValidator(
118 isNumber ? (new QIntValidator(0, INT_MAX)) : nullptr);
119
120 this->valueInput_->clear();
121 });
122
123 this->varCombo_->hide();
124 this->typeCombo_->setCurrentIndex(0);
125 }
126
setEnabled(bool enabled)127 void ChannelFilterEditorDialog::ValueSpecifier::setEnabled(bool enabled)
128 {
129 this->typeCombo_->setEnabled(enabled);
130 this->varCombo_->setEnabled(enabled);
131 this->valueInput_->setEnabled(enabled);
132 }
133
setType(const QString & type)134 void ChannelFilterEditorDialog::ValueSpecifier::setType(const QString &type)
135 {
136 this->typeCombo_->setCurrentText(type);
137 }
138
setValue(const QString & value)139 void ChannelFilterEditorDialog::ValueSpecifier::setValue(const QString &value)
140 {
141 if (this->typeCombo_->currentIndex() == 2)
142 {
143 this->varCombo_->setCurrentText(
144 filterparser::validIdentifiersMap.value(value));
145 }
146 else
147 {
148 this->valueInput_->setText(value);
149 }
150 }
151
layout() const152 QLayout *ChannelFilterEditorDialog::ValueSpecifier::layout() const
153 {
154 return this->layout_;
155 }
156
expressionText()157 QString ChannelFilterEditorDialog::ValueSpecifier::expressionText()
158 {
159 switch (this->typeCombo_->currentIndex())
160 {
161 case 0: // text
162 return QString("\"%1\"").arg(
163 this->valueInput_->text().replace("\"", "\\\""));
164 case 1: // number
165 return this->valueInput_->text();
166 case 2: // variable
167 return filterparser::validIdentifiersMap.key(
168 this->varCombo_->currentText());
169 default:
170 return "";
171 }
172 }
173
BinaryOperationSpecifier(ExpressionSpecifier * left,ExpressionSpecifier * right)174 ChannelFilterEditorDialog::BinaryOperationSpecifier::BinaryOperationSpecifier(
175 ExpressionSpecifier *left, ExpressionSpecifier *right)
176 : left_(left)
177 , right_(right)
178 {
179 this->opCombo_ = new QComboBox;
180 this->layout_ = new QVBoxLayout;
181
182 this->opCombo_->insertItems(0, friendlyBinaryOps);
183
184 this->layout_->addLayout(this->left_->layout());
185 this->layout_->addWidget(this->opCombo_);
186 this->layout_->addLayout(this->right_->layout());
187 this->layout_->setContentsMargins(5, 5, 5, 5);
188
189 QObject::connect(
190 this->opCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
191 [this](int index) {
192 // disable if set to "(nothing)"
193 this->right_->setEnabled(!realBinaryOps.at(index).isEmpty());
194 });
195 }
196
setEnabled(bool enabled)197 void ChannelFilterEditorDialog::BinaryOperationSpecifier::setEnabled(
198 bool enabled)
199 {
200 this->opCombo_->setEnabled(enabled);
201 this->left_->setEnabled(enabled);
202 this->right_->setEnabled(enabled);
203 }
204
setOperation(const QString & op)205 void ChannelFilterEditorDialog::BinaryOperationSpecifier::setOperation(
206 const QString &op)
207 {
208 this->opCombo_->setCurrentText(op);
209 }
210
layout() const211 QLayout *ChannelFilterEditorDialog::BinaryOperationSpecifier::layout() const
212 {
213 return this->layout_;
214 }
215
expressionText()216 QString ChannelFilterEditorDialog::BinaryOperationSpecifier::expressionText()
217 {
218 QString opText = realBinaryOps.at(this->opCombo_->currentIndex());
219 if (opText.isEmpty())
220 {
221 return this->left_->expressionText();
222 }
223
224 return QString("(%1) %2 (%3)")
225 .arg(this->left_->expressionText())
226 .arg(opText)
227 .arg(this->right_->expressionText());
228 }
229
230 } // namespace chatterino
231