1 /***************************************************************************
2 Copyright (C) 2001-2009 Robby Stephenson <robby@periapsis.org>
3 ***************************************************************************/
4
5 /***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License as *
9 * published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) version 3 or any later version *
11 * accepted by the membership of KDE e.V. (or its successor approved *
12 * by the membership of KDE e.V.), which shall act as a proxy *
13 * defined in Section 14 of version 3 of the license. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
22 * *
23 ***************************************************************************/
24
25 #include <config.h>
26
27 #include "configdialog.h"
28 #include "field.h"
29 #include "collection.h"
30 #include "collectionfactory.h"
31 #include "fetch/execexternalfetcher.h"
32 #include "fetch/fetchmanager.h"
33 #include "fetch/configwidget.h"
34 #include "controller.h"
35 #include "fetcherconfigdialog.h"
36 #include "tellico_kernel.h"
37 #include "utils/tellico_utils.h"
38 #include "utils/string_utils.h"
39 #include "config/tellico_config.h"
40 #include "images/imagefactory.h"
41 #include "gui/combobox.h"
42 #include "gui/collectiontypecombo.h"
43 #include "gui/previewdialog.h"
44 #include "newstuff/manager.h"
45 #include "fieldformat.h"
46 #include "tellico_debug.h"
47
48 #include <KLocalizedString>
49 #include <KConfig>
50 #include <KAcceleratorManager>
51 #include <KColorCombo>
52 #include <KHelpClient>
53 #include <KRecentDirs>
54
55 #ifdef ENABLE_KNEWSTUFF3
56 #include <KNS3/DownloadDialog>
57 #endif
58
59 #include <QSpinBox>
60 #include <QLineEdit>
61 #include <QPushButton>
62 #include <QSize>
63 #include <QLayout>
64 #include <QLabel>
65 #include <QCheckBox>
66 #include <QPixmap>
67 #include <QRegularExpression>
68 #include <QFileInfo>
69 #include <QRadioButton>
70 #include <QFrame>
71 #include <QFontComboBox>
72 #include <QGroupBox>
73 #include <QButtonGroup>
74 #include <QInputDialog>
75 #include <QVBoxLayout>
76 #include <QHBoxLayout>
77 #include <QApplication>
78 #include <QTimer>
79 #include <QFileDialog>
80
81 namespace {
82 static const int CONFIG_MIN_WIDTH = 640;
83 static const int CONFIG_MIN_HEIGHT = 420;
84 }
85
86 using Tellico::ConfigDialog;
87
ConfigDialog(QWidget * parent_)88 ConfigDialog::ConfigDialog(QWidget* parent_)
89 : KPageDialog(parent_)
90 , m_initializedPages(0)
91 , m_modifying(false)
92 , m_okClicked(false) {
93 setFaceType(List);
94 setModal(true);
95 setWindowTitle(i18n("Configure Tellico"));
96 setStandardButtons(QDialogButtonBox::Help |
97 QDialogButtonBox::Ok |
98 QDialogButtonBox::Apply |
99 QDialogButtonBox::Cancel |
100 QDialogButtonBox::RestoreDefaults);
101
102 setupGeneralPage();
103 setupPrintingPage();
104 setupTemplatePage();
105 setupFetchPage();
106
107 updateGeometry();
108 QSize s = sizeHint();
109 resize(qMax(s.width(), CONFIG_MIN_WIDTH), qMax(s.height(), CONFIG_MIN_HEIGHT));
110
111 // OK button is connected to buttonBox accepted() signal which is already connected to accept() slot
112 connect(button(QDialogButtonBox::Apply), &QAbstractButton::clicked, this, &ConfigDialog::slotApply);
113 connect(button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &ConfigDialog::slotHelp);
114 connect(button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, this, &ConfigDialog::slotDefault);
115
116 button(QDialogButtonBox::Ok)->setEnabled(false);
117 button(QDialogButtonBox::Apply)->setEnabled(false);
118 button(QDialogButtonBox::Ok)->setDefault(true);
119 button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return);
120
121 connect(this, &KPageDialog::currentPageChanged, this, &ConfigDialog::slotInitPage);
122 }
123
~ConfigDialog()124 ConfigDialog::~ConfigDialog() {
125 foreach(Fetch::ConfigWidget* widget, m_newStuffConfigWidgets) {
126 widget->removed();
127 }
128 }
129
slotInitPage(KPageWidgetItem * item_)130 void ConfigDialog::slotInitPage(KPageWidgetItem* item_) {
131 Q_ASSERT(item_);
132 // every page item has a frame
133 // if the frame has no layout, then we need to initialize the itme
134 QFrame* frame = ::qobject_cast<QFrame*>(item_->widget());
135 Q_ASSERT(frame);
136 if(frame->layout()) {
137 return;
138 }
139
140 const QString name = item_->name();
141 // these names must be kept in sync with the page names
142 if(name == i18n("General")) {
143 initGeneralPage(frame);
144 } else if(name == i18n("Printing")) {
145 initPrintingPage(frame);
146 } else if(name == i18n("Templates")) {
147 initTemplatePage(frame);
148 } else if(name == i18n("Data Sources")) {
149 initFetchPage(frame);
150 }
151 }
152
accept()153 void ConfigDialog::accept() {
154 m_okClicked = true;
155 slotApply();
156 KPageDialog::accept();
157 m_okClicked = false;
158 }
159
slotApply()160 void ConfigDialog::slotApply() {
161 emit signalConfigChanged();
162 button(QDialogButtonBox::Apply)->setEnabled(false);
163 }
164
slotDefault()165 void ConfigDialog::slotDefault() {
166 // only change the defaults on the active page
167 Config::self()->useDefaults(true);
168 const QString name = currentPage()->name();
169 if(name == i18n("General")) {
170 readGeneralConfig();
171 } else if(name == i18n("Printing")) {
172 readPrintingConfig();
173 } else if(name == i18n("Templates")) {
174 readTemplateConfig();
175 }
176 Config::self()->useDefaults(false);
177 slotModified();
178 }
179
slotHelp()180 void ConfigDialog::slotHelp() {
181 const QString name = currentPage()->name();
182 // these names must be kept in sync with the page names
183 if(name == i18n("General")) {
184 KHelpClient::invokeHelp(QStringLiteral("general-options"));
185 } else if(name == i18n("Printing")) {
186 KHelpClient::invokeHelp(QStringLiteral("printing-options"));
187 } else if(name == i18n("Templates")) {
188 KHelpClient::invokeHelp(QStringLiteral("template-options"));
189 } else if(name == i18n("Data Sources")) {
190 KHelpClient::invokeHelp(QStringLiteral("internet-sources-options"));
191 }
192 }
193
isPageInitialized(Page page_) const194 bool ConfigDialog::isPageInitialized(Page page_) const {
195 return m_initializedPages & page_;
196 }
197
setupGeneralPage()198 void ConfigDialog::setupGeneralPage() {
199 QFrame* frame = new QFrame(this);
200 KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("General"));
201 page->setHeader(i18n("General Options"));
202 page->setIcon(QIcon::fromTheme(QStringLiteral("tellico"), QIcon(QLatin1String(":/icons/tellico"))));
203 addPage(page);
204
205 // since this is the first page, go ahead and lay it out
206 initGeneralPage(frame);
207 }
208
initGeneralPage(QFrame * frame)209 void ConfigDialog::initGeneralPage(QFrame* frame) {
210 QVBoxLayout* l = new QVBoxLayout(frame);
211
212 m_cbOpenLastFile = new QCheckBox(i18n("&Reopen file at startup"), frame);
213 m_cbOpenLastFile->setWhatsThis(i18n("If checked, the file that was last open "
214 "will be re-opened at program start-up."));
215 l->addWidget(m_cbOpenLastFile);
216 connect(m_cbOpenLastFile, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
217
218 m_cbShowTipDay = new QCheckBox(i18n("&Show \"Tip of the Day\" at startup"), frame);
219 m_cbShowTipDay->setWhatsThis(i18n("If checked, the \"Tip of the Day\" will be "
220 "shown at program start-up."));
221 l->addWidget(m_cbShowTipDay);
222 connect(m_cbShowTipDay, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
223
224 m_cbEnableWebcam = new QCheckBox(i18n("&Enable webcam for barcode scanning"), frame);
225 m_cbEnableWebcam->setWhatsThis(i18n("If checked, the input from a webcam will be used "
226 "to scan barcodes for searching."));
227 l->addWidget(m_cbEnableWebcam);
228 connect(m_cbEnableWebcam, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
229
230 QGroupBox* imageGroupBox = new QGroupBox(i18n("Image Storage Options"), frame);
231 l->addWidget(imageGroupBox);
232 m_rbImageInFile = new QRadioButton(i18n("Store images in data file"), imageGroupBox);
233 m_rbImageInAppDir = new QRadioButton(i18n("Store images in common application directory"), imageGroupBox);
234 m_rbImageInLocalDir = new QRadioButton(i18n("Store images in directory relative to data file"), imageGroupBox);
235 imageGroupBox->setWhatsThis(i18n("Images may be saved in the data file itself, which can "
236 "cause Tellico to run slowly, stored in the Tellico "
237 "application directory, or stored in a directory in the "
238 "same location as the data file."));
239 QVBoxLayout* imageGroupLayout = new QVBoxLayout(imageGroupBox);
240 imageGroupLayout->addWidget(m_rbImageInFile);
241 imageGroupLayout->addWidget(m_rbImageInAppDir);
242 imageGroupLayout->addWidget(m_rbImageInLocalDir);
243 imageGroupBox->setLayout(imageGroupLayout);
244
245 QButtonGroup* imageGroup = new QButtonGroup(frame);
246 imageGroup->addButton(m_rbImageInFile);
247 imageGroup->addButton(m_rbImageInAppDir);
248 imageGroup->addButton(m_rbImageInLocalDir);
249 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
250 void (QButtonGroup::* buttonClicked)(int) = &QButtonGroup::buttonClicked;
251 connect(imageGroup, buttonClicked, this, &ConfigDialog::slotModified);
252 #else
253 connect(imageGroup, &QButtonGroup::idClicked, this, &ConfigDialog::slotModified);
254 #endif
255
256 QGroupBox* formatGroup = new QGroupBox(i18n("Formatting Options"), frame);
257 l->addWidget(formatGroup);
258 QVBoxLayout* formatGroupLayout = new QVBoxLayout(formatGroup);
259 formatGroup->setLayout(formatGroupLayout);
260
261 m_cbCapitalize = new QCheckBox(i18n("Auto capitalize &titles and names"), formatGroup);
262 m_cbCapitalize->setWhatsThis(i18n("If checked, titles and names will "
263 "be automatically capitalized."));
264 connect(m_cbCapitalize, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
265 formatGroupLayout->addWidget(m_cbCapitalize);
266
267 m_cbFormat = new QCheckBox(i18n("Auto &format titles and names"), formatGroup);
268 m_cbFormat->setWhatsThis(i18n("If checked, titles and names will "
269 "be automatically formatted."));
270 connect(m_cbFormat, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
271 formatGroupLayout->addWidget(m_cbFormat);
272
273 QWidget* g1 = new QWidget(formatGroup);
274 QGridLayout* g1Layout = new QGridLayout(g1);
275 g1->setLayout(g1Layout);
276 formatGroupLayout->addWidget(g1);
277
278 QLabel* lab = new QLabel(i18n("No capitali&zation:"), g1);
279 g1Layout->addWidget(lab, 0, 0);
280 m_leCapitals = new QLineEdit(g1);
281 g1Layout->addWidget(m_leCapitals, 0, 1);
282 lab->setBuddy(m_leCapitals);
283 QString whats = i18n("<qt>A list of words which should not be capitalized. Multiple values "
284 "should be separated by a semi-colon.</qt>");
285 lab->setWhatsThis(whats);
286 m_leCapitals->setWhatsThis(whats);
287 connect(m_leCapitals, &QLineEdit::textChanged, this, &ConfigDialog::slotModified);
288
289 lab = new QLabel(i18n("Artic&les:"), g1);
290 g1Layout->addWidget(lab, 1, 0);
291 m_leArticles = new QLineEdit(g1);
292 g1Layout->addWidget(m_leArticles, 1, 1);
293 lab->setBuddy(m_leArticles);
294 whats = i18n("<qt>A list of words which should be considered as articles "
295 "if they are the first word in a title. Multiple values "
296 "should be separated by a semi-colon.</qt>");
297 lab->setWhatsThis(whats);
298 m_leArticles->setWhatsThis(whats);
299 connect(m_leArticles, &QLineEdit::textChanged, this, &ConfigDialog::slotModified);
300
301 lab = new QLabel(i18n("Personal suffi&xes:"), g1);
302 g1Layout->addWidget(lab, 2, 0);
303 m_leSuffixes = new QLineEdit(g1);
304 g1Layout->addWidget(m_leSuffixes, 2, 1);
305 lab->setBuddy(m_leSuffixes);
306 whats = i18n("<qt>A list of suffixes which might be used in personal names. Multiple values "
307 "should be separated by a semi-colon.</qt>");
308 lab->setWhatsThis(whats);
309 m_leSuffixes->setWhatsThis(whats);
310 connect(m_leSuffixes, &QLineEdit::textChanged, this, &ConfigDialog::slotModified);
311
312 lab = new QLabel(i18n("Surname &prefixes:"), g1);
313 g1Layout->addWidget(lab, 3, 0);
314 m_lePrefixes = new QLineEdit(g1);
315 g1Layout->addWidget(m_lePrefixes, 3, 1);
316 lab->setBuddy(m_lePrefixes);
317 whats = i18n("<qt>A list of prefixes which might be used in surnames. Multiple values "
318 "should be separated by a semi-colon.</qt>");
319 lab->setWhatsThis(whats);
320 m_lePrefixes->setWhatsThis(whats);
321 connect(m_lePrefixes, &QLineEdit::textChanged, this, &ConfigDialog::slotModified);
322
323 // stretch to fill lower area
324 l->addStretch(1);
325 m_initializedPages |= General;
326 readGeneralConfig();
327 }
328
setupPrintingPage()329 void ConfigDialog::setupPrintingPage() {
330 QFrame* frame = new QFrame(this);
331 KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Printing"));
332 page->setHeader(i18n("Printing Options"));
333 page->setIcon(QIcon::fromTheme(QStringLiteral("printer")));
334 addPage(page);
335 }
336
initPrintingPage(QFrame * frame)337 void ConfigDialog::initPrintingPage(QFrame* frame) {
338 QVBoxLayout* l = new QVBoxLayout(frame);
339
340 QGroupBox* formatOptions = new QGroupBox(i18n("Formatting Options"), frame);
341 l->addWidget(formatOptions);
342 QVBoxLayout* formatLayout = new QVBoxLayout(formatOptions);
343 formatOptions->setLayout(formatLayout);
344
345 m_cbPrintFormatted = new QCheckBox(i18n("&Format titles and names"), formatOptions);
346 m_cbPrintFormatted->setWhatsThis(i18n("If checked, titles and names will be automatically formatted."));
347 connect(m_cbPrintFormatted, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
348 formatLayout->addWidget(m_cbPrintFormatted);
349
350 m_cbPrintHeaders = new QCheckBox(i18n("&Print field headers"), formatOptions);
351 m_cbPrintHeaders->setWhatsThis(i18n("If checked, the field names will be printed as table headers."));
352 connect(m_cbPrintHeaders, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
353 formatLayout->addWidget(m_cbPrintHeaders);
354
355 QGroupBox* groupOptions = new QGroupBox(i18n("Grouping Options"), frame);
356 l->addWidget(groupOptions);
357 QVBoxLayout* groupLayout = new QVBoxLayout(groupOptions);
358 groupOptions->setLayout(groupLayout);
359
360 m_cbPrintGrouped = new QCheckBox(i18n("&Group the entries"), groupOptions);
361 m_cbPrintGrouped->setWhatsThis(i18n("If checked, the entries will be grouped by the selected field."));
362 connect(m_cbPrintGrouped, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
363 groupLayout->addWidget(m_cbPrintGrouped);
364
365 QGroupBox* imageOptions = new QGroupBox(i18n("Image Options"), frame);
366 l->addWidget(imageOptions);
367
368 QGridLayout* gridLayout = new QGridLayout(imageOptions);
369 imageOptions->setLayout(gridLayout);
370
371 QLabel* lab = new QLabel(i18n("Maximum image &width:"), imageOptions);
372 gridLayout->addWidget(lab, 0, 0);
373 m_imageWidthBox = new QSpinBox(imageOptions);
374 m_imageWidthBox->setMaximum(999);
375 m_imageWidthBox->setMinimum(0);
376 m_imageWidthBox->setValue(50);
377 gridLayout->addWidget(m_imageWidthBox, 0, 1);
378 m_imageWidthBox->setSuffix(QStringLiteral(" px"));
379 lab->setBuddy(m_imageWidthBox);
380 QString whats = i18n("The maximum width of the images in the printout. The aspect ratio is preserved.");
381 lab->setWhatsThis(whats);
382 m_imageWidthBox->setWhatsThis(whats);
383 void (QSpinBox::* valueChanged)(int) = &QSpinBox::valueChanged;
384 connect(m_imageWidthBox, valueChanged, this, &ConfigDialog::slotModified);
385
386 lab = new QLabel(i18n("&Maximum image height:"), imageOptions);
387 gridLayout->addWidget(lab, 1, 0);
388 m_imageHeightBox = new QSpinBox(imageOptions);
389 m_imageHeightBox->setMaximum(999);
390 m_imageHeightBox->setMinimum(0);
391 m_imageHeightBox->setValue(50);
392 gridLayout->addWidget(m_imageHeightBox, 1, 1);
393 m_imageHeightBox->setSuffix(QStringLiteral(" px"));
394 lab->setBuddy(m_imageHeightBox);
395 whats = i18n("The maximum height of the images in the printout. The aspect ratio is preserved.");
396 lab->setWhatsThis(whats);
397 m_imageHeightBox->setWhatsThis(whats);
398 connect(m_imageHeightBox, valueChanged, this, &ConfigDialog::slotModified);
399
400 // stretch to fill lower area
401 l->addStretch(1);
402 m_initializedPages |= Printing;
403 readPrintingConfig();
404 }
405
setupTemplatePage()406 void ConfigDialog::setupTemplatePage() {
407 QFrame* frame = new QFrame(this);
408 KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Templates"));
409 page->setHeader(i18n("Template Options"));
410 // odd icon, I know, matches KMail, though...
411 page->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-theme")));
412 addPage(page);
413 }
414
initTemplatePage(QFrame * frame)415 void ConfigDialog::initTemplatePage(QFrame* frame) {
416 QVBoxLayout* l = new QVBoxLayout(frame);
417
418 QGridLayout* gridLayout = new QGridLayout();
419 l->addLayout(gridLayout);
420
421 int row = -1;
422 // so I can reuse an i18n string, a plain label can't have an '&'
423 QString s = KLocalizedString::removeAcceleratorMarker(i18n("Collection &type:"));
424 QLabel* lab = new QLabel(s, frame);
425 gridLayout->addWidget(lab, ++row, 0);
426 const int collType = Kernel::self()->collectionType();
427 lab = new QLabel(CollectionFactory::nameHash().value(collType), frame);
428 gridLayout->addWidget(lab, row, 1, 1, 2);
429
430 lab = new QLabel(i18n("Template:"), frame);
431 m_templateCombo = new GUI::ComboBox(frame);
432 void (QComboBox::* activatedInt)(int) = &QComboBox::activated;
433 connect(m_templateCombo, activatedInt, this, &ConfigDialog::slotModified);
434 lab->setBuddy(m_templateCombo);
435 QString whats = i18n("Select the template to use for the current type of collections. "
436 "Not all templates will use the font and color settings.");
437 lab->setWhatsThis(whats);
438 m_templateCombo->setWhatsThis(whats);
439 gridLayout->addWidget(lab, ++row, 0);
440 gridLayout->addWidget(m_templateCombo, row, 1);
441
442 QPushButton* btn = new QPushButton(i18n("&Preview..."), frame);
443 btn->setWhatsThis(i18n("Show a preview of the template"));
444 btn->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
445 gridLayout->addWidget(btn, row, 2);
446 connect(btn, &QAbstractButton::clicked, this, &ConfigDialog::slotShowTemplatePreview);
447
448 // so the button is squeezed small
449 gridLayout->setColumnStretch(0, 10);
450 gridLayout->setColumnStretch(1, 10);
451
452 loadTemplateList();
453
454 // QLabel* l1 = new QLabel(i18n("The options below will be passed to the template, but not "
455 // "all templates will use them. Some fonts and colors may be "
456 // "specified directly in the template."), frame);
457 // l1->setTextFormat(Qt::RichText);
458 // l->addWidget(l1);
459
460 QGroupBox* fontGroup = new QGroupBox(i18n("Font Options"), frame);
461 l->addWidget(fontGroup);
462
463 row = -1;
464 QGridLayout* fontLayout = new QGridLayout();
465 fontGroup->setLayout(fontLayout);
466
467 lab = new QLabel(i18n("Font:"), fontGroup);
468 fontLayout->addWidget(lab, ++row, 0);
469 m_fontCombo = new QFontComboBox(fontGroup);
470 fontLayout->addWidget(m_fontCombo, row, 1);
471 connect(m_fontCombo, activatedInt, this, &ConfigDialog::slotModified);
472 lab->setBuddy(m_fontCombo);
473 whats = i18n("This font is passed to the template used in the Entry View.");
474 lab->setWhatsThis(whats);
475 m_fontCombo->setWhatsThis(whats);
476
477 fontLayout->addWidget(new QLabel(i18n("Size:"), fontGroup), ++row, 0);
478 m_fontSizeInput = new QSpinBox(fontGroup);
479 m_fontSizeInput->setMaximum(30); // 30 is same max as konq config
480 m_fontSizeInput->setMinimum(5);
481 m_fontSizeInput->setSuffix(QStringLiteral("pt"));
482 fontLayout->addWidget(m_fontSizeInput, row, 1);
483 void (QSpinBox::* valueChangedInt)(int) = &QSpinBox::valueChanged;
484 connect(m_fontSizeInput, valueChangedInt, this, &ConfigDialog::slotModified);
485 lab->setBuddy(m_fontSizeInput);
486 lab->setWhatsThis(whats);
487 m_fontSizeInput->setWhatsThis(whats);
488
489 QGroupBox* colGroup = new QGroupBox(i18n("Color Options"), frame);
490 l->addWidget(colGroup);
491
492 row = -1;
493 QGridLayout* colLayout = new QGridLayout();
494 colGroup->setLayout(colLayout);
495
496 lab = new QLabel(i18n("Background color:"), colGroup);
497 colLayout->addWidget(lab, ++row, 0);
498 m_baseColorCombo = new KColorCombo(colGroup);
499 colLayout->addWidget(m_baseColorCombo, row, 1);
500 connect(m_baseColorCombo, activatedInt, this, &ConfigDialog::slotModified);
501 lab->setBuddy(m_baseColorCombo);
502 whats = i18n("This color is passed to the template used in the Entry View.");
503 lab->setWhatsThis(whats);
504 m_baseColorCombo->setWhatsThis(whats);
505
506 lab = new QLabel(i18n("Text color:"), colGroup);
507 colLayout->addWidget(lab, ++row, 0);
508 m_textColorCombo = new KColorCombo(colGroup);
509 colLayout->addWidget(m_textColorCombo, row, 1);
510 connect(m_textColorCombo, activatedInt, this, &ConfigDialog::slotModified);
511 lab->setBuddy(m_textColorCombo);
512 lab->setWhatsThis(whats);
513 m_textColorCombo->setWhatsThis(whats);
514
515 lab = new QLabel(i18n("Highlight color:"), colGroup);
516 colLayout->addWidget(lab, ++row, 0);
517 m_highBaseColorCombo = new KColorCombo(colGroup);
518 colLayout->addWidget(m_highBaseColorCombo, row, 1);
519 connect(m_highBaseColorCombo, activatedInt, this, &ConfigDialog::slotModified);
520 lab->setBuddy(m_highBaseColorCombo);
521 lab->setWhatsThis(whats);
522 m_highBaseColorCombo->setWhatsThis(whats);
523
524 lab = new QLabel(i18n("Highlighted text color:"), colGroup);
525 colLayout->addWidget(lab, ++row, 0);
526 m_highTextColorCombo = new KColorCombo(colGroup);
527 colLayout->addWidget(m_highTextColorCombo, row, 1);
528 connect(m_highTextColorCombo, activatedInt, this, &ConfigDialog::slotModified);
529 lab->setBuddy(m_highTextColorCombo);
530 lab->setWhatsThis(whats);
531 m_highTextColorCombo->setWhatsThis(whats);
532
533 QGroupBox* groupBox = new QGroupBox(i18n("Manage Templates"), frame);
534 l->addWidget(groupBox);
535 QVBoxLayout* vlay = new QVBoxLayout(groupBox);
536 groupBox->setLayout(vlay);
537
538 QWidget* box1 = new QWidget(groupBox);
539 QHBoxLayout* box1HBoxLayout = new QHBoxLayout(box1);
540 box1HBoxLayout->setMargin(0);
541 vlay->addWidget(box1);
542 box1HBoxLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
543
544 QPushButton* b1 = new QPushButton(i18n("Install..."), box1);
545 box1HBoxLayout->addWidget(b1);
546 b1->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
547 connect(b1, &QAbstractButton::clicked, this, &ConfigDialog::slotInstallTemplate);
548 whats = i18n("Click to install a new template directly.");
549 b1->setWhatsThis(whats);
550
551 QPushButton* b2 = new QPushButton(i18n("Download..."), box1);
552 box1HBoxLayout->addWidget(b2);
553 b2->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")));
554 connect(b2, &QAbstractButton::clicked, this, &ConfigDialog::slotDownloadTemplate);
555 whats = i18n("Click to download additional templates.");
556 b2->setWhatsThis(whats);
557
558 QPushButton* b3 = new QPushButton(i18n("Delete..."), box1);
559 box1HBoxLayout->addWidget(b3);
560 b3->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
561 connect(b3, &QAbstractButton::clicked, this, &ConfigDialog::slotDeleteTemplate);
562 whats = i18n("Click to select and remove installed templates.");
563 b3->setWhatsThis(whats);
564
565 // stretch to fill lower area
566 l->addStretch(1);
567
568 // purely for aesthetics make all widgets line up
569 QList<QWidget*> widgets;
570 widgets.append(m_fontCombo);
571 widgets.append(m_fontSizeInput);
572 widgets.append(m_baseColorCombo);
573 widgets.append(m_textColorCombo);
574 widgets.append(m_highBaseColorCombo);
575 widgets.append(m_highTextColorCombo);
576 int w = 0;
577 foreach(QWidget* widget, widgets) {
578 widget->ensurePolished();
579 w = qMax(w, widget->sizeHint().width());
580 }
581 foreach(QWidget* widget, widgets) {
582 widget->setMinimumWidth(w);
583 }
584
585 KAcceleratorManager::manage(frame);
586 m_initializedPages |= Template;
587 readTemplateConfig();
588 }
589
setupFetchPage()590 void ConfigDialog::setupFetchPage() {
591 QFrame* frame = new QFrame(this);
592 KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Data Sources"));
593 page->setHeader(i18n("Data Sources Options"));
594 page->setIcon(QIcon::fromTheme(QStringLiteral("network-wired")));
595 addPage(page);
596 }
597
initFetchPage(QFrame * frame)598 void ConfigDialog::initFetchPage(QFrame* frame) {
599 QHBoxLayout* l = new QHBoxLayout(frame);
600
601 QVBoxLayout* leftLayout = new QVBoxLayout();
602 l->addLayout(leftLayout);
603 m_sourceListWidget = new QListWidget(frame);
604 m_sourceListWidget->setSortingEnabled(false); // no sorting
605 m_sourceListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
606 leftLayout->addWidget(m_sourceListWidget, 1);
607 connect(m_sourceListWidget, &QListWidget::currentItemChanged, this, &ConfigDialog::slotSelectedSourceChanged);
608 connect(m_sourceListWidget, &QListWidget::itemDoubleClicked, this, &ConfigDialog::slotModifySourceClicked);
609
610 QWidget* hb = new QWidget(frame);
611 QHBoxLayout* hbHBoxLayout = new QHBoxLayout(hb);
612 hbHBoxLayout->setMargin(0);
613 leftLayout->addWidget(hb);
614 m_moveUpSourceBtn = new QPushButton(i18n("Move &Up"), hb);
615 hbHBoxLayout->addWidget(m_moveUpSourceBtn);
616 m_moveUpSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
617 m_moveUpSourceBtn->setWhatsThis(i18n("The order of the data sources sets the order "
618 "that Tellico uses when entries are automatically updated."));
619 m_moveDownSourceBtn = new QPushButton(i18n("Move &Down"), hb);
620 hbHBoxLayout->addWidget(m_moveDownSourceBtn);
621 m_moveDownSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
622 m_moveDownSourceBtn->setWhatsThis(i18n("The order of the data sources sets the order "
623 "that Tellico uses when entries are automatically updated."));
624
625 QWidget* hb2 = new QWidget(frame);
626 QHBoxLayout* hb2HBoxLayout = new QHBoxLayout(hb2);
627 hb2HBoxLayout->setMargin(0);
628 leftLayout->addWidget(hb2);
629 m_cbFilterSource = new QCheckBox(i18n("Filter by type:"), hb2);
630 hb2HBoxLayout->addWidget(m_cbFilterSource);
631 connect(m_cbFilterSource, &QAbstractButton::clicked, this, &ConfigDialog::slotSourceFilterChanged);
632 m_sourceTypeCombo = new GUI::CollectionTypeCombo(hb2);
633 hb2HBoxLayout->addWidget(m_sourceTypeCombo);
634 void (QComboBox::* currentIndexChanged)(int) = &QComboBox::currentIndexChanged;
635 connect(m_sourceTypeCombo, currentIndexChanged, this, &ConfigDialog::slotSourceFilterChanged);
636 // we want to remove the item for a custom collection
637 int index = m_sourceTypeCombo->findData(Data::Collection::Base);
638 if(index > -1) {
639 m_sourceTypeCombo->removeItem(index);
640 }
641 // disable until check box is checked
642 m_sourceTypeCombo->setEnabled(false);
643
644 // these icons are rather arbitrary, but seem to vaguely fit
645 QVBoxLayout* vlay = new QVBoxLayout();
646 l->addLayout(vlay);
647 QPushButton* newSourceBtn = new QPushButton(i18n("&New..."), frame);
648 newSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
649 newSourceBtn->setWhatsThis(i18n("Click to add a new data source."));
650 m_modifySourceBtn = new QPushButton(i18n("&Modify..."), frame);
651 m_modifySourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("network-wired")));
652 m_modifySourceBtn->setWhatsThis(i18n("Click to modify the selected data source."));
653 m_removeSourceBtn = new QPushButton(i18n("&Delete"), frame);
654 m_removeSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
655 m_removeSourceBtn->setWhatsThis(i18n("Click to delete the selected data source."));
656 m_newStuffBtn = new QPushButton(i18n("Download..."), frame);
657 m_newStuffBtn->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")));
658 m_newStuffBtn->setWhatsThis(i18n("Click to download additional data sources."));
659 // TODO: disable button for now since checksum and signature checking are no longer possible with khotnewstuff
660 m_newStuffBtn->setEnabled(false);
661
662 vlay->addWidget(newSourceBtn);
663 vlay->addWidget(m_modifySourceBtn);
664 vlay->addWidget(m_removeSourceBtn);
665 // separate newstuff button from the rest
666 vlay->addSpacing(16);
667 vlay->addWidget(m_newStuffBtn);
668 vlay->addStretch(1);
669
670 connect(newSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotNewSourceClicked);
671 connect(m_modifySourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotModifySourceClicked);
672 connect(m_moveUpSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotMoveUpSourceClicked);
673 connect(m_moveDownSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotMoveDownSourceClicked);
674 connect(m_removeSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotRemoveSourceClicked);
675 connect(m_newStuffBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotNewStuffClicked);
676
677 KAcceleratorManager::manage(frame);
678 m_initializedPages |= Fetch;
679 readFetchConfig();
680 }
681
readGeneralConfig()682 void ConfigDialog::readGeneralConfig() {
683 m_modifying = true;
684
685 m_cbShowTipDay->setChecked(Config::showTipOfDay());
686 m_cbOpenLastFile->setChecked(Config::reopenLastFile());
687 #ifdef ENABLE_WEBCAM
688 m_cbEnableWebcam->setChecked(Config::enableWebcam());
689 #else
690 m_cbEnableWebcam->setChecked(false);
691 m_cbEnableWebcam->setEnabled(false);
692 #endif
693
694 switch(Config::imageLocation()) {
695 case Config::ImagesInFile: m_rbImageInFile->setChecked(true); break;
696 case Config::ImagesInAppDir: m_rbImageInAppDir->setChecked(true); break;
697 case Config::ImagesInLocalDir: m_rbImageInLocalDir->setChecked(true); break;
698 }
699
700 bool autoCapitals = Config::autoCapitalization();
701 m_cbCapitalize->setChecked(autoCapitals);
702
703 bool autoFormat = Config::autoFormat();
704 m_cbFormat->setChecked(autoFormat);
705
706 const QRegularExpression comma(QLatin1String("\\s*,\\s*"));
707
708 m_leCapitals->setText(Config::noCapitalizationString().replace(comma, FieldFormat::delimiterString()));
709 m_leArticles->setText(Config::articlesString().replace(comma, FieldFormat::delimiterString()));
710 m_leSuffixes->setText(Config::nameSuffixesString().replace(comma, FieldFormat::delimiterString()));
711 m_lePrefixes->setText(Config::surnamePrefixesString().replace(comma, FieldFormat::delimiterString()));
712
713 m_modifying = false;
714 }
715
readPrintingConfig()716 void ConfigDialog::readPrintingConfig() {
717 m_modifying = true;
718
719 m_cbPrintHeaders->setChecked(Config::printFieldHeaders());
720 m_cbPrintFormatted->setChecked(Config::printFormatted());
721 m_cbPrintGrouped->setChecked(Config::printGrouped());
722 m_imageWidthBox->setValue(Config::maxImageWidth());
723 m_imageHeightBox->setValue(Config::maxImageHeight());
724
725 m_modifying = false;
726 }
727
readTemplateConfig()728 void ConfigDialog::readTemplateConfig() {
729 m_modifying = true;
730
731 // entry template selection
732 const int collType = Kernel::self()->collectionType();
733 QString file = Config::templateName(collType);
734 file.replace(QLatin1Char('_'), QLatin1Char(' '));
735 QString fileContext = file + QLatin1String(" XSL Template");
736 m_templateCombo->setCurrentItem(i18nc(fileContext.toUtf8().constData(), file.toUtf8().constData()));
737
738 m_fontCombo->setCurrentFont(QFont(Config::templateFont(collType).family()));
739 m_fontSizeInput->setValue(Config::templateFont(collType).pointSize());
740 m_baseColorCombo->setColor(Config::templateBaseColor(collType));
741 m_textColorCombo->setColor(Config::templateTextColor(collType));
742 m_highBaseColorCombo->setColor(Config::templateHighlightedBaseColor(collType));
743 m_highTextColorCombo->setColor(Config::templateHighlightedTextColor(collType));
744
745 m_modifying = false;
746 }
747
readFetchConfig()748 void ConfigDialog::readFetchConfig() {
749 m_modifying = true;
750
751 m_sourceListWidget->clear();
752 m_configWidgets.clear();
753
754 m_sourceListWidget->setUpdatesEnabled(false);
755 foreach(Fetch::Fetcher::Ptr fetcher, Fetch::Manager::self()->fetchers()) {
756 Fetch::FetcherInfo info(fetcher->type(), fetcher->source(),
757 fetcher->updateOverwrite(), fetcher->uuid());
758 FetcherInfoListItem* item = new FetcherInfoListItem(m_sourceListWidget, info);
759 item->setFetcher(fetcher.data());
760 }
761 m_sourceListWidget->setUpdatesEnabled(true);
762
763 if(m_sourceListWidget->count() == 0) {
764 m_modifySourceBtn->setEnabled(false);
765 m_removeSourceBtn->setEnabled(false);
766 } else {
767 // go ahead and select the first one
768 m_sourceListWidget->setCurrentItem(m_sourceListWidget->item(0));
769 }
770
771 m_modifying = false;
772 QTimer::singleShot(500, this, &ConfigDialog::slotCreateConfigWidgets);
773 }
774
saveConfiguration()775 void ConfigDialog::saveConfiguration() {
776 if(isPageInitialized(General)) saveGeneralConfig();
777 if(isPageInitialized(Printing)) savePrintingConfig();
778 if(isPageInitialized(Template)) saveTemplateConfig();
779 if(isPageInitialized(Fetch)) saveFetchConfig();
780 }
781
saveGeneralConfig()782 void ConfigDialog::saveGeneralConfig() {
783 Config::setShowTipOfDay(m_cbShowTipDay->isChecked());
784 Config::setEnableWebcam(m_cbEnableWebcam->isChecked());
785
786 int imageLocation;
787 if(m_rbImageInFile->isChecked()) {
788 imageLocation = Config::ImagesInFile;
789 } else if(m_rbImageInAppDir->isChecked()) {
790 imageLocation = Config::ImagesInAppDir;
791 } else {
792 imageLocation = Config::ImagesInLocalDir;
793 }
794 Config::setImageLocation(imageLocation);
795 Config::setReopenLastFile(m_cbOpenLastFile->isChecked());
796
797 Config::setAutoCapitalization(m_cbCapitalize->isChecked());
798 Config::setAutoFormat(m_cbFormat->isChecked());
799
800 const QRegularExpression semicolon(QLatin1String("\\s*;\\s*"));
801 const QChar comma = QLatin1Char(',');
802
803 Config::setNoCapitalizationString(m_leCapitals->text().replace(semicolon, comma));
804 Config::setArticlesString(m_leArticles->text().replace(semicolon, comma));
805 Config::setNameSuffixesString(m_leSuffixes->text().replace(semicolon, comma));
806 Config::setSurnamePrefixesString(m_lePrefixes->text().replace(semicolon, comma));
807 }
808
savePrintingConfig()809 void ConfigDialog::savePrintingConfig() {
810 Config::setPrintFieldHeaders(m_cbPrintHeaders->isChecked());
811 Config::setPrintFormatted(m_cbPrintFormatted->isChecked());
812 Config::setPrintGrouped(m_cbPrintGrouped->isChecked());
813 Config::setMaxImageWidth(m_imageWidthBox->value());
814 Config::setMaxImageHeight(m_imageHeightBox->value());
815 }
816
saveTemplateConfig()817 void ConfigDialog::saveTemplateConfig() {
818 const int collType = Kernel::self()->collectionType();
819 Config::setTemplateName(collType, m_templateCombo->currentData().toString());
820 QFont font(m_fontCombo->currentFont().family(), m_fontSizeInput->value());
821 Config::setTemplateFont(collType, font);
822 Config::setTemplateBaseColor(collType, m_baseColorCombo->color());
823 Config::setTemplateTextColor(collType, m_textColorCombo->color());
824 Config::setTemplateHighlightedBaseColor(collType, m_highBaseColorCombo->color());
825 Config::setTemplateHighlightedTextColor(collType, m_highTextColorCombo->color());
826 }
827
saveFetchConfig()828 void ConfigDialog::saveFetchConfig() {
829 // first, tell config widgets they got deleted
830 foreach(Fetch::ConfigWidget* widget, m_removedConfigWidgets) {
831 widget->removed();
832 }
833 m_removedConfigWidgets.clear();
834
835 bool reloadFetchers = false;
836 int count = 0; // start group numbering at 0
837 for( ; count < m_sourceListWidget->count(); ++count) {
838 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(count));
839 Fetch::ConfigWidget* cw = m_configWidgets[item];
840 if(!cw || (!cw->shouldSave() && !item->isNewSource())) {
841 continue;
842 }
843 m_newStuffConfigWidgets.removeAll(cw);
844 QString group = QStringLiteral("Data Source %1").arg(count);
845 // in case we later change the order, clear the group now
846 KSharedConfig::openConfig()->deleteGroup(group);
847 KConfigGroup configGroup(KSharedConfig::openConfig(), group);
848 configGroup.writeEntry("Name", item->data(Qt::DisplayRole).toString());
849 configGroup.writeEntry("Type", int(item->fetchType()));
850 configGroup.writeEntry("UpdateOverwrite", item->updateOverwrite());
851 configGroup.writeEntry("Uuid", item->uuid());
852 cw->saveConfig(configGroup);
853 item->setNewSource(false);
854 // in case the ordering changed
855 item->setConfigGroup(configGroup);
856 reloadFetchers = true;
857 }
858 // now update total number of sources
859 KConfigGroup sourceGroup(KSharedConfig::openConfig(), "Data Sources");
860 sourceGroup.writeEntry("Sources Count", count);
861 // and purge old config groups
862 QString group = QStringLiteral("Data Source %1").arg(count);
863 while(KSharedConfig::openConfig()->hasGroup(group)) {
864 KSharedConfig::openConfig()->deleteGroup(group);
865 ++count;
866 group = QStringLiteral("Data Source %1").arg(count);
867 }
868
869 Config::self()->save();
870
871 if(reloadFetchers) {
872 Fetch::Manager::self()->loadFetchers();
873 Controller::self()->updatedFetchers();
874 // reload fetcher items if OK was not clicked
875 // meaning apply was clicked
876 if(!m_okClicked) {
877 QString currentSource;
878 if(m_sourceListWidget->currentItem()) {
879 currentSource = m_sourceListWidget->currentItem()->data(Qt::DisplayRole).toString();
880 }
881 readFetchConfig();
882 if(!currentSource.isEmpty()) {
883 QList<QListWidgetItem*> items = m_sourceListWidget->findItems(currentSource, Qt::MatchExactly);
884 if(!items.isEmpty()) {
885 m_sourceListWidget->setCurrentItem(items.first());
886 m_sourceListWidget->scrollToItem(items.first());
887 }
888 }
889 }
890 }
891 }
892
slotModified()893 void ConfigDialog::slotModified() {
894 if(m_modifying) {
895 return;
896 }
897 button(QDialogButtonBox::Ok)->setEnabled(true);
898 button(QDialogButtonBox::Apply)->setEnabled(true);
899 }
900
slotNewSourceClicked()901 void ConfigDialog::slotNewSourceClicked() {
902 FetcherConfigDialog dlg(this);
903 if(dlg.exec() != QDialog::Accepted) {
904 return;
905 }
906
907 Fetch::Type type = dlg.sourceType();
908 if(type == Fetch::Unknown) {
909 return;
910 }
911
912 Fetch::FetcherInfo info(type, dlg.sourceName(), dlg.updateOverwrite());
913 FetcherInfoListItem* item = new FetcherInfoListItem(m_sourceListWidget, info);
914 m_sourceListWidget->scrollToItem(item);
915 m_sourceListWidget->setCurrentItem(item);
916 Fetch::ConfigWidget* cw = dlg.configWidget();
917 if(cw) {
918 cw->setAccepted(true);
919 cw->slotSetModified();
920 cw->setParent(this); // keep the config widget around
921 m_configWidgets.insert(item, cw);
922 }
923 m_modifySourceBtn->setEnabled(true);
924 m_removeSourceBtn->setEnabled(true);
925 slotModified(); // toggle apply button
926 }
927
slotModifySourceClicked()928 void ConfigDialog::slotModifySourceClicked() {
929 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->currentItem());
930 if(!item || !item->fetcher()) {
931 return;
932 }
933
934 Fetch::ConfigWidget* cw = nullptr;
935 if(m_configWidgets.contains(item)) {
936 cw = m_configWidgets[item];
937 } else {
938 // grab the config widget, taking ownership
939 cw = item->fetcher()->configWidget(this);
940 if(cw) { // might return 0 when no widget available for fetcher type
941 m_configWidgets.insert(item, cw);
942 // there's weird layout bug if it's not hidden
943 cw->hide();
944 }
945 }
946 if(!cw) {
947 // no config widget for this one
948 // might be because support was compiled out
949 myDebug() << "no config widget for source" << item->data(Qt::DisplayRole).toString();
950 return;
951 }
952 FetcherConfigDialog dlg(item->data(Qt::DisplayRole).toString(), item->fetchType(), item->updateOverwrite(), cw, this);
953
954 if(dlg.exec() == QDialog::Accepted) {
955 cw->setAccepted(true); // mark to save
956 QString newName = dlg.sourceName();
957 if(newName != item->data(Qt::DisplayRole).toString()) {
958 item->setData(Qt::DisplayRole, newName);
959 cw->slotSetModified();
960 }
961 item->setUpdateOverwrite(dlg.updateOverwrite());
962 slotModified(); // toggle apply button
963 }
964 cw->setParent(this); // keep the config widget around
965 }
966
slotRemoveSourceClicked()967 void ConfigDialog::slotRemoveSourceClicked() {
968 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->currentItem());
969 if(!item) {
970 return;
971 }
972
973 Tellico::NewStuff::Manager::self()->removeScriptByName(item->text());
974
975 Fetch::ConfigWidget* cw = m_configWidgets[item];
976 if(cw) {
977 m_removedConfigWidgets.append(cw);
978 // it gets deleted by the parent
979 }
980 m_configWidgets.remove(item);
981 delete item;
982 // m_sourceListWidget->setCurrentItem(m_sourceListWidget->currentItem());
983 slotModified(); // toggle apply button
984 }
985
slotMoveUpSourceClicked()986 void ConfigDialog::slotMoveUpSourceClicked() {
987 int row = m_sourceListWidget->currentRow();
988 if(row < 1) {
989 return;
990 }
991 QListWidgetItem* item = m_sourceListWidget->takeItem(row);
992 m_sourceListWidget->insertItem(row-1, item);
993 m_sourceListWidget->setCurrentItem(item);
994 slotModified(); // toggle apply button
995 }
996
slotMoveDownSourceClicked()997 void ConfigDialog::slotMoveDownSourceClicked() {
998 int row = m_sourceListWidget->currentRow();
999 if(row > m_sourceListWidget->count()-2) {
1000 return;
1001 }
1002 QListWidgetItem* item = m_sourceListWidget->takeItem(row);
1003 m_sourceListWidget->insertItem(row+1, item);
1004 m_sourceListWidget->setCurrentItem(item);
1005 slotModified(); // toggle apply button
1006 }
1007
slotSourceFilterChanged()1008 void ConfigDialog::slotSourceFilterChanged() {
1009 m_sourceTypeCombo->setEnabled(m_cbFilterSource->isChecked());
1010 const bool showAll = !m_sourceTypeCombo->isEnabled();
1011 const int type = m_sourceTypeCombo->currentType();
1012 for(int count = 0; count < m_sourceListWidget->count(); ++count) {
1013 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(count));
1014 item->setHidden(!showAll && item->fetcher() && !item->fetcher()->canFetch(type));
1015 }
1016 }
1017
slotSelectedSourceChanged(QListWidgetItem * item_)1018 void ConfigDialog::slotSelectedSourceChanged(QListWidgetItem* item_) {
1019 int row = m_sourceListWidget->row(item_);
1020 m_moveUpSourceBtn->setEnabled(row > 0);
1021 m_moveDownSourceBtn->setEnabled(row < m_sourceListWidget->count()-1);
1022 }
1023
slotNewStuffClicked()1024 void ConfigDialog::slotNewStuffClicked() {
1025 #ifdef ENABLE_KNEWSTUFF3
1026 KNS3::DownloadDialog dialog(QStringLiteral("tellico-script.knsrc"), this);
1027 dialog.exec();
1028
1029 KNS3::Entry::List entries = dialog.installedEntries();
1030 if(!entries.isEmpty()) {
1031 Fetch::Manager::self()->loadFetchers();
1032 readFetchConfig();
1033 }
1034 #endif
1035 }
1036
findItem(const QString & path_) const1037 Tellico::FetcherInfoListItem* ConfigDialog::findItem(const QString& path_) const {
1038 if(path_.isEmpty()) {
1039 myDebug() << "empty path";
1040 return nullptr;
1041 }
1042
1043 // this is a bit ugly, loop over all items, find the execexternal one
1044 // that matches the path
1045 for(int i = 0; i < m_sourceListWidget->count(); ++i) {
1046 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(i));
1047 if(item->fetchType() != Fetch::ExecExternal) {
1048 continue;
1049 }
1050 Fetch::ExecExternalFetcher* f = dynamic_cast<Fetch::ExecExternalFetcher*>(item->fetcher());
1051 if(f && f->execPath() == path_) {
1052 return item;
1053 }
1054 }
1055 myDebug() << "no matching item found";
1056 return nullptr;
1057 }
1058
slotShowTemplatePreview()1059 void ConfigDialog::slotShowTemplatePreview() {
1060 GUI::PreviewDialog* dlg = new GUI::PreviewDialog(this);
1061
1062 const QString templateName = m_templateCombo->currentData().toString();
1063 dlg->setXSLTFile(templateName + QLatin1String(".xsl"));
1064
1065 StyleOptions options;
1066 options.fontFamily = m_fontCombo->currentFont().family();
1067 options.fontSize = m_fontSizeInput->value();
1068 options.baseColor = m_baseColorCombo->color();
1069 options.textColor = m_textColorCombo->color();
1070 options.highlightedTextColor = m_highTextColorCombo->color();
1071 options.highlightedBaseColor = m_highBaseColorCombo->color();
1072 dlg->setXSLTOptions(Kernel::self()->collectionType(), options);
1073
1074 Data::CollPtr c = CollectionFactory::collection(Kernel::self()->collectionType(), true);
1075 Data::EntryPtr e(new Data::Entry(c));
1076 foreach(Data::FieldPtr f, c->fields()) {
1077 if(f->name() == QLatin1String("title")) {
1078 e->setField(f->name(), m_templateCombo->currentText());
1079 } else if(f->type() == Data::Field::Image) {
1080 continue;
1081 } else if(f->type() == Data::Field::Choice) {
1082 e->setField(f->name(), f->allowed().front());
1083 } else if(f->type() == Data::Field::Number) {
1084 e->setField(f->name(), QStringLiteral("1"));
1085 } else if(f->type() == Data::Field::Bool) {
1086 e->setField(f->name(), QStringLiteral("true"));
1087 } else if(f->type() == Data::Field::Rating) {
1088 e->setField(f->name(), QStringLiteral("5"));
1089 } else {
1090 e->setField(f->name(), f->title());
1091 }
1092 }
1093
1094 dlg->showEntry(e);
1095 dlg->show();
1096 // dlg gets deleted by itself
1097 // the finished() signal is connected in its constructor to delayedDestruct
1098 }
1099
loadTemplateList()1100 void ConfigDialog::loadTemplateList() {
1101 QStringList files = Tellico::locateAllFiles(QStringLiteral("tellico/entry-templates/*.xsl"));
1102 QMap<QString, QString> templates; // a QMap will have them values sorted by key
1103 foreach(const QString& file, files) {
1104 QFileInfo fi(file);
1105 QString lfile = fi.fileName().section(QLatin1Char('.'), 0, -2);
1106 QString name = lfile;
1107 name.replace(QLatin1Char('_'), QLatin1Char(' '));
1108 QString title = i18nc((name + QLatin1String(" XSL Template")).toUtf8().constData(), name.toUtf8().constData());
1109 templates.insert(title, lfile);
1110 }
1111
1112 QString s = m_templateCombo->currentText();
1113 m_templateCombo->clear();
1114 for(auto it2 = templates.constBegin(); it2 != templates.constEnd(); ++it2) {
1115 m_templateCombo->addItem(it2.key(), it2.value());
1116 }
1117 m_templateCombo->setCurrentItem(s);
1118 }
1119
slotInstallTemplate()1120 void ConfigDialog::slotInstallTemplate() {
1121 QString filter = i18n("XSL Files") + QLatin1String(" (*.xsl)") + QLatin1String(";;");
1122 filter += i18n("Template Packages") + QLatin1String(" (*.tar.gz *.tgz)") + QLatin1String(";;");
1123 filter += i18n("All Files") + QLatin1String(" (*)");
1124
1125 const QString fileClass(QStringLiteral(":InstallTemplate"));
1126 const QString f = QFileDialog::getOpenFileName(this, QString(), KRecentDirs::dir(fileClass), filter);
1127 if(f.isEmpty()) {
1128 return;
1129 }
1130 KRecentDirs::add(fileClass, QFileInfo(f).dir().canonicalPath());
1131
1132 if(Tellico::NewStuff::Manager::self()->installTemplate(f)) {
1133 loadTemplateList();
1134 }
1135 }
1136
slotDownloadTemplate()1137 void ConfigDialog::slotDownloadTemplate() {
1138 #ifdef ENABLE_KNEWSTUFF3
1139 KNS3::DownloadDialog dialog(QStringLiteral("tellico-template.knsrc"), this);
1140 dialog.exec();
1141
1142 KNS3::Entry::List entries = dialog.installedEntries();
1143 if(!entries.isEmpty()) {
1144 loadTemplateList();
1145 }
1146 #endif
1147 }
1148
slotDeleteTemplate()1149 void ConfigDialog::slotDeleteTemplate() {
1150 bool ok;
1151 QString name = QInputDialog::getItem(this,
1152 i18n("Delete Template"),
1153 i18n("Select template to delete:"),
1154 Tellico::NewStuff::Manager::self()->userTemplates().keys(),
1155 0, false, &ok);
1156 if(ok && !name.isEmpty()) {
1157 Tellico::NewStuff::Manager::self()->removeTemplateByName(name);
1158 loadTemplateList();
1159 }
1160 }
1161
slotCreateConfigWidgets()1162 void ConfigDialog::slotCreateConfigWidgets() {
1163 for(int count = 0; count < m_sourceListWidget->count(); ++count) {
1164 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(count));
1165 // only create a new config widget if we don't have one already
1166 if(!m_configWidgets.contains(item) && item->fetcher()) {
1167 Fetch::ConfigWidget* cw = item->fetcher()->configWidget(this);
1168 if(cw) { // might return 0 when no widget available for fetcher type
1169 m_configWidgets.insert(item, cw);
1170 // there's weird layout bug if it's not hidden
1171 cw->hide();
1172 }
1173 }
1174 }
1175 }
1176