From 7167ce41b61d2ba2cdb526777a4233eb84a3b66a Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Sat, 6 Dec 2014 17:33:25 -0500 Subject: Imported Upstream version 2.99.6 --- .../guiSQLiteStudio/dialogs/configdialog.cpp | 1529 ++++++++++++++++++++ 1 file changed, 1529 insertions(+) create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp (limited to 'SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp') diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp new file mode 100644 index 0000000..932036e --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp @@ -0,0 +1,1529 @@ +#include "configdialog.h" +#include "ui_configdialog.h" +#include "services/config.h" +#include "uiconfig.h" +#include "customconfigwidgetplugin.h" +#include "services/pluginmanager.h" +#include "formmanager.h" +#include "services/codeformatter.h" +#include "plugins/codeformatterplugin.h" +#include "configwidgets/styleconfigwidget.h" +#include "configwidgets/combodatawidget.h" +#include "configwidgets/listtostringlisthash.h" +#include "iconmanager.h" +#include "common/userinputfilter.h" +#include "multieditor/multieditorwidget.h" +#include "multieditor/multieditorwidgetplugin.h" +#include "plugins/confignotifiableplugin.h" +#include "mainwindow.h" +#include "common/unused.h" +#include "sqlitestudio.h" +#include "configmapper.h" +#include "datatype.h" +#include "uiutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GET_FILTER_STRING(Widget, WidgetType, Method) \ + if (qobject_cast(Widget))\ + return qobject_cast(Widget)->Method() + " " + Widget->toolTip();\ + +#define GET_FILTER_STRING2(Widget, WidgetType) \ + WidgetType* w##WidgetType = qobject_cast(widget);\ + if (w##WidgetType)\ + return getFilterString(w##WidgetType) + " " + Widget->toolTip(); + +ConfigDialog::ConfigDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ConfigDialog) +{ + init(); +} + +ConfigDialog::~ConfigDialog() +{ + // Cancel transaction on CfgMain objects from plugins + rollbackPluginConfigs(); + + // Notify plugins about dialog being closed + UiConfiguredPlugin* cfgPlugin = nullptr; + foreach (Plugin* plugin, PLUGINS->getLoadedPlugins()) + { + cfgPlugin = dynamic_cast(plugin); + if (!cfgPlugin) + continue; + + cfgPlugin->configDialogClosed(); + } + + // Delete UI and other resources + delete ui; + safe_delete(configMapper); + + for (ConfigMapper* mapper : pluginConfigMappers.values()) + delete mapper; + + pluginConfigMappers.clear(); + +} + +void ConfigDialog::configureDataEditors(const QString& dataTypeString) +{ + ui->categoriesWidget->setVisible(false); + ui->stackedWidget->setCurrentWidget(ui->dataEditorsPage); + + for (int i = 0; i < ui->dataEditorsTypesList->count(); i++) + { + if (ui->dataEditorsTypesList->item(i)->text() == dataTypeString.toUpper()) + { + ui->dataEditorsTypesList->setCurrentRow(i); + return; + } + } + + addDataType(dataTypeString.toUpper()); +} + +QString ConfigDialog::getFilterString(QWidget *widget) +{ + // Common code for widgets with single method call + GET_FILTER_STRING(widget, QLabel, text); + GET_FILTER_STRING(widget, QAbstractButton, text); + GET_FILTER_STRING(widget, QLineEdit, text); + GET_FILTER_STRING(widget, QTextEdit, toPlainText); + GET_FILTER_STRING(widget, QPlainTextEdit, toPlainText); + GET_FILTER_STRING(widget, QGroupBox, title); + GET_FILTER_STRING(widget, QKeySequenceEdit, keySequence().toString); + + // Widgets needs a little more than single method call + GET_FILTER_STRING2(widget, QComboBox); + GET_FILTER_STRING2(widget, QTreeWidget); + GET_FILTER_STRING2(widget, QListWidget); + GET_FILTER_STRING2(widget, QTableWidget); + + return QString::null; +} + +QString ConfigDialog::getFilterString(QComboBox *widget) +{ + QStringList items; + for (int i = 0; i < widget->count(); i++) + items << widget->itemText(i); + + return items.join(" "); +} + +QString ConfigDialog::getFilterString(QTreeWidget *widget) +{ + QList items = widget->findItems("*", Qt::MatchWildcard|Qt::MatchRecursive); + QStringList strList; + foreach (QTreeWidgetItem* item, items) + for (int i = 0; i < widget->columnCount(); i++) + strList << item->text(i) + " " + item->toolTip(0); + + return strList.join(" "); +} + +QString ConfigDialog::getFilterString(QListWidget *widget) +{ + QList items = widget->findItems("*", Qt::MatchWildcard|Qt::MatchRecursive); + QStringList strList; + foreach (QListWidgetItem* item, items) + strList << item->text() + " " + item->toolTip(); + + return strList.join(" "); +} + +QString ConfigDialog::getFilterString(QTableWidget *widget) +{ + QList items = widget->findItems("*", Qt::MatchWildcard|Qt::MatchRecursive); + QStringList strList; + foreach (QTableWidgetItem* item, items) + strList << item->text() + " " + item->toolTip(); + + return strList.join(" "); +} + +void ConfigDialog::init() +{ + ui->setupUi(this); + setWindowIcon(ICONS.CONFIGURE); + + ui->categoriesTree->setCurrentItem(ui->categoriesTree->topLevelItem(0)); + + configMapper = new ConfigMapper(CfgMain::getPersistableInstances()); + connect(configMapper, SIGNAL(modified()), this, SLOT(markModified())); + connect(configMapper, &ConfigMapper::notifyEnabledWidgetModified, [=](QWidget* widget, CfgEntry* key, const QVariant& value) + { + UNUSED(widget); + for (ConfigNotifiablePlugin* plugin : notifiablePlugins) + plugin->configModified(key, value); + }); + + ui->categoriesFilter->setClearButtonEnabled(true); + UserInputFilter* filter = new UserInputFilter(ui->categoriesFilter, this, SLOT(applyFilter(QString))); + filter->setDelay(500); + + ui->stackedWidget->setCurrentWidget(ui->generalPage); + initPageMap(); + initInternalCustomConfigWidgets(); + initPlugins(); + initPluginsPage(); + initFormatterPlugins(); + initDataEditors(); + initShortcuts(); + + connect(ui->categoriesTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(switchPage(QTreeWidgetItem*))); + connect(ui->previewTabs, SIGNAL(currentChanged(int)), this, SLOT(updateStylePreview())); + connect(ui->activeStyleCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateStylePreview())); + connect(ui->buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); + connect(ui->hideBuiltInPluginsCheck, SIGNAL(toggled(bool)), this, SLOT(updateBuiltInPluginsVisibility())); + + ui->activeStyleCombo->addItems(QStyleFactory::keys()); + + connect(ui->stackedWidget, SIGNAL(currentChanged(int)), this, SLOT(pageSwitched())); + + ui->hideBuiltInPluginsCheck->setChecked(true); + +#ifdef NO_AUTO_UPDATES + ui->updatesGroup->setVisible(false); +#endif + + load(); + updateStylePreview(); +} + +void ConfigDialog::load() +{ + updatingDataEditorItem = true; + configMapper->loadToWidget(ui->stackedWidget); + updatingDataEditorItem = false; + setModified(false); +} + +void ConfigDialog::save() +{ + MainWindow::getInstance()->setStyle(ui->activeStyleCombo->currentText()); + + QString loadedPlugins = collectLoadedPlugins(); + storeSelectedFormatters(); + CFG->beginMassSave(); + CFG_CORE.General.LoadedPlugins.set(loadedPlugins); + configMapper->saveFromWidget(ui->stackedWidget, true); + commitPluginConfigs(); + CFG->commitMassSave(); +} + +void ConfigDialog::storeSelectedFormatters() +{ + CodeFormatterPlugin* plugin = nullptr; + QTreeWidgetItem* item = nullptr; + QComboBox* combo = nullptr; + QString lang; + QString pluginName; + for (int i = 0, total = ui->formatterPluginsTree->topLevelItemCount(); i < total; ++i) + { + item = ui->formatterPluginsTree->topLevelItem(i); + lang = item->text(0); + + combo = formatterLangToPluginComboMap[lang]; + if (!combo) + { + qCritical() << "Could not find combo for lang " << lang << " in storeSelectedFormatters()"; + continue; + } + + pluginName = combo->currentData().toString(); + plugin = dynamic_cast(PLUGINS->getLoadedPlugin(pluginName)); + if (!plugin) + { + qCritical() << "Could not find plugin for lang " << lang << " in storeSelectedFormatters()"; + continue; + } + + FORMATTER->setFormatter(lang, plugin); + } + + FORMATTER->storeCurrentSettings(); +} + +void ConfigDialog::markModified() +{ + setModified(true); +} + +void ConfigDialog::setModified(bool modified) +{ + modifiedFlag = modified; + updateModified(); +} + +void ConfigDialog::updateModified() +{ + ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(modifiedFlag); +} + +void ConfigDialog::applyFilter(const QString &filter) +{ + QColor normalColor = ui->categoriesTree->palette().color(QPalette::Active, QPalette::WindowText); + QColor disabledColor = ui->categoriesTree->palette().color(QPalette::Disabled, QPalette::WindowText); + if (filter.isEmpty()) + { + foreach (QTreeWidgetItem* item, getAllCategoryItems()) + item->setForeground(0, normalColor); + + return; + } + + QList widgets = ui->stackedWidget->findChildren(); + QList matchedWidgets; + foreach (QWidget* widget, widgets) + { + if (getFilterString(widget).contains(filter, Qt::CaseInsensitive)) + matchedWidgets << widget; + } + + QHash pageToCategoryItem = buildPageToCategoryItemMap(); + QSet matchedCategories; + foreach (QWidget* page, pageToCategoryItem.keys()) + { + foreach (QWidget* matched, matchedWidgets) + { + if (page->isAncestorOf(matched)) + { + if (!pageToCategoryItem.contains(page)) + { + qCritical() << "Page" << page << "not on page-to-category-item mapping."; + continue; + } + + matchedCategories << pageToCategoryItem[page]; + break; + } + } + } + + foreach (QTreeWidgetItem* item, getAllCategoryItems()) + item->setForeground(0, disabledColor); + + foreach (QTreeWidgetItem* item, matchedCategories) + { + item->setForeground(0, normalColor); + while ((item = item->parent()) != nullptr) + item->setForeground(0, normalColor); + } +} + +QHash ConfigDialog::buildPageToCategoryItemMap() const +{ + QHash pageNameToCategoryItem; + foreach (QTreeWidgetItem* item, getAllCategoryItems()) + pageNameToCategoryItem[item->statusTip(0)] = item; + + QWidget* page = nullptr; + QHash pageToCategoryItem; + for (int i = 0; i < ui->stackedWidget->count(); i++) + { + page = ui->stackedWidget->widget(i); + pageToCategoryItem[page] = pageNameToCategoryItem[page->objectName()]; + } + return pageToCategoryItem; +} + +QList ConfigDialog::getAllCategoryItems() const +{ + return ui->categoriesTree->findItems("*", Qt::MatchWildcard|Qt::MatchRecursive); +} + +QList ConfigDialog::getDefaultEditorsForType(DataType::Enum dataType) +{ + QList plugins = PLUGINS->getLoadedPlugins(); + DataType modelDataType; + modelDataType.setType(dataType); + + typedef QPair PluginWithPriority; + QList sortedPlugins; + PluginWithPriority editorWithPrio; + for (MultiEditorWidgetPlugin* plugin : plugins) + { + if (!plugin->validFor(modelDataType)) + continue; + + editorWithPrio.first = plugin->getPriority(modelDataType); + editorWithPrio.second = plugin; + sortedPlugins << editorWithPrio; + } + + qSort(sortedPlugins.begin(), sortedPlugins.end(), [=](const PluginWithPriority& p1, const PluginWithPriority& p2) -> bool + { + return p1.first < p2.first; + }); + + QList results; + for (const PluginWithPriority& p: sortedPlugins) + results << p.second; + + return results; +} + +void ConfigDialog::pageSwitched() +{ + if (ui->stackedWidget->currentWidget() == ui->dataEditorsPage) + { + updateDataTypeEditors(); + return; + } +} + +void ConfigDialog::updateDataTypeEditors() +{ + QString typeName = ui->dataEditorsTypesList->currentItem()->text(); + DataType::Enum typeEnum = DataType::fromString(typeName); + bool usingCustomOrder = false; + QStringList editorsOrder = getPluginNamesFromDataTypeItem(ui->dataEditorsTypesList->currentItem(), &usingCustomOrder); + QList sortedPlugins; + + while (ui->dataEditorsSelectedTabs->count() > 0) + delete ui->dataEditorsSelectedTabs->widget(0); + + ui->dataEditorsAvailableList->clear(); + if (usingCustomOrder) + sortedPlugins = updateCustomDataTypeEditors(editorsOrder); + else + sortedPlugins = updateDefaultDataTypeEditors(typeEnum); + + ui->dataEditorsAvailableList->sortItems(); + + for (MultiEditorWidgetPlugin* plugin : sortedPlugins) + addDataTypeEditor(plugin); +} + +QList ConfigDialog::updateCustomDataTypeEditors(const QStringList& editorsOrder) +{ + // Building plugins list + QList plugins = PLUGINS->getLoadedPlugins(); + QList enabledPlugins; + QListWidgetItem* item = nullptr; + for (MultiEditorWidgetPlugin* plugin : plugins) + { + item = new QListWidgetItem(plugin->getTitle()); + item->setFlags(item->flags()|Qt::ItemIsUserCheckable); + item->setCheckState(editorsOrder.contains(plugin->getName()) ? Qt::Checked : Qt::Unchecked); + item->setData(QListWidgetItem::UserType, plugin->getName()); + if (item->checkState() == Qt::Checked) + enabledPlugins << plugin; + + ui->dataEditorsAvailableList->addItem(item); + } + + qSort(enabledPlugins.begin(), enabledPlugins.end(), [=](MultiEditorWidgetPlugin* p1, MultiEditorWidgetPlugin* p2) -> bool + { + return editorsOrder.indexOf(p1->getName()) < editorsOrder.indexOf(p2->getName()); + }); + + return enabledPlugins; +} + +QList ConfigDialog::updateDefaultDataTypeEditors(DataType::Enum typeEnum) +{ + // Building plugins list + QList plugins = PLUGINS->getLoadedPlugins(); + QList enabledPlugins = getDefaultEditorsForType(typeEnum); + QListWidgetItem* item = nullptr; + for (MultiEditorWidgetPlugin* plugin : plugins) + { + item = new QListWidgetItem(plugin->getTitle()); + item->setFlags(item->flags()|Qt::ItemIsUserCheckable); + item->setCheckState(enabledPlugins.contains(plugin) ? Qt::Checked : Qt::Unchecked); + item->setData(QListWidgetItem::UserType, plugin->getName()); + ui->dataEditorsAvailableList->addItem(item); + } + return enabledPlugins; +} + +void ConfigDialog::addDataTypeEditor(const QString& pluginName) +{ + MultiEditorWidgetPlugin* plugin = dynamic_cast(PLUGINS->getLoadedPlugin(pluginName)); + if (!plugin) + { + qCritical() << "Could not find plugin" << pluginName << " in ConfigDialog::addDataTypeEditor()"; + return; + } + + addDataTypeEditor(plugin); +} + +void ConfigDialog::addDataTypeEditor(MultiEditorWidgetPlugin* plugin) +{ + MultiEditorWidget* editor = plugin->getInstance(); + ui->dataEditorsSelectedTabs->addTab(editor, editor->getTabLabel().replace("&", "&&")); +} + +void ConfigDialog::removeDataTypeEditor(QListWidgetItem* item, const QString& pluginName) +{ + QStringList orderedPlugins = getPluginNamesFromDataTypeItem(item); + int idx = orderedPlugins.indexOf(pluginName); + removeDataTypeEditor(idx); +} + +void ConfigDialog::removeDataTypeEditor(int idx) +{ + if (idx < 0 || idx > (ui->dataEditorsSelectedTabs->count() - 1)) + { + qCritical() << "Index out of range in ConfigDialog::removeDataTypeEditor():" << idx << "(tabs:" << ui->dataEditorsSelectedTabs->count() << ")"; + return; + } + + delete ui->dataEditorsSelectedTabs->widget(idx); +} + +void ConfigDialog::transformDataTypeEditorsToCustomList(QListWidgetItem* typeItem) +{ + DataType::Enum dataType = DataType::fromString(typeItem->text()); + QList plugins = getDefaultEditorsForType(dataType); + + QStringList pluginNames; + for (MultiEditorWidgetPlugin* plugin : plugins) + pluginNames << plugin->getName(); + + setPluginNamesForDataTypeItem(typeItem, pluginNames); +} + +QStringList ConfigDialog::getPluginNamesFromDataTypeItem(QListWidgetItem* typeItem, bool* exists) +{ + QVariant data = typeItem->data(QListWidgetItem::UserType); + if (exists) + *exists = data.isValid(); + + return data.toStringList(); +} + +void ConfigDialog::setPluginNamesForDataTypeItem(QListWidgetItem* typeItem, const QStringList& pluginNames) +{ + updatingDataEditorItem = true; + typeItem->setData(QListWidgetItem::UserType, pluginNames); + updatingDataEditorItem = false; +} + +void ConfigDialog::addDataType(const QString& typeStr) +{ + QListWidgetItem* item = new QListWidgetItem(typeStr); + item->setFlags(item->flags()|Qt::ItemIsEditable); + ui->dataEditorsTypesList->addItem(item); + ui->dataEditorsTypesList->setCurrentRow(ui->dataEditorsTypesList->count() - 1, QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent); + markModified(); +} + +void ConfigDialog::rollbackPluginConfigs() +{ + CfgMain* mainCfg = nullptr; + for (UiConfiguredPlugin* plugin : pluginConfigMappers.keys()) + { + mainCfg = plugin->getMainUiConfig(); + if (mainCfg) + mainCfg->rollback(); + } +} + +void ConfigDialog::commitPluginConfigs() +{ + CfgMain* mainCfg = nullptr; + for (UiConfiguredPlugin* plugin : pluginConfigMappers.keys()) + { + mainCfg = plugin->getMainUiConfig(); + if (mainCfg) + { + mainCfg->commit(); + mainCfg->begin(); // be prepared for further changes after "Apply" + } + } +} + +void ConfigDialog::updateDataTypeListState() +{ + bool listEditingEnabled = ui->dataEditorsTypesList->selectedItems().size() > 0 && ui->dataEditorsTypesList->currentItem()->flags().testFlag(Qt::ItemIsEditable); + dataEditRenameAction->setEnabled(listEditingEnabled); + dataEditDeleteAction->setEnabled(listEditingEnabled); + + bool orderEditingEnabled = ui->dataEditorsTypesList->selectedItems().size() > 0; + ui->dataEditorsAvailableList->setEnabled(orderEditingEnabled); + ui->dataEditorsSelectedTabs->setEnabled(orderEditingEnabled); +} + +void ConfigDialog::dataEditorItemEdited(QListWidgetItem* item) +{ + if (updatingDataEditorItem) + return; + + updatingDataEditorItem = true; + QString txt = item->text().toUpper(); + if (DataType::getAllNames().contains(txt)) + txt += "_"; + + while (ui->dataEditorsTypesList->findItems(txt, Qt::MatchExactly).size() > 1) + txt += "_"; + + item->setText(txt); + updatingDataEditorItem = false; +} + +void ConfigDialog::dataEditorAvailableChanged(QListWidgetItem* item) +{ + QListWidgetItem* typeItem = ui->dataEditorsTypesList->currentItem(); + if (!typeItem) + return; + + bool exists = false; + QStringList pluginNames = getPluginNamesFromDataTypeItem(typeItem, &exists); + if (!exists) + { + transformDataTypeEditorsToCustomList(typeItem); + pluginNames = getPluginNamesFromDataTypeItem(typeItem); + } + + QString pluginName = item->data(QListWidgetItem::UserType).toString(); + Qt::CheckState state = item->checkState(); + if (pluginNames.contains(pluginName) && state == Qt::Unchecked) + { + removeDataTypeEditor(typeItem, pluginName); + pluginNames.removeOne(pluginName); + + } + else if (!pluginNames.contains(pluginName) && state == Qt::Checked) + { + addDataTypeEditor(pluginName); + pluginNames << pluginName; + } + + setPluginNamesForDataTypeItem(typeItem, pluginNames); +} + +void ConfigDialog::dataEditorTabsOrderChanged(int from, int to) +{ + QListWidgetItem* typeItem = ui->dataEditorsTypesList->currentItem(); + if (!typeItem) + return; + + bool exists = false; + QStringList pluginNames = getPluginNamesFromDataTypeItem(typeItem, &exists); + if (!exists) + { + transformDataTypeEditorsToCustomList(typeItem); + pluginNames = getPluginNamesFromDataTypeItem(typeItem); + } + + int pluginSize = pluginNames.size(); + if (from >= pluginSize || to >= pluginSize) + { + qCritical() << "Tabse moved out of range. in ConfigDialog::dataEditorTabsOrderChanged(). Range was: " << pluginSize << "and indexes were:" << from << to; + return; + } + + QString pluginName = pluginNames[from]; + pluginNames.removeAt(from); + pluginNames.insert(to, pluginName); + + setPluginNamesForDataTypeItem(typeItem, pluginNames); +} + +void ConfigDialog::addDataType() +{ + addDataType(""); + renameDataType(); +} + +void ConfigDialog::renameDataType() +{ + QListWidgetItem* item = ui->dataEditorsTypesList->currentItem(); + if (!item) + return; + + ui->dataEditorsTypesList->editItem(item); +} + +void ConfigDialog::delDataType() +{ + QListWidgetItem* item = ui->dataEditorsTypesList->currentItem(); + if (!item) + return; + + int row = ui->dataEditorsTypesList->currentRow(); + delete ui->dataEditorsTypesList->takeItem(row); + + if (ui->dataEditorsTypesList->count() > 0) + { + if (ui->dataEditorsTypesList->count() <= row) + { + row--; + if (row < 0) + row = 0; + } + + ui->dataEditorsTypesList->setCurrentRow(row, QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent); + } + + updateDataTypeListState(); + markModified(); +} + +void ConfigDialog::dataTypesHelp() +{ + static const QString url = QStringLiteral("http://wiki.sqlitestudio.pl/index.php/User_Manual#Customizing_data_type_editors"); + QDesktopServices::openUrl(QUrl(url, QUrl::StrictMode)); +} + +void ConfigDialog::updateActiveFormatterState() +{ + CodeFormatterPlugin* plugin = nullptr; + QTreeWidgetItem* item = nullptr; + QComboBox* combo = nullptr; + QToolButton* button = nullptr; + QString lang; + QString pluginName; + for (int i = 0, total = ui->formatterPluginsTree->topLevelItemCount(); i < total; ++i) + { + item = ui->formatterPluginsTree->topLevelItem(i); + lang = item->text(0); + + combo = formatterLangToPluginComboMap[lang]; + button = formatterLangToConfigButtonMap[lang]; + if (!button) + { + qCritical() << "Could not find button for lang " << lang << " in updateActiveFormatterState()"; + continue; + } + + if (!combo) + { + qCritical() << "Could not find combo for lang " << lang << " in updateActiveFormatterState()"; + button->setEnabled(false); + continue; + } + + pluginName = combo->currentData().toString(); + plugin = dynamic_cast(PLUGINS->getLoadedPlugin(pluginName)); + if (!plugin) + { + qCritical() << "Could not find plugin for lang " << lang << " in updateActiveFormatterState()"; + button->setEnabled(false); + continue; + } + + button->setEnabled(dynamic_cast(plugin)); + } +} + +void ConfigDialog::configureFormatter(const QString& pluginTitle) +{ + QTreeWidgetItem* item = getItemByTitle(pluginTitle); + if (!item) + return; + + ui->categoriesTree->setCurrentItem(item); +} + +void ConfigDialog::activeFormatterChanged() +{ + markModified(); + updateActiveFormatterState(); +} + +void ConfigDialog::detailsClicked(const QString& pluginName) +{ + static const QString details = QStringLiteral( + "" + "" + "" + "" + "" + "%2" + "
%1
"); + static const QString row = QStringLiteral("%1%2"); + static const QString hline = QStringLiteral("
"); + + PluginType* type = PLUGINS->getPluginType(pluginName); + Q_ASSERT(type != nullptr); + + // Rows + QStringList rows; + rows << row.arg(tr("Description:", "plugin details")).arg(PLUGINS->getDescription(pluginName)); + rows << row.arg(tr("Category:", "plugin details")).arg(type->getTitle()); + rows << row.arg(tr("Version:", "plugin details")).arg(PLUGINS->getPrintableVersion(pluginName)); + rows << row.arg(tr("Author:", "plugin details")).arg(PLUGINS->getAuthor(pluginName)); + rows << hline; + rows << row.arg(tr("Internal name:", "plugin details")).arg(pluginName); + rows << row.arg(tr("Dependencies:", "plugin details")).arg(PLUGINS->getDependencies(pluginName).join(", ")); + rows << row.arg(tr("Conflicts:", "plugin details")).arg(PLUGINS->getConflicts(pluginName).join(", ")); + + // Message + QString pluginDetails = details.arg(PLUGINS->getTitle(pluginName)).arg(rows.join("")); + QMessageBox::information(this, tr("Plugin details"), pluginDetails); +} + +void ConfigDialog::failedToLoadPlugin(const QString& pluginName) +{ + QTreeWidgetItem* theItem = itemToPluginNameMap.valueByRight(pluginName); + if (!theItem) + { + qWarning() << "Plugin" << pluginName << "failed to load, but it could not be found on the plugins list in ConfigDialog."; + return; + } + + theItem->setCheckState(0, Qt::Unchecked); +} + +void ConfigDialog::codeFormatterUnloaded() +{ + refreshFormattersPage(); +} + +void ConfigDialog::codeFormatterLoaded() +{ + refreshFormattersPage(); +} + +void ConfigDialog::loadUnloadPlugin(QTreeWidgetItem* item, int column) +{ + if (column != 0) + return; + + QString pluginName = itemToPluginNameMap.valueByLeft(item); + if (PLUGINS->isBuiltIn(pluginName)) + return; + + bool wasLoaded = PLUGINS->isLoaded(pluginName); + + if (wasLoaded == (item->checkState(0) == Qt::Checked)) + return; + + if (wasLoaded) + PLUGINS->unload(pluginName); + else + PLUGINS->load(pluginName); + + markModified(); +} + +void ConfigDialog::pluginAboutToUnload(Plugin* plugin, PluginType* type) +{ + // Deinit tree item + QTreeWidgetItem* typeItem = getPluginsCategoryItem(type); + QTreeWidgetItem* pluginItem = getPluginItem(plugin); + if (pluginItem) + { + typeItem->removeChild(pluginItem); + pluginToItemMap.remove(plugin); + } + + // Notifiable plugin + ConfigNotifiablePlugin* notifiablePlugin = dynamic_cast(plugin); + if (notifiablePlugin && notifiablePlugins.contains(notifiablePlugin)) + notifiablePlugins.removeOne(notifiablePlugin); + + // Deinit page + deinitPluginPage(plugin); + + // Update tree categories + updatePluginCategoriesVisibility(); +} + +void ConfigDialog::pluginLoaded(Plugin* plugin, PluginType* type, bool skipConfigLoading) +{ + // Update formatters page + if (type->isForPluginType()) + codeFormatterLoaded(); + + // Init page + if (!initPluginPage(plugin, skipConfigLoading)) + return; + + // Init tree item + QTreeWidgetItem* typeItem = getPluginsCategoryItem(type); + QTreeWidgetItem* pluginItem = new QTreeWidgetItem({plugin->getTitle()}); + pluginItem->setStatusTip(0, plugin->getName()); + typeItem->addChild(pluginItem); + pluginToItemMap[plugin] = pluginItem; + + // Update tree categories + updatePluginCategoriesVisibility(); + + // Notifiable plugin + ConfigNotifiablePlugin* notifiablePlugin = dynamic_cast(plugin); + if (notifiablePlugin) + notifiablePlugins << notifiablePlugin; +} + +void ConfigDialog::pluginUnloaded(const QString& pluginName, PluginType* type) +{ + UNUSED(pluginName); + + // Update formatters page + if (type->isForPluginType()) + codeFormatterUnloaded(); +} + +void ConfigDialog::updatePluginCategoriesVisibility() +{ + QTreeWidgetItem* categories = getPluginsCategoryItem(); + for (int i = 0; i < categories->childCount(); i++) + updatePluginCategoriesVisibility(categories->child(i)); +} + +void ConfigDialog::updateBuiltInPluginsVisibility() +{ + bool hideBuiltIn = ui->hideBuiltInPluginsCheck->isChecked(); + QHashIterator it = itemToPluginNameMap.iterator(); + while (it.hasNext()) + { + it.next(); + if (PLUGINS->isBuiltIn(it.value())) + ui->pluginsList->setItemHidden(it.key(), hideBuiltIn); + else + ui->pluginsList->setItemHidden(it.key(), false); + } +} + +void ConfigDialog::applyShortcutsFilter(const QString &filter) +{ + QTreeWidgetItem* categoryItem = nullptr; + QTreeWidgetItem* item = nullptr; + QKeySequenceEdit* seqEdit = nullptr; + bool empty = filter.isEmpty(); + bool visible = true; + int foundInCategory = 0; + for (int i = 0, total_i = ui->shortcutsTable->topLevelItemCount(); i < total_i; ++i) + { + foundInCategory = 0; + categoryItem = ui->shortcutsTable->topLevelItem(i); + for (int j = 0 , total_j = categoryItem->childCount(); j < total_j; ++j) + { + item = categoryItem->child(j); + seqEdit = dynamic_cast(ui->shortcutsTable->itemWidget(item, 1)); + visible = empty || item->text(0).contains(filter, Qt::CaseInsensitive) || + seqEdit->keySequence().toString().contains(filter, Qt::CaseInsensitive); + + item->setHidden(!visible); + if (visible) + foundInCategory++; + } + + categoryItem->setHidden(foundInCategory == 0); + } +} + +void ConfigDialog::updatePluginCategoriesVisibility(QTreeWidgetItem* categoryItem) +{ + categoryItem->setHidden(categoryItem->childCount() == 0); +} + +QString ConfigDialog::collectLoadedPlugins() const +{ + QStringList loaded; + QHashIterator it = itemToPluginNameMap.iterator(); + while (it.hasNext()) + { + it.next(); + loaded << (it.value() + "=" + ((it.key()->checkState(0) == Qt::Checked) ? "1" : "0")); + } + + return loaded.join(","); +} + +void ConfigDialog::initPageMap() +{ + int pages = ui->stackedWidget->count(); + QWidget* widget = nullptr; + for (int i = 0; i < pages; i++) + { + widget = ui->stackedWidget->widget(i); + nameToPage[widget->objectName()] = widget; + } +} + +void ConfigDialog::initInternalCustomConfigWidgets() +{ + QList customWidgets; + customWidgets << new StyleConfigWidget(); + customWidgets << new ListToStringListHash(&CFG_UI.General.DataEditorsOrder); + configMapper->setInternalCustomConfigWidgets(customWidgets); +} + +void ConfigDialog::initFormatterPlugins() +{ + ui->formatterPluginsTree->header()->setSectionsMovable(false); + ui->formatterPluginsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->formatterPluginsTree->resizeColumnToContents(1); + ui->formatterPluginsTree->resizeColumnToContents(2); + + refreshFormattersPage(); +} + +void ConfigDialog::refreshFormattersPage() +{ + ui->formatterPluginsTree->clear(); + + QHash activeFormatters = CFG_CORE.General.ActiveCodeFormatter.get(); + + QList plugins = PLUGINS->getLoadedPlugins(); + QHash> groupedPlugins; + for (CodeFormatterPlugin* plugin : plugins) + groupedPlugins[plugin->getLanguage()] << plugin; + + formatterLangToPluginComboMap.clear(); + formatterLangToConfigButtonMap.clear(); + int row = 0; + QTreeWidgetItem* item = nullptr; + QComboBox* combo = nullptr; + QToolButton* configButton = nullptr; + QStringList pluginTitles; + QStringList pluginNames; + QStringList sortedPluginNames; + QString selectedPluginName; + QModelIndex index; + QString groupName; + QHashIterator> it(groupedPlugins); + while (it.hasNext()) + { + it.next(); + groupName = it.key(); + + item = new QTreeWidgetItem({groupName}); + ui->formatterPluginsTree->addTopLevelItem(item); + + pluginNames.clear(); + pluginTitles.clear(); + for (CodeFormatterPlugin* plugin : it.value()) + { + pluginNames << plugin->getName(); + pluginTitles << plugin->getTitle(); + } + sortedPluginNames = pluginNames; + qSort(sortedPluginNames); + + combo = new QComboBox(ui->formatterPluginsTree); + for (int i = 0, total = pluginNames.size(); i < total; ++i) + combo->addItem(pluginTitles[i], pluginNames[i]); + + connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(activeFormatterChanged())); + index = ui->formatterPluginsTree->model()->index(row, 1); + ui->formatterPluginsTree->setIndexWidget(index, combo); + formatterLangToPluginComboMap[groupName] = combo; + + if (activeFormatters.contains(groupName) && pluginNames.contains(activeFormatters[groupName].toString())) + { + selectedPluginName = activeFormatters[groupName].toString(); + } + else + { + // Pick first from sorted list and put it to combobox + selectedPluginName = sortedPluginNames.first(); + } + + configButton = new QToolButton(ui->formatterPluginsTree); + configButton->setIcon(ICONS.CONFIGURE); + index = ui->formatterPluginsTree->model()->index(row, 2); + ui->formatterPluginsTree->setIndexWidget(index, configButton); + connect(configButton, &QToolButton::clicked, [this, combo]() {configureFormatter(combo->currentText());}); + formatterLangToConfigButtonMap[groupName] = configButton; + + combo->setCurrentIndex(pluginNames.indexOf(selectedPluginName)); + + row++; + } + + updateActiveFormatterState(); +} + +void ConfigDialog::applyStyle(QWidget *widget, QStyle *style) +{ + widget->setStyle(style); + foreach (QObject* child, widget->children()) + { + if (!qobject_cast(child)) + continue; + + applyStyle(qobject_cast(child), style); + } +} + +QTreeWidgetItem* ConfigDialog::getPluginsCategoryItem() const +{ + QTreeWidgetItem* item = nullptr; + for (int i = 0; i < ui->categoriesTree->topLevelItemCount(); i++) + { + item = ui->categoriesTree->topLevelItem(i); + if (item->statusTip(0) == ui->pluginsPage->objectName()) + return item; + } + Q_ASSERT_X(true, "ConfigDialog", "No Plugins toplevel item in config categories tree!"); + return nullptr; +} + +QTreeWidgetItem* ConfigDialog::getPluginsCategoryItem(PluginType* type) const +{ + if (!pluginTypeToItemMap.contains(type)) + return nullptr; + + return pluginTypeToItemMap[type]; +} + +QTreeWidgetItem* ConfigDialog::getPluginItem(Plugin* plugin) const +{ + if (!pluginToItemMap.contains(plugin)) + return nullptr; + + return pluginToItemMap[plugin]; +} + +QTreeWidgetItem* ConfigDialog::createPluginsTypeItem(const QString& widgetName, const QString& title) const +{ + if (FORMS->hasWidget(widgetName)) + return new QTreeWidgetItem({title}); + + QTreeWidgetItem* pluginsCategoryItem = getPluginsCategoryItem(); + QTreeWidgetItem* item = nullptr; + for (int i = 0; i < pluginsCategoryItem->childCount(); i++) + { + item = pluginsCategoryItem->child(i); + if (item->statusTip(0) == widgetName) + return item; + } + return nullptr; + +} + +QTreeWidgetItem* ConfigDialog::getItemByTitle(const QString& title) const +{ + QList items = ui->categoriesTree->findItems(title, Qt::MatchExactly|Qt::MatchRecursive); + if (items.size() == 0) + return nullptr; + + return items.first(); +} + +void ConfigDialog::switchPage(QTreeWidgetItem *item) +{ + if (isPluginCategoryItem((item))) + { + switchPageToPlugin(item); + return; + } + + QString name = item->statusTip(0); + if (!nameToPage.contains(name)) + { + qWarning() << "Switched page to item" << name << "but there's no such named page defined in ConfigDialog."; + return; + } + + ui->stackedWidget->setCurrentWidget(nameToPage[name]); +} + +void ConfigDialog::switchPageToPlugin(QTreeWidgetItem *item) +{ + QString pluginName = item->statusTip(0); + if (!nameToPage.contains(pluginName)) + { + qCritical() << "No plugin page available for plugin:" << pluginName; + return; + } + ui->stackedWidget->setCurrentWidget(nameToPage[pluginName]); +} + +void ConfigDialog::initPlugins() +{ + QTreeWidgetItem *item = getPluginsCategoryItem(); + + // Recreate + QTreeWidgetItem *typeItem = nullptr; + foreach (PluginType* pluginType, PLUGINS->getPluginTypes()) + { + typeItem = createPluginsTypeItem(pluginType->getConfigUiForm(), pluginType->getTitle()); + if (!typeItem) + continue; + + item->addChild(typeItem); + pluginTypeToItemMap[pluginType] = typeItem; + + foreach (Plugin* plugin, pluginType->getLoadedPlugins()) + pluginLoaded(plugin, pluginType, true); + } + + updatePluginCategoriesVisibility(); + + connect(PLUGINS, SIGNAL(loaded(Plugin*,PluginType*)), this, SLOT(pluginLoaded(Plugin*,PluginType*))); + connect(PLUGINS, SIGNAL(aboutToUnload(Plugin*,PluginType*)), this, SLOT(pluginAboutToUnload(Plugin*,PluginType*))); +} + +void ConfigDialog::initPluginsPage() +{ + setValidStateTooltip(ui->pluginsList, tr("Plugins are loaded/unloaded immediately when checked/unchecked, " + "but modified list of plugins to load at startup is not saved until " + "you commit the whole configuration dialog.")); + + QTreeWidgetItem* category = nullptr; + QTreeWidgetItem* item = nullptr; + QFont font; + QModelIndex categoryIndex; + QModelIndex itemIndex; + int itemRow; + int categoryRow; + bool builtIn; + QLabel* detailsLabel = nullptr; + QString title; + QSize itemSize; + QStringList pluginNames; + + // Font and metrics + item = new QTreeWidgetItem({""}); + font = item->font(0); + + QFontMetrics fm(font); + itemSize = QSize(-1, (fm.ascent() + fm.descent() + 4)); + + delete item; + + // Creating... + ui->pluginsList->header()->setSectionsMovable(false); + ui->pluginsList->header()->setSectionResizeMode(0, QHeaderView::Stretch); + + QBrush categoryBg = ui->pluginsList->palette().button(); + QBrush categoryFg = ui->pluginsList->palette().buttonText(); + + connect(ui->pluginsList, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(loadUnloadPlugin(QTreeWidgetItem*,int))); + connect(PLUGINS, SIGNAL(failedToLoad(QString)), this, SLOT(failedToLoadPlugin(QString))); + + categoryRow = 0; + QList pluginTypes = PLUGINS->getPluginTypes(); + qSort(pluginTypes.begin(), pluginTypes.end(), PluginType::nameLessThan); + foreach (PluginType* pluginType, pluginTypes) + { + category = new QTreeWidgetItem({pluginType->getTitle()}); + font.setItalic(false); + font.setBold(true); + category->setFont(0, font); + for (int i = 0; i < 2; i++) + { + category->setBackground(i, categoryBg); + category->setForeground(i, categoryFg); + } + category->setSizeHint(0, itemSize); + ui->pluginsList->addTopLevelItem(category); + + categoryIndex = ui->pluginsList->model()->index(categoryRow, 0); + categoryRow++; + + itemRow = 0; + pluginNames = pluginType->getAllPluginNames(); + qSort(pluginNames); + foreach (const QString& pluginName, pluginNames) + { + builtIn = PLUGINS->isBuiltIn(pluginName); + title = PLUGINS->getTitle(pluginName); + if (builtIn) + title += tr(" (built-in)", "plugins manager in configuration dialog"); + + item = new QTreeWidgetItem({title}); + item->setCheckState(0, PLUGINS->isLoaded(pluginName) ? Qt::Checked : Qt::Unchecked); + item->setSizeHint(0, itemSize); + if (builtIn) + item->setDisabled(true); + + category->addChild(item); + + itemToPluginNameMap.insert(item, pluginName); + + // Details button + detailsLabel = new QLabel(QString("%2 ").arg(pluginName).arg(tr("Details")), ui->pluginsList); + detailsLabel->setAlignment(Qt::AlignRight); + itemIndex = ui->pluginsList->model()->index(itemRow, 1, categoryIndex); + ui->pluginsList->setIndexWidget(itemIndex, detailsLabel); + + connect(detailsLabel, SIGNAL(linkActivated(QString)), this, SLOT(detailsClicked(QString))); + + itemRow++; + } + + if (itemRow == 0) + { + item = new QTreeWidgetItem({tr("No plugins in this category.")}); + item->setDisabled(true); + item->setSizeHint(0, itemSize); + + font.setItalic(true); + font.setBold(false); + item->setFont(0, font); + + category->addChild(item); + } + + category->setExpanded(true); + } +} + +bool ConfigDialog::initPluginPage(Plugin* plugin, bool skipConfigLoading) +{ + if (!dynamic_cast(plugin)) + return false; + + UiConfiguredPlugin* cfgPlugin = dynamic_cast(plugin); + QString pluginName = plugin->getName(); + QString formName = cfgPlugin->getConfigUiForm(); + QWidget* widget = FORMS->createWidget(formName); + if (!widget) + { + qWarning() << "Could not load plugin UI file" << formName << "for plugin:" << pluginName; + return false; + } + + nameToPage[pluginName] = widget; + ui->stackedWidget->addWidget(widget); + CfgMain* mainConfig = cfgPlugin->getMainUiConfig(); + if (mainConfig) + { + pluginConfigMappers[cfgPlugin] = new ConfigMapper(mainConfig); + pluginConfigMappers[cfgPlugin]->bindToConfig(widget); + mainConfig->begin(); + } + else if (!skipConfigLoading) + { + configMapper->loadToWidget(widget); + } + + cfgPlugin->configDialogOpen(); + return true; +} + +void ConfigDialog::deinitPluginPage(Plugin* plugin) +{ + QString pluginName = plugin->getName(); + if (!nameToPage.contains(pluginName)) + return; + + if (!dynamic_cast(plugin)) + { + UiConfiguredPlugin* cfgPlugin = dynamic_cast(plugin); + CfgMain* mainCfg = cfgPlugin->getMainUiConfig(); + if (mainCfg) + mainCfg->rollback(); + + cfgPlugin->configDialogClosed(); + + if (pluginConfigMappers.contains(cfgPlugin)) + { + delete pluginConfigMappers[cfgPlugin]; + pluginConfigMappers.remove(cfgPlugin); + } + } + + QWidget* widget = nameToPage[pluginName]; + nameToPage.remove(pluginName); + ui->stackedWidget->removeWidget(widget); + delete widget; +} + +void ConfigDialog::initDataEditors() +{ + ui->dataEditorsAvailableList->setSpacing(1); + + QHash editorsOrder = CFG_UI.General.DataEditorsOrder.get(); + QSet dataTypeSet = editorsOrder.keys().toSet(); + dataTypeSet += DataType::getAllNames().toSet(); + QStringList dataTypeList = dataTypeSet.toList(); + qSort(dataTypeList); + + QListWidgetItem* item = nullptr; + for (const QString& type : dataTypeList) + { + item = new QListWidgetItem(type); + if (!DataType::getAllNames().contains(type)) + item->setFlags(item->flags()|Qt::ItemIsEditable); + + ui->dataEditorsTypesList->addItem(item); + } + + QAction* act = new QAction(ICONS.INSERT_DATATYPE, tr("Add new data type"), ui->dataEditorsTypesToolbar); + connect(act, SIGNAL(triggered()), this, SLOT(addDataType())); + ui->dataEditorsTypesToolbar->addAction(act); + + dataEditRenameAction = new QAction(ICONS.RENAME_DATATYPE, tr("Rename selected data type"), ui->dataEditorsTypesToolbar); + connect(dataEditRenameAction, SIGNAL(triggered()), this, SLOT(renameDataType())); + ui->dataEditorsTypesToolbar->addAction(dataEditRenameAction); + + dataEditDeleteAction = new QAction(ICONS.DELETE_DATATYPE, tr("Delete selected data type"), ui->dataEditorsTypesToolbar); + connect(dataEditDeleteAction, SIGNAL(triggered()), this, SLOT(delDataType())); + ui->dataEditorsTypesToolbar->addAction(dataEditDeleteAction); + + act = new QAction(ICONS.HELP, tr("Help for configuring data type editors"), ui->dataEditorsTypesToolbar); + connect(act, SIGNAL(triggered()), this, SLOT(dataTypesHelp())); + ui->dataEditorsTypesToolbar->addAction(act); + + connect(ui->dataEditorsTypesList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateDataTypeEditors())); + connect(ui->dataEditorsTypesList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateDataTypeListState())); + connect(ui->dataEditorsTypesList, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(dataEditorItemEdited(QListWidgetItem*))); + connect(ui->dataEditorsAvailableList, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(dataEditorAvailableChanged(QListWidgetItem*))); + connect(ui->dataEditorsSelectedTabs->tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(dataEditorTabsOrderChanged(int,int))); + + ui->dataEditorsTypesList->setCurrentRow(0, QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent); + updateDataTypeListState(); +} + +void ConfigDialog::initShortcuts() +{ + ui->shortcutsTable->header()->setSectionsMovable(false); + ui->shortcutsTable->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->shortcutsTable->header()->setSectionResizeMode(1, QHeaderView::Fixed); + ui->shortcutsTable->header()->setSectionResizeMode(2, QHeaderView::Fixed); + ui->shortcutsTable->header()->resizeSection(1, 150); + ui->shortcutsTable->header()->resizeSection(2, 26); + + ui->shortcutsFilterEdit->setClearButtonEnabled(true); + new UserInputFilter(ui->shortcutsFilterEdit, this, SLOT(applyShortcutsFilter(QString))); + + static const QString metaName = CFG_SHORTCUTS_METANAME; + QList categories; + for (CfgMain* cfgMain : CfgMain::getInstances()) + { + if (cfgMain->getMetaName() != metaName) + continue; + + for (CfgCategory* cat : cfgMain->getCategories().values()) + categories << cat; + } + + qSort(categories.begin(), categories.end(), [](CfgCategory* cat1, CfgCategory* cat2) -> bool + { + return cat1->getTitle().compare(cat2->getTitle()) < 0; + }); + + for (CfgCategory* cat : categories) + initShortcuts(cat); +} + +void ConfigDialog::initShortcuts(CfgCategory *cfgCategory) +{ + QTreeWidgetItem* item = nullptr; + QFont font; + QModelIndex categoryIndex; + QModelIndex itemIndex; + QKeySequenceEdit *sequenceEdit = nullptr; + QToolButton* clearButton = nullptr; + QString title; + QSize itemSize; + + // Font and metrics + item = new QTreeWidgetItem({""}); + font = item->font(0); + + QFontMetrics fm(font); + itemSize = QSize(-1, (fm.ascent() + fm.descent() + 4)); + + delete item; + + // Creating... + QBrush categoryBg = ui->shortcutsTable->palette().button(); + QBrush categoryFg = ui->shortcutsTable->palette().buttonText(); + + QTreeWidgetItem* category = new QTreeWidgetItem({cfgCategory->getTitle()}); + font.setItalic(false); + font.setBold(true); + category->setFont(0, font); + for (int i = 0; i < 3; i++) + { + category->setBackground(i, categoryBg); + category->setForeground(i, categoryFg); + } + category->setSizeHint(0, itemSize); + category->setFlags(category->flags() ^ Qt::ItemIsSelectable); + ui->shortcutsTable->addTopLevelItem(category); + + int categoryRow = ui->shortcutsTable->topLevelItemCount() - 1; + categoryIndex = ui->shortcutsTable->model()->index(categoryRow, 0); + + int itemRow = 0; + QStringList entryNames = cfgCategory->getEntries().keys(); + qSort(entryNames); + foreach (const QString& entryName, entryNames) + { + // Title + title = cfgCategory->getEntries()[entryName]->getTitle(); + item = new QTreeWidgetItem(category, {title}); + + // Key edit + sequenceEdit = new QKeySequenceEdit(ui->shortcutsTable); + sequenceEdit->setFixedWidth(150); + sequenceEdit->setProperty("cfg", cfgCategory->getEntries()[entryName]->getFullKey()); + itemIndex = ui->shortcutsTable->model()->index(itemRow, 1, categoryIndex); + ui->shortcutsTable->setIndexWidget(itemIndex, sequenceEdit); + configMapper->addExtraWidget(sequenceEdit); + + // Clear button + clearButton = new QToolButton(ui->shortcutsTable); + clearButton->setIcon(ICONS.CLEAR_LINEEDIT); + connect(clearButton, &QToolButton::clicked, [this, sequenceEdit]() + { + sequenceEdit->clear(); + this->markModified(); + + }); + itemIndex = ui->shortcutsTable->model()->index(itemRow, 2, categoryIndex); + ui->shortcutsTable->setIndexWidget(itemIndex, clearButton); + + itemRow++; + } + + category->setExpanded(true); +} + +bool ConfigDialog::isPluginCategoryItem(QTreeWidgetItem *item) const +{ + return item->parent() && item->parent()->parent() && item->parent()->parent() == getPluginsCategoryItem(); +} + +void ConfigDialog::updateStylePreview() +{ + ui->previewWidget->parentWidget()->layout()->removeWidget(ui->previewWidget); + ui->previewTabs->currentWidget()->layout()->addWidget(ui->previewWidget); + ui->previewWidget->setEnabled(ui->previewTabs->currentIndex() == 0); + + QStyle* previousStyle = previewStyle; + previewStyle = QStyleFactory::create(ui->activeStyleCombo->currentText()); + if (!previewStyle) + { + qWarning() << "Could not create style:" << ui->activeStyleCombo->currentText(); + return; + } + + applyStyle(ui->activeStylePreviewGroup, previewStyle); + + if (previousStyle) + delete previousStyle; +} + +void ConfigDialog::apply() +{ + if (modifiedFlag) + save(); + + setModified(false); +} + +void ConfigDialog::accept() +{ + apply(); + QDialog::accept(); +} -- cgit v1.2.3