0ebfb1448b85cf822dab246992fa02bcd65df0a3
[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 "ui_DomainSettingsDialog.h"
23 #include "helpers/DomainsDatabaseHelper.h"
24
25 // Qt toolkit headers.
26 #include <QInputDialog>
27 #include <QMessageBox>
28 #include <QPushButton>
29
30 DomainSettingsDialog::DomainSettingsDialog(QWidget *parent) : QDialog(parent)
31 {
32     // Instantiate the domain settings view UI.
33     Ui::DomainSettingsDialog domainSettingsDialogUi;
34
35     // Setup the UI.
36     domainSettingsDialogUi.setupUi(this);
37
38     // Get handles for the views.
39     domainsListViewPointer = domainSettingsDialogUi.domainsListView;
40     domainSettingsWidgetPointer = domainSettingsDialogUi.domainSettingsWidget;
41     domainNameLineEditPointer = domainSettingsDialogUi.domainNameLineEdit;
42     javaScriptComboBoxPointer = domainSettingsDialogUi.javaScriptComboBox;
43     QPushButton *addDomainButtonPointer = domainSettingsDialogUi.addDomainButton;
44     deleteDomainButtonPointer = domainSettingsDialogUi.deleteDomainButton;
45     QDialogButtonBox *dialogButtonBoxPointer = domainSettingsDialogUi.dialogButtonBox;
46     applyButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Apply);
47     resetButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::StandardButton::Reset);
48
49     // Create a table model.
50     domainsTableModelPointer = new QSqlTableModel(nullptr, QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME));
51
52     // Set the table for the model.
53     domainsTableModelPointer->setTable(DomainsDatabaseHelper::DOMAINS_TABLE);
54
55     // Set the edit strategy to be manual.
56     domainsTableModelPointer->setEditStrategy(QSqlTableModel::EditStrategy::OnManualSubmit);
57
58     // Sort the output alphabetically.
59     domainsTableModelPointer->setSort(1, Qt::SortOrder::AscendingOrder);
60
61     // Set the model for the list view.
62     domainsListViewPointer->setModel(domainsTableModelPointer);
63
64     // Set the visible column to be the domain name.
65     domainsListViewPointer->setModelColumn(1);
66
67     // Disable editing of the list view.
68     domainsListViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers);
69
70     // Read the data from the database and apply it to the table model.
71     domainsTableModelPointer->select();
72
73     // Select the first entry in the list view.
74     domainsListViewPointer->setCurrentIndex(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
75
76     // Populate the domain settings.
77     domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
78
79     // Handle clicks on the domains.
80     connect(domainsListViewPointer, SIGNAL(activated(QModelIndex)), this, SLOT(domainSelected(QModelIndex)));
81
82     // Connect the domain settings.
83     connect(domainNameLineEditPointer, SIGNAL(textEdited(QString)), this, SLOT(domainNameChanged(QString)));
84     connect(javaScriptComboBoxPointer, SIGNAL(currentIndexChanged(int)), this, SLOT(javaScriptChanged(int)));
85
86     // Connect the buttons.
87     connect(addDomainButtonPointer, SIGNAL(released()), this, SLOT(showAddMessageBox()));
88     connect(deleteDomainButtonPointer, SIGNAL(released()), this, SLOT(showDeleteMessageBox()));
89     connect(resetButtonPointer, SIGNAL(released()), this, SLOT(reset()));
90     connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(ok()));
91     connect(applyButtonPointer, SIGNAL(released()), this, SLOT(apply()));
92     connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(cancel()));
93
94     // Update the UI.
95     updateUi();
96 }
97
98 void DomainSettingsDialog::apply() const
99 {
100     // Get the current index.
101     QModelIndex currentIndex = domainsListViewPointer->currentIndex();
102
103     // Get the ID of the current index row.
104     QVariant currentId = currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)).data();
105
106     // Submit all pending changes.
107     domainsTableModelPointer->submitAll();
108
109     // Find the new index for the selected id.  The `1` keeps searching after the first match.
110     QModelIndexList newIndexList = domainsTableModelPointer->match(currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::_ID)), Qt::DisplayRole, currentId,
111                                                                    1, Qt::MatchWrap);
112
113     // Select the new index.
114     domainsListViewPointer->setCurrentIndex(newIndexList[0].siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)));
115
116     // Update the UI.
117     updateUi();
118 }
119
120 void DomainSettingsDialog::cancel()
121 {
122     // Revert all pending changes.
123     domainsTableModelPointer->revertAll();
124
125     // Close the dialog.
126     reject();
127 }
128
129 void DomainSettingsDialog::domainNameChanged(QString updatedDomainName) const
130 {
131     // Update the domains table model.
132     domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex(), updatedDomainName);
133
134     // Update the UI.
135     updateUi();
136 }
137
138
139 void DomainSettingsDialog::domainSelected(QModelIndex modelIndex) const
140 {
141     // Populate the domain name line edit pointer.
142     domainNameLineEditPointer->setText(modelIndex.data().toString());
143
144     // Populate the JavaScript combo box.
145     javaScriptComboBoxPointer->setCurrentIndex(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)).data().toInt());
146
147     // Update the UI.
148     updateUi();
149 }
150
151 void DomainSettingsDialog::javaScriptChanged(int newIndex) const
152 {
153     // Update the domains table model.
154     domainsTableModelPointer->setData(domainsListViewPointer->selectionModel()->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT)),
155                                       newIndex);
156
157     // Update the UI.
158     updateUi();
159 }
160
161
162 void DomainSettingsDialog::ok()
163 {
164     // Submit all pending changes.
165     domainsTableModelPointer->submitAll();
166
167     // Close the dialog.
168     accept();
169 }
170
171 void DomainSettingsDialog::reset() const
172 {
173     // Cancel all pending changes.
174     domainsTableModelPointer->revertAll();
175
176     // Repopulate the domain name line edit.
177     domainNameLineEditPointer->setText(domainsListViewPointer->currentIndex().data().toString());
178
179     // Update the UI.
180     updateUi();
181 }
182
183 void DomainSettingsDialog::showAddMessageBox()
184 {
185     // Create an OK flag.
186     bool okClicked;
187
188     // Display a dialog to request the new domain name from the user.
189     QString newDomainName = QInputDialog::getText(this, i18nc("Add domain dialog title", "Add Domain"),
190                                                   i18nc("Add domain message.  The \n\n are newline codes that should be retained",
191                                                         "Add a new domain.  Doing so will also save any pending changes that have been made to other domains.\n\n"
192                                                         "*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)."),
193                                                   QLineEdit::Normal, QString(), &okClicked);
194
195     // Add the new domain if the user clicked OK.
196     if (okClicked)
197     {
198         // Create a new domain record.
199         QSqlRecord newDomainRecord = QSqlDatabase::database(DomainsDatabaseHelper::CONNECTION_NAME).record(DomainsDatabaseHelper::DOMAINS_TABLE);
200
201         // Add the new domain name.
202         newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME), newDomainName);
203
204         // Set the default value of `0` for the other columns.
205         newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::JAVASCRIPT), 0);
206
207         // Insert the new domain.  `-1` appends it to the end.
208         domainsTableModelPointer->insertRecord(-1, newDomainRecord);
209
210         // Submit all pending changes.
211         domainsTableModelPointer->submitAll();
212
213         // Find the index for the new domain.  `-1` allows for multiple entries to be returned.
214         QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabaseHelper::DOMAIN_NAME)),
215                                                                          Qt::DisplayRole, newDomainName, -1, Qt::MatchWrap);
216
217         // Move to the new domain.  If there are multiple domains with the same name, the new one should be the last in the list.
218         domainsListViewPointer->setCurrentIndex(newDomainIndex[newDomainIndex.size() - 1]);
219
220         // Populate the domain settings.
221         domainSelected(domainsListViewPointer->selectionModel()->currentIndex());
222
223         // Update the UI.
224         updateUi();
225     }
226 }
227
228 void DomainSettingsDialog::showDeleteMessageBox() const
229 {
230     // Instantiate a delete dialog message box.
231     QMessageBox deleteDialogMessageBox;
232
233     // Set the icon.
234     deleteDialogMessageBox.setIcon(QMessageBox::Warning);
235
236     // Set the window title.
237     deleteDialogMessageBox.setWindowTitle(i18nc("Delete domain dialog title", "Delete Domain"));
238
239     // Set the text.
240     deleteDialogMessageBox.setText(i18nc("Delete domain main message", "Delete the current domain?"));
241
242     // Set the informative text.
243     deleteDialogMessageBox.setInformativeText(i18nc("Delete domain secondary message", "Doing so will also save any pending changes that have been made to other domains."));
244
245     // Set the standard buttons.
246     deleteDialogMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
247
248     // Set the default button.
249     deleteDialogMessageBox.setDefaultButton(QMessageBox::No);
250
251     // Display the dialog and capture the return value.
252     int returnValue = deleteDialogMessageBox.exec();
253
254     if (returnValue == QMessageBox::Yes)
255     {
256         // Get the current index.
257         QModelIndex currentIndex = domainsListViewPointer->currentIndex();
258
259         // Delete the current row.
260         domainsTableModelPointer->removeRow(domainsListViewPointer->selectionModel()->currentIndex().row());
261
262         // Submit all pending changes.
263         domainsTableModelPointer->submitAll();
264
265         // Select the row next to the deleted item if one exists.
266         if (domainsTableModelPointer->rowCount() > 0)
267         {
268             // Check the row of the deleted item.
269             if (currentIndex.row() == 0)  // The first row was deleted.
270             {
271                 // Reselect the current index.
272                 domainsListViewPointer->setCurrentIndex(currentIndex);
273             }
274             else  // A subsequent row was deleted.
275             {
276                 // Select the crow above the deleted itemm.
277                 domainsListViewPointer->setCurrentIndex(currentIndex.siblingAtRow(currentIndex.row() - 1));
278             }
279
280             // Populate the domain settings.
281             domainSelected(domainsListViewPointer->currentIndex());
282         }
283
284         // Update the Ui.
285         updateUi();
286     }
287 }
288
289 void DomainSettingsDialog::updateUi() const
290 {
291     // Update the delete button status.
292     deleteDomainButtonPointer->setEnabled(domainsListViewPointer->selectionModel()->hasSelection());
293
294     // Update the apply button status.
295     applyButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
296
297     // Update the reset button status.
298     resetButtonPointer->setEnabled(domainsTableModelPointer->isDirty());
299
300     // Display the domain settings if there is at least one domain.
301     domainSettingsWidgetPointer->setVisible(domainsTableModelPointer->rowCount() > 0);
302 }
303