]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/dialogs/DomainSettingsDialog.cpp
Add zoom factor domain settings.
[PrivacyBrowserPC.git] / src / dialogs / DomainSettingsDialog.cpp
1 /*
2  * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
5  *
6  * Privacy Browser PC is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser PC is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser PC.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 // Application headers.
21 #include "DomainSettingsDialog.h"
22 #include "Settings.h"
23 #include "ui_DomainSettingsDialog.h"
24 #include "helpers/DomainsDatabaseHelper.h"
25 #include "helpers/UserAgentHelper.h"
26
27 // Qt toolkit headers.
28 #include <QInputDialog>
29 #include <QMessageBox>
30 #include <QPushButton>
31
32 DomainSettingsDialog::DomainSettingsDialog(QWidget *parent) : QDialog(parent)
33 {
34     // Instantiate the domain settings view UI.
35     Ui::DomainSettingsDialog domainSettingsDialogUi;
36
37     // Setup the UI.
38     domainSettingsDialogUi.setupUi(this);
39
40     // Get handles for the views.
41     domainsListViewPointer = domainSettingsDialogUi.domainsListView;
42     domainSettingsWidgetPointer = domainSettingsDialogUi.domainSettingsWidget;
43     domainNameLineEditPointer = domainSettingsDialogUi.domainNameLineEdit;
44     javaScriptComboBoxPointer = domainSettingsDialogUi.javaScriptComboBox;
45     javaScriptLabelPointer = domainSettingsDialogUi.javaScriptLabel;
46     userAgentComboBoxPointer = domainSettingsDialogUi.userAgentComboBox;
47     userAgentLabelPointer = domainSettingsDialogUi.userAgentLabel;
48     zoomFactorComboBoxPointer = domainSettingsDialogUi.zoomFactorComboBox;
49     customZoomFactorSpinBoxPointer = domainSettingsDialogUi.customZoomFactorSpinBox;
50     QPushButton *addDomainButtonPointer = domainSettingsDialogUi.addDomainButton;
51     deleteDomainButtonPointer = domainSettingsDialogUi.deleteDomainButton;
52     QDialogButtonBox *dialogButtonBoxPointer = domainSettingsDialogUi.dialogButtonBox;
53     applyButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Apply);
54     resetButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Reset);
55
56     // Create a table model.
57     domainsTableModelPointer = new QSqlTableModel(nullptr, QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME));
58
59     // Set the table for the model.
60     domainsTableModelPointer->setTable(DomainsDatabaseHelper::DOMAINS_TABLE);
61
62     // Set the edit strategy to be manual.
63     domainsTableModelPointer->setEditStrategy(QSqlTableModel::EditStrategy::OnManualSubmit);
64
65     // Sort the output alphabetically.
66     domainsTableModelPointer->setSort(1, Qt::SortOrder::AscendingOrder);
67
68     // Set the model for the list view.
69     domainsListViewPointer->setModel(domainsTableModelPointer);
70
71     // Set the visible column to be the domain name.
72     domainsListViewPointer->setModelColumn(1);
73
74     // Disable editing of the list view.
75     domainsListViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers);
76
77     // Read the data from the database and apply it to the table model.
78     domainsTableModelPointer->select();
79
80     // Select the first entry in the list view.
81     domainsListViewPointer->setCurrentIndex(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
82
83     // Populate the domain settings.
84     domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
85
86     // Handle clicks on the domains.
87     connect(domainsListViewPointer, SIGNAL(activated(QModelIndex)), this, SLOT(domainSelected(QModelIndex)));
88
89     // Connect the domain settings.
90     connect(domainNameLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(domainNameChanged(QString)));
91     connect(javaScriptComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(javaScriptChanged(int)));
92     connect(userAgentComboBoxPointer, SIGNAL(currentTextChanged(QString)), this, SLOT(userAgentChanged(QString)));
93     connect(zoomFactorComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(zoomFactorComboBoxChanged(int)));
94     connect(customZoomFactorSpinBoxPointer, SIGNAL(valueChanged(double)), this, SLOT(customZoomFactorChanged(double)));
95
96     // Connect the buttons.
97     connect(addDomainButtonPointer, SIGNAL(released()), this, SLOT(showAddMessageBox()));
98     connect(deleteDomainButtonPointer, SIGNAL(released()), this, SLOT(showDeleteMessageBox()));
99     connect(resetButtonPointer, SIGNAL(released()), this, SLOT(reset()));
100     connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(ok()));
101     connect(applyButtonPointer, SIGNAL(released()), this, SLOT(apply()));
102     connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(cancel()));
103
104     // Update the UI.
105     updateUi();
106 }
107
108 void DomainSettingsDialog::apply() const
109 {
110     // Get the current index.
111     QModelIndex currentIndex = domainsListViewPointer->currentIndex();
112
113     // Get the ID of the current index row.
114     QVariant currentId = currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)).data();
115
116     // Submit all pending changes.
117     domainsTableModelPointer->submitAll();
118
119     // Find the new index for the selected id.  The `1` keeps searching after the first match.
120     QModelIndexList newIndexList = domainsTableModelPointer->match(currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)), Qt::DisplayRole, currentId,
121                                                                    1, Qt::MatchWrap);
122
123     // Select the new index.
124     domainsListViewPointer->setCurrentIndex(newIndexList[0].siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
125
126     // Update the UI.
127     updateUi();
128
129     // Emit the domain settings updated signal.
130     emit domainSettingsUpdated();
131 }
132
133 void DomainSettingsDialog::cancel()
134 {
135     // Revert all pending changes.
136     domainsTableModelPointer->revertAll();
137
138     // Close the dialog.
139     reject();
140 }
141
142 void DomainSettingsDialog::customZoomFactorChanged(const double &newValue) const
143 {
144     // Update the domains table model.
145     domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR)),
146                                       newValue);
147
148     // Update the UI.
149     updateUi();
150 }
151
152
153 void DomainSettingsDialog::domainNameChanged(const QString &updatedDomainName) const
154 {
155     // Update the domains table model.
156     domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex(), updatedDomainName);
157
158     // Update the UI.
159     updateUi();
160 }
161
162
163 void DomainSettingsDialog::domainSelected(const QModelIndex &modelIndex) const
164 {
165     // Populate the domain name line edit pointer.
166     domainNameLineEditPointer->setText(modelIndex.data().toString());
167
168     // Populate the JavaScript combo box.
169     javaScriptComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)).data().toInt());
170
171     // Get the user agent string.
172     QString userAgent = modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT)).data().toString();
173
174     // Get the user agent index.
175     int userAgentIndex = UserAgentHelper::getDomainSettingsUserAgentIndex(userAgent);
176
177     // Set the user agent combo box index.
178     userAgentComboBoxPointer->setCurrentIndex(userAgentIndex);
179
180     // Set the custom user agent if specified.
181     if (userAgentIndex == -1) userAgentComboBoxPointer->setCurrentText(userAgent);
182
183     // Get the zoom factor combo box index.
184     int zoomFactorComboBoxIndex = modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR)).data().toInt();
185
186     // Populate the zoom factor combo box.
187     zoomFactorComboBoxPointer->setCurrentIndex(zoomFactorComboBoxIndex);
188
189     // Populate the custom zoom factor spin box.
190     customZoomFactorSpinBoxPointer->setValue(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR)).data().toDouble());
191
192     // Set the initial visibility of the custom zoom factor spin box.
193     customZoomFactorSpinBoxPointer->setVisible(zoomFactorComboBoxIndex);
194
195     // Populate the labels.
196     populateJavaScriptLabel();
197     populateUserAgentLabel(userAgentComboBoxPointer->currentText());
198
199     // Update the UI.
200     updateUi();
201 }
202
203 void DomainSettingsDialog::javaScriptChanged(const int &newIndex) const
204 {
205     // Update the domains table model.
206     domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)),
207                                       newIndex);
208
209     // Populate the JavaScript label.
210     populateJavaScriptLabel();
211
212     // Update the UI.
213     updateUi();
214 }
215
216
217 void DomainSettingsDialog::ok()
218 {
219     // Submit all pending changes.
220     domainsTableModelPointer->submitAll();
221
222     // Emit the domain settings updated signal.
223     domainSettingsUpdated();
224
225     // Close the dialog.
226     accept();
227 }
228
229 void DomainSettingsDialog::populateJavaScriptLabel() const
230 {
231     // Populate the label according to the currently selected index.
232     switch (javaScriptComboBoxPointer->currentIndex())
233     {
234         case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
235         {
236             // Set the text according to the system default.
237             if (Settings::javaScript())
238             {
239                 javaScriptLabelPointer->setText(i18nc("Domains settings labels", "JavaScript enabled"));
240             }
241             else
242             {
243                 javaScriptLabelPointer->setText(i18nc("Domain settings labels", "JavaScript disabled"));
244             }
245
246             break;
247         }
248
249         case (DomainsDatabaseHelper::DISABLED):
250         {
251             // Set the label text in bold.
252             javaScriptLabelPointer->setText(i18nc("Domain settings labels.  The <strong> tags should be retained.", "<strong>JavaScript disabled</strong>"));
253
254             break;
255         }
256
257         case (DomainsDatabaseHelper::ENABLED):
258         {
259             // Set the label text in bold.
260             javaScriptLabelPointer->setText(i18nc("Domains settings labels.  The <strong> tags should be retained.", "<strong>JavaScript enabled</strong>"));
261
262             break;
263         }
264     }
265 }
266
267 void DomainSettingsDialog::populateUserAgentLabel(const QString &userAgentName) const
268 {
269     // Populate the label according to the type.
270     if (userAgentName == UserAgentHelper::SYSTEM_DEFAULT_TRANSLATED)
271     {
272         // Display the system default user agent name.
273         userAgentLabelPointer->setText(UserAgentHelper::getTranslatedUserAgentName(Settings::userAgent()));
274     }
275     else
276     {
277         // Display the user agent name in bold.
278         userAgentLabelPointer->setText("<strong>" + userAgentName + "</strong>");
279     }
280 }
281
282 void DomainSettingsDialog::reset() const
283 {
284     // Cancel all pending changes.
285     domainsTableModelPointer->revertAll();
286
287     // Repopulate the domain settings.
288     domainSelected(domainsListViewPointer->currentIndex());
289
290     // Update the UI.
291     updateUi();
292 }
293
294 void DomainSettingsDialog::showAddMessageBox()
295 {
296     // Create an OK flag.
297     bool okClicked;
298
299     // Display a dialog to request the new domain name from the user.
300     QString newDomainName = QInputDialog::getText(this, i18nc("Add domain dialog title", "Add Domain"),
301                                                   i18nc("Add domain message.  The \n\n are newline codes that should be retained",
302                                                         "Add a new domain.  Doing so will also save any pending changes that have been made to other domains.\n\n"
303                                                         "*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)."),
304                                                   QLineEdit::Normal, QString(), &okClicked);
305
306     // Add the new domain if the user clicked OK.
307     if (okClicked)
308     {
309         // Create a new domain record.
310         QSqlRecord newDomainRecord = QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME).record(DomainsDatabaseHelper::DOMAINS_TABLE);
311
312         // Set the values for the new domain.
313         newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME), newDomainName);
314         newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT), DomainsDatabaseHelper::SYSTEM_DEFAULT);
315         newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT), UserAgentHelper::SYSTEM_DEFAULT_DATABASE);
316         newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR), DomainsDatabaseHelper::SYSTEM_DEFAULT);
317         newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR), 1.0);
318
319         // Insert the new domain.  `-1` appends it to the end.
320         domainsTableModelPointer->insertRecord(-1, newDomainRecord);
321
322         // Submit all pending changes.
323         domainsTableModelPointer->submitAll();
324
325         // Find the index for the new domain.  `-1` allows for multiple entries to be returned.
326         QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)),
327                                                                          Qt::DisplayRole, newDomainName, -1, Qt::MatchWrap);
328
329         // Move to the new domain.  If there are multiple domains with the same name, the new one should be the last in the list.
330         domainsListViewPointer->setCurrentIndex(newDomainIndex[newDomainIndex.size() - 1]);
331
332         // Populate the domain settings.
333         domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
334
335         // Update the UI.
336         updateUi();
337     }
338 }
339
340 void DomainSettingsDialog::showDeleteMessageBox() const
341 {
342     // Instantiate a delete dialog message box.
343     QMessageBox deleteDialogMessageBox;
344
345     // Set the icon.
346     deleteDialogMessageBox.setIcon(QMessageBox::Warning);
347
348     // Set the window title.
349     deleteDialogMessageBox.setWindowTitle(i18nc("Delete domain dialog title", "Delete Domain"));
350
351     // Set the text.
352     deleteDialogMessageBox.setText(i18nc("Delete domain main message", "Delete the current domain?"));
353
354     // Set the informative text.
355     deleteDialogMessageBox.setInformativeText(i18nc("Delete domain secondary message", "Doing so will also save any pending changes that have been made to other domains."));
356
357     // Set the standard buttons.
358     deleteDialogMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
359
360     // Set the default button.
361     deleteDialogMessageBox.setDefaultButton(QMessageBox::No);
362
363     // Display the dialog and capture the return value.
364     int returnValue = deleteDialogMessageBox.exec();
365
366     if (returnValue == QMessageBox::Yes)
367     {
368         // Get the current index.
369         QModelIndex currentIndex = domainsListViewPointer->currentIndex();
370
371         // Delete the current row.
372         domainsTableModelPointer->removeRow(domainsListViewPointer->selectionModel()->currentIndex().row());
373
374         // Submit all pending changes.
375         domainsTableModelPointer->submitAll();
376
377         // Select the row next to the deleted item if one exists.
378         if (domainsTableModelPointer->rowCount() > 0)
379         {
380             // Check the row of the deleted item.
381             if (currentIndex.row() == 0)  // The first row was deleted.
382             {
383                 // Reselect the current index.
384                 domainsListViewPointer->setCurrentIndex(currentIndex);
385             }
386             else  // A subsequent row was deleted.
387             {
388                 // Select the crow above the deleted itemm.
389                 domainsListViewPointer->setCurrentIndex(currentIndex.siblingAtRow(currentIndex.row() - 1));
390             }
391
392             // Populate the domain settings.
393             domainSelected(domainsListViewPointer->currentIndex());
394         }
395
396         // Update the Ui.
397         updateUi();
398     }
399 }
400
401 void DomainSettingsDialog::updateUi() const
402 {
403     // Update the delete button status.
404     deleteDomainButtonPointer->setEnabled(domainsListViewPointer->selectionModel()->hasSelection());
405
406     // Update the apply button status.
407     applyButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
408
409     // Update the reset button status.
410     resetButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
411
412     // Display the domain settings if there is at least one domain.
413     domainSettingsWidgetPointer->setVisible(domainsTableModelPointer->rowCount() > 0);
414 }
415
416 void DomainSettingsDialog::userAgentChanged(const QString &updatedUserAgent) const
417 {
418     // Update the domains table model.
419     domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::USER_AGENT)),
420                                       UserAgentHelper::getDatabaseUserAgentName(updatedUserAgent));
421
422     // Populate the user agent label.
423     populateUserAgentLabel(updatedUserAgent);
424
425     // Update the UI.
426     updateUi();
427 }
428
429 void DomainSettingsDialog::zoomFactorComboBoxChanged(const int &newIndex) const
430 {
431     // Update the domains table model.
432     domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::ZOOM_FACTOR)),
433                                       newIndex);
434
435     // Update the visibility of the custom zoom factor spin box.
436     customZoomFactorSpinBoxPointer->setVisible(newIndex);
437
438     // Update the UI.
439     updateUi();
440 }