Loading files
/*
* 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/colorschememodel.h"
#include "../modelview/comboboxdelegate.h"
#include "../modelview/editablelistmodel.h"
#include "../modelview/keyboardshortcutsmodel.h"
#include "../modelview/keysequencedelegate.h"
#include "../utils/editortoolbox.h"
#include "../utils/slinthelpers.h"
#include "../utils/uihelpers.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/colorrole.h>
#include <librepcb/core/workspace/uitheme.h>
#include <librepcb/core/workspace/workspace.h>
#include <librepcb/core/workspace/workspacesettings.h>
#include <QtCore>
#include <QtWidgets>
#include <algorithm>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
WorkspaceSettingsDialog::WorkspaceSettingsDialog(Workspace& workspace,
const UiTheme* const& theme,
QWidget* parent)
: QDialog(parent),
mWorkspace(workspace),
mSettings(workspace.getSettings()),
mTheme(theme),
mLibLocaleOrderModel(new LibraryLocaleOrderModel()),
mLibNormOrderModel(new LibraryNormOrderModel()),
mApiEndpointModel(new ApiEndpointListModelLegacy()),
mKeyboardShortcutsModel(new KeyboardShortcutsModel(this)),
mKeyboardShortcutsFilterModel(new QSortFilterProxyModel(this)),
mUi(new Ui::WorkspaceSettingsDialog),
mOldSchematicGridStyle(mSettings.schematicGridStyle.get()),
mOldBoardGridStyle(mSettings.boardGridStyle.get()),
mOldSchColorSchemeActive(mSettings.schematicColorSchemes.getActiveUuid()),
mOldBrdColorSchemeActive(mSettings.boardColorSchemes.getActiveUuid()),
mOld3dColorSchemeActive(mSettings.view3dColorSchemes.getActiveUuid()) {
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());
}
});
}
// 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,
[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",
"Description for '{{FILEPATH}}' placeholder"));
auto placeholderUrl =
std::make_pair(QString("{{URL}}"),
tr("URL to the file to open (file://)",
"Description 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",
"Description 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 color schemes widgets.
{
auto setup = [this](WorkspaceSettingsItem_ColorSchemes& schemes,
QComboBox* cbx, QToolButton* btnModify,
QToolButton* btnRemove, QToolButton* btnDuplicate) {
connect(cbx,
static_cast<void (QComboBox::*)(int)>(
&QComboBox::currentIndexChanged),
this, [&schemes, cbx, btnModify, btnRemove](int index) {
const auto uuid =
Uuid::tryFromString(cbx->itemData(index).toString());
if (uuid) {
schemes.setActiveUuid(*uuid);
}
const bool editable = uuid && schemes.getUserScheme(*uuid);
btnModify->setEnabled(editable);
btnRemove->setEnabled(editable);
});
connect(btnModify, &QToolButton::clicked, this, [this, &schemes]() {
if (auto s = schemes.getUserScheme(schemes.getActiveUuid())) {
execColorSchemeDialog(s);
}
});
connect(btnRemove, &QToolButton::clicked, this, [this, &schemes]() {
if (auto s = schemes.getUserScheme(schemes.getActiveUuid())) {
schemes.setActiveUuid(s->getBase().getUuid());
auto userSchemes = schemes.getUserSchemes();
userSchemes.remove(s->getUuid());
schemes.setUserSchemes(userSchemes);
updateColorSchemes();
}
});
connect(btnDuplicate, &QToolButton::clicked, this, [this, &schemes]() {
const QString name =
tr("Copy of %1").arg(schemes.getActive().getName());
std::optional<UserColorScheme> newScheme;
if (auto s =
dynamic_cast<const UserColorScheme*>(&schemes.getActive())) {
newScheme = UserColorScheme(Uuid::createRandom(), name, *s);
} else if (auto s = dynamic_cast<const BaseColorScheme*>(
&schemes.getActive())) {
newScheme = UserColorScheme(Uuid::createRandom(), name, *s);
}
if (newScheme) {
auto userSchemes = schemes.getUserSchemes();
userSchemes.insert(newScheme->getUuid(), *newScheme);
schemes.setUserSchemes(userSchemes);
schemes.setActiveUuid(newScheme->getUuid());
updateColorSchemes();
if (auto s = schemes.getUserScheme(newScheme->getUuid())) {
execColorSchemeDialog(s, true);
}
}
});
};
setup(mSettings.schematicColorSchemes, mUi->cbxSchematicColorSchemes,
mUi->btnEditSchematicColorScheme, mUi->btnRemoveSchematicColorScheme,
mUi->btnDuplicateSchematicColorScheme);
setup(mSettings.boardColorSchemes, mUi->cbxBoard2dColorSchemes,
mUi->btnEditBoard2dColorScheme, mUi->btnRemoveBoard2dColorScheme,
mUi->btnDuplicateBoard2dColorScheme);
setup(mSettings.view3dColorSchemes, mUi->cbxBoard3dColorSchemes,
mUi->btnEditBoard3dColorScheme, mUi->btnRemoveBoard3dColorScheme,
mUi->btnDuplicateBoard3dColorScheme);
}
// Initialize grid style widgets.
{
auto setup = [this](
QComboBox* cbx,
WorkspaceSettingsItem_GenericValue<GridStyle>& setting) {
cbx->addItem(tr("None", "Grid style"),
QVariant::fromValue(GridStyle::None));
cbx->addItem(tr("Dots", "Grid style"),
QVariant::fromValue(GridStyle::Dots));
cbx->addItem(tr("Lines", "Grid style"),
QVariant::fromValue(GridStyle::Lines));
connect(cbx,
static_cast<void (QComboBox::*)(int)>(
&QComboBox::currentIndexChanged),
this, [cbx, &setting](int index) {
const QVariant data = cbx->itemData(index);
if (data.isValid()) {
setting.set(data.value<GridStyle>());
}
});
};
setup(mUi->cbxSchematicGridStyle, mSettings.schematicGridStyle);
setup(mUi->cbxBoardGridStyle, mSettings.boardGridStyle);
}
// Initialize automatic library update mode.
{
mUi->cbxLibrariesAutoUpdateMode->addItem(
tr("Disabled", "Update mode"),
QVariant::fromValue(AutoUpdateMode::Disabled));
mUi->cbxLibrariesAutoUpdateMode->addItem(
tr("Check (Silent)", "Update mode"),
QVariant::fromValue(AutoUpdateMode::Check));
mUi->cbxLibrariesAutoUpdateMode->addItem(
tr("Check & Notify", "Update mode"),
QVariant::fromValue(AutoUpdateMode::Notify));
mUi->cbxLibrariesAutoUpdateMode->addItem(
tr("Check & Install", "Update mode"),
QVariant::fromValue(AutoUpdateMode::Install));
}
// 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::LanguageChange) {
mUi->retranslateUi(this);
}
QDialog::changeEvent(event);
}
void WorkspaceSettingsDialog::reject() noexcept {
if (hasTemporaryModifications()) {
const int ret = QMessageBox::question(
this, tr("Discard Changes?"),
tr("You made changes to some settings which will be lost when closing "
"the dialog. Are you sure to discard them?"),
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
if (ret != QMessageBox::Yes) {
return;
}
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::updateColorSchemes() noexcept {
auto update = [](const WorkspaceSettingsItem_ColorSchemes& schemes,
QComboBox* cbx) {
{
QSignalBlocker blocker(cbx);
cbx->clear();
for (const ColorScheme* scheme : schemes.getBaseSchemes()) {
cbx->addItem(scheme->getName(), scheme->getUuid().toStr());
}
for (const ColorScheme& scheme : schemes.getUserSchemes()) {
cbx->addItem("*" + scheme.getName(), scheme.getUuid().toStr());
}
cbx->setCurrentIndex(-1);
}
cbx->setCurrentIndex(cbx->findData(schemes.getActiveUuid().toStr()));
};
update(mSettings.schematicColorSchemes, mUi->cbxSchematicColorSchemes);
update(mSettings.boardColorSchemes, mUi->cbxBoard2dColorSchemes);
update(mSettings.view3dColorSchemes, mUi->cbxBoard3dColorSchemes);
}
void WorkspaceSettingsDialog::execColorSchemeDialog(
std::shared_ptr<UserColorScheme> scheme, bool focusNameEdt) noexcept {
if (!mColorSchemeDialog) {
mColorSchemeDialog = ui::ColorSchemeDialog::create();
}
auto applyChanges = [scheme, self = QPointer(this)]() {
if (self && self->mColorSchemeDialog) {
const QString s = s2q((*self->mColorSchemeDialog)->get_name()).trimmed();
if (!s.isEmpty()) {
scheme->setName(s);
}
self->updateColorSchemes();
(*self->mColorSchemeDialog)->hide();
}
return slint::CloseRequestResponse::HideWindow;
};
auto& win = *mColorSchemeDialog;
win->global<ui::Data>().set_theme(l2s(*mTheme));
win->set_name(q2s(scheme->getName()));
win->set_model(std::make_shared<ColorSchemeModel>(scheme));
win->set_current_index(0);
win->set_current_secondary(false);
win->on_copy_to_clipboard([scheme]() {
QStringList lines;
for (const auto& base : scheme->getBase().getAllColors()) {
const auto colors = scheme->getColors(*base.role);
lines.append(QString("\"%1\", \"%2\", %3")
.arg(colors.primary.name(QColor::HexArgb),
colors.secondary.name(QColor::HexArgb),
base.role->getId()));
}
const qsizetype maxLen =
std::ranges::max(lines, {}, &QString::length).length();
for (QString& line : lines) {
line.append(QString(" ").repeated(maxLen - line.length()));
}
QApplication::clipboard()->setText(lines.join("\n"));
return true;
});
win->on_close_requested(applyChanges); // Closed with "Close" button
win->window().on_close_requested(applyChanges); // Closed with "X" button
QWidget* widget =
static_cast<QWidget*>(slint::cbindgen_private::slint_qt_get_widget(
&win->window().window_handle()));
Q_ASSERT(widget);
widget->setWindowModality(Qt::ApplicationModal);
win->show();
widget->move(geometry().center() - widget->rect().center());
if (focusNameEdt) {
win->invoke_focus_name_edt();
}
}
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();
// Color Schemes
updateColorSchemes();
// Grid style
auto loadGridStyle = [](QComboBox* cbx, GridStyle gridStyle) {
cbx->setCurrentIndex(cbx->findData(QVariant::fromValue(gridStyle)));
};
loadGridStyle(mUi->cbxSchematicGridStyle, mSettings.schematicGridStyle.get());
loadGridStyle(mUi->cbxBoardGridStyle, mSettings.boardGridStyle.get());
// Automatic library update mode.
mUi->cbxLibrariesAutoUpdateMode->setCurrentIndex(
mUi->cbxLibrariesAutoUpdateMode->findData(
QVariant::fromValue(mSettings.librariesAutoUpdateMode.get())));
}
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 were applied immediately.
// Grid style was applied immediately.
// Automatic library update mode.
if (mUi->cbxLibrariesAutoUpdateMode->currentIndex() >= 0) {
mSettings.librariesAutoUpdateMode.set(
mUi->cbxLibrariesAutoUpdateMode->currentData()
.value<AutoUpdateMode>());
}
// 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();
mOldSchematicGridStyle = mSettings.schematicGridStyle.get();
mOldBoardGridStyle = mSettings.boardGridStyle.get();
mOldSchColorSchemes = mSettings.schematicColorSchemes.getUserSchemes();
mOldBrdColorSchemes = mSettings.boardColorSchemes.getUserSchemes();
mOld3dColorSchemes = mSettings.view3dColorSchemes.getUserSchemes();
mOldSchColorSchemeActive = mSettings.schematicColorSchemes.getActiveUuid();
mOldBrdColorSchemeActive = mSettings.boardColorSchemes.getActiveUuid();
mOld3dColorSchemeActive = mSettings.view3dColorSchemes.getActiveUuid();
}
void WorkspaceSettingsDialog::revertTemporaryModifications() noexcept {
mSettings.uiTheme.set(mOldUiTheme);
mSettings.applicationLocale.set(mOldApplicationLocale);
mSettings.schematicGridStyle.set(mOldSchematicGridStyle);
mSettings.boardGridStyle.set(mOldBoardGridStyle);
mSettings.schematicColorSchemes.setUserSchemes(mOldSchColorSchemes);
mSettings.boardColorSchemes.setUserSchemes(mOldBrdColorSchemes);
mSettings.view3dColorSchemes.setUserSchemes(mOld3dColorSchemes);
mSettings.schematicColorSchemes.setActiveUuid(mOldSchColorSchemeActive);
mSettings.boardColorSchemes.setActiveUuid(mOldBrdColorSchemeActive);
mSettings.view3dColorSchemes.setActiveUuid(mOld3dColorSchemeActive);
}
bool WorkspaceSettingsDialog::hasTemporaryModifications() const noexcept {
return (mSettings.uiTheme.get() != mOldUiTheme) //
|| (mSettings.applicationLocale.get() != mOldApplicationLocale) //
|| (mSettings.schematicGridStyle.get() != mOldSchematicGridStyle) //
|| (mSettings.boardGridStyle.get() != mOldBoardGridStyle) //
|| (mSettings.schematicColorSchemes.getUserSchemes() !=
mOldSchColorSchemes) //
||
(mSettings.boardColorSchemes.getUserSchemes() != mOldBrdColorSchemes) //
||
(mSettings.view3dColorSchemes.getUserSchemes() != mOld3dColorSchemes) //
|| (mSettings.schematicColorSchemes.getActiveUuid() !=
mOldSchColorSchemeActive) //
|| (mSettings.boardColorSchemes.getActiveUuid() !=
mOldBrdColorSchemeActive) //
|| (mSettings.view3dColorSchemes.getActiveUuid() !=
mOld3dColorSchemeActive) //
;
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb
9ea812f0aThis file is pinned to the selected release. Download the original to open it in your design tool.