1 /*
2     SPDX-FileCopyrightText: 2021 Valentin Boettcher <hiro at protagon.space; @hiro98:tchncs.de>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include <QCheckBox>
8 #include <QMessageBox>
9 #include <QFileDialog>
10 #include "catalogsdbui.h"
11 #include "ui_catalogsdbui.h"
12 #include "catalogeditform.h"
13 #include "catalogdetails.h"
14 #include "catalogcoloreditor.h"
15 
CatalogsDBUI(QWidget * parent,const QString & db_path)16 CatalogsDBUI::CatalogsDBUI(QWidget *parent, const QString &db_path)
17     : QDialog(parent), ui{ new Ui::CatalogsDBUI }, m_manager{ db_path }, m_last_dir{
18           QDir::homePath()
19       }
20 {
21     ui->setupUi(this);
22     ui->objectsTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
23 
24     const QStringList labels = {
25         i18n("Enabled"),    i18n("ID"),      i18n("Name"),
26         i18n("Precedence"), i18n("Author"),  i18n("Mutable"),
27         i18n("Version"),    i18n("License"), i18n("Maintainer")
28     };
29 
30     ui->objectsTable->setColumnCount(labels.size());
31     ui->objectsTable->setHorizontalHeaderLabels(labels);
32 
33     refresh_db_table();
34 
35     connect(ui->objectsTable, &QTableWidget::cellClicked, this,
36             &CatalogsDBUI::row_selected);
37 
38     connect(ui->objectsTable, &QTableWidget::itemSelectionChanged, this,
39             &CatalogsDBUI::disable_buttons);
40 
41     connect(ui->activateButton, &QPushButton::clicked, this,
42             &CatalogsDBUI::enable_disable_catalog);
43 
44     connect(ui->importButton, &QPushButton::clicked, this, &CatalogsDBUI::import_catalog);
45 
46     connect(ui->exportButton, &QPushButton::clicked, this, &CatalogsDBUI::export_catalog);
47 
48     connect(ui->removeButton, &QPushButton::clicked, this, &CatalogsDBUI::remove_catalog);
49 
50     connect(ui->createButton, &QPushButton::clicked, this,
51             qOverload<>(&CatalogsDBUI::create_new_catalog));
52 
53     connect(ui->moreButton, &QPushButton::clicked, this, &CatalogsDBUI::show_more_dialog);
54 
55     connect(ui->dublicateButton, &QPushButton::clicked, this,
56             &CatalogsDBUI::dublicate_catalog);
57 
58     connect(ui->colorButton, &QPushButton::clicked, this,
59             &CatalogsDBUI::show_color_editor);
60 }
61 
~CatalogsDBUI()62 CatalogsDBUI::~CatalogsDBUI()
63 {
64     delete ui;
65 }
66 
centered_check_box_widget(bool checked)67 QWidget *centered_check_box_widget(bool checked)
68 {
69     auto *pWidget   = new QWidget{}; // no parent as receiver takes posession
70     auto *pLayout   = new QHBoxLayout(pWidget);
71     auto *pCheckBox = new QCheckBox(pWidget);
72 
73     pCheckBox->setChecked(checked);
74     pCheckBox->setEnabled(false);
75     pLayout->addWidget(pCheckBox);
76     pLayout->setAlignment(Qt::AlignCenter);
77     pLayout->setContentsMargins(0, 0, 0, 0);
78 
79     pWidget->setLayout(pLayout);
80 
81     return pWidget;
82 }
83 
refresh_db_table()84 void CatalogsDBUI::refresh_db_table()
85 {
86     const auto catalogs = m_manager.get_catalogs(true);
87 
88     m_catalogs.resize(catalogs.size());
89 
90     auto &table = *ui->objectsTable;
91     table.setRowCount(catalogs.size());
92 
93     int row{ 0 };
94     for (const auto &catalog : catalogs)
95     {
96         m_catalogs[row] = catalog.id;
97 
98         // the table takes ownership of its items
99         table.setCellWidget(row, 0, centered_check_box_widget(catalog.enabled));
100 
101         table.setItem(row, 1, new QTableWidgetItem{ QString::number(catalog.id) });
102         table.setItem(row, 2, new QTableWidgetItem{ catalog.name });
103         table.setItem(row, 3,
104                       new QTableWidgetItem{ QString::number(catalog.precedence) });
105         table.setItem(row, 4, new QTableWidgetItem{ catalog.author });
106         table.setCellWidget(row, 5, centered_check_box_widget(catalog.mut));
107         table.setItem(row, 6, new QTableWidgetItem{ QString::number(catalog.version) });
108         table.setItem(row, 7, new QTableWidgetItem{ catalog.license });
109         table.setItem(row, 8, new QTableWidgetItem{ catalog.maintainer });
110         row++;
111     }
112 }
113 
row_selected(int row,int)114 void CatalogsDBUI::row_selected(int row, int)
115 {
116     const auto &success = m_manager.get_catalog(m_catalogs[row]);
117     if (!success.first)
118         return;
119 
120     const auto cat = success.second;
121     ui->activateButton->setText(!cat.enabled ? i18n("Enable") : i18n("Disable"));
122     ui->activateButton->setEnabled(true);
123     ui->moreButton->setEnabled(true);
124     ui->removeButton->setEnabled(true);
125     ui->exportButton->setEnabled(true);
126     ui->dublicateButton->setEnabled(true);
127     ui->colorButton->setEnabled(true);
128 }
129 
disable_buttons()130 void CatalogsDBUI::disable_buttons()
131 {
132     if (ui->objectsTable->selectedItems().length() > 0)
133         return;
134 
135     ui->activateButton->setText(i18n("Enable"));
136     ui->activateButton->setEnabled(false);
137     ui->moreButton->setEnabled(false);
138     ui->removeButton->setEnabled(false);
139     ui->exportButton->setEnabled(false);
140     ui->dublicateButton->setEnabled(false);
141 }
142 
enable_disable_catalog()143 void CatalogsDBUI::enable_disable_catalog()
144 {
145     const auto catalog = get_selected_catalog();
146     if (!catalog.first)
147         return;
148 
149     const auto success =
150         m_manager.set_catalog_enabled(catalog.second.id, !catalog.second.enabled);
151     if (!success.first)
152         QMessageBox::warning(
153             this, i18n("Warning"),
154             i18n("Could not enable/disable the catalog.<br>%1", success.second));
155 
156     refresh_db_table();
157     row_selected(ui->objectsTable->selectedItems().first()->row(), 0);
158 }
159 
get_selected_catalog()160 const std::pair<bool, CatalogsDB::Catalog> CatalogsDBUI::get_selected_catalog()
161 {
162     const auto items = ui->objectsTable->selectedItems();
163     if (items.length() == 0)
164         return { false, {} };
165 
166     return m_manager.get_catalog(m_catalogs[items.first()->row()]);
167 }
168 
export_catalog()169 void CatalogsDBUI::export_catalog()
170 {
171     const auto cat = get_selected_catalog();
172     if (!cat.first)
173         return;
174 
175     QFileDialog dialog(this, i18nc("@title:window", "Export Catalog"), m_last_dir,
176                        i18n("Catalog") +
177                            QString(" (*.%1);;").arg(CatalogsDB::db_file_extension));
178     dialog.setAcceptMode(QFileDialog::AcceptSave);
179     dialog.setDefaultSuffix(CatalogsDB::db_file_extension);
180 
181     if (dialog.exec() != QDialog::Accepted)
182         return;
183 
184     const auto fileName = dialog.selectedUrls().value(0).toLocalFile();
185     const auto success  = m_manager.dump_catalog(cat.second.id, fileName);
186     m_last_dir          = QFileInfo(fileName).absolutePath();
187 
188     if (!success.first)
189         QMessageBox::warning(this, i18n("Warning"),
190                              i18n("Could not export the catalog.<br>%1", success.second));
191 }
192 
import_catalog(bool force)193 void CatalogsDBUI::import_catalog(bool force)
194 {
195     QFileDialog dialog(this, i18nc("@title:window", "Import Catalog"), m_last_dir,
196                        i18n("Catalog") +
197                            QString(" (*.%1);;").arg(CatalogsDB::db_file_extension));
198     dialog.setAcceptMode(QFileDialog::AcceptOpen);
199     dialog.setDefaultSuffix(CatalogsDB::db_file_extension);
200 
201     if (dialog.exec() != QDialog::Accepted)
202         return;
203 
204     const auto fileName = dialog.selectedUrls().value(0).toLocalFile();
205     const auto success  = m_manager.import_catalog(fileName, force);
206     m_last_dir          = QFileInfo(fileName).absolutePath();
207 
208     if (!success.first && !force)
209     {
210         QMessageBox::warning(this, i18n("Warning"),
211                              i18n("Could not import the catalog.<br>%1", success.second));
212 
213         if (QMessageBox::question(this, "Retry", "Retry and overwrite?",
214                                   QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
215             import_catalog(true);
216     }
217 
218     refresh_db_table();
219 }
220 
remove_catalog()221 void CatalogsDBUI::remove_catalog()
222 {
223     const auto cat = get_selected_catalog();
224     if (!cat.first)
225         return;
226 
227     const auto success = m_manager.remove_catalog(cat.second.id);
228 
229     if (!success.first)
230         QMessageBox::warning(this, i18n("Warning"),
231                              i18n("Could not remove the catalog.<br>%1", success.second));
232 
233     refresh_db_table();
234 }
235 
create_new_catalog(const CatalogsDB::Catalog & catalog)236 std::pair<bool, int> CatalogsDBUI::create_new_catalog(const CatalogsDB::Catalog &catalog)
237 {
238     auto *dialog = new CatalogEditForm(this, catalog);
239     if (dialog->exec() != QDialog::Accepted)
240         return { false, -1 };
241 
242     auto cat            = dialog->getCatalog();
243     cat.mut             = true;
244     const auto &success = m_manager.register_catalog(cat);
245 
246     if (!success.first)
247     {
248         QMessageBox::warning(this, i18n("Warning"),
249                              i18n("Could not create the catalog.<br>%1", success.second));
250         return create_new_catalog(cat);
251     }
252 
253     refresh_db_table();
254     return { true, cat.id };
255 }
256 
create_new_catalog()257 void CatalogsDBUI::create_new_catalog()
258 {
259     create_new_catalog({ m_manager.find_suitable_catalog_id() });
260 }
261 
dublicate_catalog()262 void CatalogsDBUI::dublicate_catalog()
263 {
264     const auto &success = get_selected_catalog();
265     if (!success.first)
266         return;
267 
268     const auto &src = success.second;
269 
270     auto src_new       = src;
271     src_new.id         = m_manager.find_suitable_catalog_id();
272     src_new.enabled    = false;
273     src_new.precedence = 1;
274 
275     const auto &create_success = create_new_catalog(src_new);
276 
277     if (!create_success.first)
278         return;
279 
280     const auto copy_success = m_manager.copy_objects(src.id, create_success.second);
281     if (!copy_success.first)
282     {
283         QMessageBox::warning(this, i18n("Warning"),
284                              i18n("Could not copy the objects to the new catalog.<br>%1")
285                                  .arg(copy_success.second));
286 
287         const auto &remove_success = m_manager.remove_catalog(create_success.second);
288         if (!remove_success.first)
289             QMessageBox::critical(
290                 this, i18n("Critical error"),
291                 i18n("Could not clean up and remove the new catalog.<br>%1",
292                      remove_success.second));
293     };
294 }
295 
show_more_dialog()296 void CatalogsDBUI::show_more_dialog()
297 {
298     const auto success = get_selected_catalog();
299     if (!success.first)
300         return;
301 
302     auto *dialog = new CatalogDetails(this, m_manager.db_file_name(), success.second.id);
303 
304     connect(this, &QDialog::finished, dialog, &QDialog::done);
305     connect(dialog, &QDialog::finished, this, &CatalogsDBUI::refresh_db_table);
306 
307     dialog->show();
308 }
309 
show_color_editor()310 void CatalogsDBUI::show_color_editor()
311 {
312     const auto &success = get_selected_catalog();
313     if (!success.first)
314         return;
315 
316     auto *dialog = new CatalogColorEditor(success.second.id, this);
317 
318     connect(this, &QDialog::finished, dialog, &QDialog::reject);
319     dialog->show();
320 }
321