File indexing completed on 2024-04-28 05:08:16
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 "configdialog.h" 0026 #include "field.h" 0027 #include "collection.h" 0028 #include "collectionfactory.h" 0029 #include "fetch/execexternalfetcher.h" 0030 #include "fetch/fetchmanager.h" 0031 #include "fetch/configwidget.h" 0032 #include "controller.h" 0033 #include "fetcherconfigdialog.h" 0034 #include "tellico_kernel.h" 0035 #include "utils/tellico_utils.h" 0036 #include "utils/string_utils.h" 0037 #include "config/tellico_config.h" 0038 #include "core/tellico_strings.h" 0039 #include "images/imagefactory.h" 0040 #include "gui/combobox.h" 0041 #include "gui/collectiontypecombo.h" 0042 #include "gui/previewdialog.h" 0043 #include "newstuff/manager.h" 0044 #include "fieldformat.h" 0045 #include "tellico_debug.h" 0046 0047 #include <KLocalizedString> 0048 #include <KConfig> 0049 #include <KAcceleratorManager> 0050 #include <KColorCombo> 0051 #include <KHelpClient> 0052 #include <KRecentDirs> 0053 0054 #ifdef ENABLE_KNEWSTUFF3 0055 #if KNEWSTUFF_VERSION < QT_VERSION_CHECK(5, 91, 0) 0056 #include <KNS3/Button> 0057 #else 0058 #include <KNSWidgets/Button> 0059 #endif 0060 #endif 0061 0062 #include <QSpinBox> 0063 #include <QLineEdit> 0064 #include <QPushButton> 0065 #include <QSize> 0066 #include <QLayout> 0067 #include <QLabel> 0068 #include <QCheckBox> 0069 #include <QPixmap> 0070 #include <QRegularExpression> 0071 #include <QFileInfo> 0072 #include <QRadioButton> 0073 #include <QFrame> 0074 #include <QFontComboBox> 0075 #include <QGroupBox> 0076 #include <QButtonGroup> 0077 #include <QInputDialog> 0078 #include <QVBoxLayout> 0079 #include <QHBoxLayout> 0080 #include <QApplication> 0081 #include <QTimer> 0082 #include <QFileDialog> 0083 0084 namespace { 0085 static const int CONFIG_MIN_WIDTH = 640; 0086 static const int CONFIG_MIN_HEIGHT = 420; 0087 } 0088 0089 using Tellico::ConfigDialog; 0090 0091 ConfigDialog::ConfigDialog(QWidget* parent_) 0092 : KPageDialog(parent_) 0093 , m_initializedPages(0) 0094 , m_modifying(false) 0095 , m_okClicked(false) { 0096 setFaceType(List); 0097 setModal(true); 0098 setWindowTitle(i18n("Configure Tellico")); 0099 setStandardButtons(QDialogButtonBox::Help | 0100 QDialogButtonBox::Ok | 0101 QDialogButtonBox::Apply | 0102 QDialogButtonBox::Cancel | 0103 QDialogButtonBox::RestoreDefaults); 0104 0105 setupGeneralPage(); 0106 setupPrintingPage(); 0107 setupTemplatePage(); 0108 setupFetchPage(); 0109 0110 updateGeometry(); 0111 QSize s = sizeHint(); 0112 resize(qMax(s.width(), CONFIG_MIN_WIDTH), qMax(s.height(), CONFIG_MIN_HEIGHT)); 0113 0114 // OK button is connected to buttonBox accepted() signal which is already connected to accept() slot 0115 connect(button(QDialogButtonBox::Apply), &QAbstractButton::clicked, this, &ConfigDialog::slotApply); 0116 connect(button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &ConfigDialog::slotHelp); 0117 connect(button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, this, &ConfigDialog::slotDefault); 0118 0119 button(QDialogButtonBox::Ok)->setEnabled(false); 0120 button(QDialogButtonBox::Apply)->setEnabled(false); 0121 button(QDialogButtonBox::Ok)->setDefault(true); 0122 button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return); 0123 0124 connect(this, &KPageDialog::currentPageChanged, this, &ConfigDialog::slotInitPage); 0125 } 0126 0127 ConfigDialog::~ConfigDialog() { 0128 foreach(Fetch::ConfigWidget* widget, m_newStuffConfigWidgets) { 0129 widget->removed(); 0130 } 0131 } 0132 0133 void ConfigDialog::slotInitPage(KPageWidgetItem* item_) { 0134 Q_ASSERT(item_); 0135 // every page item has a frame 0136 // if the frame has no layout, then we need to initialize the itme 0137 QFrame* frame = ::qobject_cast<QFrame*>(item_->widget()); 0138 Q_ASSERT(frame); 0139 if(frame->layout()) { 0140 return; 0141 } 0142 0143 const QString name = item_->name(); 0144 // these names must be kept in sync with the page names 0145 if(name == i18n("General")) { 0146 initGeneralPage(frame); 0147 } else if(name == i18n("Printing")) { 0148 initPrintingPage(frame); 0149 } else if(name == i18n("Templates")) { 0150 initTemplatePage(frame); 0151 } else if(name == i18n("Data Sources")) { 0152 initFetchPage(frame); 0153 } 0154 } 0155 0156 void ConfigDialog::accept() { 0157 m_okClicked = true; 0158 slotApply(); 0159 KPageDialog::accept(); 0160 m_okClicked = false; 0161 } 0162 0163 void ConfigDialog::slotApply() { 0164 emit signalConfigChanged(); 0165 button(QDialogButtonBox::Apply)->setEnabled(false); 0166 } 0167 0168 void ConfigDialog::slotDefault() { 0169 // only change the defaults on the active page 0170 Config::self()->useDefaults(true); 0171 const QString name = currentPage()->name(); 0172 if(name == i18n("General")) { 0173 readGeneralConfig(); 0174 } else if(name == i18n("Printing")) { 0175 readPrintingConfig(); 0176 } else if(name == i18n("Templates")) { 0177 readTemplateConfig(); 0178 } 0179 Config::self()->useDefaults(false); 0180 slotModified(); 0181 } 0182 0183 void ConfigDialog::slotHelp() { 0184 const QString name = currentPage()->name(); 0185 // these names must be kept in sync with the page names 0186 if(name == i18n("General")) { 0187 KHelpClient::invokeHelp(QStringLiteral("configuration.html#general-options")); 0188 } else if(name == i18n("Printing")) { 0189 KHelpClient::invokeHelp(QStringLiteral("configuration.html#printing-options")); 0190 } else if(name == i18n("Templates")) { 0191 KHelpClient::invokeHelp(QStringLiteral("configuration.html#template-options")); 0192 } else if(name == i18n("Data Sources")) { 0193 KHelpClient::invokeHelp(QStringLiteral("configuration.html#data-sources-options")); 0194 } 0195 } 0196 0197 bool ConfigDialog::isPageInitialized(Page page_) const { 0198 return m_initializedPages & page_; 0199 } 0200 0201 void ConfigDialog::setupGeneralPage() { 0202 QFrame* frame = new QFrame(this); 0203 KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("General")); 0204 page->setHeader(i18n("General Options")); 0205 page->setIcon(QIcon::fromTheme(QStringLiteral("tellico"), QIcon(QLatin1String(":/icons/tellico")))); 0206 addPage(page); 0207 0208 // since this is the first page, go ahead and lay it out 0209 initGeneralPage(frame); 0210 } 0211 0212 void ConfigDialog::initGeneralPage(QFrame* frame) { 0213 QVBoxLayout* l = new QVBoxLayout(frame); 0214 0215 m_cbOpenLastFile = new QCheckBox(i18n("&Reopen file at startup"), frame); 0216 m_cbOpenLastFile->setWhatsThis(i18n("If checked, the file that was last open " 0217 "will be re-opened at program start-up.")); 0218 l->addWidget(m_cbOpenLastFile); 0219 connect(m_cbOpenLastFile, &QAbstractButton::clicked, this, &ConfigDialog::slotModified); 0220 0221 m_cbQuickFilterRegExp = new QCheckBox(i18n("&Enable regular expressions in quick filter"), frame); 0222 m_cbQuickFilterRegExp->setWhatsThis(i18n("If checked, the quick filter will " 0223 "interpret text as a regular expression.")); 0224 l->addWidget(m_cbQuickFilterRegExp); 0225 connect(m_cbQuickFilterRegExp, &QAbstractButton::clicked, this, &ConfigDialog::slotModified); 0226 0227 m_cbEnableWebcam = new QCheckBox(i18n("&Enable webcam for barcode scanning"), frame); 0228 m_cbEnableWebcam->setWhatsThis(i18n("If checked, the input from a webcam will be used " 0229 "to scan barcodes for searching.")); 0230 l->addWidget(m_cbEnableWebcam); 0231 connect(m_cbEnableWebcam, &QAbstractButton::clicked, this, &ConfigDialog::slotModified); 0232 0233 QGroupBox* imageGroupBox = new QGroupBox(i18n("Image Storage Options"), frame); 0234 l->addWidget(imageGroupBox); 0235 m_rbImageInFile = new QRadioButton(i18n("Store images in data file"), imageGroupBox); 0236 m_rbImageInAppDir = new QRadioButton(i18n("Store images in common application directory"), imageGroupBox); 0237 m_rbImageInLocalDir = new QRadioButton(i18n("Store images in directory relative to data file"), imageGroupBox); 0238 imageGroupBox->setWhatsThis(i18n("Images may be saved in the data file itself, which can " 0239 "cause Tellico to run slowly, stored in the Tellico " 0240 "application directory, or stored in a directory in the " 0241 "same location as the data file.")); 0242 QVBoxLayout* imageGroupLayout = new QVBoxLayout(imageGroupBox); 0243 imageGroupLayout->addWidget(m_rbImageInFile); 0244 imageGroupLayout->addWidget(m_rbImageInAppDir); 0245 imageGroupLayout->addWidget(m_rbImageInLocalDir); 0246 imageGroupBox->setLayout(imageGroupLayout); 0247 0248 QButtonGroup* imageGroup = new QButtonGroup(frame); 0249 imageGroup->addButton(m_rbImageInFile); 0250 imageGroup->addButton(m_rbImageInAppDir); 0251 imageGroup->addButton(m_rbImageInLocalDir); 0252 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 0253 void (QButtonGroup::* buttonClicked)(int) = &QButtonGroup::buttonClicked; 0254 connect(imageGroup, buttonClicked, this, &ConfigDialog::slotModified); 0255 #else 0256 connect(imageGroup, &QButtonGroup::idClicked, this, &ConfigDialog::slotModified); 0257 #endif 0258 0259 QGroupBox* formatGroup = new QGroupBox(i18n("Formatting Options"), frame); 0260 l->addWidget(formatGroup); 0261 QVBoxLayout* formatGroupLayout = new QVBoxLayout(formatGroup); 0262 formatGroup->setLayout(formatGroupLayout); 0263 0264 m_cbCapitalize = new QCheckBox(i18n("Auto capitalize &titles and names"), formatGroup); 0265 m_cbCapitalize->setWhatsThis(i18n("If checked, titles and names will " 0266 "be automatically capitalized.")); 0267 connect(m_cbCapitalize, &QAbstractButton::clicked, this, &ConfigDialog::slotModified); 0268 formatGroupLayout->addWidget(m_cbCapitalize); 0269 0270 m_cbFormat = new QCheckBox(i18n("Auto &format titles and names"), formatGroup); 0271 m_cbFormat->setWhatsThis(i18n("If checked, titles and names will " 0272 "be automatically formatted.")); 0273 connect(m_cbFormat, &QAbstractButton::clicked, this, &ConfigDialog::slotModified); 0274 formatGroupLayout->addWidget(m_cbFormat); 0275 0276 QWidget* g1 = new QWidget(formatGroup); 0277 QGridLayout* g1Layout = new QGridLayout(g1); 0278 g1->setLayout(g1Layout); 0279 formatGroupLayout->addWidget(g1); 0280 0281 QLabel* lab = new QLabel(i18n("No capitali&zation:"), g1); 0282 g1Layout->addWidget(lab, 0, 0); 0283 m_leCapitals = new QLineEdit(g1); 0284 g1Layout->addWidget(m_leCapitals, 0, 1); 0285 lab->setBuddy(m_leCapitals); 0286 QString whats = i18n("<qt>A list of words which should not be capitalized. Multiple values " 0287 "should be separated by a semi-colon.</qt>"); 0288 lab->setWhatsThis(whats); 0289 m_leCapitals->setWhatsThis(whats); 0290 connect(m_leCapitals, &QLineEdit::textChanged, this, &ConfigDialog::slotModified); 0291 0292 lab = new QLabel(i18n("Artic&les:"), g1); 0293 g1Layout->addWidget(lab, 1, 0); 0294 m_leArticles = new QLineEdit(g1); 0295 g1Layout->addWidget(m_leArticles, 1, 1); 0296 lab->setBuddy(m_leArticles); 0297 whats = i18n("<qt>A list of words which should be considered as articles " 0298 "if they are the first word in a title. Multiple values " 0299 "should be separated by a semi-colon.</qt>"); 0300 lab->setWhatsThis(whats); 0301 m_leArticles->setWhatsThis(whats); 0302 connect(m_leArticles, &QLineEdit::textChanged, this, &ConfigDialog::slotModified); 0303 0304 lab = new QLabel(i18n("Personal suffi&xes:"), g1); 0305 g1Layout->addWidget(lab, 2, 0); 0306 m_leSuffixes = new QLineEdit(g1); 0307 g1Layout->addWidget(m_leSuffixes, 2, 1); 0308 lab->setBuddy(m_leSuffixes); 0309 whats = i18n("<qt>A list of suffixes which might be used in personal names. Multiple values " 0310 "should be separated by a semi-colon.</qt>"); 0311 lab->setWhatsThis(whats); 0312 m_leSuffixes->setWhatsThis(whats); 0313 connect(m_leSuffixes, &QLineEdit::textChanged, this, &ConfigDialog::slotModified); 0314 0315 lab = new QLabel(i18n("Surname &prefixes:"), g1); 0316 g1Layout->addWidget(lab, 3, 0); 0317 m_lePrefixes = new QLineEdit(g1); 0318 g1Layout->addWidget(m_lePrefixes, 3, 1); 0319 lab->setBuddy(m_lePrefixes); 0320 whats = i18n("<qt>A list of prefixes which might be used in surnames. Multiple values " 0321 "should be separated by a semi-colon.</qt>"); 0322 lab->setWhatsThis(whats); 0323 m_lePrefixes->setWhatsThis(whats); 0324 connect(m_lePrefixes, &QLineEdit::textChanged, this, &ConfigDialog::slotModified); 0325 0326 // stretch to fill lower area 0327 l->addStretch(1); 0328 m_initializedPages |= General; 0329 readGeneralConfig(); 0330 } 0331 0332 void ConfigDialog::setupPrintingPage() { 0333 QFrame* frame = new QFrame(this); 0334 KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Printing")); 0335 page->setHeader(i18n("Printing Options")); 0336 page->setIcon(QIcon::fromTheme(QStringLiteral("printer"))); 0337 addPage(page); 0338 } 0339 0340 void ConfigDialog::initPrintingPage(QFrame* frame) { 0341 QVBoxLayout* l = new QVBoxLayout(frame); 0342 0343 QGroupBox* formatOptions = new QGroupBox(i18n("Formatting Options"), frame); 0344 l->addWidget(formatOptions); 0345 QVBoxLayout* formatLayout = new QVBoxLayout(formatOptions); 0346 formatOptions->setLayout(formatLayout); 0347 0348 m_cbPrintFormatted = new QCheckBox(i18n("&Format titles and names"), formatOptions); 0349 m_cbPrintFormatted->setWhatsThis(i18n("If checked, titles and names will be automatically formatted.")); 0350 connect(m_cbPrintFormatted, &QAbstractButton::clicked, this, &ConfigDialog::slotModified); 0351 formatLayout->addWidget(m_cbPrintFormatted); 0352 0353 m_cbPrintHeaders = new QCheckBox(i18n("&Print field headers"), formatOptions); 0354 m_cbPrintHeaders->setWhatsThis(i18n("If checked, the field names will be printed as table headers.")); 0355 connect(m_cbPrintHeaders, &QAbstractButton::clicked, this, &ConfigDialog::slotModified); 0356 formatLayout->addWidget(m_cbPrintHeaders); 0357 0358 QGroupBox* groupOptions = new QGroupBox(i18n("Grouping Options"), frame); 0359 l->addWidget(groupOptions); 0360 QVBoxLayout* groupLayout = new QVBoxLayout(groupOptions); 0361 groupOptions->setLayout(groupLayout); 0362 0363 m_cbPrintGrouped = new QCheckBox(i18n("&Group the entries"), groupOptions); 0364 m_cbPrintGrouped->setWhatsThis(i18n("If checked, the entries will be grouped by the selected field.")); 0365 connect(m_cbPrintGrouped, &QAbstractButton::clicked, this, &ConfigDialog::slotModified); 0366 groupLayout->addWidget(m_cbPrintGrouped); 0367 0368 QGroupBox* imageOptions = new QGroupBox(i18n("Image Options"), frame); 0369 l->addWidget(imageOptions); 0370 0371 QGridLayout* gridLayout = new QGridLayout(imageOptions); 0372 imageOptions->setLayout(gridLayout); 0373 0374 QLabel* lab = new QLabel(i18n("Maximum image &width:"), imageOptions); 0375 gridLayout->addWidget(lab, 0, 0); 0376 m_imageWidthBox = new QSpinBox(imageOptions); 0377 m_imageWidthBox->setMaximum(999); 0378 m_imageWidthBox->setMinimum(0); 0379 m_imageWidthBox->setValue(50); 0380 gridLayout->addWidget(m_imageWidthBox, 0, 1); 0381 m_imageWidthBox->setSuffix(QStringLiteral(" px")); 0382 lab->setBuddy(m_imageWidthBox); 0383 QString whats = i18n("The maximum width of the images in the printout. The aspect ratio is preserved."); 0384 lab->setWhatsThis(whats); 0385 m_imageWidthBox->setWhatsThis(whats); 0386 void (QSpinBox::* valueChanged)(int) = &QSpinBox::valueChanged; 0387 connect(m_imageWidthBox, valueChanged, this, &ConfigDialog::slotModified); 0388 0389 lab = new QLabel(i18n("&Maximum image height:"), imageOptions); 0390 gridLayout->addWidget(lab, 1, 0); 0391 m_imageHeightBox = new QSpinBox(imageOptions); 0392 m_imageHeightBox->setMaximum(999); 0393 m_imageHeightBox->setMinimum(0); 0394 m_imageHeightBox->setValue(50); 0395 gridLayout->addWidget(m_imageHeightBox, 1, 1); 0396 m_imageHeightBox->setSuffix(QStringLiteral(" px")); 0397 lab->setBuddy(m_imageHeightBox); 0398 whats = i18n("The maximum height of the images in the printout. The aspect ratio is preserved."); 0399 lab->setWhatsThis(whats); 0400 m_imageHeightBox->setWhatsThis(whats); 0401 connect(m_imageHeightBox, valueChanged, this, &ConfigDialog::slotModified); 0402 0403 // stretch to fill lower area 0404 l->addStretch(1); 0405 m_initializedPages |= Printing; 0406 readPrintingConfig(); 0407 } 0408 0409 void ConfigDialog::setupTemplatePage() { 0410 QFrame* frame = new QFrame(this); 0411 KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Templates")); 0412 page->setHeader(i18n("Template Options")); 0413 // odd icon, I know, matches KMail, though... 0414 page->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-theme"))); 0415 addPage(page); 0416 } 0417 0418 void ConfigDialog::initTemplatePage(QFrame* frame) { 0419 QVBoxLayout* l = new QVBoxLayout(frame); 0420 0421 QGridLayout* gridLayout = new QGridLayout(); 0422 l->addLayout(gridLayout); 0423 0424 int row = -1; 0425 // so I can reuse an i18n string, a plain label can't have an '&' 0426 QString s = KLocalizedString::removeAcceleratorMarker(i18n("Collection &type:")); 0427 QLabel* lab = new QLabel(s, frame); 0428 gridLayout->addWidget(lab, ++row, 0); 0429 const int collType = Kernel::self()->collectionType(); 0430 lab = new QLabel(CollectionFactory::nameHash().value(collType), frame); 0431 gridLayout->addWidget(lab, row, 1, 1, 2); 0432 0433 lab = new QLabel(i18n("Template:"), frame); 0434 m_templateCombo = new GUI::ComboBox(frame); 0435 void (QComboBox::* activatedInt)(int) = &QComboBox::activated; 0436 connect(m_templateCombo, activatedInt, this, &ConfigDialog::slotModified); 0437 lab->setBuddy(m_templateCombo); 0438 QString whats = i18n("Select the template to use for the current type of collections. " 0439 "Not all templates will use the font and color settings."); 0440 lab->setWhatsThis(whats); 0441 m_templateCombo->setWhatsThis(whats); 0442 gridLayout->addWidget(lab, ++row, 0); 0443 gridLayout->addWidget(m_templateCombo, row, 1); 0444 0445 QPushButton* btn = new QPushButton(i18n("&Preview..."), frame); 0446 btn->setWhatsThis(i18n("Show a preview of the template")); 0447 btn->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original"))); 0448 gridLayout->addWidget(btn, row, 2); 0449 connect(btn, &QAbstractButton::clicked, this, &ConfigDialog::slotShowTemplatePreview); 0450 0451 // so the button is squeezed small 0452 gridLayout->setColumnStretch(0, 10); 0453 gridLayout->setColumnStretch(1, 10); 0454 0455 loadTemplateList(); 0456 0457 // QLabel* l1 = new QLabel(i18n("The options below will be passed to the template, but not " 0458 // "all templates will use them. Some fonts and colors may be " 0459 // "specified directly in the template."), frame); 0460 // l1->setTextFormat(Qt::RichText); 0461 // l->addWidget(l1); 0462 0463 QGroupBox* fontGroup = new QGroupBox(i18n("Font Options"), frame); 0464 l->addWidget(fontGroup); 0465 0466 row = -1; 0467 QGridLayout* fontLayout = new QGridLayout(); 0468 fontGroup->setLayout(fontLayout); 0469 0470 lab = new QLabel(i18n("Font:"), fontGroup); 0471 fontLayout->addWidget(lab, ++row, 0); 0472 m_fontCombo = new QFontComboBox(fontGroup); 0473 fontLayout->addWidget(m_fontCombo, row, 1); 0474 connect(m_fontCombo, activatedInt, this, &ConfigDialog::slotModified); 0475 lab->setBuddy(m_fontCombo); 0476 whats = i18n("This font is passed to the template used in the Entry View."); 0477 lab->setWhatsThis(whats); 0478 m_fontCombo->setWhatsThis(whats); 0479 0480 fontLayout->addWidget(new QLabel(i18n("Size:"), fontGroup), ++row, 0); 0481 m_fontSizeInput = new QSpinBox(fontGroup); 0482 m_fontSizeInput->setMaximum(30); // 30 is same max as konq config 0483 m_fontSizeInput->setMinimum(5); 0484 m_fontSizeInput->setSuffix(QStringLiteral("pt")); 0485 fontLayout->addWidget(m_fontSizeInput, row, 1); 0486 void (QSpinBox::* valueChangedInt)(int) = &QSpinBox::valueChanged; 0487 connect(m_fontSizeInput, valueChangedInt, this, &ConfigDialog::slotModified); 0488 lab->setBuddy(m_fontSizeInput); 0489 lab->setWhatsThis(whats); 0490 m_fontSizeInput->setWhatsThis(whats); 0491 0492 QGroupBox* colGroup = new QGroupBox(i18n("Color Options"), frame); 0493 l->addWidget(colGroup); 0494 0495 row = -1; 0496 QGridLayout* colLayout = new QGridLayout(); 0497 colGroup->setLayout(colLayout); 0498 0499 lab = new QLabel(i18n("Background color:"), colGroup); 0500 colLayout->addWidget(lab, ++row, 0); 0501 m_baseColorCombo = new KColorCombo(colGroup); 0502 colLayout->addWidget(m_baseColorCombo, row, 1); 0503 connect(m_baseColorCombo, activatedInt, this, &ConfigDialog::slotModified); 0504 lab->setBuddy(m_baseColorCombo); 0505 whats = i18n("This color is passed to the template used in the Entry View."); 0506 lab->setWhatsThis(whats); 0507 m_baseColorCombo->setWhatsThis(whats); 0508 0509 lab = new QLabel(i18n("Text color:"), colGroup); 0510 colLayout->addWidget(lab, ++row, 0); 0511 m_textColorCombo = new KColorCombo(colGroup); 0512 colLayout->addWidget(m_textColorCombo, row, 1); 0513 connect(m_textColorCombo, activatedInt, this, &ConfigDialog::slotModified); 0514 lab->setBuddy(m_textColorCombo); 0515 lab->setWhatsThis(whats); 0516 m_textColorCombo->setWhatsThis(whats); 0517 0518 lab = new QLabel(i18n("Highlight color:"), colGroup); 0519 colLayout->addWidget(lab, ++row, 0); 0520 m_highBaseColorCombo = new KColorCombo(colGroup); 0521 colLayout->addWidget(m_highBaseColorCombo, row, 1); 0522 connect(m_highBaseColorCombo, activatedInt, this, &ConfigDialog::slotModified); 0523 lab->setBuddy(m_highBaseColorCombo); 0524 lab->setWhatsThis(whats); 0525 m_highBaseColorCombo->setWhatsThis(whats); 0526 0527 lab = new QLabel(i18n("Highlighted text color:"), colGroup); 0528 colLayout->addWidget(lab, ++row, 0); 0529 m_highTextColorCombo = new KColorCombo(colGroup); 0530 colLayout->addWidget(m_highTextColorCombo, row, 1); 0531 connect(m_highTextColorCombo, activatedInt, this, &ConfigDialog::slotModified); 0532 lab->setBuddy(m_highTextColorCombo); 0533 lab->setWhatsThis(whats); 0534 m_highTextColorCombo->setWhatsThis(whats); 0535 0536 lab = new QLabel(i18n("Link color:"), colGroup); 0537 colLayout->addWidget(lab, ++row, 0); 0538 m_linkColorCombo = new KColorCombo(colGroup); 0539 colLayout->addWidget(m_linkColorCombo, row, 1); 0540 connect(m_linkColorCombo, activatedInt, this, &ConfigDialog::slotModified); 0541 lab->setBuddy(m_linkColorCombo); 0542 lab->setWhatsThis(whats); 0543 m_linkColorCombo->setWhatsThis(whats); 0544 0545 QGroupBox* groupBox = new QGroupBox(i18n("Manage Templates"), frame); 0546 l->addWidget(groupBox); 0547 QVBoxLayout* vlay = new QVBoxLayout(groupBox); 0548 groupBox->setLayout(vlay); 0549 0550 QWidget* box1 = new QWidget(groupBox); 0551 QHBoxLayout* box1HBoxLayout = new QHBoxLayout(box1); 0552 box1HBoxLayout->setMargin(0); 0553 vlay->addWidget(box1); 0554 box1HBoxLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); 0555 0556 QPushButton* b1 = new QPushButton(i18n("Install..."), box1); 0557 box1HBoxLayout->addWidget(b1); 0558 b1->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0559 connect(b1, &QAbstractButton::clicked, this, &ConfigDialog::slotInstallTemplate); 0560 whats = i18n("Click to install a new template directly."); 0561 b1->setWhatsThis(whats); 0562 0563 #ifdef ENABLE_KNEWSTUFF3 0564 #if KNEWSTUFF_VERSION < QT_VERSION_CHECK(5, 91, 0) 0565 auto b2 = new KNS3::Button(i18n("Download..."), QStringLiteral("tellico-template.knsrc"), box1); 0566 connect(b2, &KNS3::Button::dialogFinished, this, &ConfigDialog::slotUpdateTemplates); 0567 #else 0568 auto b2 = new KNSWidgets::Button(i18n("Download..."), QStringLiteral("tellico-template.knsrc"), box1); 0569 connect(b2, &KNSWidgets::Button::dialogFinished, this, &ConfigDialog::slotUpdateTemplates); 0570 #endif 0571 #else 0572 QPushButton* b2 = new QPushButton(i18n("Download..."), box1); 0573 b2->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff"))); 0574 b2->setEnabled(false); 0575 #endif 0576 box1HBoxLayout->addWidget(b2); 0577 whats = i18n("Click to download additional templates."); 0578 b2->setWhatsThis(whats); 0579 0580 QPushButton* b3 = new QPushButton(i18n("Delete..."), box1); 0581 box1HBoxLayout->addWidget(b3); 0582 b3->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0583 connect(b3, &QAbstractButton::clicked, this, &ConfigDialog::slotDeleteTemplate); 0584 whats = i18n("Click to select and remove installed templates."); 0585 b3->setWhatsThis(whats); 0586 0587 // stretch to fill lower area 0588 l->addStretch(1); 0589 0590 // purely for aesthetics make all widgets line up 0591 QList<QWidget*> widgets; 0592 widgets.append(m_fontCombo); 0593 widgets.append(m_fontSizeInput); 0594 widgets.append(m_baseColorCombo); 0595 widgets.append(m_textColorCombo); 0596 widgets.append(m_highBaseColorCombo); 0597 widgets.append(m_highTextColorCombo); 0598 widgets.append(m_linkColorCombo); 0599 int w = 0; 0600 foreach(QWidget* widget, widgets) { 0601 widget->ensurePolished(); 0602 w = qMax(w, widget->sizeHint().width()); 0603 } 0604 foreach(QWidget* widget, widgets) { 0605 widget->setMinimumWidth(w); 0606 } 0607 0608 KAcceleratorManager::manage(frame); 0609 m_initializedPages |= Template; 0610 readTemplateConfig(); 0611 } 0612 0613 void ConfigDialog::setupFetchPage() { 0614 QFrame* frame = new QFrame(this); 0615 KPageWidgetItem* page = new KPageWidgetItem(frame, i18n("Data Sources")); 0616 page->setHeader(i18n("Data Sources Options")); 0617 page->setIcon(QIcon::fromTheme(QStringLiteral("network-wired"))); 0618 addPage(page); 0619 } 0620 0621 void ConfigDialog::initFetchPage(QFrame* frame) { 0622 QHBoxLayout* l = new QHBoxLayout(frame); 0623 0624 QVBoxLayout* leftLayout = new QVBoxLayout(); 0625 l->addLayout(leftLayout); 0626 m_sourceListWidget = new QListWidget(frame); 0627 m_sourceListWidget->setSortingEnabled(false); // no sorting 0628 m_sourceListWidget->setSelectionMode(QAbstractItemView::SingleSelection); 0629 leftLayout->addWidget(m_sourceListWidget, 1); 0630 connect(m_sourceListWidget, &QListWidget::currentItemChanged, this, &ConfigDialog::slotSelectedSourceChanged); 0631 connect(m_sourceListWidget, &QListWidget::itemDoubleClicked, this, &ConfigDialog::slotModifySourceClicked); 0632 0633 QWidget* hb = new QWidget(frame); 0634 QHBoxLayout* hbHBoxLayout = new QHBoxLayout(hb); 0635 hbHBoxLayout->setMargin(0); 0636 leftLayout->addWidget(hb); 0637 m_moveUpSourceBtn = new QPushButton(i18n("Move &Up"), hb); 0638 hbHBoxLayout->addWidget(m_moveUpSourceBtn); 0639 m_moveUpSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); 0640 const QString moveTip(i18n("The order of the data sources sets the order " 0641 "that Tellico uses when entries are automatically updated.")); 0642 m_moveUpSourceBtn->setWhatsThis(moveTip); 0643 m_moveDownSourceBtn = new QPushButton(i18n("Move &Down"), hb); 0644 hbHBoxLayout->addWidget(m_moveDownSourceBtn); 0645 m_moveDownSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); 0646 m_moveDownSourceBtn->setWhatsThis(moveTip); 0647 0648 QWidget* hb2 = new QWidget(frame); 0649 QHBoxLayout* hb2HBoxLayout = new QHBoxLayout(hb2); 0650 hb2HBoxLayout->setMargin(0); 0651 leftLayout->addWidget(hb2); 0652 m_cbFilterSource = new QCheckBox(i18n("Filter by type:"), hb2); 0653 hb2HBoxLayout->addWidget(m_cbFilterSource); 0654 connect(m_cbFilterSource, &QAbstractButton::clicked, this, &ConfigDialog::slotSourceFilterChanged); 0655 m_sourceTypeCombo = new GUI::CollectionTypeCombo(hb2); 0656 hb2HBoxLayout->addWidget(m_sourceTypeCombo); 0657 void (QComboBox::* currentIndexChanged)(int) = &QComboBox::currentIndexChanged; 0658 connect(m_sourceTypeCombo, currentIndexChanged, this, &ConfigDialog::slotSourceFilterChanged); 0659 // we want to remove the item for a custom collection 0660 int index = m_sourceTypeCombo->findData(Data::Collection::Base); 0661 if(index > -1) { 0662 m_sourceTypeCombo->removeItem(index); 0663 } 0664 // disable until check box is checked 0665 m_sourceTypeCombo->setEnabled(false); 0666 0667 // these icons are rather arbitrary, but seem to vaguely fit 0668 QVBoxLayout* vlay = new QVBoxLayout(); 0669 l->addLayout(vlay); 0670 QPushButton* newSourceBtn = new QPushButton(i18n("&New..."), frame); 0671 newSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); 0672 newSourceBtn->setWhatsThis(i18n("Click to add a new data source.")); 0673 m_modifySourceBtn = new QPushButton(i18n("&Modify..."), frame); 0674 m_modifySourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("network-wired"))); 0675 m_modifySourceBtn->setWhatsThis(i18n("Click to modify the selected data source.")); 0676 m_removeSourceBtn = new QPushButton(i18n("&Delete"), frame); 0677 m_removeSourceBtn->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0678 m_removeSourceBtn->setWhatsThis(i18n("Click to delete the selected data source.")); 0679 0680 vlay->addWidget(newSourceBtn); 0681 vlay->addWidget(m_modifySourceBtn); 0682 vlay->addWidget(m_removeSourceBtn); 0683 vlay->addStretch(1); 0684 0685 connect(newSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotNewSourceClicked); 0686 connect(m_modifySourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotModifySourceClicked); 0687 connect(m_moveUpSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotMoveUpSourceClicked); 0688 connect(m_moveDownSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotMoveDownSourceClicked); 0689 connect(m_removeSourceBtn, &QAbstractButton::clicked, this, &ConfigDialog::slotRemoveSourceClicked); 0690 0691 KAcceleratorManager::manage(frame); 0692 m_initializedPages |= Fetch; 0693 readFetchConfig(); 0694 } 0695 0696 void ConfigDialog::readGeneralConfig() { 0697 m_modifying = true; 0698 0699 m_cbQuickFilterRegExp->setChecked(Config::quickFilterRegExp()); 0700 m_cbOpenLastFile->setChecked(Config::reopenLastFile()); 0701 #ifdef ENABLE_WEBCAM 0702 m_cbEnableWebcam->setChecked(Config::enableWebcam()); 0703 #else 0704 m_cbEnableWebcam->setChecked(false); 0705 m_cbEnableWebcam->setEnabled(false); 0706 #endif 0707 0708 switch(Config::imageLocation()) { 0709 case Config::ImagesInFile: m_rbImageInFile->setChecked(true); break; 0710 case Config::ImagesInAppDir: m_rbImageInAppDir->setChecked(true); break; 0711 case Config::ImagesInLocalDir: m_rbImageInLocalDir->setChecked(true); break; 0712 } 0713 0714 bool autoCapitals = Config::autoCapitalization(); 0715 m_cbCapitalize->setChecked(autoCapitals); 0716 0717 bool autoFormat = Config::autoFormat(); 0718 m_cbFormat->setChecked(autoFormat); 0719 0720 static const QRegularExpression comma(QLatin1String("\\s*,\\s*")); 0721 0722 m_leCapitals->setText(Config::noCapitalizationString().replace(comma, FieldFormat::delimiterString())); 0723 m_leArticles->setText(Config::articlesString().replace(comma, FieldFormat::delimiterString())); 0724 m_leSuffixes->setText(Config::nameSuffixesString().replace(comma, FieldFormat::delimiterString())); 0725 m_lePrefixes->setText(Config::surnamePrefixesString().replace(comma, FieldFormat::delimiterString())); 0726 0727 m_modifying = false; 0728 } 0729 0730 void ConfigDialog::readPrintingConfig() { 0731 m_modifying = true; 0732 0733 m_cbPrintHeaders->setChecked(Config::printFieldHeaders()); 0734 m_cbPrintFormatted->setChecked(Config::printFormatted()); 0735 m_cbPrintGrouped->setChecked(Config::printGrouped()); 0736 m_imageWidthBox->setValue(Config::maxImageWidth()); 0737 m_imageHeightBox->setValue(Config::maxImageHeight()); 0738 0739 m_modifying = false; 0740 } 0741 0742 void ConfigDialog::readTemplateConfig() { 0743 m_modifying = true; 0744 0745 // entry template selection 0746 const int collType = Kernel::self()->collectionType(); 0747 QString file = Config::templateName(collType); 0748 file.replace(QLatin1Char('_'), QLatin1Char(' ')); 0749 QString fileContext = file + QLatin1String(" XSL Template"); 0750 m_templateCombo->setCurrentItem(i18nc(fileContext.toUtf8().constData(), file.toUtf8().constData())); 0751 0752 m_fontCombo->setCurrentFont(QFont(Config::templateFont(collType).family())); 0753 m_fontSizeInput->setValue(Config::templateFont(collType).pointSize()); 0754 m_baseColorCombo->setColor(Config::templateBaseColor(collType)); 0755 m_textColorCombo->setColor(Config::templateTextColor(collType)); 0756 m_highBaseColorCombo->setColor(Config::templateHighlightedBaseColor(collType)); 0757 m_highTextColorCombo->setColor(Config::templateHighlightedTextColor(collType)); 0758 m_linkColorCombo->setColor(Config::templateLinkColor(collType)); 0759 0760 m_modifying = false; 0761 } 0762 0763 void ConfigDialog::readFetchConfig() { 0764 m_modifying = true; 0765 0766 m_sourceListWidget->clear(); 0767 m_configWidgets.clear(); 0768 0769 m_sourceListWidget->setUpdatesEnabled(false); 0770 foreach(Fetch::Fetcher::Ptr fetcher, Fetch::Manager::self()->fetchers()) { 0771 Fetch::FetcherInfo info(fetcher->type(), fetcher->source(), 0772 fetcher->updateOverwrite(), fetcher->uuid()); 0773 FetcherInfoListItem* item = new FetcherInfoListItem(m_sourceListWidget, info); 0774 item->setFetcher(fetcher.data()); 0775 } 0776 m_sourceListWidget->setUpdatesEnabled(true); 0777 0778 if(m_sourceListWidget->count() == 0) { 0779 m_modifySourceBtn->setEnabled(false); 0780 m_removeSourceBtn->setEnabled(false); 0781 } else { 0782 // go ahead and select the first one 0783 m_sourceListWidget->setCurrentItem(m_sourceListWidget->item(0)); 0784 } 0785 0786 m_modifying = false; 0787 QTimer::singleShot(500, this, &ConfigDialog::slotCreateConfigWidgets); 0788 } 0789 0790 void ConfigDialog::saveConfiguration() { 0791 if(isPageInitialized(General)) saveGeneralConfig(); 0792 if(isPageInitialized(Printing)) savePrintingConfig(); 0793 if(isPageInitialized(Template)) saveTemplateConfig(); 0794 if(isPageInitialized(Fetch)) saveFetchConfig(); 0795 } 0796 0797 void ConfigDialog::saveGeneralConfig() { 0798 Config::setQuickFilterRegExp(m_cbQuickFilterRegExp->isChecked()); 0799 Config::setEnableWebcam(m_cbEnableWebcam->isChecked()); 0800 0801 int imageLocation; 0802 if(m_rbImageInFile->isChecked()) { 0803 imageLocation = Config::ImagesInFile; 0804 } else if(m_rbImageInAppDir->isChecked()) { 0805 imageLocation = Config::ImagesInAppDir; 0806 } else { 0807 imageLocation = Config::ImagesInLocalDir; 0808 } 0809 Config::setImageLocation(imageLocation); 0810 Config::setReopenLastFile(m_cbOpenLastFile->isChecked()); 0811 0812 Config::setAutoCapitalization(m_cbCapitalize->isChecked()); 0813 Config::setAutoFormat(m_cbFormat->isChecked()); 0814 0815 static const QRegularExpression semicolon(QLatin1String("\\s*;\\s*")); 0816 const QChar comma = QLatin1Char(','); 0817 0818 Config::setNoCapitalizationString(m_leCapitals->text().replace(semicolon, comma)); 0819 Config::setArticlesString(m_leArticles->text().replace(semicolon, comma)); 0820 Config::setNameSuffixesString(m_leSuffixes->text().replace(semicolon, comma)); 0821 Config::setSurnamePrefixesString(m_lePrefixes->text().replace(semicolon, comma)); 0822 } 0823 0824 void ConfigDialog::savePrintingConfig() { 0825 Config::setPrintFieldHeaders(m_cbPrintHeaders->isChecked()); 0826 Config::setPrintFormatted(m_cbPrintFormatted->isChecked()); 0827 Config::setPrintGrouped(m_cbPrintGrouped->isChecked()); 0828 Config::setMaxImageWidth(m_imageWidthBox->value()); 0829 Config::setMaxImageHeight(m_imageHeightBox->value()); 0830 } 0831 0832 void ConfigDialog::saveTemplateConfig() { 0833 const int collType = Kernel::self()->collectionType(); 0834 if(collType == Data::Collection::Base && 0835 Kernel::self()->URL().fileName() != i18n(Tellico::untitledFilename)) { 0836 // use a nested config group for template specific to custom collections 0837 // using the filename alone as a keyEvents 0838 const QString configGroup = QStringLiteral("Options - %1").arg(CollectionFactory::typeName(collType)); 0839 KConfigGroup group(KSharedConfig::openConfig(), configGroup); 0840 KConfigGroup subGroup(&group, Kernel::self()->URL().fileName()); 0841 subGroup.writeEntry(QStringLiteral("Template Name"), m_templateCombo->currentData().toString()); 0842 } else { 0843 Config::setTemplateName(collType, m_templateCombo->currentData().toString()); 0844 } 0845 QFont font(m_fontCombo->currentFont().family(), m_fontSizeInput->value()); 0846 Config::setTemplateFont(collType, font); 0847 Config::setTemplateBaseColor(collType, m_baseColorCombo->color()); 0848 Config::setTemplateTextColor(collType, m_textColorCombo->color()); 0849 Config::setTemplateHighlightedBaseColor(collType, m_highBaseColorCombo->color()); 0850 Config::setTemplateHighlightedTextColor(collType, m_highTextColorCombo->color()); 0851 Config::setTemplateLinkColor(collType, m_linkColorCombo->color()); 0852 } 0853 0854 void ConfigDialog::saveFetchConfig() { 0855 // first, tell config widgets they got deleted 0856 foreach(Fetch::ConfigWidget* widget, m_removedConfigWidgets) { 0857 widget->removed(); 0858 } 0859 m_removedConfigWidgets.clear(); 0860 0861 bool reloadFetchers = false; 0862 int count = 0; // start group numbering at 0 0863 for( ; count < m_sourceListWidget->count(); ++count) { 0864 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(count)); 0865 Fetch::ConfigWidget* cw = m_configWidgets[item]; 0866 if(!cw || (!cw->shouldSave() && !item->isNewSource())) { 0867 continue; 0868 } 0869 m_newStuffConfigWidgets.removeAll(cw); 0870 QString group = QStringLiteral("Data Source %1").arg(count); 0871 // in case we later change the order, clear the group now 0872 KSharedConfig::openConfig()->deleteGroup(group); 0873 KConfigGroup configGroup(KSharedConfig::openConfig(), group); 0874 configGroup.writeEntry("Name", item->data(Qt::DisplayRole).toString()); 0875 configGroup.writeEntry("Type", int(item->fetchType())); 0876 configGroup.writeEntry("UpdateOverwrite", item->updateOverwrite()); 0877 configGroup.writeEntry("Uuid", item->uuid()); 0878 cw->saveConfig(configGroup); 0879 item->setNewSource(false); 0880 // in case the ordering changed 0881 item->setConfigGroup(configGroup); 0882 reloadFetchers = true; 0883 } 0884 // now update total number of sources 0885 KConfigGroup sourceGroup(KSharedConfig::openConfig(), "Data Sources"); 0886 sourceGroup.writeEntry("Sources Count", count); 0887 // and purge old config groups 0888 QString group = QStringLiteral("Data Source %1").arg(count); 0889 while(KSharedConfig::openConfig()->hasGroup(group)) { 0890 KSharedConfig::openConfig()->deleteGroup(group); 0891 ++count; 0892 group = QStringLiteral("Data Source %1").arg(count); 0893 } 0894 0895 Config::self()->save(); 0896 0897 if(reloadFetchers) { 0898 Fetch::Manager::self()->loadFetchers(); 0899 Controller::self()->updatedFetchers(); 0900 // reload fetcher items if OK was not clicked 0901 // meaning apply was clicked 0902 if(!m_okClicked) { 0903 QString currentSource; 0904 if(m_sourceListWidget->currentItem()) { 0905 currentSource = m_sourceListWidget->currentItem()->data(Qt::DisplayRole).toString(); 0906 } 0907 readFetchConfig(); 0908 if(!currentSource.isEmpty()) { 0909 QList<QListWidgetItem*> items = m_sourceListWidget->findItems(currentSource, Qt::MatchExactly); 0910 if(!items.isEmpty()) { 0911 m_sourceListWidget->setCurrentItem(items.first()); 0912 m_sourceListWidget->scrollToItem(items.first()); 0913 } 0914 } 0915 } 0916 } 0917 } 0918 0919 void ConfigDialog::slotModified() { 0920 if(m_modifying) { 0921 return; 0922 } 0923 button(QDialogButtonBox::Ok)->setEnabled(true); 0924 button(QDialogButtonBox::Apply)->setEnabled(true); 0925 } 0926 0927 void ConfigDialog::slotNewSourceClicked() { 0928 FetcherConfigDialog dlg(this); 0929 if(dlg.exec() != QDialog::Accepted) { 0930 return; 0931 } 0932 0933 Fetch::Type type = dlg.sourceType(); 0934 if(type == Fetch::Unknown) { 0935 return; 0936 } 0937 0938 Fetch::FetcherInfo info(type, dlg.sourceName(), dlg.updateOverwrite()); 0939 FetcherInfoListItem* item = new FetcherInfoListItem(m_sourceListWidget, info); 0940 m_sourceListWidget->scrollToItem(item); 0941 m_sourceListWidget->setCurrentItem(item); 0942 Fetch::ConfigWidget* cw = dlg.configWidget(); 0943 if(cw) { 0944 cw->setAccepted(true); 0945 cw->slotSetModified(); 0946 cw->setParent(this); // keep the config widget around 0947 m_configWidgets.insert(item, cw); 0948 } 0949 m_modifySourceBtn->setEnabled(true); 0950 m_removeSourceBtn->setEnabled(true); 0951 slotModified(); // toggle apply button 0952 } 0953 0954 void ConfigDialog::slotModifySourceClicked() { 0955 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->currentItem()); 0956 if(!item) { 0957 return; 0958 } 0959 0960 Fetch::ConfigWidget* cw = nullptr; 0961 if(m_configWidgets.contains(item)) { 0962 cw = m_configWidgets[item]; 0963 } else if(item->fetcher()) { 0964 // grab the config widget, taking ownership 0965 cw = item->fetcher()->configWidget(this); 0966 if(cw) { // might return null when no widget available for fetcher type 0967 m_configWidgets.insert(item, cw); 0968 // there's weird layout bug if it's not hidden 0969 cw->hide(); 0970 } 0971 } else { 0972 myDebug() << "no config item fetcher!"; 0973 } 0974 if(!cw) { 0975 // no config widget for this one 0976 // might be because support was compiled out 0977 myDebug() << "no config widget for source" << item->data(Qt::DisplayRole).toString(); 0978 return; 0979 } 0980 FetcherConfigDialog dlg(item->data(Qt::DisplayRole).toString(), item->fetchType(), item->updateOverwrite(), cw, this); 0981 0982 if(dlg.exec() == QDialog::Accepted) { 0983 cw->setAccepted(true); // mark to save 0984 QString newName = dlg.sourceName(); 0985 if(newName != item->data(Qt::DisplayRole).toString()) { 0986 item->setData(Qt::DisplayRole, newName); 0987 cw->slotSetModified(); 0988 } 0989 item->setUpdateOverwrite(dlg.updateOverwrite()); 0990 slotModified(); // toggle apply button 0991 } 0992 cw->setParent(this); // keep the config widget around 0993 } 0994 0995 void ConfigDialog::slotRemoveSourceClicked() { 0996 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->currentItem()); 0997 if(!item) { 0998 return; 0999 } 1000 1001 Tellico::NewStuff::Manager::self()->removeScriptByName(item->text()); 1002 1003 Fetch::ConfigWidget* cw = m_configWidgets[item]; 1004 if(cw) { 1005 m_removedConfigWidgets.append(cw); 1006 // it gets deleted by the parent 1007 } 1008 m_configWidgets.remove(item); 1009 delete item; 1010 // m_sourceListWidget->setCurrentItem(m_sourceListWidget->currentItem()); 1011 slotModified(); // toggle apply button 1012 } 1013 1014 void ConfigDialog::slotMoveUpSourceClicked() { 1015 int row = m_sourceListWidget->currentRow(); 1016 if(row < 1) { 1017 return; 1018 } 1019 QListWidgetItem* item = m_sourceListWidget->takeItem(row); 1020 m_sourceListWidget->insertItem(row-1, item); 1021 m_sourceListWidget->setCurrentItem(item); 1022 slotModified(); // toggle apply button 1023 } 1024 1025 void ConfigDialog::slotMoveDownSourceClicked() { 1026 int row = m_sourceListWidget->currentRow(); 1027 if(row > m_sourceListWidget->count()-2) { 1028 return; 1029 } 1030 QListWidgetItem* item = m_sourceListWidget->takeItem(row); 1031 m_sourceListWidget->insertItem(row+1, item); 1032 m_sourceListWidget->setCurrentItem(item); 1033 slotModified(); // toggle apply button 1034 } 1035 1036 void ConfigDialog::slotSourceFilterChanged() { 1037 m_sourceTypeCombo->setEnabled(m_cbFilterSource->isChecked()); 1038 const bool showAll = !m_sourceTypeCombo->isEnabled(); 1039 const int type = m_sourceTypeCombo->currentType(); 1040 for(int count = 0; count < m_sourceListWidget->count(); ++count) { 1041 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(count)); 1042 item->setHidden(!showAll && item->fetcher() && !item->fetcher()->canFetch(type)); 1043 } 1044 } 1045 1046 void ConfigDialog::slotSelectedSourceChanged(QListWidgetItem* item_) { 1047 int row = m_sourceListWidget->row(item_); 1048 m_moveUpSourceBtn->setEnabled(row > 0); 1049 m_moveDownSourceBtn->setEnabled(row < m_sourceListWidget->count()-1); 1050 } 1051 1052 Tellico::FetcherInfoListItem* ConfigDialog::findItem(const QString& path_) const { 1053 if(path_.isEmpty()) { 1054 myDebug() << "empty path"; 1055 return nullptr; 1056 } 1057 1058 // this is a bit ugly, loop over all items, find the execexternal one 1059 // that matches the path 1060 for(int i = 0; i < m_sourceListWidget->count(); ++i) { 1061 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(i)); 1062 if(item->fetchType() != Fetch::ExecExternal) { 1063 continue; 1064 } 1065 Fetch::ExecExternalFetcher* f = dynamic_cast<Fetch::ExecExternalFetcher*>(item->fetcher()); 1066 if(f && f->execPath() == path_) { 1067 return item; 1068 } 1069 } 1070 myDebug() << "no matching item found"; 1071 return nullptr; 1072 } 1073 1074 void ConfigDialog::slotShowTemplatePreview() { 1075 GUI::PreviewDialog* dlg = new GUI::PreviewDialog(this); 1076 1077 const QString templateName = m_templateCombo->currentData().toString(); 1078 dlg->setXSLTFile(templateName + QLatin1String(".xsl")); 1079 1080 StyleOptions options; 1081 options.fontFamily = m_fontCombo->currentFont().family(); 1082 options.fontSize = m_fontSizeInput->value(); 1083 options.baseColor = m_baseColorCombo->color(); 1084 options.textColor = m_textColorCombo->color(); 1085 options.highlightedTextColor = m_highTextColorCombo->color(); 1086 options.highlightedBaseColor = m_highBaseColorCombo->color(); 1087 options.linkColor = m_linkColorCombo->color(); 1088 dlg->setXSLTOptions(Kernel::self()->collectionType(), options); 1089 1090 // always want to include a url to show link color too 1091 bool hasLink = false; 1092 Data::CollPtr c = CollectionFactory::collection(Kernel::self()->collectionType(), true); 1093 Data::EntryPtr e(new Data::Entry(c)); 1094 foreach(Data::FieldPtr f, c->fields()) { 1095 if(f->name() == QLatin1String("title")) { 1096 e->setField(f->name(), m_templateCombo->currentText()); 1097 } else if(f->type() == Data::Field::Image) { 1098 continue; 1099 } else if(f->type() == Data::Field::Choice) { 1100 e->setField(f->name(), f->allowed().front()); 1101 } else if(f->type() == Data::Field::Number) { 1102 e->setField(f->name(), QStringLiteral("1")); 1103 } else if(f->type() == Data::Field::Bool) { 1104 e->setField(f->name(), QStringLiteral("true")); 1105 } else if(f->type() == Data::Field::Rating) { 1106 e->setField(f->name(), QStringLiteral("4")); 1107 } else if(f->type() == Data::Field::URL) { 1108 e->setField(f->name(), QStringLiteral("https://tellico-project.org")); 1109 hasLink = true; 1110 } else if(f->type() == Data::Field::Table) { 1111 QStringList values; 1112 bool ok; 1113 int ncols = Tellico::toUInt(f->property(QStringLiteral("columns")), &ok); 1114 ncols = qMax(ncols, 1); 1115 for(int ncol = 1; ncol <= ncols; ++ncol) { 1116 const auto prop = QStringLiteral("column%1").arg(ncol); 1117 const auto col = f->property(prop); 1118 values += col.isEmpty() ? prop : col; 1119 } 1120 e->setField(f->name(), values.join(FieldFormat::columnDelimiterString())); 1121 } else { 1122 e->setField(f->name(), f->title()); 1123 } 1124 } 1125 if(!hasLink) { 1126 Data::FieldPtr f(new Data::Field(QStringLiteral("url"), 1127 QLatin1String("URL"), 1128 Data::Field::URL)); 1129 f->setCategory(i18n("General")); 1130 c->addField(f); 1131 e->setField(f->name(), QStringLiteral("https://tellico-project.org")); 1132 } 1133 1134 dlg->showEntry(e); 1135 dlg->show(); 1136 // dlg gets deleted by itself 1137 // the finished() signal is connected in its constructor to delayedDestruct 1138 } 1139 1140 void ConfigDialog::loadTemplateList() { 1141 QStringList files = Tellico::locateAllFiles(QStringLiteral("tellico/entry-templates/*.xsl")); 1142 QMap<QString, QString> templates; // a QMap will have them values sorted by key 1143 foreach(const QString& file, files) { 1144 QFileInfo fi(file); 1145 QString lfile = fi.fileName().section(QLatin1Char('.'), 0, -2); 1146 QString name = lfile; 1147 name.replace(QLatin1Char('_'), QLatin1Char(' ')); 1148 QString title = i18nc((name + QLatin1String(" XSL Template")).toUtf8().constData(), name.toUtf8().constData()); 1149 templates.insert(title, lfile); 1150 } 1151 1152 QString s = m_templateCombo->currentText(); 1153 m_templateCombo->clear(); 1154 for(auto it2 = templates.constBegin(); it2 != templates.constEnd(); ++it2) { 1155 m_templateCombo->addItem(it2.key(), it2.value()); 1156 } 1157 m_templateCombo->setCurrentItem(s); 1158 } 1159 1160 void ConfigDialog::slotInstallTemplate() { 1161 QString filter = i18n("XSL Files") + QLatin1String(" (*.xsl)") + QLatin1String(";;"); 1162 filter += i18n("Template Packages") + QLatin1String(" (*.tar.gz *.tgz)") + QLatin1String(";;"); 1163 filter += i18n("All Files") + QLatin1String(" (*)"); 1164 1165 const QString fileClass(QStringLiteral(":InstallTemplate")); 1166 const QString f = QFileDialog::getOpenFileName(this, QString(), KRecentDirs::dir(fileClass), filter); 1167 if(f.isEmpty()) { 1168 return; 1169 } 1170 KRecentDirs::add(fileClass, QFileInfo(f).dir().canonicalPath()); 1171 1172 if(Tellico::NewStuff::Manager::self()->installTemplate(f)) { 1173 loadTemplateList(); 1174 } 1175 } 1176 1177 #ifdef ENABLE_KNEWSTUFF3 1178 #if KNEWSTUFF_VERSION < QT_VERSION_CHECK(5, 91, 0) 1179 void ConfigDialog::slotUpdateTemplates(const QList<KNS3::Entry>& list_) { 1180 #else 1181 void ConfigDialog::slotUpdateTemplates(const QList<KNSCore::Entry>& list_) { 1182 #endif 1183 if(!list_.isEmpty()) { 1184 loadTemplateList(); 1185 } 1186 } 1187 #endif 1188 1189 void ConfigDialog::slotDeleteTemplate() { 1190 bool ok; 1191 QString name = QInputDialog::getItem(this, 1192 i18n("Delete Template"), 1193 i18n("Select template to delete:"), 1194 Tellico::NewStuff::Manager::self()->userTemplates().keys(), 1195 0, false, &ok); 1196 if(ok && !name.isEmpty()) { 1197 Tellico::NewStuff::Manager::self()->removeTemplateByName(name); 1198 loadTemplateList(); 1199 } 1200 } 1201 1202 void ConfigDialog::slotCreateConfigWidgets() { 1203 for(int count = 0; count < m_sourceListWidget->count(); ++count) { 1204 FetcherInfoListItem* item = static_cast<FetcherInfoListItem*>(m_sourceListWidget->item(count)); 1205 // only create a new config widget if we don't have one already 1206 if(!m_configWidgets.contains(item) && item->fetcher()) { 1207 Fetch::ConfigWidget* cw = item->fetcher()->configWidget(this); 1208 if(cw) { // might return 0 when no widget available for fetcher type 1209 m_configWidgets.insert(item, cw); 1210 // there's weird layout bug if it's not hidden 1211 cw->hide(); 1212 } 1213 } 1214 } 1215 }