Loading files
acb84dbb7This file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "workspacesettingsdialog.h"
#include "../dialogs/filedialog.h"
#include "../editorcommandset.h"
#include "../modelview/apiendpointlistmodellegacy.h"
#include "../modelview/comboboxdelegate.h"
#include "../modelview/editablelistmodel.h"
#include "../modelview/keyboardshortcutsmodel.h"
#include "../modelview/keysequencedelegate.h"
#include "../utils/editortoolbox.h"
#include "desktopintegration.h"
#include "desktopservices.h"
#include "ui_workspacesettingsdialog.h"
#include <librepcb/core/application.h>
#include <librepcb/core/norms.h>
#include <librepcb/core/utils/toolbox.h>
#include <librepcb/core/workspace/uitheme.h>
#include <librepcb/core/workspace/workspace.h>
#include <librepcb/core/workspace/workspacesettings.h>
#include <QtCore>
#include <QtWidgets>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
WorkspaceSettingsDialog::WorkspaceSettingsDialog(Workspace& workspace,
QWidget* parent)
: QDialog(parent),
mWorkspace(workspace),
mSettings(workspace.getSettings()),
mLibLocaleOrderModel(new LibraryLocaleOrderModel()),
mLibNormOrderModel(new LibraryNormOrderModel()),
mApiEndpointModel(new ApiEndpointListModelLegacy()),
mKeyboardShortcutsModel(new KeyboardShortcutsModel(this)),
mKeyboardShortcutsFilterModel(new QSortFilterProxyModel(this)),
mUi(new Ui::WorkspaceSettingsDialog) {
mUi->setupUi(this);
discardTemporaryModifications();
const EditorCommandSet& cmd = EditorCommandSet::instance();
// Initialize UI theme widgets
{
mUi->cbxUiTheme->addItem(tr("System Theme"), QString(""));
for (const UiTheme* theme : UiTheme::all()) {
mUi->cbxUiTheme->addItem(theme->getNameTr(), theme->id);
}
// Apply this setting immediately.
connect(mUi->cbxUiTheme, &QComboBox::currentIndexChanged, this,
[this](int i) {
if (i >= 0) {
mSettings.uiTheme.set(mUi->cbxUiTheme->itemData(i).toString());
}
});
// Link to color schemes tab.
connect(mUi->lblSeeColorSchemes, &QLabel::linkActivated, this,
[this]() { mUi->tabWidget->setCurrentWidget(mUi->themesTab); });
}
// Initialize application locale widgets
{
mUi->cbxAppLocale->addItem(tr("System Language"), QString(""));
QMap<QString, QString> map; // map will be sorted by key
foreach (const QString& locale, Application::getTranslationLocales()) {
map.insert(Toolbox::prettyPrintLocale(locale), locale);
}
QMap<QString, QString>::const_iterator i = map.constBegin();
while (i != map.constEnd()) {
mUi->cbxAppLocale->addItem(i.key(), i.value());
++i;
}
// Apply this setting immediately.
connect(mUi->cbxAppLocale, &QComboBox::currentIndexChanged,
[this](int index) {
if (index >= 0) {
mSettings.applicationLocale.set(
mUi->cbxAppLocale->itemData(index).toString());
}
});
}
// Initialize desktop integration widgets
if (DesktopIntegration::isSupported()) {
connect(mUi->btnInstallApp, &QPushButton::clicked, this, [this]() {
DesktopIntegration::execDialog(DesktopIntegration::Mode::Install, this);
updateDesktopIntegrationStatus();
emit desktopIntegrationStatusChanged();
});
connect(mUi->btnUninstallApp, &QPushButton::clicked, this, [this]() {
DesktopIntegration::execDialog(DesktopIntegration::Mode::Uninstall, this);
updateDesktopIntegrationStatus();
emit desktopIntegrationStatusChanged();
});
updateDesktopIntegrationStatus();
} else {
EditorToolbox::removeFormLayoutRow(*mUi->lblDesktopIntegration);
}
// Initialize "reset dismissed messages" button
updateDismissedMessagesCount();
connect(mUi->btnResetDismissedMessages, &QPushButton::clicked, this,
[this]() {
try {
mSettings.dismissedMessages.restoreDefault();
mWorkspace.saveSettings(); // can throw
} catch (const Exception& e) {
QMessageBox::critical(this, tr("Error"), e.getMsg());
}
updateDismissedMessagesCount();
});
// Initialize library locale order widgets
{
QList<QLocale> locales = QLocale::matchingLocales(
QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
QStringList localesStr;
foreach (const QLocale& l, locales) {
localesStr.append(l.name());
}
mLibLocaleOrderModel->setPlaceholderText(tr("Click here to add a locale"));
mLibLocaleOrderModel->setDefaultValue(QString(""));
mLibLocaleOrderModel->setChoices(localesStr);
mUi->tblLibLocaleOrder->setShowMoveButtons(true);
mUi->tblLibLocaleOrder->setModel(mLibLocaleOrderModel.data());
mUi->tblLibLocaleOrder->setItemDelegateForColumn(
LibraryLocaleOrderModel::COLUMN_TEXT,
new ComboBoxDelegate(false, this));
mUi->tblLibLocaleOrder->horizontalHeader()->setSectionResizeMode(
LibraryLocaleOrderModel::COLUMN_TEXT, QHeaderView::Stretch);
mUi->tblLibLocaleOrder->horizontalHeader()->setSectionResizeMode(
LibraryLocaleOrderModel::COLUMN_ACTIONS, QHeaderView::ResizeToContents);
connect(mUi->tblLibLocaleOrder, &EditableTableWidget::btnAddClicked,
mLibLocaleOrderModel.data(), &LibraryLocaleOrderModel::add);
connect(mUi->tblLibLocaleOrder, &EditableTableWidget::btnRemoveClicked,
mLibLocaleOrderModel.data(), &LibraryLocaleOrderModel::remove);
connect(mUi->tblLibLocaleOrder, &EditableTableWidget::btnMoveUpClicked,
mLibLocaleOrderModel.data(), &LibraryLocaleOrderModel::moveUp);
connect(mUi->tblLibLocaleOrder, &EditableTableWidget::btnMoveDownClicked,
mLibLocaleOrderModel.data(), &LibraryLocaleOrderModel::moveDown);
}
// Initialize library norm order widgets
{
mLibNormOrderModel->setPlaceholderText(tr("Click here to add a norm"));
mLibNormOrderModel->setDefaultValue(QString(""));
mLibNormOrderModel->setChoices(getAvailableNorms());
mUi->tblLibNormOrder->setShowMoveButtons(true);
mUi->tblLibNormOrder->setModel(mLibNormOrderModel.data());
mUi->tblLibNormOrder->setItemDelegateForColumn(
LibraryNormOrderModel::COLUMN_TEXT, new ComboBoxDelegate(true, this));
mUi->tblLibNormOrder->horizontalHeader()->setSectionResizeMode(
LibraryNormOrderModel::COLUMN_TEXT, QHeaderView::Stretch);
mUi->tblLibNormOrder->horizontalHeader()->setSectionResizeMode(
LibraryNormOrderModel::COLUMN_ACTIONS, QHeaderView::ResizeToContents);
connect(mUi->tblLibNormOrder, &EditableTableWidget::btnAddClicked,
mLibNormOrderModel.data(), &LibraryNormOrderModel::add);
connect(mUi->tblLibNormOrder, &EditableTableWidget::btnRemoveClicked,
mLibNormOrderModel.data(), &LibraryNormOrderModel::remove);
connect(mUi->tblLibNormOrder, &EditableTableWidget::btnMoveUpClicked,
mLibNormOrderModel.data(), &LibraryNormOrderModel::moveUp);
connect(mUi->tblLibNormOrder, &EditableTableWidget::btnMoveDownClicked,
mLibNormOrderModel.data(), &LibraryNormOrderModel::moveDown);
}
// Initialize API endpoint widgets
{
mUi->edtApiEndpoints->setShowMoveButtons(true);
mUi->edtApiEndpoints->setModel(mApiEndpointModel.data());
mUi->edtApiEndpoints->horizontalHeader()->setSectionResizeMode(
ApiEndpointListModelLegacy::COLUMN_URL, QHeaderView::Stretch);
mUi->edtApiEndpoints->horizontalHeader()->setSectionResizeMode(
ApiEndpointListModelLegacy::COLUMN_ACTIONS,
QHeaderView::ResizeToContents);
connect(mUi->edtApiEndpoints, &EditableTableWidget::btnAddClicked,
mApiEndpointModel.data(), &ApiEndpointListModelLegacy::add);
connect(mUi->edtApiEndpoints, &EditableTableWidget::btnRemoveClicked,
mApiEndpointModel.data(), &ApiEndpointListModelLegacy::remove);
connect(mUi->edtApiEndpoints, &EditableTableWidget::btnMoveUpClicked,
mApiEndpointModel.data(), &ApiEndpointListModelLegacy::moveUp);
connect(mUi->edtApiEndpoints, &EditableTableWidget::btnMoveDownClicked,
mApiEndpointModel.data(), &ApiEndpointListModelLegacy::moveDown);
connect(mUi->lblApiEndpointsInfo, &QLabel::linkActivated, this,
[this](const QString& url) {
DesktopServices ds(mWorkspace.getSettings());
ds.openWebUrl(QUrl(url));
});
}
// Initialize external applications widgets
{
auto placeholderFilePath =
std::make_pair(QString("{{FILEPATH}}"),
tr("Absolute path to the file to open",
"Decription for '{{FILEPATH}}' placeholder"));
auto placeholderUrl =
std::make_pair(QString("{{URL}}"),
tr("URL to the file to open (file://)",
"Decription for '{{URL}}' placeholder"));
connect(mUi->lstExternalApplications, &QListWidget::currentRowChanged, this,
&WorkspaceSettingsDialog::externalApplicationListIndexChanged);
mUi->lstExternalApplications->addItem(new QListWidgetItem(
QIcon(":/img/actions/open_browser.png"), tr("Web Browser")));
mExternalApplications.append(ExternalApplication{
&mSettings.externalWebBrowserCommands,
"firefox",
"\"{{URL}}\"",
{std::make_pair(
QString("{{URL}}"),
tr("Website URL to open", "Decription for '{{URL}}' placeholder"))},
{},
});
mUi->lstExternalApplications->addItem(new QListWidgetItem(
QIcon(":/img/actions/open.png"), tr("File Manager")));
mExternalApplications.append(ExternalApplication{
&mSettings.externalFileManagerCommands,
"explorer",
"\"{{FILEPATH}}\"",
{placeholderFilePath, placeholderUrl},
{},
});
mUi->lstExternalApplications->addItem(
new QListWidgetItem(QIcon(":/img/actions/pdf.png"), tr("PDF Reader")));
mExternalApplications.append(ExternalApplication{
&mSettings.externalPdfReaderCommands,
"evince",
"\"{{FILEPATH}}\"",
{placeholderFilePath, placeholderUrl},
{},
});
mUi->lstExternalApplications->setMinimumWidth(
mUi->lstExternalApplications->sizeHintForColumn(0) + 20);
mUi->lstExternalApplications->setCurrentRow(0);
}
// Initialize keyboard shortcuts widgets
{
mKeyboardShortcutsFilterModel->setSourceModel(
mKeyboardShortcutsModel.data());
mKeyboardShortcutsFilterModel->setFilterCaseSensitivity(
Qt::CaseInsensitive);
mKeyboardShortcutsFilterModel->setFilterKeyColumn(-1); // All columns.
mKeyboardShortcutsFilterModel->setRecursiveFilteringEnabled(true);
connect(mUi->edtCommandFilter, &QLineEdit::textChanged,
mKeyboardShortcutsFilterModel.data(),
&QSortFilterProxyModel::setFilterFixedString);
connect(mUi->edtCommandFilter, &QLineEdit::textChanged,
mUi->treeKeyboardShortcuts, &QTreeView::expandAll);
mUi->treeKeyboardShortcuts->setModel(mKeyboardShortcutsFilterModel.data());
mUi->treeKeyboardShortcuts->header()->setMinimumSectionSize(
QKeySequenceEdit().sizeHint().width());
mUi->treeKeyboardShortcuts->header()->setSectionResizeMode(
0, QHeaderView::ResizeToContents);
mUi->treeKeyboardShortcuts->header()->setSectionResizeMode(
1, QHeaderView::Stretch);
mUi->treeKeyboardShortcuts->header()->setSectionResizeMode(
2, QHeaderView::ResizeToContents);
KeySequenceDelegate* delegate = new KeySequenceDelegate(this);
mUi->treeKeyboardShortcuts->setItemDelegateForColumn(2, delegate);
mUi->treeKeyboardShortcuts->addAction(cmd.find.createAction(
this, this,
[this]() { mUi->edtCommandFilter->setFocus(Qt::ShortcutFocusReason); },
EditorCommand::ActionFlag::WidgetShortcut));
}
// Initialize themes widgets.
{
for (QComboBox* cbx :
{mUi->cbxSchematicGridStyle, mUi->cbxBoardGridStyle}) {
cbx->addItem(tr("None", "Grid style"),
QVariant::fromValue(Theme::GridStyle::None));
cbx->addItem(tr("Dots", "Grid style"),
QVariant::fromValue(Theme::GridStyle::Dots));
cbx->addItem(tr("Lines", "Grid style"),
QVariant::fromValue(Theme::GridStyle::Lines));
}
auto askName = [this](const QString& title, const QString& defaultName) {
return QInputDialog::getText(this, title, tr("Name:"), QLineEdit::Normal,
defaultName);
};
connect(
mUi->cbxThemes,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &WorkspaceSettingsDialog::themeIndexChanged);
connect(mUi->btnRemoveTheme, &QToolButton::clicked, this, [this]() {
if (Theme* theme = getCurrentTheme()) {
mThemes.remove(theme->getUuid());
updateThemesList(mSettings.themes.getActiveUuid());
}
});
connect(mUi->btnRenameTheme, &QToolButton::clicked, this,
[this, askName]() {
if (Theme* theme = getCurrentTheme()) {
const QString name =
askName(mUi->btnRenameTheme->text(), theme->getName());
if (!name.isEmpty()) {
theme->setName(name);
updateThemesList(theme->getUuid());
}
}
});
connect(mUi->btnResetTheme, &QToolButton::clicked, this, [this]() {
if (Theme* theme = getCurrentTheme()) {
theme->restoreDefaults();
updateThemesList(theme->getUuid());
}
});
connect(mUi->btnDuplicateTheme, &QToolButton::clicked, this,
[this, askName]() {
if (Theme* theme = getCurrentTheme()) {
const QString name =
askName(mUi->btnDuplicateTheme->text(),
tr("Copy of %1").arg(theme->getName()));
if (!name.isEmpty()) {
const Theme copy(Uuid::createRandom(), name, *theme);
mThemes.insert(copy.getUuid(), copy);
updateThemesList(copy.getUuid());
}
}
});
connect(mUi->btnNewTheme, &QToolButton::clicked, this, [this, askName]() {
const QString name = askName(mUi->btnNewTheme->text(), tr("New Theme"));
if (!name.isEmpty()) {
const Theme theme(Uuid::createRandom(), name);
mThemes.insert(theme.getUuid(), theme);
updateThemesList(theme.getUuid());
}
});
mUi->treeThemeColors->header()->setSectionsMovable(false);
mUi->treeThemeColors->header()->setSectionResizeMode(
0, QHeaderView::ResizeToContents);
mUi->treeThemeColors->header()->setSectionResizeMode(
1, QHeaderView::ResizeToContents);
mUi->treeThemeColors->header()->setSectionResizeMode(
2, QHeaderView::ResizeToContents);
mUi->treeThemeColors->header()->setSectionResizeMode(3,
QHeaderView::Stretch);
connect(mUi->treeThemeColors, &QTreeWidget::itemDoubleClicked, this,
[this](QTreeWidgetItem* item, int column) {
Theme* theme = getCurrentTheme();
if ((!item) || (column < 0) || (column > 1) || (!theme)) return;
QList<ThemeColor> colors = theme->getColors();
const int index = mUi->treeThemeColors->indexOfTopLevelItem(item);
if ((index < 0) || (index >= colors.count())) return;
QColor value = (column == 1) ? colors[index].getSecondaryColor()
: colors[index].getPrimaryColor();
if (!value.isValid()) return;
value = QColorDialog::getColor(value, this, QString(),
QColorDialog::ShowAlphaChannel);
if (!value.isValid()) return;
if (column == 1) {
colors[index].setSecondaryColor(value);
} else {
colors[index].setPrimaryColor(value);
}
theme->setColors(colors);
initColorTreeWidgetItem(*item, colors[index]);
});
for (auto cfg : {
std::make_pair(mUi->cbxSchematicGridStyle,
&Theme::setSchematicGridStyle),
std::make_pair(mUi->cbxBoardGridStyle, &Theme::setBoardGridStyle),
}) {
connect(cfg.first,
static_cast<void (QComboBox::*)(int)>(
&QComboBox::currentIndexChanged),
this, [this, cfg](int index) {
if (Theme* theme = getCurrentTheme()) {
const Theme::GridStyle style =
cfg.first->itemData(index).value<Theme::GridStyle>();
(theme->*cfg.second)(style);
}
});
}
}
// Now load all current settings
loadSettings();
// Load client settings.
QSettings cs;
const QSize windowSize =
cs.value("workspace_settings_dialog/window_size").toSize();
if (!windowSize.isEmpty()) {
resize(windowSize);
}
// Just in case that the wrong tab is selected in the UI designer:
mUi->tabWidget->setCurrentIndex(0);
// Connect event handlers
connect(mUi->buttonBox, &QDialogButtonBox::clicked, this,
&WorkspaceSettingsDialog::buttonBoxClicked);
}
WorkspaceSettingsDialog::~WorkspaceSettingsDialog() {
QSettings cs;
cs.setValue("workspace_settings_dialog/window_size", size());
}
/*******************************************************************************
* Private Slots for the GUI elements
******************************************************************************/
void WorkspaceSettingsDialog::buttonBoxClicked(
QAbstractButton* button) noexcept {
switch (mUi->buttonBox->buttonRole(button)) {
case QDialogButtonBox::RejectRole: {
reject();
break;
}
case QDialogButtonBox::AcceptRole: {
saveSettings();
accept();
break;
}
case QDialogButtonBox::ApplyRole: {
saveSettings();
break;
}
case QDialogButtonBox::ResetRole: {
int answer = QMessageBox::question(
this, tr("Restore default settings"),
tr("Are you sure to reset all settings to their default values?\n\n"
"Attention: This will be applied immediately and cannot be "
"undone!"));
if (answer == QMessageBox::Yes) {
mSettings.restoreDefaults();
loadSettings(); // updating all widgets with the new values
saveSettings(); // save now since "cancel" does not revert!
}
break;
}
default:
Q_ASSERT(false);
break;
}
}
void WorkspaceSettingsDialog::keyPressEvent(QKeyEvent* event) noexcept {
// If the keyboard shortcuts tab is opened and a filter is active, discard
// the filter with the escape key.
if ((event->key() == Qt::Key_Escape) &&
(mUi->tabWidget->currentWidget() == mUi->keyboardShortcutsTab) &&
(!mUi->edtCommandFilter->text().isEmpty())) {
mUi->edtCommandFilter->clear();
return;
}
QDialog::keyPressEvent(event);
}
void WorkspaceSettingsDialog::changeEvent(QEvent* event) noexcept {
if (event->type() == QEvent::PaletteChange) {
// Workaround to avoid outdated hyperlink color.
mUi->lblSeeColorSchemes->setText("");
mUi->retranslateUi(this);
} else if (event->type() == QEvent::LanguageChange) {
mUi->retranslateUi(this);
}
QDialog::changeEvent(event);
}
void WorkspaceSettingsDialog::reject() noexcept {
revertTemporaryModifications();
QDialog::reject();
}
void WorkspaceSettingsDialog::externalApplicationListIndexChanged(
int index) noexcept {
if ((index < 0) || (index >= mExternalApplications.count())) {
return;
}
while (mUi->layoutExternalApplicationCommands->count() > 0) {
QLayoutItem* item = mUi->layoutExternalApplicationCommands->takeAt(0);
Q_ASSERT(item);
EditorToolbox::deleteLayoutItemRecursively(item);
}
const EditorCommandSet& cmd = EditorCommandSet::instance();
QStringList commands = mExternalApplications[index].currentValue;
for (int i = 0; i <= commands.count(); ++i) {
QLineEdit* edit = new QLineEdit(commands.value(i), this);
edit->setPlaceholderText(
tr("Example:") % " " % mExternalApplications[index].exampleExecutable %
" " % mExternalApplications[index].defaultArgument);
if (i < commands.count()) {
connect(edit, &QLineEdit::textChanged, edit, [this, index, edit, i]() {
mExternalApplications[index].currentValue.replace(i, edit->text());
});
} else {
connect(edit, &QLineEdit::editingFinished, edit, [this, index, edit]() {
if (!edit->text().isEmpty()) {
mExternalApplications[index].currentValue.append(edit->text());
}
});
connect(
edit, &QLineEdit::editingFinished, edit,
[this]() {
externalApplicationListIndexChanged(
mUi->lstExternalApplications->currentRow());
},
Qt::QueuedConnection);
}
mUi->layoutExternalApplicationCommands->addWidget(edit);
// Add 'browse' action.
QAction* aBrowse = cmd.inputBrowse.createAction(
edit, this,
[this, edit, index]() {
QString fp = FileDialog::getOpenFileName(
this, tr("Select executable"), QDir::rootPath());
if (!fp.isEmpty()) {
edit->setText(fp % " " %
mExternalApplications[index].defaultArgument);
emit edit->editingFinished();
}
},
EditorCommand::ActionFlag::WidgetShortcut);
edit->addAction(aBrowse, QLineEdit::TrailingPosition);
// Add 'remove' action.
if (i < commands.count()) {
QAction* aRemove = cmd.inputRemove.createAction(
edit, this,
[this, index, i]() {
mExternalApplications[index].currentValue.removeAt(i);
externalApplicationListIndexChanged(index);
},
EditorCommand::ActionFlag::WidgetShortcut |
EditorCommand::ActionFlag::QueuedConnection);
edit->addAction(aRemove, QLineEdit::TrailingPosition);
}
}
QString placeholdersText =
"<p>" % tr("Available placeholders:") % "</p><p><ul>";
foreach (const auto& p, mExternalApplications[index].placeholders) {
placeholdersText += "<li><tt>" % p.first % "</tt>: " % p.second % "</li>";
}
placeholdersText += "</ul></p>";
mUi->lblExternalApplicationsPlaceholders->setText(placeholdersText);
}
void WorkspaceSettingsDialog::updateThemesList(
const Uuid& selectedTheme) noexcept {
mUi->cbxThemes->clear();
foreach (const Theme& theme, mThemes) {
mUi->cbxThemes->addItem(theme.getName(), theme.getUuid().toStr());
}
const int index =
std::max(0, mUi->cbxThemes->findData(selectedTheme.toStr()));
mUi->cbxThemes->setCurrentIndex(index);
}
void WorkspaceSettingsDialog::themeIndexChanged(int index) noexcept {
const bool valid = (index >= 0) && (index < mThemes.count());
const Theme theme = mThemes.values().value(index);
mUi->btnRemoveTheme->setEnabled(valid);
mUi->btnRenameTheme->setEnabled(valid);
mUi->btnResetTheme->setEnabled(valid);
mUi->btnDuplicateTheme->setEnabled(valid);
// Colors.
mUi->treeThemeColors->clear();
for (int i = 0; i < theme.getColors().count(); ++i) {
QTreeWidgetItem* item = new QTreeWidgetItem(mUi->treeThemeColors);
initColorTreeWidgetItem(*item, theme.getColors().at(i));
}
mUi->treeThemeColors->setEnabled(valid);
// Grid style.
for (const auto& cfg : {
std::make_pair(mUi->cbxSchematicGridStyle,
theme.getSchematicGridStyle()),
std::make_pair(mUi->cbxBoardGridStyle, theme.getBoardGridStyle()),
}) {
const int index = cfg.first->findData(QVariant::fromValue(cfg.second));
cfg.first->setCurrentIndex(index);
cfg.first->setEnabled(valid);
}
}
void WorkspaceSettingsDialog::initColorTreeWidgetItem(
QTreeWidgetItem& item, const ThemeColor& color) noexcept {
auto init = [&item](int column, const QString& toolTip, const QColor& value) {
item.setBackground(column, value.isValid() ? value : Qt::transparent);
item.setToolTip(
column,
toolTip.arg(value.isValid() ? value.name(QColor::HexArgb).toUpper()
: tr("N/A")));
item.setText(column, value.isValid() ? "" : "✖");
item.setTextAlignment(column, Qt::AlignCenter);
};
init(0, tr("Primary color: %1"), color.getPrimaryColor());
init(1, tr("Secondary color: %1"), color.getSecondaryColor());
item.setText(2, color.getCategoryTr());
item.setText(3, color.getNameTr());
}
Theme* WorkspaceSettingsDialog::getCurrentTheme() noexcept {
const int index = mUi->cbxThemes->currentIndex();
const std::optional<Uuid> uuid =
Uuid::tryFromString(mUi->cbxThemes->itemData(index).toString());
return (uuid && mThemes.contains(*uuid)) ? &mThemes[*uuid] : nullptr;
}
void WorkspaceSettingsDialog::updateDismissedMessagesCount() noexcept {
const int count = mSettings.dismissedMessages.get().count();
mUi->btnResetDismissedMessages->setText(tr("Reset") % " (" %
QString::number(count) % ")");
mUi->btnResetDismissedMessages->setToolTip(
tr("Reset all permanently dismissed messages (something like \"do not "
"show again\") to make them appear again.") %
"\n\n" %
tr("Currently there are %1 dismissed message(s).", nullptr, count)
.arg(count));
}
void WorkspaceSettingsDialog::updateDesktopIntegrationStatus() noexcept {
const DesktopIntegration::Status status = DesktopIntegration::getStatus();
switch (status) {
case DesktopIntegration::Status::InstalledThis:
mUi->lblDesktopIntegrationStatus->setText(
QString("<font color=\"#62de70\">✔ %1</font>").arg(tr("Installed")));
break;
case DesktopIntegration::Status::InstalledOther:
mUi->lblDesktopIntegrationStatus->setText(
QString("<font color=\"orange\">✖ %1</font>")
.arg(tr("Other application installed")));
break;
case DesktopIntegration::Status::NothingInstalled:
case DesktopIntegration::Status::InstalledUnknown:
default:
mUi->lblDesktopIntegrationStatus->setText(
QString("<font color=\"red\">✖ %1</font>").arg(tr("Not installed")));
break;
}
mUi->btnInstallApp->setEnabled(status !=
DesktopIntegration::Status::InstalledThis);
mUi->btnUninstallApp->setEnabled(
status != DesktopIntegration::Status::NothingInstalled);
}
void WorkspaceSettingsDialog::loadSettings() noexcept {
discardTemporaryModifications();
// UI Theme
{
QSignalBlocker blocker(mUi->cbxUiTheme);
mUi->cbxUiTheme->setCurrentIndex(
mUi->cbxUiTheme->findData(mSettings.uiTheme.get()));
}
// Application Locale
{
QSignalBlocker blocker(mUi->cbxAppLocale);
mUi->cbxAppLocale->setCurrentIndex(
mUi->cbxAppLocale->findData(mSettings.applicationLocale.get()));
}
// Default Length Unit
mUi->cbxDefaultLengthUnit->clear();
foreach (const LengthUnit& unit, LengthUnit::getAllUnits()) {
mUi->cbxDefaultLengthUnit->addItem(unit.toStringTr(), unit.getIndex());
}
mUi->cbxDefaultLengthUnit->setCurrentIndex(
mSettings.defaultLengthUnit.get().getIndex());
// Autosave Interval
mUi->spbAutosaveInterval->setValue(
mSettings.projectAutosaveIntervalSeconds.get());
// Use OpenGL
mUi->cbxUseOpenGl->setChecked(mSettings.useOpenGl.get());
// User Name
mUi->edtUserName->setText(mSettings.userName.get());
// Library Locale Order
mLibLocaleOrderModel->setValues(mSettings.libraryLocaleOrder.get());
// Library Norm Order
mLibNormOrderModel->setValues(mSettings.libraryNormOrder.get());
// API Endpoints
mApiEndpointModel->setValues(mSettings.apiEndpoints.get());
// Auto-Fetch Live Part Information
mUi->cbxAutofetchLivePartInformation->setChecked(
mSettings.autofetchLivePartInformation.get());
// External Applications
for (auto& app : mExternalApplications) {
app.currentValue = app.setting->get();
}
externalApplicationListIndexChanged(
mUi->lstExternalApplications->currentRow());
// Keyboard Shortcuts
mKeyboardShortcutsModel->setOverrides(mSettings.keyboardShortcuts.get());
mUi->treeKeyboardShortcuts->expandAll();
// Themes
mThemes = mSettings.themes.getAll();
updateThemesList(mSettings.themes.getActiveUuid());
}
void WorkspaceSettingsDialog::saveSettings() noexcept {
discardTemporaryModifications();
try {
// UI Theme was applied immediately.
// Application Locale was applied immediately.
// Default Length Unit
if (mUi->cbxDefaultLengthUnit->currentIndex() >= 0) {
mSettings.defaultLengthUnit.set(LengthUnit::fromIndex(
mUi->cbxDefaultLengthUnit->currentIndex())); // can throw
}
// Autosave Interval
mSettings.projectAutosaveIntervalSeconds.set(
mUi->spbAutosaveInterval->value());
// Use OpenGL
mSettings.useOpenGl.set(mUi->cbxUseOpenGl->isChecked());
// User Name
mSettings.userName.set(mUi->edtUserName->text().trimmed());
// Library Locale Order
mSettings.libraryLocaleOrder.set(mLibLocaleOrderModel->getValues());
// Library Norm Order
mSettings.libraryNormOrder.set(mLibNormOrderModel->getValues());
// API Endpoints
mSettings.apiEndpoints.set(mApiEndpointModel->getValues());
// Auto-Fetch Live Part Information
mSettings.autofetchLivePartInformation.set(
mUi->cbxAutofetchLivePartInformation->isChecked());
// External Applications
for (auto& app : mExternalApplications) {
QStringList commands;
foreach (const QString& cmd, app.currentValue) {
if (!cmd.trimmed().isEmpty()) {
commands.append(cmd.trimmed());
}
}
app.setting->set(commands);
}
// Keyboard shortcuts
mSettings.keyboardShortcuts.set(mKeyboardShortcutsModel->getOverrides());
// Themes
mSettings.themes.setAll(mThemes);
if (const Theme* theme = getCurrentTheme()) {
mSettings.themes.setActiveUuid(theme->getUuid());
}
// Save settings to disk.
mWorkspace.saveSettings(); // can throw
} catch (const Exception& e) {
QMessageBox::critical(this, tr("Error"), e.getMsg());
}
}
void WorkspaceSettingsDialog::discardTemporaryModifications() noexcept {
mOldUiTheme = mSettings.uiTheme.get();
mOldApplicationLocale = mSettings.applicationLocale.get();
}
void WorkspaceSettingsDialog::revertTemporaryModifications() noexcept {
mSettings.uiTheme.set(mOldUiTheme);
mSettings.applicationLocale.set(mOldApplicationLocale);
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb