File indexing completed on 2024-05-19 05:42:25

0001 // ct_lvtqtw_configurationdialog.cpp                               -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 // own
0021 #include <ct_lvtplg_pluginmanager.h>
0022 #include <ct_lvtqtw_configurationdialog.h>
0023 
0024 #include <ct_lvtprj_projectfile.h>
0025 #include <ct_lvtqtw_modifierhelpers.h>
0026 
0027 // autogen
0028 #include <preferences.h>
0029 #ifdef KDE_FRAMEWORKS_IS_OLD
0030 #include <ui_ct_lvtqtw_configurationdialog_oldkf5.h>
0031 #else
0032 #include <ui_ct_lvtqtw_configurationdialog.h>
0033 #endif
0034 
0035 // Qt
0036 #include <QDebug>
0037 #include <QDialogButtonBox>
0038 #include <QPushButton>
0039 
0040 // KDE
0041 #include <KMessageBox>
0042 #ifndef KDE_FRAMEWORKS_IS_OLD
0043 #include <KPluginWidget>
0044 #endif
0045 
0046 // std
0047 #include <cassert>
0048 
0049 namespace Codethink::lvtqtw {
0050 
0051 struct ConfigurationDialog::Private {
0052     Ui::ConfigurationDialog ui;
0053     Codethink::lvtplg::PluginManager *pluginManager;
0054 };
0055 
0056 namespace {
0057 Qt::Corner stringToCorner(const QString& txt)
0058 {
0059     return txt == QObject::tr("Top Left")   ? Qt::TopLeftCorner
0060         : txt == QObject::tr("Top Right")   ? Qt::TopRightCorner
0061         : txt == QObject::tr("Bottom Left") ? Qt::BottomLeftCorner
0062                                             : Qt::BottomRightCorner;
0063 }
0064 } // namespace
0065 
0066 ConfigurationDialog::ConfigurationDialog(lvtplg::PluginManager *pluginManager, QWidget *parent):
0067     QDialog(parent), d(std::make_unique<ConfigurationDialog::Private>())
0068 {
0069     d->ui.setupUi(this);
0070     d->pluginManager = pluginManager;
0071 #ifndef KDE_FRAMEWORKS_IS_OLD
0072     d->ui.getNewPlugins->setConfigFile(QStringLiteral("codevis.knsrc"));
0073 #endif
0074 
0075     setWindowTitle("Configure Software");
0076 
0077     populateMouseTabOptions();
0078     load();
0079 
0080 #ifndef KDE_FRAMEWORKS_IS_OLD
0081     connect(d->ui.getNewPlugins,
0082             &KNSWidgets::Button::dialogFinished,
0083             this,
0084             &Codethink::lvtqtw::ConfigurationDialog::getNewScriptFinished);
0085 #endif
0086 
0087     connect(d->ui.debugContextMenu, &QCheckBox::toggled, Preferences::self(), &Preferences::setEnableSceneContextMenu);
0088     connect(d->ui.enableDebugOutput, &QCheckBox::toggled, Preferences::self(), &Preferences::setEnableDebugOutput);
0089     connect(d->ui.enableCodeParseDebugOutput,
0090             &QCheckBox::toggled,
0091             Preferences::self(),
0092             &Preferences::setEnableCodeParseDebugOutput);
0093     connect(d->ui.storeDebugOutput, &QCheckBox::toggled, Preferences::self(), &Preferences::setStoreDebugOutput);
0094 
0095     connect(d->ui.isARelation, &QCheckBox::toggled, Preferences::self(), &Preferences::setShowIsARelation);
0096     connect(d->ui.usesInTheImplementation,
0097             &QCheckBox::toggled,
0098             Preferences::self(),
0099             &Preferences::setShowUsesInTheImplementationRelation);
0100     connect(d->ui.usesInTheInterface,
0101             &QCheckBox::toggled,
0102             Preferences::self(),
0103             &Preferences::setShowUsesInTheInterfaceRelation);
0104 
0105     connect(d->ui.showClients, &QCheckBox::toggled, Preferences::self(), &Preferences::setShowClients);
0106     connect(d->ui.showProviders, &QCheckBox::toggled, Preferences::self(), &Preferences::setShowProviders);
0107 
0108     connect(d->ui.minimap, &QCheckBox::toggled, Preferences::self(), &Preferences::setShowMinimap);
0109     connect(d->ui.toolBox, &QCheckBox::toggled, Preferences::self(), &Preferences::setShowLegend);
0110     connect(d->ui.classLimit,
0111             QOverload<int>::of(&QSpinBox::valueChanged),
0112             Preferences::self(),
0113             &Preferences::setClassLimit);
0114     connect(d->ui.relationLimit,
0115             QOverload<int>::of(&QSpinBox::valueChanged),
0116             Preferences::self(),
0117             &Preferences::setRelationLimit);
0118     connect(d->ui.zoomLevel,
0119             QOverload<int>::of(&QSpinBox::valueChanged),
0120             Preferences::self(),
0121             &Preferences::setZoomLevel);
0122 
0123     connect(d->ui.comboPanModifier, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this] {
0124         Preferences::setPanModifier(ModifierHelpers::stringToModifier(d->ui.comboPanModifier->currentText()));
0125     });
0126 
0127     connect(d->ui.showLevelNumbers, &QCheckBox::toggled, this, [this] {
0128         Preferences::setShowLevelNumbers(d->ui.showLevelNumbers->isChecked());
0129     });
0130 
0131     connect(d->ui.backgroundColor, &KColorButton::changed, this, [this] {
0132         Preferences::setBackgroundColor(d->ui.backgroundColor->color());
0133     });
0134     connect(d->ui.entityBackgroundColor, &KColorButton::changed, this, [this] {
0135         Preferences::setEntityBackgroundColor(d->ui.entityBackgroundColor->color());
0136     });
0137     connect(d->ui.selectedEntityBackgroundColor, &KColorButton::changed, this, [this] {
0138         Preferences::setSelectedEntityBackgroundColor(d->ui.selectedEntityBackgroundColor->color());
0139     });
0140     connect(d->ui.chkSelectedEntityHasGradient,
0141             &QCheckBox::toggled,
0142             Preferences::self(),
0143             &Preferences::setEnableGradientOnMainNode);
0144 
0145     connect(d->ui.edgeColor, &KColorButton::changed, this, [this] {
0146         Preferences::setEdgeColor(d->ui.edgeColor->color());
0147     });
0148     connect(d->ui.highlightEdgeColor, &KColorButton::changed, this, [this] {
0149         Preferences::setHighlightEdgeColor(d->ui.highlightEdgeColor->color());
0150     });
0151 
0152     connect(d->ui.comboZoomModifier, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this] {
0153         Preferences::setZoomModifier(ModifierHelpers::stringToModifier(d->ui.comboZoomModifier->currentText()));
0154     });
0155     connect(d->ui.chkColorBlindness, &QCheckBox::toggled, Preferences::self(), &Preferences::setColorBlindMode);
0156     connect(d->ui.chkColorPattern, &QCheckBox::toggled, Preferences::self(), &Preferences::setUseColorBlindFill);
0157 
0158     connect(d->ui.entityNamePos, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this] {
0159         Preferences::setLakosEntityNamePos(stringToCorner(d->ui.entityNamePos->currentText()));
0160     });
0161 
0162     connect(d->ui.lakosianRules, &QCheckBox::toggled, Preferences::self(), &Preferences::setUseLakosianRules);
0163     connect(d->ui.showRedundantEdgesDefaultCheckbox,
0164             &QCheckBox::toggled,
0165             Preferences::self(),
0166             &Preferences::setShowRedundantEdgesDefault);
0167     connect(d->ui.hidePkgPrefixOnComponents,
0168             &QCheckBox::toggled,
0169             Preferences::self(),
0170             &Preferences::setHidePackagePrefixOnComponents);
0171     connect(d->ui.invertHorizontalLvlLayout,
0172             &QCheckBox::toggled,
0173             Preferences::self(),
0174             &Preferences::setInvertHorizontalLevelizationLayout);
0175     connect(d->ui.invertVerticalLvlLayout,
0176             &QCheckBox::toggled,
0177             Preferences::self(),
0178             &Preferences::setInvertVerticalLevelizationLayout);
0179 
0180     connect(d->ui.spaceBetweenLevels,
0181             QOverload<int>::of(&QSpinBox::valueChanged),
0182             Preferences::self(),
0183             &Preferences::setSpaceBetweenLevels);
0184     connect(d->ui.spaceBetweenSublevels,
0185             QOverload<int>::of(&QSpinBox::valueChanged),
0186             Preferences::self(),
0187             &Preferences::setSpaceBetweenSublevels);
0188     connect(d->ui.spaceBetweenEntities,
0189             QOverload<int>::of(&QSpinBox::valueChanged),
0190             Preferences::self(),
0191             &Preferences::setSpaceBetweenEntities);
0192     connect(d->ui.maxEntitiesPerLevel,
0193             QOverload<int>::of(&QSpinBox::valueChanged),
0194             Preferences::self(),
0195             &Preferences::setMaxEntitiesPerLevel);
0196 
0197     connect(d->ui.pkgGroupFont, &KFontRequester::fontSelected, Preferences::self(), &Preferences::setPkgGroupFont);
0198     connect(d->ui.pkgFont, &KFontRequester::fontSelected, Preferences::self(), &Preferences::setPkgFont);
0199     connect(d->ui.componentFont, &KFontRequester::fontSelected, Preferences::self(), &Preferences::setComponentFont);
0200     connect(d->ui.classFont, &KFontRequester::fontSelected, Preferences::self(), &Preferences::setClassFont);
0201     connect(d->ui.structFont, &KFontRequester::fontSelected, Preferences::self(), &Preferences::setStructFont);
0202     connect(d->ui.enumFont, &KFontRequester::fontSelected, Preferences::self(), &Preferences::setEnumFont);
0203 
0204     connect(d->ui.autoSaveBackupIntervalMsecs,
0205             QOverload<int>::of(&QSpinBox::valueChanged),
0206             Preferences::self(),
0207             &Preferences::setAutoSaveBackupIntervalMsecs);
0208 
0209     auto *btn = d->ui.buttonBox->button(QDialogButtonBox::Save);
0210     auto *btnDefaults = d->ui.buttonBox->button(QDialogButtonBox::RestoreDefaults);
0211 
0212     connect(btnDefaults, &QPushButton::clicked, this, &ConfigurationDialog::restoreDefaults);
0213     connect(btn, &QPushButton::clicked, this, &ConfigurationDialog::save);
0214 
0215     connect(d->ui.listWidget, &QListWidget::currentItemChanged, this, [this](const QListWidgetItem *item) {
0216         const auto text = item->text();
0217         auto *selectedWidget = [&]() {
0218             if (text == tr("Colors")) {
0219                 return d->ui.colorsPage;
0220             }
0221             if (text == tr("Debug")) {
0222                 return d->ui.debugPage;
0223             }
0224             if (text == tr("Design")) {
0225                 return d->ui.designPage;
0226             }
0227             if (text == tr("Document")) {
0228                 return d->ui.documentPage;
0229             }
0230             if (text == tr("Graphics")) {
0231                 return d->ui.graphicsPage;
0232             }
0233             if (text == tr("Mouse")) {
0234                 return d->ui.mousePage;
0235             }
0236             if (text == tr("Plugins")) {
0237                 return d->ui.pluginsPage;
0238             }
0239             // Default for unknown pages
0240             return d->ui.colorsPage;
0241         }();
0242         d->ui.stackedWidget->setCurrentWidget(selectedWidget);
0243     });
0244 
0245     const QString backupFolder = QString::fromStdString(lvtprj::ProjectFile::backupFolder().string());
0246     d->ui.saveFolder->setText(backupFolder);
0247 }
0248 
0249 ConfigurationDialog::~ConfigurationDialog() = default;
0250 
0251 void ConfigurationDialog::showEvent(QShowEvent *ev)
0252 {
0253     updatePluginInformation();
0254 }
0255 
0256 void ConfigurationDialog::updatePluginInformation()
0257 {
0258 #ifndef KDE_FRAMEWORKS_IS_OLD
0259     auto plugins = QVector<KPluginMetaData>{};
0260     QString categoryLabel = "Codevis Plugins";
0261     for (auto const& metadataFile : d->pluginManager->getPluginsMetadataFilePaths()) {
0262         plugins.push_back(KPluginMetaData::fromJsonFile(QString::fromStdString(metadataFile)));
0263     }
0264 
0265     d->ui.pluginWidget->clear();
0266     d->ui.pluginWidget->addPlugins(plugins, categoryLabel);
0267     connect(d->ui.pluginWidget,
0268             &KPluginWidget::pluginEnabledChanged,
0269             this,
0270             [this](const QString& pluginId, bool enabled) {
0271                 auto plugin = d->pluginManager->getPluginById(pluginId.toStdString());
0272                 if (!plugin) {
0273                     return;
0274                 }
0275                 plugin->get().setEnabled(enabled);
0276             });
0277 #endif
0278 }
0279 
0280 void ConfigurationDialog::load()
0281 {
0282     d->ui.debugContextMenu->setChecked(Preferences::enableSceneContextMenu());
0283     d->ui.enableDebugOutput->setChecked(Preferences::enableDebugOutput());
0284     d->ui.enableCodeParseDebugOutput->setChecked(Preferences::enableCodeParseDebugOutput());
0285     d->ui.storeDebugOutput->setChecked(Preferences::storeDebugOutput());
0286     d->ui.showProviders->setChecked(Preferences::showProviders());
0287     d->ui.showClients->setChecked(Preferences::showClients());
0288     d->ui.isARelation->setChecked(Preferences::showIsARelation());
0289     d->ui.usesInTheImplementation->setChecked(Preferences::showUsesInTheImplementationRelation());
0290     d->ui.usesInTheInterface->setChecked(Preferences::showUsesInTheInterfaceRelation());
0291     d->ui.minimap->setChecked(Preferences::showMinimap());
0292     d->ui.toolBox->setChecked(Preferences::showLegend());
0293     d->ui.classLimit->setValue(Preferences::classLimit());
0294     d->ui.relationLimit->setValue(Preferences::relationLimit());
0295     d->ui.zoomLevel->setValue(Preferences::zoomLevel());
0296 
0297     d->ui.chkColorBlindness->setChecked(Preferences::colorBlindMode());
0298     d->ui.chkColorPattern->setChecked(Preferences::useColorBlindFill());
0299     d->ui.showLevelNumbers->setChecked(Preferences::showLevelNumbers());
0300 
0301     d->ui.comboPanModifier->setCurrentText(
0302         ModifierHelpers::modifierToText(static_cast<Qt::KeyboardModifier>(Preferences::panModifier())));
0303     d->ui.comboZoomModifier->setCurrentText(
0304         ModifierHelpers::modifierToText(static_cast<Qt::KeyboardModifier>(Preferences::zoomModifier())));
0305 
0306     d->ui.backgroundColor->setColor(Preferences::backgroundColor());
0307     d->ui.entityBackgroundColor->setColor(Preferences::entityBackgroundColor());
0308     d->ui.selectedEntityBackgroundColor->setColor(Preferences::selectedEntityBackgroundColor());
0309     d->ui.edgeColor->setColor(Preferences::edgeColor());
0310     d->ui.highlightEdgeColor->setColor(Preferences::highlightEdgeColor());
0311 
0312     const Qt::Corner cnr = static_cast<Qt::Corner>(Preferences::lakosEntityNamePos());
0313     d->ui.entityNamePos->setCurrentText(cnr == Qt::TopLeftCorner          ? tr("Top Left")
0314                                             : cnr == Qt::TopRightCorner   ? tr("Top Right")
0315                                             : cnr == Qt::BottomLeftCorner ? tr("Bottom Left")
0316                                                                           : tr("Bottom Right"));
0317 
0318     d->ui.pkgGroupFont->setFont(Preferences::pkgGroupFont());
0319     d->ui.pkgFont->setFont(Preferences::pkgFont());
0320     d->ui.componentFont->setFont(Preferences::componentFont());
0321     d->ui.classFont->setFont(Preferences::classFont());
0322     d->ui.structFont->setFont(Preferences::structFont());
0323     d->ui.enumFont->setFont(Preferences::enumFont());
0324 
0325     d->ui.autoSaveBackupIntervalMsecs->setValue(Preferences::autoSaveBackupIntervalMsecs());
0326 
0327     d->ui.lakosianRules->setChecked(Preferences::useLakosianRules());
0328     d->ui.showRedundantEdgesDefaultCheckbox->setChecked(Preferences::showRedundantEdgesDefault());
0329     d->ui.hidePkgPrefixOnComponents->setChecked(Preferences::hidePackagePrefixOnComponents());
0330     d->ui.invertHorizontalLvlLayout->setChecked(Preferences::invertHorizontalLevelizationLayout());
0331     d->ui.invertVerticalLvlLayout->setChecked(Preferences::invertVerticalLevelizationLayout());
0332     d->ui.spaceBetweenLevels->setValue(Preferences::spaceBetweenLevels());
0333     d->ui.spaceBetweenSublevels->setValue(Preferences::spaceBetweenSublevels());
0334     d->ui.spaceBetweenEntities->setValue(Preferences::spaceBetweenEntities());
0335     d->ui.maxEntitiesPerLevel->setValue(Preferences::maxEntitiesPerLevel());
0336 }
0337 
0338 void ConfigurationDialog::save()
0339 {
0340     Preferences::self()->save();
0341 }
0342 
0343 void ConfigurationDialog::restoreDefaults()
0344 {
0345     // TODO: Figure out how to load defaults.
0346     Preferences::self()->setDefaults();
0347     load();
0348 }
0349 
0350 void ConfigurationDialog::changeCurrentWidgetByString(QString const& text)
0351 {
0352     auto items = d->ui.listWidget->findItems(text, Qt::MatchFlag::MatchExactly);
0353     if (items.isEmpty()) {
0354         // Option not found
0355         return;
0356     }
0357     d->ui.listWidget->setCurrentItem(items[0]);
0358 }
0359 
0360 void ConfigurationDialog::populateMouseTabOptions()
0361 {
0362     std::initializer_list<QComboBox *> comboBoxes = {
0363         d->ui.comboPanModifier,
0364         d->ui.comboZoomModifier,
0365     };
0366     for (QComboBox *combo : comboBoxes) {
0367 #ifdef __APPLE__
0368         combo->addItem(tr("OPTION"));
0369 #else
0370         combo->addItem(tr("ALT"));
0371 #endif
0372 
0373 #ifdef __APPLE__
0374         combo->addItem(tr("COMMAND"));
0375 #else
0376         combo->addItem(tr("CONTROL"));
0377 #endif
0378 
0379         combo->addItem(tr("SHIFT"));
0380         combo->addItem(tr("No modifier"));
0381     }
0382 }
0383 
0384 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
0385 void ConfigurationDialog::getNewScriptFinished(const QList<KNSCore::Entry>& changedEntries)
0386 #else
0387 void ConfigurationDialog::getNewScriptFinished(const KNSCore::EntryInternal::List& changedEntries)
0388 #endif
0389 {
0390 // Error build on Qt5 - the definitions of
0391 // KNSCore changed and things got messy.
0392 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0393 #undef KNSCore
0394 #define KNSCore KNS3
0395 #endif
0396 
0397     bool installed = false;
0398     bool removed = false;
0399     // Our plugins are gzipped, and the installedFiles returns with `/*` in the end to denote
0400     // multiple files. we need to chop that off when we want to install it.
0401     QList<QString> installedPlugins;
0402     QList<QString> removedPlugins;
0403     for (const auto& entry : qAsConst(changedEntries)) {
0404         switch (entry.status()) {
0405         case KNSCore::Entry::Installed: {
0406             if (entry.installedFiles().count() == 0) {
0407                 continue;
0408             }
0409 
0410             QString filename = entry.installedFiles()[0];
0411             // remove /* from the string.
0412             installedPlugins.append(filename.chopped(2));
0413 
0414             installed = true;
0415         } break;
0416         case KNSCore::Entry::Deleted: {
0417             if (entry.uninstalledFiles().count() == 0) {
0418                 continue;
0419             }
0420 
0421             QString filename = entry.uninstalledFiles()[0];
0422             // remove /* from the string.
0423             removedPlugins.append(filename.chopped(2));
0424 
0425             removed = true;
0426         }
0427         case KNSCore::Entry::Invalid:
0428         case KNSCore::Entry::Installing:
0429         case KNSCore::Entry::Downloadable:
0430         case KNSCore::Entry::Updateable:
0431         case KNSCore::Entry::Updating:
0432             // Not interesting.
0433             break;
0434         }
0435     }
0436 
0437     // Refresh the plugins installed by GetNewStuff
0438     if (installed) {
0439         for (const auto& installedPlugin : installedPlugins) {
0440             d->pluginManager->reloadPlugin(installedPlugin);
0441         }
0442     }
0443     if (removed) {
0444         for (const auto& uninstalledFile : removedPlugins) {
0445             d->pluginManager->removePlugin(uninstalledFile);
0446         }
0447     }
0448 
0449     updatePluginInformation();
0450 }
0451 } // namespace Codethink::lvtqtw