File indexing completed on 2024-04-28 16:31:53

0001 /***************************************************************************
0002     Copyright (C) 2001-2009 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 #include <config.h>
0026 
0027 #include "configdialog.h"
0028 #include "field.h"
0029 #include "collection.h"
0030 #include "collectionfactory.h"
0031 #include "fetch/execexternalfetcher.h"
0032 #include "fetch/fetchmanager.h"
0033 #include "fetch/configwidget.h"
0034 #include "controller.h"
0035 #include "fetcherconfigdialog.h"
0036 #include "tellico_kernel.h"
0037 #include "utils/tellico_utils.h"
0038 #include "utils/string_utils.h"
0039 #include "config/tellico_config.h"
0040 #include "images/imagefactory.h"
0041 #include "gui/combobox.h"
0042 #include "gui/collectiontypecombo.h"
0043 #include "gui/previewdialog.h"
0044 #include "newstuff/manager.h"
0045 #include "fieldformat.h"
0046 #include "tellico_debug.h"
0047 
0048 #include <KLocalizedString>
0049 #include <KConfig>
0050 #include <KAcceleratorManager>
0051 #include <KColorCombo>
0052 #include <KHelpClient>
0053 #include <KRecentDirs>
0054 
0055 #ifdef ENABLE_KNEWSTUFF3
0056 #include <KNS3/DownloadDialog>
0057 #endif
0058 
0059 #include <QSpinBox>
0060 #include <QLineEdit>
0061 #include <QPushButton>
0062 #include <QSize>
0063 #include <QLayout>
0064 #include <QLabel>
0065 #include <QCheckBox>
0066 #include <QPixmap>
0067 #include <QRegularExpression>
0068 #include <QFileInfo>
0069 #include <QRadioButton>
0070 #include <QFrame>
0071 #include <QFontComboBox>
0072 #include <QGroupBox>
0073 #include <QButtonGroup>
0074 #include <QInputDialog>
0075 #include <QVBoxLayout>
0076 #include <QHBoxLayout>
0077 #include <QApplication>
0078 #include <QTimer>
0079 #include <QFileDialog>
0080 
0081 namespace {
0082   static const int CONFIG_MIN_WIDTH = 640;
0083   static const int CONFIG_MIN_HEIGHT = 420;
0084 }
0085 
0086 using Tellico::ConfigDialog;
0087 
0088 ConfigDialog::ConfigDialog(QWidget* parent_)
0089     : KPageDialog(parent_)
0090     , m_initializedPages(0)
0091     , m_modifying(false)
0092     , m_okClicked(false) {
0093   setFaceType(List);
0094   setModal(true);
0095   setWindowTitle(i18n("Configure Tellico"));
0096   setStandardButtons(QDialogButtonBox::Help |
0097                      QDialogButtonBox::Ok |
0098                      QDialogButtonBox::Apply |
0099                      QDialogButtonBox::Cancel |
0100                      QDialogButtonBox::RestoreDefaults);
0101 
0102   setupGeneralPage();
0103   setupPrintingPage();
0104   setupTemplatePage();
0105   setupFetchPage();
0106 
0107   updateGeometry();
0108   QSize s = sizeHint();
0109   resize(qMax(s.width(), CONFIG_MIN_WIDTH), qMax(s.height(), CONFIG_MIN_HEIGHT));
0110 
0111   // OK button is connected to buttonBox accepted() signal which is already connected to accept() slot
0112   connect(button(QDialogButtonBox::Apply), &QAbstractButton::clicked, this, &ConfigDialog::slotApply);
0113   connect(button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &ConfigDialog::slotHelp);
0114   connect(button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, this, &ConfigDialog::slotDefault);
0115 
0116   button(QDialogButtonBox::Ok)->setEnabled(false);
0117   button(QDialogButtonBox::Apply)->setEnabled(false);
0118   button(QDialogButtonBox::Ok)->setDefault(true);
0119   button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return);
0120 
0121   connect(this, &KPageDialog::currentPageChanged, this, &ConfigDialog::slotInitPage);
0122 }
0123 
0124 ConfigDialog::~ConfigDialog() {
0125   foreach(Fetch::ConfigWidget* widget, m_newStuffConfigWidgets) {
0126     widget->removed();
0127   }
0128 }
0129 
0130 void ConfigDialog::slotInitPage(KPageWidgetItem* item_) {
0131   Q_ASSERT(item_);
0132   // every page item has a frame
0133   // if the frame has no layout, then we need to initialize the itme
0134   QFrame* frame = ::qobject_cast<QFrame*>(item_->widget());
0135   Q_ASSERT(frame);
0136   if(frame->layout()) {
0137     return;
0138   }
0139 
0140   const QString name = item_->name();
0141   // these names must be kept in sync with the page names
0142   if(name == i18n("General")) {
0143     initGeneralPage(frame);
0144   } else if(name == i18n("Printing")) {
0145     initPrintingPage(frame);
0146   } else if(name == i18n("Templates")) {
0147     initTemplatePage(frame);
0148   } else if(name == i18n("Data Sources")) {
0149     initFetchPage(frame);
0150   }
0151 }
0152 
0153 void ConfigDialog::accept() {
0154   m_okClicked = true;
0155   slotApply();
0156   KPageDialog::accept();
0157   m_okClicked = false;
0158 }
0159 
0160 void ConfigDialog::slotApply() {
0161   emit signalConfigChanged();
0162   button(QDialogButtonBox::Apply)->setEnabled(false);
0163 }
0164 
0165 void ConfigDialog::slotDefault() {
0166   // only change the defaults on the active page
0167   Config::self()->useDefaults(true);
0168   const QString name = currentPage()->name();
0169   if(name == i18n("General")) {
0170     readGeneralConfig();
0171   } else if(name == i18n("Printing")) {
0172     readPrintingConfig();
0173   } else if(name == i18n("Templates")) {
0174     readTemplateConfig();
0175   }
0176   Config::self()->useDefaults(false);
0177   slotModified();
0178 }
0179 
0180 void ConfigDialog::slotHelp() {
0181   const QString name = currentPage()->name();
0182   // these names must be kept in sync with the page names
0183   if(name == i18n("General")) {
0184     KHelpClient::invokeHelp(QStringLiteral("general-options"));
0185   } else if(name == i18n("Printing")) {
0186     KHelpClient::invokeHelp(QStringLiteral("printing-options"));
0187   } else if(name == i18n("Templates")) {
0188     KHelpClient::invokeHelp(QStringLiteral("template-options"));
0189   } else if(name == i18n("Data Sources")) {
0190     KHelpClient::invokeHelp(QStringLiteral("internet-sources-options"));
0191   }
0192 }
0193 
0194 bool ConfigDialog::isPageInitialized(Page page_) const {
0195   return m_initializedPages & page_;
0196 }
0197 
0198 void ConfigDialog::setupGeneralPage() {
0199   QFrame* frame = new QFrame(this);
0200   KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("General"));
0201   page->setHeader(i18n("General Options"));
0202   page->setIcon(QIcon::fromTheme(QStringLiteral("tellico"), QIcon(QLatin1String(":/icons/tellico"))));
0203   addPage(page);
0204 
0205   // since this is the first page, go ahead and lay it out
0206   initGeneralPage(frame);
0207 }
0208 
0209 void ConfigDialog::initGeneralPage(QFrame* frame) {
0210   QVBoxLayout* l = new QVBoxLayout(frame);
0211 
0212   m_cbOpenLastFile = new QCheckBox(i18n("&Reopen file at startup"), frame);
0213   m_cbOpenLastFile->setWhatsThis(i18n("If checked, the file that was last open "
0214                                       "will be re-opened at program start-up."));
0215   l->addWidget(m_cbOpenLastFile);
0216   connect(m_cbOpenLastFile, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
0217 
0218   m_cbShowTipDay = new QCheckBox(i18n("&Show \"Tip of the Day\" at startup"), frame);
0219   m_cbShowTipDay->setWhatsThis(i18n("If checked, the \"Tip of the Day\" will be "
0220                                     "shown at program start-up."));
0221   l->addWidget(m_cbShowTipDay);
0222   connect(m_cbShowTipDay, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
0223 
0224   m_cbEnableWebcam = new QCheckBox(i18n("&Enable webcam for barcode scanning"), frame);
0225   m_cbEnableWebcam->setWhatsThis(i18n("If checked, the input from a webcam will be used "
0226                                       "to scan barcodes for searching."));
0227   l->addWidget(m_cbEnableWebcam);
0228   connect(m_cbEnableWebcam, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
0229 
0230   QGroupBox* imageGroupBox = new QGroupBox(i18n("Image Storage Options"), frame);
0231   l->addWidget(imageGroupBox);
0232   m_rbImageInFile = new QRadioButton(i18n("Store images in data file"), imageGroupBox);
0233   m_rbImageInAppDir = new QRadioButton(i18n("Store images in common application directory"), imageGroupBox);
0234   m_rbImageInLocalDir = new QRadioButton(i18n("Store images in directory relative to data file"), imageGroupBox);
0235   imageGroupBox->setWhatsThis(i18n("Images may be saved in the data file itself, which can "
0236                                    "cause Tellico to run slowly, stored in the Tellico "
0237                                    "application directory, or stored in a directory in the "
0238                                    "same location as the data file."));
0239   QVBoxLayout* imageGroupLayout = new QVBoxLayout(imageGroupBox);
0240   imageGroupLayout->addWidget(m_rbImageInFile);
0241   imageGroupLayout->addWidget(m_rbImageInAppDir);
0242   imageGroupLayout->addWidget(m_rbImageInLocalDir);
0243   imageGroupBox->setLayout(imageGroupLayout);
0244 
0245   QButtonGroup* imageGroup = new QButtonGroup(frame);
0246   imageGroup->addButton(m_rbImageInFile);
0247   imageGroup->addButton(m_rbImageInAppDir);
0248   imageGroup->addButton(m_rbImageInLocalDir);
0249 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
0250   void (QButtonGroup::* buttonClicked)(int) = &QButtonGroup::buttonClicked;
0251   connect(imageGroup, buttonClicked, this, &ConfigDialog::slotModified);
0252 #else
0253   connect(imageGroup, &QButtonGroup::idClicked, this, &ConfigDialog::slotModified);
0254 #endif
0255 
0256   QGroupBox* formatGroup = new QGroupBox(i18n("Formatting Options"), frame);
0257   l->addWidget(formatGroup);
0258   QVBoxLayout* formatGroupLayout = new QVBoxLayout(formatGroup);
0259   formatGroup->setLayout(formatGroupLayout);
0260 
0261   m_cbCapitalize = new QCheckBox(i18n("Auto capitalize &titles and names"), formatGroup);
0262   m_cbCapitalize->setWhatsThis(i18n("If checked, titles and names will "
0263                                     "be automatically capitalized."));
0264   connect(m_cbCapitalize, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
0265   formatGroupLayout->addWidget(m_cbCapitalize);
0266 
0267   m_cbFormat = new QCheckBox(i18n("Auto &format titles and names"), formatGroup);
0268   m_cbFormat->setWhatsThis(i18n("If checked, titles and names will "
0269                                 "be automatically formatted."));
0270   connect(m_cbFormat, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
0271   formatGroupLayout->addWidget(m_cbFormat);
0272 
0273   QWidget* g1 = new QWidget(formatGroup);
0274   QGridLayout* g1Layout = new QGridLayout(g1);
0275   g1->setLayout(g1Layout);
0276   formatGroupLayout->addWidget(g1);
0277 
0278   QLabel* lab = new QLabel(i18n("No capitali&zation:"), g1);
0279   g1Layout->addWidget(lab, 0, 0);
0280   m_leCapitals = new QLineEdit(g1);
0281   g1Layout->addWidget(m_leCapitals, 0, 1);
0282   lab->setBuddy(m_leCapitals);
0283   QString whats = i18n("<qt>A list of words which should not be capitalized. Multiple values "
0284                        "should be separated by a semi-colon.</qt>");
0285   lab->setWhatsThis(whats);
0286   m_leCapitals->setWhatsThis(whats);
0287   connect(m_leCapitals, &QLineEdit::textChanged, this, &ConfigDialog::slotModified);
0288 
0289   lab = new QLabel(i18n("Artic&les:"), g1);
0290   g1Layout->addWidget(lab, 1, 0);
0291   m_leArticles = new QLineEdit(g1);
0292   g1Layout->addWidget(m_leArticles, 1, 1);
0293   lab->setBuddy(m_leArticles);
0294   whats = i18n("<qt>A list of words which should be considered as articles "
0295                "if they are the first word in a title. Multiple values "
0296                "should be separated by a semi-colon.</qt>");
0297   lab->setWhatsThis(whats);
0298   m_leArticles->setWhatsThis(whats);
0299   connect(m_leArticles, &QLineEdit::textChanged, this, &ConfigDialog::slotModified);
0300 
0301   lab = new QLabel(i18n("Personal suffi&xes:"), g1);
0302   g1Layout->addWidget(lab, 2, 0);
0303   m_leSuffixes = new QLineEdit(g1);
0304   g1Layout->addWidget(m_leSuffixes, 2, 1);
0305   lab->setBuddy(m_leSuffixes);
0306   whats = i18n("<qt>A list of suffixes which might be used in personal names. Multiple values "
0307                "should be separated by a semi-colon.</qt>");
0308   lab->setWhatsThis(whats);
0309   m_leSuffixes->setWhatsThis(whats);
0310   connect(m_leSuffixes, &QLineEdit::textChanged, this, &ConfigDialog::slotModified);
0311 
0312   lab = new QLabel(i18n("Surname &prefixes:"), g1);
0313   g1Layout->addWidget(lab, 3, 0);
0314   m_lePrefixes = new QLineEdit(g1);
0315   g1Layout->addWidget(m_lePrefixes, 3, 1);
0316   lab->setBuddy(m_lePrefixes);
0317   whats = i18n("<qt>A list of prefixes which might be used in surnames. Multiple values "
0318                "should be separated by a semi-colon.</qt>");
0319   lab->setWhatsThis(whats);
0320   m_lePrefixes->setWhatsThis(whats);
0321   connect(m_lePrefixes, &QLineEdit::textChanged, this, &ConfigDialog::slotModified);
0322 
0323   // stretch to fill lower area
0324   l->addStretch(1);
0325   m_initializedPages |= General;
0326   readGeneralConfig();
0327 }
0328 
0329 void ConfigDialog::setupPrintingPage() {
0330   QFrame* frame = new QFrame(this);
0331   KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Printing"));
0332   page->setHeader(i18n("Printing Options"));
0333   page->setIcon(QIcon::fromTheme(QStringLiteral("printer")));
0334   addPage(page);
0335 }
0336 
0337 void ConfigDialog::initPrintingPage(QFrame* frame) {
0338   QVBoxLayout* l = new QVBoxLayout(frame);
0339 
0340   QGroupBox* formatOptions = new QGroupBox(i18n("Formatting Options"), frame);
0341   l->addWidget(formatOptions);
0342   QVBoxLayout* formatLayout = new QVBoxLayout(formatOptions);
0343   formatOptions->setLayout(formatLayout);
0344 
0345   m_cbPrintFormatted = new QCheckBox(i18n("&Format titles and names"), formatOptions);
0346   m_cbPrintFormatted->setWhatsThis(i18n("If checked, titles and names will be automatically formatted."));
0347   connect(m_cbPrintFormatted, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
0348   formatLayout->addWidget(m_cbPrintFormatted);
0349 
0350   m_cbPrintHeaders = new QCheckBox(i18n("&Print field headers"), formatOptions);
0351   m_cbPrintHeaders->setWhatsThis(i18n("If checked, the field names will be printed as table headers."));
0352   connect(m_cbPrintHeaders, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
0353   formatLayout->addWidget(m_cbPrintHeaders);
0354 
0355   QGroupBox* groupOptions = new QGroupBox(i18n("Grouping Options"), frame);
0356   l->addWidget(groupOptions);
0357   QVBoxLayout* groupLayout = new QVBoxLayout(groupOptions);
0358   groupOptions->setLayout(groupLayout);
0359 
0360   m_cbPrintGrouped = new QCheckBox(i18n("&Group the entries"), groupOptions);
0361   m_cbPrintGrouped->setWhatsThis(i18n("If checked, the entries will be grouped by the selected field."));
0362   connect(m_cbPrintGrouped, &QAbstractButton::clicked, this, &ConfigDialog::slotModified);
0363   groupLayout->addWidget(m_cbPrintGrouped);
0364 
0365   QGroupBox* imageOptions = new QGroupBox(i18n("Image Options"), frame);
0366   l->addWidget(imageOptions);
0367 
0368   QGridLayout* gridLayout = new QGridLayout(imageOptions);
0369   imageOptions->setLayout(gridLayout);
0370 
0371   QLabel* lab = new QLabel(i18n("Maximum image &width:"), imageOptions);
0372   gridLayout->addWidget(lab, 0, 0);
0373   m_imageWidthBox = new QSpinBox(imageOptions);
0374   m_imageWidthBox->setMaximum(999);
0375   m_imageWidthBox->setMinimum(0);
0376   m_imageWidthBox->setValue(50);
0377   gridLayout->addWidget(m_imageWidthBox, 0, 1);
0378   m_imageWidthBox->setSuffix(QStringLiteral(" px"));
0379   lab->setBuddy(m_imageWidthBox);
0380   QString whats = i18n("The maximum width of the images in the printout. The aspect ratio is preserved.");
0381   lab->setWhatsThis(whats);
0382   m_imageWidthBox->setWhatsThis(whats);
0383   void (QSpinBox::* valueChanged)(int) = &QSpinBox::valueChanged;
0384   connect(m_imageWidthBox, valueChanged, this, &ConfigDialog::slotModified);
0385 
0386   lab = new QLabel(i18n("&Maximum image height:"), imageOptions);
0387   gridLayout->addWidget(lab, 1, 0);
0388   m_imageHeightBox = new QSpinBox(imageOptions);
0389   m_imageHeightBox->setMaximum(999);
0390   m_imageHeightBox->setMinimum(0);
0391   m_imageHeightBox->setValue(50);
0392   gridLayout->addWidget(m_imageHeightBox, 1, 1);
0393   m_imageHeightBox->setSuffix(QStringLiteral(" px"));
0394   lab->setBuddy(m_imageHeightBox);
0395   whats = i18n("The maximum height of the images in the printout. The aspect ratio is preserved.");
0396   lab->setWhatsThis(whats);
0397   m_imageHeightBox->setWhatsThis(whats);
0398   connect(m_imageHeightBox, valueChanged, this, &ConfigDialog::slotModified);
0399 
0400   // stretch to fill lower area
0401   l->addStretch(1);
0402   m_initializedPages |= Printing;
0403   readPrintingConfig();
0404 }
0405 
0406 void ConfigDialog::setupTemplatePage() {
0407   QFrame* frame = new QFrame(this);
0408   KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Templates"));
0409   page->setHeader(i18n("Template Options"));
0410   // odd icon, I know, matches KMail, though...
0411   page->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-theme")));
0412   addPage(page);
0413 }
0414 
0415 void ConfigDialog::initTemplatePage(QFrame* frame) {
0416   QVBoxLayout* l = new QVBoxLayout(frame);
0417 
0418   QGridLayout* gridLayout = new QGridLayout();
0419   l->addLayout(gridLayout);
0420 
0421   int row = -1;
0422   // so I can reuse an i18n string, a plain label can't have an '&'
0423   QString s = KLocalizedString::removeAcceleratorMarker(i18n("Collection &type:"));
0424   QLabel* lab = new QLabel(s, frame);
0425   gridLayout->addWidget(lab, ++row, 0);
0426   const int collType = Kernel::self()->collectionType();
0427   lab = new QLabel(CollectionFactory::nameHash().value(collType), frame);
0428   gridLayout->addWidget(lab, row, 1, 1, 2);
0429 
0430   lab = new QLabel(i18n("Template:"), frame);
0431   m_templateCombo = new GUI::ComboBox(frame);
0432   void (QComboBox::* activatedInt)(int) = &QComboBox::activated;
0433   connect(m_templateCombo, activatedInt, this, &ConfigDialog::slotModified);
0434   lab->setBuddy(m_templateCombo);
0435   QString whats = i18n("Select the template to use for the current type of collections. "
0436                        "Not all templates will use the font and color settings.");
0437   lab->setWhatsThis(whats);
0438   m_templateCombo->setWhatsThis(whats);
0439   gridLayout->addWidget(lab, ++row, 0);
0440   gridLayout->addWidget(m_templateCombo, row, 1);
0441 
0442   QPushButton* btn = new QPushButton(i18n("&Preview..."), frame);
0443   btn->setWhatsThis(i18n("Show a preview of the template"));
0444   btn->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
0445   gridLayout->addWidget(btn, row, 2);
0446   connect(btn, &QAbstractButton::clicked, this, &ConfigDialog::slotShowTemplatePreview);
0447 
0448   // so the button is squeezed small
0449   gridLayout->setColumnStretch(0, 10);
0450   gridLayout->setColumnStretch(1, 10);
0451 
0452   loadTemplateList();
0453 
0454 //  QLabel* l1 = new QLabel(i18n("The options below will be passed to the template, but not "
0455 //                               "all templates will use them. Some fonts and colors may be "
0456 //                               "specified directly in the template."), frame);
0457 //  l1->setTextFormat(Qt::RichText);
0458 //  l->addWidget(l1);
0459 
0460   QGroupBox* fontGroup = new QGroupBox(i18n("Font Options"), frame);
0461   l->addWidget(fontGroup);
0462 
0463   row = -1;
0464   QGridLayout* fontLayout = new QGridLayout();
0465   fontGroup->setLayout(fontLayout);
0466 
0467   lab = new QLabel(i18n("Font:"), fontGroup);
0468   fontLayout->addWidget(lab, ++row, 0);
0469   m_fontCombo = new QFontComboBox(fontGroup);
0470   fontLayout->addWidget(m_fontCombo, row, 1);
0471   connect(m_fontCombo, activatedInt, this, &ConfigDialog::slotModified);
0472   lab->setBuddy(m_fontCombo);
0473   whats = i18n("This font is passed to the template used in the Entry View.");
0474   lab->setWhatsThis(whats);
0475   m_fontCombo->setWhatsThis(whats);
0476 
0477   fontLayout->addWidget(new QLabel(i18n("Size:"), fontGroup), ++row, 0);
0478   m_fontSizeInput = new QSpinBox(fontGroup);
0479   m_fontSizeInput->setMaximum(30); // 30 is same max as konq config
0480   m_fontSizeInput->setMinimum(5);
0481   m_fontSizeInput->setSuffix(QStringLiteral("pt"));
0482   fontLayout->addWidget(m_fontSizeInput, row, 1);
0483   void (QSpinBox::* valueChangedInt)(int) = &QSpinBox::valueChanged;
0484   connect(m_fontSizeInput, valueChangedInt, this, &ConfigDialog::slotModified);
0485   lab->setBuddy(m_fontSizeInput);
0486   lab->setWhatsThis(whats);
0487   m_fontSizeInput->setWhatsThis(whats);
0488 
0489   QGroupBox* colGroup = new QGroupBox(i18n("Color Options"), frame);
0490   l->addWidget(colGroup);
0491 
0492   row = -1;
0493   QGridLayout* colLayout = new QGridLayout();
0494   colGroup->setLayout(colLayout);
0495 
0496   lab = new QLabel(i18n("Background color:"), colGroup);
0497   colLayout->addWidget(lab, ++row, 0);
0498   m_baseColorCombo = new KColorCombo(colGroup);
0499   colLayout->addWidget(m_baseColorCombo, row, 1);
0500   connect(m_baseColorCombo, activatedInt, this, &ConfigDialog::slotModified);
0501   lab->setBuddy(m_baseColorCombo);
0502   whats = i18n("This color is passed to the template used in the Entry View.");
0503   lab->setWhatsThis(whats);
0504   m_baseColorCombo->setWhatsThis(whats);
0505 
0506   lab = new QLabel(i18n("Text color:"), colGroup);
0507   colLayout->addWidget(lab, ++row, 0);
0508   m_textColorCombo = new KColorCombo(colGroup);
0509   colLayout->addWidget(m_textColorCombo, row, 1);
0510   connect(m_textColorCombo, activatedInt, this, &ConfigDialog::slotModified);
0511   lab->setBuddy(m_textColorCombo);
0512   lab->setWhatsThis(whats);
0513   m_textColorCombo->setWhatsThis(whats);
0514 
0515   lab = new QLabel(i18n("Highlight color:"), colGroup);
0516   colLayout->addWidget(lab, ++row, 0);
0517   m_highBaseColorCombo = new KColorCombo(colGroup);
0518   colLayout->addWidget(m_highBaseColorCombo, row, 1);
0519   connect(m_highBaseColorCombo, activatedInt, this, &ConfigDialog::slotModified);
0520   lab->setBuddy(m_highBaseColorCombo);
0521   lab->setWhatsThis(whats);
0522   m_highBaseColorCombo->setWhatsThis(whats);
0523 
0524   lab = new QLabel(i18n("Highlighted text color:"), colGroup);
0525   colLayout->addWidget(lab, ++row, 0);
0526   m_highTextColorCombo = new KColorCombo(colGroup);
0527   colLayout->addWidget(m_highTextColorCombo, row, 1);
0528   connect(m_highTextColorCombo, activatedInt, this, &ConfigDialog::slotModified);
0529   lab->setBuddy(m_highTextColorCombo);
0530   lab->setWhatsThis(whats);
0531   m_highTextColorCombo->setWhatsThis(whats);
0532 
0533   QGroupBox* groupBox = new QGroupBox(i18n("Manage Templates"), frame);
0534   l->addWidget(groupBox);
0535   QVBoxLayout* vlay = new QVBoxLayout(groupBox);
0536   groupBox->setLayout(vlay);
0537 
0538   QWidget* box1 = new QWidget(groupBox);
0539   QHBoxLayout* box1HBoxLayout = new QHBoxLayout(box1);
0540   box1HBoxLayout->setMargin(0);
0541   vlay->addWidget(box1);
0542   box1HBoxLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
0543 
0544   QPushButton* b1 = new QPushButton(i18n("Install..."), box1);
0545   box1HBoxLayout->addWidget(b1);
0546   b1->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0547   connect(b1, &QAbstractButton::clicked, this, &ConfigDialog::slotInstallTemplate);
0548   whats = i18n("Click to install a new template directly.");
0549   b1->setWhatsThis(whats);
0550 
0551   QPushButton* b2 = new QPushButton(i18n("Download..."), box1);
0552   box1HBoxLayout->addWidget(b2);
0553   b2->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")));
0554   connect(b2, &QAbstractButton::clicked, this, &ConfigDialog::slotDownloadTemplate);
0555   whats = i18n("Click to download additional templates.");
0556   b2->setWhatsThis(whats);
0557 
0558   QPushButton* b3 = new QPushButton(i18n("Delete..."), box1);
0559   box1HBoxLayout->addWidget(b3);
0560   b3->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
0561   connect(b3, &QAbstractButton::clicked, this, &ConfigDialog::slotDeleteTemplate);
0562   whats = i18n("Click to select and remove installed templates.");
0563   b3->setWhatsThis(whats);
0564 
0565   // stretch to fill lower area
0566   l->addStretch(1);
0567 
0568   // purely for aesthetics make all widgets line up
0569   QList<QWidget*> widgets;
0570   widgets.append(m_fontCombo);
0571   widgets.append(m_fontSizeInput);
0572   widgets.append(m_baseColorCombo);
0573   widgets.append(m_textColorCombo);
0574   widgets.append(m_highBaseColorCombo);
0575   widgets.append(m_highTextColorCombo);
0576   int w = 0;
0577   foreach(QWidget* widget, widgets) {
0578     widget->ensurePolished();
0579     w = qMax(w, widget->sizeHint().width());
0580   }
0581   foreach(QWidget* widget, widgets) {
0582     widget->setMinimumWidth(w);
0583   }
0584 
0585   KAcceleratorManager::manage(frame);
0586   m_initializedPages |= Template;
0587   readTemplateConfig();
0588 }
0589 
0590 void ConfigDialog::setupFetchPage() {
0591   QFrame* frame = new QFrame(this);
0592   KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Data Sources"));
0593   page->setHeader(i18n("Data Sources Options"));
0594   page->setIcon(QIcon::fromTheme(QStringLiteral("network-wired")));
0595   addPage(page);
0596 }
0597 
0598 void ConfigDialog::initFetchPage(QFrame* frame) {
0599   QHBoxLayout* l = new QHBoxLayout(frame);
0600 
0601   QVBoxLayout* leftLayout = new QVBoxLayout();
0602   l->addLayout(leftLayout);
0603   m_sourceListWidget = new QListWidget(frame);
0604   m_sourceListWidget->setSortingEnabled(false); // no sorting
0605   m_sourceListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
0606   leftLayout->addWidget(m_sourceListWidget, 1);
0607   connect(m_sourceListWidget, &QListWidget::currentItemChanged, this, &ConfigDialog::slotSelectedSourceChanged);
0608   connect(m_sourceListWidget, &QListWidget::itemDoubleClicked, this, &ConfigDialog::slotModifySourceClicked);
0609 
0610   QWidget* hb = new QWidget(frame);
0611   QHBoxLayout* hbHBoxLayout = new QHBoxLayout(hb);
0612   hbHBoxLayout->setMargin(0);
0613   leftLayout->addWidget(hb);
0614   m_moveUpSourceBtn = new QPushButton(i18n("Move &Up"), hb);
0615   hbHBoxLayout->addWidget(m_moveUpSourceBtn);
0616   m_moveUpSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
0617   m_moveUpSourceBtn->setWhatsThis(i18n("The order of the data sources sets the order "
0618                                        "that Tellico uses when entries are automatically updated."));
0619   m_moveDownSourceBtn = new QPushButton(i18n("Move &Down"), hb);
0620   hbHBoxLayout->addWidget(m_moveDownSourceBtn);
0621   m_moveDownSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
0622   m_moveDownSourceBtn->setWhatsThis(i18n("The order of the data sources sets the order "
0623                                          "that Tellico uses when entries are automatically updated."));
0624 
0625   QWidget* hb2 = new QWidget(frame);
0626   QHBoxLayout* hb2HBoxLayout = new QHBoxLayout(hb2);
0627   hb2HBoxLayout->setMargin(0);
0628   leftLayout->addWidget(hb2);
0629   m_cbFilterSource = new QCheckBox(i18n("Filter by type:"), hb2);
0630   hb2HBoxLayout->addWidget(m_cbFilterSource);
0631   connect(m_cbFilterSource, &QAbstractButton::clicked, this, &ConfigDialog::slotSourceFilterChanged);
0632   m_sourceTypeCombo = new GUI::CollectionTypeCombo(hb2);
0633   hb2HBoxLayout->addWidget(m_sourceTypeCombo);
0634   void (QComboBox::* currentIndexChanged)(int) = &QComboBox::currentIndexChanged;
0635   connect(m_sourceTypeCombo, currentIndexChanged, this, &ConfigDialog::slotSourceFilterChanged);
0636   // we want to remove the item for a custom collection
0637   int index = m_sourceTypeCombo->findData(Data::Collection::Base);
0638   if(index > -1) {
0639     m_sourceTypeCombo->removeItem(index);
0640   }
0641   // disable until check box is checked
0642   m_sourceTypeCombo->setEnabled(false);
0643 
0644   // these icons are rather arbitrary, but seem to vaguely fit
0645   QVBoxLayout* vlay = new QVBoxLayout();
0646   l->addLayout(vlay);
0647   QPushButton* newSourceBtn = new QPushButton(i18n("&New..."), frame);
0648   newSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0649   newSourceBtn->setWhatsThis(i18n("Click to add a new data source."));
0650   m_modifySourceBtn = new QPushButton(i18n("&Modify..."), frame);
0651   m_modifySourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("network-wired")));
0652   m_modifySourceBtn->setWhatsThis(i18n("Click to modify the selected data source."));
0653   m_removeSourceBtn = new QPushButton(i18n("&Delete"), frame);
0654   m_removeSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
0655   m_removeSourceBtn->setWhatsThis(i18n("Click to delete the selected data source."));
0656   m_newStuffBtn = new QPushButton(i18n("Download..."), frame);
0657   m_newStuffBtn->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")));
0658   m_newStuffBtn->setWhatsThis(i18n("Click to download additional data sources."));
0659   // TODO: disable button for now since checksum and signature checking are no longer possible with khotnewstuff
0660   m_newStuffBtn->setEnabled(false);
0661 
0662   vlay->addWidget(newSourceBtn);
0663   vlay->addWidget(m_modifySourceBtn);
0664   vlay->addWidget(m_removeSourceBtn);
0665   // separate newstuff button from the rest
0666   vlay->addSpacing(16);
0667   vlay->addWidget(m_newStuffBtn);
0668   vlay->addStretch(1);
0669 
0670   connect(newSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotNewSourceClicked);
0671   connect(m_modifySourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotModifySourceClicked);
0672   connect(m_moveUpSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotMoveUpSourceClicked);
0673   connect(m_moveDownSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotMoveDownSourceClicked);
0674   connect(m_removeSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotRemoveSourceClicked);
0675   connect(m_newStuffBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotNewStuffClicked);
0676 
0677   KAcceleratorManager::manage(frame);
0678   m_initializedPages |= Fetch;
0679   readFetchConfig();
0680 }
0681 
0682 void ConfigDialog::readGeneralConfig() {
0683   m_modifying = true;
0684 
0685   m_cbShowTipDay->setChecked(Config::showTipOfDay());
0686   m_cbOpenLastFile->setChecked(Config::reopenLastFile());
0687 #ifdef ENABLE_WEBCAM
0688   m_cbEnableWebcam->setChecked(Config::enableWebcam());
0689 #else
0690   m_cbEnableWebcam->setChecked(false);
0691   m_cbEnableWebcam->setEnabled(false);
0692 #endif
0693 
0694   switch(Config::imageLocation()) {
0695     case Config::ImagesInFile: m_rbImageInFile->setChecked(true); break;
0696     case Config::ImagesInAppDir: m_rbImageInAppDir->setChecked(true); break;
0697     case Config::ImagesInLocalDir: m_rbImageInLocalDir->setChecked(true); break;
0698   }
0699 
0700   bool autoCapitals = Config::autoCapitalization();
0701   m_cbCapitalize->setChecked(autoCapitals);
0702 
0703   bool autoFormat = Config::autoFormat();
0704   m_cbFormat->setChecked(autoFormat);
0705 
0706   const QRegularExpression comma(QLatin1String("\\s*,\\s*"));
0707 
0708   m_leCapitals->setText(Config::noCapitalizationString().replace(comma, FieldFormat::delimiterString()));
0709   m_leArticles->setText(Config::articlesString().replace(comma, FieldFormat::delimiterString()));
0710   m_leSuffixes->setText(Config::nameSuffixesString().replace(comma, FieldFormat::delimiterString()));
0711   m_lePrefixes->setText(Config::surnamePrefixesString().replace(comma, FieldFormat::delimiterString()));
0712 
0713   m_modifying = false;
0714 }
0715 
0716 void ConfigDialog::readPrintingConfig() {
0717   m_modifying = true;
0718 
0719   m_cbPrintHeaders->setChecked(Config::printFieldHeaders());
0720   m_cbPrintFormatted->setChecked(Config::printFormatted());
0721   m_cbPrintGrouped->setChecked(Config::printGrouped());
0722   m_imageWidthBox->setValue(Config::maxImageWidth());
0723   m_imageHeightBox->setValue(Config::maxImageHeight());
0724 
0725   m_modifying = false;
0726 }
0727 
0728 void ConfigDialog::readTemplateConfig() {
0729   m_modifying = true;
0730 
0731   // entry template selection
0732   const int collType = Kernel::self()->collectionType();
0733   QString file = Config::templateName(collType);
0734   file.replace(QLatin1Char('_'), QLatin1Char(' '));
0735   QString fileContext = file + QLatin1String(" XSL Template");
0736   m_templateCombo->setCurrentItem(i18nc(fileContext.toUtf8().constData(), file.toUtf8().constData()));
0737 
0738   m_fontCombo->setCurrentFont(QFont(Config::templateFont(collType).family()));
0739   m_fontSizeInput->setValue(Config::templateFont(collType).pointSize());
0740   m_baseColorCombo->setColor(Config::templateBaseColor(collType));
0741   m_textColorCombo->setColor(Config::templateTextColor(collType));
0742   m_highBaseColorCombo->setColor(Config::templateHighlightedBaseColor(collType));
0743   m_highTextColorCombo->setColor(Config::templateHighlightedTextColor(collType));
0744 
0745   m_modifying = false;
0746 }
0747 
0748 void ConfigDialog::readFetchConfig() {
0749   m_modifying = true;
0750 
0751   m_sourceListWidget->clear();
0752   m_configWidgets.clear();
0753 
0754   m_sourceListWidget->setUpdatesEnabled(false);
0755   foreach(Fetch::Fetcher::Ptr fetcher, Fetch::Manager::self()->fetchers()) {
0756     Fetch::FetcherInfo info(fetcher->type(), fetcher->source(),
0757                             fetcher->updateOverwrite(), fetcher->uuid());
0758     FetcherInfoListItem* item = new FetcherInfoListItem(m_sourceListWidget, info);
0759     item->setFetcher(fetcher.data());
0760   }
0761   m_sourceListWidget->setUpdatesEnabled(true);
0762 
0763   if(m_sourceListWidget->count() == 0) {
0764     m_modifySourceBtn->setEnabled(false);
0765     m_removeSourceBtn->setEnabled(false);
0766   } else {
0767     // go ahead and select the first one
0768     m_sourceListWidget->setCurrentItem(m_sourceListWidget->item(0));
0769   }
0770 
0771   m_modifying = false;
0772   QTimer::singleShot(500, this, &ConfigDialog::slotCreateConfigWidgets);
0773 }
0774 
0775 void ConfigDialog::saveConfiguration() {
0776   if(isPageInitialized(General)) saveGeneralConfig();
0777   if(isPageInitialized(Printing)) savePrintingConfig();
0778   if(isPageInitialized(Template)) saveTemplateConfig();
0779   if(isPageInitialized(Fetch)) saveFetchConfig();
0780 }
0781 
0782 void ConfigDialog::saveGeneralConfig() {
0783   Config::setShowTipOfDay(m_cbShowTipDay->isChecked());
0784   Config::setEnableWebcam(m_cbEnableWebcam->isChecked());
0785 
0786   int imageLocation;
0787   if(m_rbImageInFile->isChecked()) {
0788     imageLocation = Config::ImagesInFile;
0789   } else if(m_rbImageInAppDir->isChecked()) {
0790     imageLocation = Config::ImagesInAppDir;
0791   } else {
0792     imageLocation = Config::ImagesInLocalDir;
0793   }
0794   Config::setImageLocation(imageLocation);
0795   Config::setReopenLastFile(m_cbOpenLastFile->isChecked());
0796 
0797   Config::setAutoCapitalization(m_cbCapitalize->isChecked());
0798   Config::setAutoFormat(m_cbFormat->isChecked());
0799 
0800   const QRegularExpression semicolon(QLatin1String("\\s*;\\s*"));
0801   const QChar comma = QLatin1Char(',');
0802 
0803   Config::setNoCapitalizationString(m_leCapitals->text().replace(semicolon, comma));
0804   Config::setArticlesString(m_leArticles->text().replace(semicolon, comma));
0805   Config::setNameSuffixesString(m_leSuffixes->text().replace(semicolon, comma));
0806   Config::setSurnamePrefixesString(m_lePrefixes->text().replace(semicolon, comma));
0807 }
0808 
0809 void ConfigDialog::savePrintingConfig() {
0810   Config::setPrintFieldHeaders(m_cbPrintHeaders->isChecked());
0811   Config::setPrintFormatted(m_cbPrintFormatted->isChecked());
0812   Config::setPrintGrouped(m_cbPrintGrouped->isChecked());
0813   Config::setMaxImageWidth(m_imageWidthBox->value());
0814   Config::setMaxImageHeight(m_imageHeightBox->value());
0815 }
0816 
0817 void ConfigDialog::saveTemplateConfig() {
0818   const int collType = Kernel::self()->collectionType();
0819   Config::setTemplateName(collType, m_templateCombo->currentData().toString());
0820   QFont font(m_fontCombo->currentFont().family(), m_fontSizeInput->value());
0821   Config::setTemplateFont(collType, font);
0822   Config::setTemplateBaseColor(collType, m_baseColorCombo->color());
0823   Config::setTemplateTextColor(collType, m_textColorCombo->color());
0824   Config::setTemplateHighlightedBaseColor(collType, m_highBaseColorCombo->color());
0825   Config::setTemplateHighlightedTextColor(collType, m_highTextColorCombo->color());
0826 }
0827 
0828 void ConfigDialog::saveFetchConfig() {
0829   // first, tell config widgets they got deleted
0830   foreach(Fetch::ConfigWidget* widget, m_removedConfigWidgets) {
0831     widget->removed();
0832   }
0833   m_removedConfigWidgets.clear();
0834 
0835   bool reloadFetchers = false;
0836   int count = 0; // start group numbering at 0
0837   for( ; count < m_sourceListWidget->count(); ++count) {
0838     FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(count));
0839     Fetch::ConfigWidget* cw = m_configWidgets[item];
0840     if(!cw || (!cw->shouldSave() && !item->isNewSource())) {
0841       continue;
0842     }
0843     m_newStuffConfigWidgets.removeAll(cw);
0844     QString group = QStringLiteral("Data Source %1").arg(count);
0845     // in case we later change the order, clear the group now
0846     KSharedConfig::openConfig()->deleteGroup(group);
0847     KConfigGroup configGroup(KSharedConfig::openConfig(), group);
0848     configGroup.writeEntry("Name", item->data(Qt::DisplayRole).toString());
0849     configGroup.writeEntry("Type", int(item->fetchType()));
0850     configGroup.writeEntry("UpdateOverwrite", item->updateOverwrite());
0851     configGroup.writeEntry("Uuid", item->uuid());
0852     cw->saveConfig(configGroup);
0853     item->setNewSource(false);
0854     // in case the ordering changed
0855     item->setConfigGroup(configGroup);
0856     reloadFetchers = true;
0857   }
0858   // now update total number of sources
0859   KConfigGroup sourceGroup(KSharedConfig::openConfig(), "Data Sources");
0860   sourceGroup.writeEntry("Sources Count", count);
0861   // and purge old config groups
0862   QString group = QStringLiteral("Data Source %1").arg(count);
0863   while(KSharedConfig::openConfig()->hasGroup(group)) {
0864     KSharedConfig::openConfig()->deleteGroup(group);
0865     ++count;
0866     group = QStringLiteral("Data Source %1").arg(count);
0867   }
0868 
0869   Config::self()->save();
0870 
0871   if(reloadFetchers) {
0872     Fetch::Manager::self()->loadFetchers();
0873     Controller::self()->updatedFetchers();
0874     // reload fetcher items if OK was not clicked
0875     // meaning apply was clicked
0876     if(!m_okClicked) {
0877       QString currentSource;
0878       if(m_sourceListWidget->currentItem()) {
0879         currentSource = m_sourceListWidget->currentItem()->data(Qt::DisplayRole).toString();
0880       }
0881       readFetchConfig();
0882       if(!currentSource.isEmpty()) {
0883         QList<QListWidgetItem*> items = m_sourceListWidget->findItems(currentSource, Qt::MatchExactly);
0884         if(!items.isEmpty()) {
0885           m_sourceListWidget->setCurrentItem(items.first());
0886           m_sourceListWidget->scrollToItem(items.first());
0887         }
0888       }
0889     }
0890   }
0891 }
0892 
0893 void ConfigDialog::slotModified() {
0894   if(m_modifying) {
0895     return;
0896   }
0897   button(QDialogButtonBox::Ok)->setEnabled(true);
0898   button(QDialogButtonBox::Apply)->setEnabled(true);
0899 }
0900 
0901 void ConfigDialog::slotNewSourceClicked() {
0902   FetcherConfigDialog dlg(this);
0903   if(dlg.exec() != QDialog::Accepted) {
0904     return;
0905   }
0906 
0907   Fetch::Type type = dlg.sourceType();
0908   if(type == Fetch::Unknown) {
0909     return;
0910   }
0911 
0912   Fetch::FetcherInfo info(type, dlg.sourceName(), dlg.updateOverwrite());
0913   FetcherInfoListItem* item = new FetcherInfoListItem(m_sourceListWidget, info);
0914   m_sourceListWidget->scrollToItem(item);
0915   m_sourceListWidget->setCurrentItem(item);
0916   Fetch::ConfigWidget* cw = dlg.configWidget();
0917   if(cw) {
0918     cw->setAccepted(true);
0919     cw->slotSetModified();
0920     cw->setParent(this); // keep the config widget around
0921     m_configWidgets.insert(item, cw);
0922   }
0923   m_modifySourceBtn->setEnabled(true);
0924   m_removeSourceBtn->setEnabled(true);
0925   slotModified(); // toggle apply button
0926 }
0927 
0928 void ConfigDialog::slotModifySourceClicked() {
0929   FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->currentItem());
0930   if(!item || !item->fetcher()) {
0931     return;
0932   }
0933 
0934   Fetch::ConfigWidget* cw = nullptr;
0935   if(m_configWidgets.contains(item)) {
0936     cw = m_configWidgets[item];
0937   } else {
0938     // grab the config widget, taking ownership
0939     cw = item->fetcher()->configWidget(this);
0940     if(cw) { // might return 0 when no widget available for fetcher type
0941       m_configWidgets.insert(item, cw);
0942       // there's weird layout bug if it's not hidden
0943       cw->hide();
0944     }
0945   }
0946   if(!cw) {
0947     // no config widget for this one
0948     // might be because support was compiled out
0949     myDebug() << "no config widget for source" << item->data(Qt::DisplayRole).toString();
0950     return;
0951   }
0952   FetcherConfigDialog dlg(item->data(Qt::DisplayRole).toString(), item->fetchType(), item->updateOverwrite(), cw, this);
0953 
0954   if(dlg.exec() == QDialog::Accepted) {
0955     cw->setAccepted(true); // mark to save
0956     QString newName = dlg.sourceName();
0957     if(newName != item->data(Qt::DisplayRole).toString()) {
0958       item->setData(Qt::DisplayRole, newName);
0959       cw->slotSetModified();
0960     }
0961     item->setUpdateOverwrite(dlg.updateOverwrite());
0962     slotModified(); // toggle apply button
0963   }
0964   cw->setParent(this); // keep the config widget around
0965 }
0966 
0967 void ConfigDialog::slotRemoveSourceClicked() {
0968   FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->currentItem());
0969   if(!item) {
0970     return;
0971   }
0972 
0973   Tellico::NewStuff::Manager::self()->removeScriptByName(item->text());
0974 
0975   Fetch::ConfigWidget* cw = m_configWidgets[item];
0976   if(cw) {
0977     m_removedConfigWidgets.append(cw);
0978     // it gets deleted by the parent
0979   }
0980   m_configWidgets.remove(item);
0981   delete item;
0982 //  m_sourceListWidget->setCurrentItem(m_sourceListWidget->currentItem());
0983   slotModified(); // toggle apply button
0984 }
0985 
0986 void ConfigDialog::slotMoveUpSourceClicked() {
0987   int row = m_sourceListWidget->currentRow();
0988   if(row < 1) {
0989     return;
0990   }
0991   QListWidgetItem* item = m_sourceListWidget->takeItem(row);
0992   m_sourceListWidget->insertItem(row-1, item);
0993   m_sourceListWidget->setCurrentItem(item);
0994   slotModified(); // toggle apply button
0995 }
0996 
0997 void ConfigDialog::slotMoveDownSourceClicked() {
0998   int row = m_sourceListWidget->currentRow();
0999   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 
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 
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 
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 
1037 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 
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 
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 
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 
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 
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 
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 }