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