diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index d59831672b..e159c30a36 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -44,6 +44,7 @@ #endif #include +#include #include #if defined(HAVE_QT5_OPENGL) #include @@ -72,6 +73,7 @@ #include "WidgetFactory.h" #include "Command.h" #include "Macro.h" +#include "PreferencePackManager.h" #include "ProgressBar.h" #include "Workbench.h" #include "WorkbenchManager.h" @@ -159,6 +161,9 @@ struct ApplicationP macroMngr = new MacroManager(); else macroMngr = nullptr; + + // Create the Theme Manager + prefPackManager = new PreferencePackManager(); } ~ApplicationP() @@ -172,6 +177,7 @@ struct ApplicationP Gui::Document* activeDocument; Gui::Document* editDocument; MacroManager* macroMngr; + PreferencePackManager* prefPackManager; /// List of all registered views std::list passive; bool isClosing; @@ -1616,6 +1622,12 @@ CommandManager &Application::commandManager(void) return d->commandManager; } +Gui::PreferencePackManager* Application::prefPackManager(void) +{ + return d->prefPackManager; +} + + //************************************************************************** // Init, Destruct and singleton diff --git a/src/Gui/Application.h b/src/Gui/Application.h index 0f596d3276..05b976bfdc 100644 --- a/src/Gui/Application.h +++ b/src/Gui/Application.h @@ -43,6 +43,7 @@ class MacroManager; class MDIView; class MainWindow; class MenuItem; +class PreferencePackManager; class ViewProvider; class ViewProviderDocumentObject; @@ -216,6 +217,8 @@ public: void createStandardOperations(); //@} + Gui::PreferencePackManager* prefPackManager(void); + /** @name Init, Destruct an Access methods */ //@{ /// some kind of singelton diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index e682cc825b..ac49841c8a 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -1,5 +1,7 @@ #add_subdirectory(Icons) add_subdirectory(Stylesheets) +add_subdirectory(PreferencePacks) +add_subdirectory(PreferencePackTemplates) if(WIN32) add_definitions(-DFCGui -DQIIS_MAKEDLL -DQSINT_MAKEDLL -DOVR_OS_WIN32 -DQUARTER_INTERNAL -DQUARTER_MAKE_DLL -DCOIN_DLL) @@ -306,6 +308,7 @@ SET(Gui_UIC_SRCS DlgAuthorization.ui DlgChooseIcon.ui DlgCommands.ui + DlgCreateNewPreferencePack.ui DlgCustomizeSpNavSettings.ui DlgDisplayProperties.ui DlgEditor.ui @@ -403,6 +406,7 @@ SET(Dialog_CPP_SRCS Clipping.cpp DemoMode.cpp DlgActivateWindowImp.cpp + DlgCreateNewPreferencePackImp.cpp DlgUnitsCalculatorImp.cpp DlgDisplayPropertiesImp.cpp DlgInputDialogImp.cpp @@ -440,6 +444,7 @@ SET(Dialog_HPP_SRCS Clipping.h DemoMode.h DlgActivateWindowImp.h + DlgCreateNewPreferencePackImp.h DlgUnitsCalculatorImp.h DlgDisplayPropertiesImp.h DlgInputDialogImp.h @@ -499,6 +504,7 @@ SET(Dialog_SRCS DlgCheckableMessageBox.ui DlgTreeWidget.ui DlgExpressionInput.ui + DlgCreateNewPreferencePack.ui DownloadManager.ui DownloadItem.ui DocumentRecovery.ui @@ -1139,6 +1145,7 @@ SET(FreeCADGui_CPP_SRCS resource.cpp Control.cpp SpaceballEvent.cpp + PreferencePackManager.cpp Thumbnail.cpp Utilities.cpp WaitCursor.cpp @@ -1171,6 +1178,7 @@ SET(FreeCADGui_SRCS Qt4All.h Control.h SpaceballEvent.h + PreferencePackManager.h Thumbnail.h Utilities.h WaitCursor.h diff --git a/src/Gui/DlgCreateNewPreferencePack.ui b/src/Gui/DlgCreateNewPreferencePack.ui new file mode 100644 index 0000000000..5165aeffe1 --- /dev/null +++ b/src/Gui/DlgCreateNewPreferencePack.ui @@ -0,0 +1,107 @@ + + + Gui::Dialog::DlgCreateNewPreferencePack + + + Qt::ApplicationModal + + + + 0 + 0 + 580 + 520 + + + + Create New Preference Pack + + + true + + + + + + + + Name + + + + + + + + + + + + 50 + + + 250 + + + true + + + + Property group templates + + + + + Template Type + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Gui::Dialog::DlgCreateNewPreferencePack + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Gui::Dialog::DlgCreateNewPreferencePack + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/Gui/DlgCreateNewPreferencePackImp.cpp b/src/Gui/DlgCreateNewPreferencePackImp.cpp new file mode 100644 index 0000000000..498199f7b6 --- /dev/null +++ b/src/Gui/DlgCreateNewPreferencePackImp.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (c) 2021 Chris Hennes * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +#endif + +#include "DlgCreateNewPreferencePackImp.h" +#include "ui_DlgCreateNewPreferencePack.h" + +#include + +using namespace Gui::Dialog; + +const auto TemplateRole = Qt::UserRole; + +/* TRANSLATOR Gui::Dialog::DlgCreateNewPreferencePackImp */ + +/** + * Constructs a Gui::Dialog::DlgCreateNewPreferencePackImp as a child of 'parent' + */ +DlgCreateNewPreferencePackImp::DlgCreateNewPreferencePackImp(QWidget* parent) + : QDialog(parent) + , ui(new Ui_DlgCreateNewPreferencePack) +{ + ui->setupUi(this); + + QRegExp validNames(QString::fromUtf8("[^/\\\\?%*:|\"<>]+")); + _nameValidator.setRegExp(validNames); + ui->lineEdit->setValidator(&_nameValidator); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + connect(ui->treeWidget, &QTreeWidget::itemChanged, this, &DlgCreateNewPreferencePackImp::onItemChanged); +} + + +DlgCreateNewPreferencePackImp::~DlgCreateNewPreferencePackImp() +{ +} + +void DlgCreateNewPreferencePackImp::setPreferencePackTemplates(const std::vector& availableTemplates) +{ + ui->treeWidget->clear(); + _groups.clear(); + + ui->treeWidget->header()->setDefaultSectionSize(250); + + _templates = availableTemplates; + for (const auto &t : _templates) { + + QTreeWidgetItem* group; + if (auto foundGroup = _groups.find(t.group); foundGroup != _groups.end()) { + group = foundGroup->second; + } + else { + group = new QTreeWidgetItem(ui->treeWidget, QStringList(QString::fromStdString(t.group))); + group->setCheckState(0, Qt::Checked); + group->setExpanded(true); + _groups.insert(std::make_pair(t.group, group)); + } + + QStringList itemColumns; + itemColumns.push_back(QString::fromStdString(t.name)); + switch (t.type) { + case Gui::PreferencePack::Type::Appearance: itemColumns.push_back(tr("Appearance")); break; + case Gui::PreferencePack::Type::Behavior: itemColumns.push_back(tr("Behavior")); break; + case Gui::PreferencePack::Type::Combination: itemColumns.push_back(tr("Combination")); break; + } + auto newItem = new QTreeWidgetItem(group, itemColumns); + newItem->setCheckState(0, Qt::Checked); + if (group->checkState(0) != newItem->checkState(0)) + group->setCheckState(0, Qt::PartiallyChecked); + newItem->setData(0, TemplateRole, QVariant::fromValue(t)); + group->addChild(newItem); + } +} + +std::vector DlgCreateNewPreferencePackImp::selectedTemplates() const +{ + std::vector results; + + for (const auto& group : _groups) + for (int childIndex = 0; childIndex < group.second->childCount(); ++childIndex) + if (auto child = group.second->child(childIndex); child->checkState(0) == Qt::Checked) + if (child->data(0, TemplateRole).canConvert()) + results.push_back(child->data(0, TemplateRole).value()); + + return results; +} + +std::string DlgCreateNewPreferencePackImp::preferencePackName() const +{ + return ui->lineEdit->text().toStdString(); +} + +void DlgCreateNewPreferencePackImp::onItemChanged(QTreeWidgetItem* item, int column) +{ + Q_UNUSED(column); + const QSignalBlocker blocker(ui->treeWidget); + if (auto group = item->parent(); group) { + // Child clicked + bool firstItemChecked = false; + for (int childIndex = 0; childIndex < group->childCount(); ++childIndex) { + auto child = group->child(childIndex); + if (childIndex == 0) { + firstItemChecked = child->checkState(0) == Qt::Checked; + } + else { + bool thisItemChecked = child->checkState(0) == Qt::Checked; + if (firstItemChecked != thisItemChecked) { + group->setCheckState(0, Qt::PartiallyChecked); + return; + } + } + } + group->setCheckState(0, firstItemChecked ? Qt::Checked : Qt::Unchecked); + } + else { + // Group clicked: + auto groupCheckState = item->checkState(0); + for (int childIndex = 0; childIndex < item->childCount(); ++childIndex) { + auto child = item->child(childIndex); + child->setCheckState(0, groupCheckState); + } + } +} + +void DlgCreateNewPreferencePackImp::on_lineEdit_textEdited(const QString& text) +{ + ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty()); +} + + +#include "moc_DlgCreateNewPreferencePackImp.cpp" diff --git a/src/Gui/DlgCreateNewPreferencePackImp.h b/src/Gui/DlgCreateNewPreferencePackImp.h new file mode 100644 index 0000000000..6e4f75058b --- /dev/null +++ b/src/Gui/DlgCreateNewPreferencePackImp.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (c) 2021 Chris Hennes * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef GUI_DIALOG_DLGCREATENEWTHEMEIMP_H +#define GUI_DIALOG_DLGCREATENEWTHEMEIMP_H + +#include +#include +#include + +#include "PreferencePackManager.h" + +class QTreeWidgetItem; + +namespace Gui { + +namespace Dialog { + +class Ui_DlgCreateNewPreferencePack; + +/** + * \class DlgCreateNewPreferencePackImp + * + * A dialog to request a preferencePack name and a set of preferencePack templates. + * + * \author Chris Hennes + */ +class GuiExport DlgCreateNewPreferencePackImp : public QDialog +{ + Q_OBJECT + +public: + + DlgCreateNewPreferencePackImp(QWidget* parent = nullptr); + ~DlgCreateNewPreferencePackImp(); + + void setPreferencePackTemplates(const std::vector &availableTemplates); + + std::vector selectedTemplates() const; + std::string preferencePackName() const; + +protected Q_SLOTS: + + void onItemChanged(QTreeWidgetItem* item, int column); + + void on_lineEdit_textEdited(const QString &text); + +private: + std::unique_ptr ui; + std::map _groups; + std::vector _templates; + QRegExpValidator _nameValidator; +}; + +} // namespace Dialog +} // namespace Gui + +#endif // GUI_DIALOG_DLGCREATENEWTHEMEIMP_H diff --git a/src/Gui/DlgGeneral.ui b/src/Gui/DlgGeneral.ui index 0b83dac463..411e8cb717 100644 --- a/src/Gui/DlgGeneral.ui +++ b/src/Gui/DlgGeneral.ui @@ -14,7 +14,16 @@ General - + + 9 + + + 9 + + + 9 + + 9 @@ -32,7 +41,16 @@ Language - + + 11 + + + 11 + + + 11 + + 11 @@ -43,7 +61,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -65,6 +92,146 @@ + + + + Preference Packs + + + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + true + + + false + + + false + + + true + + + true + + + 75 + + + 200 + + + true + + + false + + + 16 + + + 24 + + + + Name + + + + 75 + true + + + + + + Type + + + + 75 + true + + + + + + + + + + + + 0 + 0 + + + + Run + + + + + + + Save new... + + + + + + + + 0 + 0 + + + + Manage... + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + @@ -74,7 +241,16 @@ 6 - + + 11 + + + 11 + + + 11 + + 11 @@ -82,7 +258,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -131,7 +316,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -152,7 +346,16 @@ See the FreeCAD Wiki for details about the image. 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -176,7 +379,16 @@ See the FreeCAD Wiki for details about the image. 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -201,7 +413,16 @@ this according to your screen size or personal taste 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -214,7 +435,7 @@ this according to your screen size or personal taste - Customize how tree view is shown in the panel (restart required). + Customize how tree view is shown in the panel (restart required). 'ComboView': combine tree view and property view into one panel. 'TreeView and PropertyView': split tree view and property view into separate panel. @@ -233,7 +454,16 @@ this according to your screen size or personal taste Start up - + + 11 + + + 11 + + + 11 + + 11 @@ -262,7 +492,16 @@ display the splash screen - + + 0 + + + 0 + + + 0 + + 0 @@ -294,7 +533,16 @@ after FreeCAD launches Python console - + + 11 + + + 11 + + + 11 + + 11 @@ -323,19 +571,6 @@ horizontal space in Python console - - - - Qt::Vertical - - - - 352 - 221 - - - - @@ -353,11 +588,6 @@ horizontal space in Python console QSpinBox
Gui/PrefWidgets.h
- - Gui::PrefComboBox - QComboBox -
Gui/PrefWidgets.h
-
Languages diff --git a/src/Gui/DlgGeneralImp.cpp b/src/Gui/DlgGeneralImp.cpp index 39075aefa9..b4bd12ebea 100644 --- a/src/Gui/DlgGeneralImp.cpp +++ b/src/Gui/DlgGeneralImp.cpp @@ -33,11 +33,16 @@ #include "ui_DlgGeneral.h" #include "Action.h" #include "Application.h" +#include "Command.h" #include "DockWindowManager.h" #include "MainWindow.h" #include "PrefWidgets.h" #include "PythonConsole.h" #include "Language/Translator.h" +#include "Gui/PreferencePackManager.h" +#include "DlgPreferencesImp.h" + +#include "DlgCreateNewPreferencePackImp.h" using namespace Gui::Dialog; @@ -82,6 +87,15 @@ DlgGeneralImp::DlgGeneralImp( QWidget* parent ) else ui->AutoloadModuleCombo->addItem(px, it.key(), QVariant(it.value())); } + + recreatePreferencePackMenu(); + connect(ui->PreferencePacks, &QTableWidget::itemSelectionChanged, this, &DlgGeneralImp::preferencePackSelectionChanged); + connect(ui->ApplyPreferencePack, &QPushButton::clicked, this, &DlgGeneralImp::applyPreferencePackClicked); + connect(ui->SaveNewPreferencePack, &QPushButton::clicked, this, &DlgGeneralImp::saveAsNewPreferencePack); + + // Future work: the Add-On Manager will be modified to include a section for Preference Packs, at which point this + // button will be enabled to open the Add-On Manager to that tab. + ui->ManagePreferencePacks->hide(); } /** @@ -299,4 +313,90 @@ void DlgGeneralImp::changeEvent(QEvent *e) } } +void DlgGeneralImp::recreatePreferencePackMenu() +{ + // Populate the Preference Packs list + auto appearancePacks = Application::Instance->prefPackManager()->preferencePackNames(PreferencePack::Type::Appearance); + auto behaviorPacks = Application::Instance->prefPackManager()->preferencePackNames(PreferencePack::Type::Behavior); + auto combinationPacks = Application::Instance->prefPackManager()->preferencePackNames(PreferencePack::Type::Combination); + + ui->PreferencePacks->setRowCount(appearancePacks.size() + behaviorPacks.size() + combinationPacks.size()); + + int row = 0; + for (const auto& pack : appearancePacks) { + auto name = new QTableWidgetItem(QString::fromStdString(pack)); + ui->PreferencePacks->setItem(row, 0, name); + auto kind = new QTableWidgetItem(tr("Appearance")); + ui->PreferencePacks->setItem(row, 1, kind); + ++row; + } + for (const auto& pack : behaviorPacks) { + auto name = new QTableWidgetItem(QString::fromStdString(pack)); + ui->PreferencePacks->setItem(row, 0, name); + auto kind = new QTableWidgetItem(tr("Behavior")); + ui->PreferencePacks->setItem(row, 1, kind); + ++row; + } + for (const auto& pack : combinationPacks) { + auto name = new QTableWidgetItem(QString::fromStdString(pack)); + ui->PreferencePacks->setItem(row, 0, name); + auto kind = new QTableWidgetItem(tr("Combination")); + ui->PreferencePacks->setItem(row, 1, kind); + ++row; + } + ui->PreferencePacks->setRangeSelected(QTableWidgetSelectionRange(), true); + ui->ApplyPreferencePack->setEnabled(false); +} + +void DlgGeneralImp::preferencePackSelectionChanged() +{ + if (ui->PreferencePacks->selectedItems().isEmpty()) + ui->ApplyPreferencePack->setEnabled(false); + else + ui->ApplyPreferencePack->setEnabled(true); +} + +void DlgGeneralImp::saveAsNewPreferencePack() +{ + // Create and run a modal New PreferencePack dialog box + newPreferencePackDialog = std::make_unique(this); + newPreferencePackDialog->setPreferencePackTemplates(Application::Instance->prefPackManager()->templateFiles()); + connect(newPreferencePackDialog.get(), &DlgCreateNewPreferencePackImp::accepted, this, &DlgGeneralImp::newPreferencePackDialogAccepted); + newPreferencePackDialog->open(); +} + +void DlgGeneralImp::newPreferencePackDialogAccepted() +{ + auto preferencePackTemplates = Application::Instance->prefPackManager()->templateFiles(); + auto selection = newPreferencePackDialog->selectedTemplates(); + std::vector selectedTemplates; + std::copy_if(preferencePackTemplates.begin(), preferencePackTemplates.end(), std::back_inserter(selectedTemplates), [selection](PreferencePackManager::TemplateFile& t) { + for (const auto& item : selection) + if (item.group == t.group && item.name == t.name) + return true; + return false; + }); + auto preferencePackName = newPreferencePackDialog->preferencePackName(); + Application::Instance->prefPackManager()->save(preferencePackName, selectedTemplates); + Application::Instance->prefPackManager()->rescan(); + recreatePreferencePackMenu(); +} + +void DlgGeneralImp::applyPreferencePackClicked() +{ + auto selectedPreferencePacks = ui->PreferencePacks->selectedItems(); + + for (const auto pack : selectedPreferencePacks) { + if (pack->column() == 0) { + auto packName = pack->text().toStdString(); + if (Application::Instance->prefPackManager()->apply(packName)) { + auto parentDialog = qobject_cast (this->window()); + if (parentDialog) + parentDialog->reload(); + } + } + } +} + + #include "moc_DlgGeneralImp.cpp" diff --git a/src/Gui/DlgGeneralImp.h b/src/Gui/DlgGeneralImp.h index eac13fa8b8..6c928efd3e 100644 --- a/src/Gui/DlgGeneralImp.h +++ b/src/Gui/DlgGeneralImp.h @@ -32,6 +32,7 @@ class QTabWidget; namespace Gui { namespace Dialog { class Ui_DlgGeneral; +class DlgCreateNewPreferencePackImp; /** This class implements the settings for the application. * You can change window style, size of pixmaps, size of recent file list and so on @@ -51,11 +52,19 @@ public: protected: void changeEvent(QEvent *e); +protected Q_SLOTS: + void preferencePackSelectionChanged(); + void applyPreferencePackClicked(); + void recreatePreferencePackMenu(); + void newPreferencePackDialogAccepted(); + private: void setRecentFileSize(); + void saveAsNewPreferencePack(); private: std::unique_ptr ui; + std::unique_ptr newPreferencePackDialog; }; } // namespace Dialog diff --git a/src/Gui/DlgPreferencesImp.cpp b/src/Gui/DlgPreferencesImp.cpp index d547121d94..92a1f1f559 100644 --- a/src/Gui/DlgPreferencesImp.cpp +++ b/src/Gui/DlgPreferencesImp.cpp @@ -466,4 +466,17 @@ void DlgPreferencesImp::changeEvent(QEvent *e) } } +void DlgPreferencesImp::reload() +{ + for (int i = 0; i < ui->tabWidgetStack->count(); i++) { + QTabWidget* tabWidget = (QTabWidget*)ui->tabWidgetStack->widget(i); + for (int j = 0; j < tabWidget->count(); j++) { + PreferencePage* page = qobject_cast(tabWidget->widget(j)); + if (page) + page->loadSettings(); + } + } + applyChanges(); +} + #include "moc_DlgPreferencesImp.cpp" diff --git a/src/Gui/DlgPreferencesImp.h b/src/Gui/DlgPreferencesImp.h index 172baba36d..5ac6fcb190 100644 --- a/src/Gui/DlgPreferencesImp.h +++ b/src/Gui/DlgPreferencesImp.h @@ -113,11 +113,13 @@ class GuiExport DlgPreferencesImp : public QDialog public: static void addPage(const std::string& className, const std::string& group); static void removePage(const std::string& className, const std::string& group); + static void reloadSettings(); DlgPreferencesImp(QWidget* parent = 0, Qt::WindowFlags fl = Qt::WindowFlags()); ~DlgPreferencesImp(); void accept(); + void reload(); void activateGroupPage(const QString& group, int id); protected: @@ -135,11 +137,11 @@ private: /** @name for internal use only */ //@{ void setupPages(); + void reloadPages(); QTabWidget* createTabForGroup(const std::string& groupName); void createPageInGroup(QTabWidget* tabWidget, const std::string& pageName); void applyChanges(); void restoreDefaults(); - void reloadPages(); //@} private: diff --git a/src/Gui/PreferencePackManager.cpp b/src/Gui/PreferencePackManager.cpp new file mode 100644 index 0000000000..3f71314702 --- /dev/null +++ b/src/Gui/PreferencePackManager.cpp @@ -0,0 +1,454 @@ +/*************************************************************************** + * Copyright (c) 2021 Chris Hennes * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +#endif + +#include +#include + +#include "PreferencePackManager.h" +#include "App/Metadata.h" +#include "Base/Parameter.h" +#include "Base/Interpreter.h" +#include "Base/Console.h" + +#include + +#include // For generating a timestamped filename + + +using namespace Gui; +using namespace xercesc; +namespace fs = boost::filesystem; + +PreferencePack::PreferencePack(const fs::path& path, const App::Metadata& metadata) : + _path(path), _metadata(metadata) +{ + if (!fs::exists(_path)) { + throw std::runtime_error{ "Cannot access " + path.string() }; + } + + auto qssPaths = QDir::searchPaths(QString::fromUtf8("qss")); + auto cssPaths = QDir::searchPaths(QString::fromUtf8("css")); + + qssPaths.append(QString::fromStdString(_path.string())); + cssPaths.append(QString::fromStdString(_path.string())); + + QDir::setSearchPaths(QString::fromUtf8("qss"), qssPaths); + QDir::setSearchPaths(QString::fromUtf8("css"), cssPaths); +} + +std::string PreferencePack::name() const +{ + return _metadata.name(); +} + +bool PreferencePack::apply() const +{ + // Run the pre.FCMacro, if it exists: if it raises an exception, abort the process + auto preMacroPath = _path / "pre.FCMacro"; + if (fs::exists(preMacroPath)) { + try { + Base::Interpreter().runFile(preMacroPath.string().c_str(), false); + } + catch (...) { + Base::Console().Message("PreferencePack application aborted by the preferencePack's pre.FCMacro"); + return false; + } + } + + // Back up the old config file + auto savedPreferencePacksDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks"; + auto backupFile = savedPreferencePacksDirectory / "user.cfg.backup"; + try { + fs::remove(backupFile); + } + catch (...) {} + App::GetApplication().GetUserParameter().SaveDocument(backupFile.string().c_str()); + + // Apply the config settings + applyConfigChanges(); + + // Run the Post.FCMacro, if it exists + auto postMacroPath = _path / "post.FCMacro"; + if (fs::exists(postMacroPath)) { + try { + Base::Interpreter().runFile(postMacroPath.string().c_str(), false); + } + catch (...) { + Base::Console().Message("PreferencePack application reverted by the preferencePack's post.FCMacro"); + App::GetApplication().GetUserParameter().LoadDocument(backupFile.string().c_str()); + return false; + } + } + + return true; +} + +PreferencePack::Type PreferencePack::type() const +{ + auto typeList = _metadata["type"]; + if (typeList.empty()) + return Type::Combination; + + auto typeString = typeList.front().contents; + if (typeString == "appearance") + return Type::Appearance; + else if (typeString == "behavior" || typeString == "behaviour") + return Type::Behavior; + else + return Type::Combination; +} + +void PreferencePack::applyConfigChanges() const +{ + auto configFile = _path / (_metadata.name() + ".cfg"); + if (fs::exists(configFile)) { + ParameterManager newParameters; + newParameters.LoadDocument(configFile.string().c_str()); + auto baseAppGroup = App::GetApplication().GetUserParameter().GetGroup("BaseApp"); + newParameters.GetGroup("BaseApp")->copyTo(baseAppGroup); + } +} + + + +PreferencePackManager::PreferencePackManager() +{ + auto modPath = fs::path(App::Application::getUserAppDataDir()) / "Mod"; + auto savedPath = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks"; + auto resourcePath = fs::path(App::Application::getResourceDir()) / "Gui" / "PreferencePacks"; + _preferencePackPaths.push_back(resourcePath); + _preferencePackPaths.push_back(modPath); + _preferencePackPaths.push_back(savedPath); + rescan(); + + // Housekeeping: + DeleteOldBackups(); +} + +void PreferencePackManager::rescan() +{ + std::lock_guard lock(_mutex); + for (const auto& path : _preferencePackPaths) { + if (fs::exists(path) && fs::is_directory(path)) { + FindPreferencePacksInPackage(path); + for (const auto& mod : fs::directory_iterator(path)) { + if (fs::is_directory(mod)) { + FindPreferencePacksInPackage(mod); + } + } + } + } +} + +void Gui::PreferencePackManager::FindPreferencePacksInPackage(const fs::path& mod) +{ + auto packageMetadataFile = mod / "package.xml"; + if (fs::exists(packageMetadataFile) && fs::is_regular_file(packageMetadataFile)) { + try { + App::Metadata metadata(packageMetadataFile); + auto content = metadata.content(); + for (const auto& item : content) { + if (item.first == "preferencepack") { + PreferencePack newPreferencePack(mod / item.second.name(), item.second); + _preferencePacks.insert(std::make_pair(newPreferencePack.name(), newPreferencePack)); + } + } + } + catch (...) { + // Failed to read the metadata, or to create the preferencePack based on it... + Base::Console().Error(("Failed to read " + packageMetadataFile.string()).c_str()); + } + } +} + +std::vector PreferencePackManager::preferencePackNames(PreferencePack::Type type) const +{ + std::lock_guard lock(_mutex); + std::vector names; + for (const auto& preferencePack : _preferencePacks) + if (preferencePack.second.type() == type) + names.push_back(preferencePack.first); + return names; +} + +bool PreferencePackManager::apply(const std::string& preferencePackName) const +{ + std::lock_guard lock(_mutex); + if (auto preferencePack = _preferencePacks.find(preferencePackName); preferencePack != _preferencePacks.end()) { + BackupCurrentConfig(); + return preferencePack->second.apply(); + } + else { + throw std::runtime_error("No such Preference Pack: " + preferencePackName); + } +} + +void copyTemplateParameters(Base::Reference templateGroup, const std::string& path, Base::Reference outputGroup) +{ + auto userParameterHandle = App::GetApplication().GetParameterGroupByPath(path.c_str()); + + auto boolMap = templateGroup->GetBoolMap(); + for (const auto& kv : boolMap) { + auto currentValue = userParameterHandle->GetBool(kv.first.c_str(), kv.second); + outputGroup->SetBool(kv.first.c_str(), currentValue); + } + + auto intMap = templateGroup->GetIntMap(); + for (const auto& kv : intMap) { + auto currentValue = userParameterHandle->GetInt(kv.first.c_str(), kv.second); + outputGroup->SetInt(kv.first.c_str(), currentValue); + } + + auto uintMap = templateGroup->GetUnsignedMap(); + for (const auto& kv : uintMap) { + auto currentValue = userParameterHandle->GetUnsigned(kv.first.c_str(), kv.second); + outputGroup->SetUnsigned(kv.first.c_str(), currentValue); + } + + auto floatMap = templateGroup->GetFloatMap(); + for (const auto& kv : floatMap) { + auto currentValue = userParameterHandle->GetFloat(kv.first.c_str(), kv.second); + outputGroup->SetFloat(kv.first.c_str(), currentValue); + } + + auto asciiMap = templateGroup->GetASCIIMap(); + for (const auto& kv : asciiMap) { + auto currentValue = userParameterHandle->GetASCII(kv.first.c_str(), kv.second.c_str()); + outputGroup->SetASCII(kv.first.c_str(), currentValue.c_str()); + } + + // Recurse... + auto templateSubgroups = templateGroup->GetGroups(); + for (auto& templateSubgroup : templateSubgroups) { + std::string sgName = templateSubgroup->GetGroupName(); + auto outputSubgroupHandle = outputGroup->GetGroup(sgName.c_str()); + copyTemplateParameters(templateSubgroup, path + "/" + sgName, outputSubgroupHandle); + } +} + +void copyTemplateParameters(/*const*/ ParameterManager& templateParameterManager, ParameterManager& outputParameterManager) +{ + auto groups = templateParameterManager.GetGroups(); + for (auto& group : groups) { + std::string name = group->GetGroupName(); + auto groupHandle = outputParameterManager.GetGroup(name.c_str()); + copyTemplateParameters(group, "User parameter:" + name, groupHandle); + } +} + +void PreferencePackManager::save(const std::string& name, const std::vector& templates) +{ + if (templates.empty()) + return; + + std::lock_guard lock(_mutex); + auto savedPreferencePacksDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks"; + fs::path preferencePackDirectory(savedPreferencePacksDirectory / name); + if (fs::exists(preferencePackDirectory) && !fs::is_directory(preferencePackDirectory)) + throw std::runtime_error("Cannot create " + savedPreferencePacksDirectory.string() + ": file with that name exists already"); + + if (!fs::exists(preferencePackDirectory)) + fs::create_directories(preferencePackDirectory); + + // Create or update the saved user preferencePacks package.xml metadata file + std::unique_ptr metadata; + if (fs::exists(savedPreferencePacksDirectory / "package.xml")) { + metadata = std::make_unique(savedPreferencePacksDirectory / "package.xml"); + } + else { + // Create and set all of the required metadata to make it easier for PreferencePack authors to copy this + // file into their preferencePack distributions. + metadata = std::make_unique(); + metadata->setName("User-Saved PreferencePacks"); + metadata->setDescription("Generated automatically -- edits may be lost when saving new preferencePacks"); + metadata->setVersion(1); + metadata->addMaintainer(App::Meta::Contact("No Maintainer", "email@freecadweb.org")); + metadata->addLicense(App::Meta::License("(Unspecified)", "(Unspecified)")); + metadata->addUrl(App::Meta::Url("https://github.com/FreeCAD/FreeCAD", App::Meta::UrlType::repository)); + } + App::Metadata newPreferencePackMetadata; + newPreferencePackMetadata.setName(name); + newPreferencePackMetadata.setVersion(1); + + auto templateType = templates.front().type; + for (const auto& t : templates) { + if (t.type != templateType) { + templateType = PreferencePack::Type::Combination; + break; + } + } + std::string typeString; + switch (templateType) { + case PreferencePack::Type::Appearance: typeString = "appearance"; break; + case PreferencePack::Type::Behavior: typeString = "behavior"; break; + case PreferencePack::Type::Combination: typeString = "combination"; break; + } + newPreferencePackMetadata.addGenericMetadata("type", App::Meta::GenericMetadata(typeString)); + + metadata->addContentItem("preferencepack", newPreferencePackMetadata); + metadata->write(savedPreferencePacksDirectory / "package.xml"); + + // Create the config file + ParameterManager outputParameterManager; + outputParameterManager.CreateDocument(); + for (const auto& t : templates) { + ParameterManager templateParameterManager; + templateParameterManager.LoadDocument(t.path.string().c_str()); + copyTemplateParameters(templateParameterManager, outputParameterManager); + } + auto cfgFilename = savedPreferencePacksDirectory / name / (name + ".cfg"); + outputParameterManager.SaveDocument(cfgFilename.string().c_str()); +} + +// Needed until we support only C++20 and above and can use std::string's built-in ends_with() +bool fc_ends_with(std::string_view str, std::string_view suffix) +{ + return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; +} + +std::vector scanForTemplateFolders(const std::string& groupName, const fs::path& entry) +{ + // From this location, find the folder(s) called "PreferencePackTemplates" + std::vector templateFolders; + if (fs::exists(entry)) { + if (fs::is_directory(entry)) { + if (entry.filename() == "PreferencePackTemplates" || + entry.filename() == "preference_pack_templates") { + templateFolders.push_back(entry); + } + else { + std::string subgroupName = groupName + "/" + entry.filename().string(); + for (const auto& subentry : fs::directory_iterator(entry)) { + auto contents = scanForTemplateFolders(subgroupName, subentry); + std::copy(contents.begin(), contents.end(), std::back_inserter(templateFolders)); + } + } + } + } + return templateFolders; +} + +std::vector scanForTemplateFiles(const std::string& groupName, const fs::path& entry) +{ + auto templateFolders = scanForTemplateFolders(groupName, entry); + + std::vector templateFiles; + for (const auto& dir : templateFolders) { + auto templateDirs = std::vector>({ + std::make_pair(dir / "Appearance", PreferencePack::Type::Appearance), + std::make_pair(dir / "appearance", PreferencePack::Type::Appearance), + std::make_pair(dir / "Behavior", PreferencePack::Type::Behavior), + std::make_pair(dir / "behavior", PreferencePack::Type::Behavior), + std::make_pair(dir / "Behaviour", PreferencePack::Type::Behavior), + std::make_pair(dir / "behaviour", PreferencePack::Type::Behavior) }); + for (const auto& templateDir : templateDirs) { + if (!fs::exists(templateDir.first) || !fs::is_directory(templateDir.first)) + continue; + for (const auto& entry : fs::directory_iterator(templateDir.first)) { + if (entry.path().extension() == ".cfg") { + auto name = entry.path().filename().stem().string(); + std::replace(name.begin(), name.end(), '_', ' '); + // Make sure we don't insert the same thing twice... + if (std::find_if(templateFiles.begin(), templateFiles.end(), [groupName, name](const auto &rhs)->bool { + return groupName == rhs.group && name == rhs.name; + } ) != templateFiles.end()) + continue; + templateFiles.push_back({ groupName, name, entry, templateDir.second }); + } + } + } + } + return templateFiles; +} + +std::vector PreferencePackManager::templateFiles(bool rescan) +{ + std::lock_guard lock(_mutex); + if (!_templateFiles.empty() && !rescan) + return _templateFiles; + + // Locate all of the template files available on this system + // Template files end in ".cfg" -- They are located in: + // * $INSTALL_DIR/data/Gui/PreferencePackTemplates/(Appearance|Behavior)/* + // * $DATA_DIR/Mod/**/PreferencePackTemplates/(Appearance|Behavior)/* + // (alternate spellings are provided for packages using CamelCase and snake_case, and both major English dialects) + + auto resourcePath = fs::path(App::Application::getResourceDir()) / "Gui"; + auto modPath = fs::path(App::Application::getUserAppDataDir()) / "Mod"; + + std::string group = "Built-In"; + if (fs::exists(resourcePath) && fs::is_directory(resourcePath)) { + const auto localFiles = scanForTemplateFiles(group, resourcePath); + std::copy(localFiles.begin(), localFiles.end(), std::back_inserter(_templateFiles)); + } + + if (fs::exists(modPath) && fs::is_directory(modPath)) { + for (const auto& mod : fs::directory_iterator(modPath)) { + group = mod.path().filename().string(); + const auto localFiles = scanForTemplateFiles(group, mod); + std::copy(localFiles.begin(), localFiles.end(), std::back_inserter(_templateFiles)); + } + } + + return _templateFiles; +} + +void Gui::PreferencePackManager::BackupCurrentConfig() const +{ + auto backupDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks" / "Backups"; + fs::create_directories(backupDirectory); + + // Create a timestamped filename: + auto time = std::time(nullptr); + std::ostringstream timestampStream; + timestampStream << "user." << time << ".cfg"; + auto filename = backupDirectory / timestampStream.str(); + + // Save the current config: + App::GetApplication().GetUserParameter().SaveDocument(filename.string().c_str()); +} + +void Gui::PreferencePackManager::DeleteOldBackups() const +{ + constexpr auto oneWeek = 60.0 * 60.0 * 24.0 * 7.0; + const auto now = std::time(nullptr); + auto backupDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks" / "Backups"; + if (fs::exists(backupDirectory) && fs::is_directory(backupDirectory)) { + for (const auto& backup : fs::directory_iterator(backupDirectory)) { + if (std::difftime(now, fs::last_write_time(backup)) > oneWeek) { + try { + fs::remove(backup); + } + catch (...) {} + } + } + } +} \ No newline at end of file diff --git a/src/Gui/PreferencePackManager.h b/src/Gui/PreferencePackManager.h new file mode 100644 index 0000000000..33bc43bdf6 --- /dev/null +++ b/src/Gui/PreferencePackManager.h @@ -0,0 +1,190 @@ +/*************************************************************************** + * Copyright (c) 2021 Chris Hennes * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef BASE_THEMEMANAGER_H +#define BASE_THEMEMANAGER_H + +#include +#include +#include + +#include "App/Metadata.h" + +namespace Gui { + + /** + * \class PreferencePack A collection of user preferences stored in files on disk + */ + class PreferencePack { + + public: + + /** + * Construct a preferencePack from a directory + * + * \param path A path to a mod directory that contains a preferencePack + * \param metadata The metadata from the package.xml file describing this preferencePack + */ + PreferencePack(const boost::filesystem::path& path, const App::Metadata& metadata); + + ~PreferencePack() = default; + + /** + * Get the name of the PreferencePack + */ + std::string name() const; + + /** + * Apply the PreferencePack over the top of the current preferences set + * \returns True if the preferencePack was applied, or false if not + */ + bool apply() const; + + enum class Type { + Appearance, + Behavior, + Combination + }; + + /** + * Get the type of PreferencePack (appearance, behavior, or a combination of the two) + */ + Type type() const; + + private: + + void applyConfigChanges() const; + + boost::filesystem::path _path; + App::Metadata _metadata; + + }; + + + + + /** + * \class PreferencePackManager handles storable and loadable collections of user preferences + * + * This class provides some additional utility functions for allowing users to save their current + * preferences as a PreferencePack based on a set of template files provided either in the main + * FreeCAD distribution, or inside various installed mods. + */ + class PreferencePackManager { + public: + PreferencePackManager(); + ~PreferencePackManager() = default; + + /** + * Rescan the preferencePack directory and update the available PreferencePacks + */ + void rescan(); + + /** + * Get an alphabetical list of names of all installed PreferencePacks of a given type + */ + std::vector preferencePackNames(PreferencePack::Type type) const; + + /** + * Apply the named preferencePack + * \return True if the preferencePack was applied, or false if it was not + */ + bool apply(const std::string& preferencePackName) const; + + /** + * \struct TemplateFile A file containing a set of preferences that can be saved into + * a PreferencePack + * + * PreferencePacks can contain any parameters at all, but inside FreeCAD there is no + * centralized list of all of these parameters, and at any given time the user.cfg file + * usually does not store a value for all possible parameters. Instead, it simply allows + * calling code to use whatever default values that code sets. This is all completely + * hidden from the users: FreeCAD behaves as though those values exist in the config file. + * + * When a user saves their current configuration as a pack, they likely expect that saved + * configuration to include those default values, so that if they later apply their saved + * configuration those defaults are restored. To enable this a set of template files + * listing default values for various types of parameters can be used. Each file is + * presented to the user as a checkable box when saving a new preferences pack, and the + * intention is that these files will list out the various user-facing parameters that + * someone might want to save into a preferences pack. + * + * These files are themselves valid user.cfg files, that if loaded and applied will result + * in the default values of their contained variables being set. For this to work, these + * files should be kept up-to-date with the default values set in the code. They are not + * required to contain an exhaustive listing of all possible parameters: in most cases it + * is enough that they list the variables that a user would expect for a given name. For + * example, "Sketcher Colors.cfg" should list out all of the default colors used in + * Sketcher that a user can set in Preferences, but it is not necessary that it contain any + * color that is only used internally, and it should not include things like fonts, or + * behavior information. + * + * Template files are always located in a directory hierarchy that differentiates between + * templates that only affect appearance, and those that affect behavior. + * + * The base FreeCAD installation includes default templates in: + * $INSTALL_DIR/data/Gui/PreferencePackTemplates/(Appearance|Behavior)/ + * + * External add-ons are also searched for any directory called PreferencePackTemplates or + * preference_pack_templates -- either of which is expected to contain appearance and/or + * behavior subdirectories. In this way external add-ons can allow a user to easily save + * their preferences to a PreferencePack, or even to add additional templates representing + * sets of core FreeCAD preferences. + */ + struct TemplateFile { + std::string group; // Generally the Add-On/Mod/Package name + std::string name; + boost::filesystem::path path; + PreferencePack::Type type; + }; + + /** + * Save current settings as a (possibly new) preferencePack + * + * If the named preferencePack does not exist, this creates it on disk. If it does exist, this overwrites the original. + */ + void save(const std::string& name, const std::vector& templates); + + + std::vector templateFiles(bool rescan = false); + + private: + + void FindPreferencePacksInPackage(const boost::filesystem::path& mod); + + void BackupCurrentConfig() const; + + void DeleteOldBackups() const; + + std::vector _preferencePackPaths; + std::vector _templateFiles; + std::map _preferencePacks; + mutable std::mutex _mutex; + + }; + +} + +Q_DECLARE_METATYPE(Gui::PreferencePackManager::TemplateFile) // So it can be used with QVariant + + +#endif diff --git a/src/Gui/PreferencePackTemplates/Appearance/Arch_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/Arch_Colors.cfg new file mode 100644 index 0000000000..9b7d90987c --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/Arch_Colors.cfg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Appearance/CMakeLists.txt b/src/Gui/PreferencePackTemplates/Appearance/CMakeLists.txt new file mode 100644 index 0000000000..6cb3d4c0dc --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/CMakeLists.txt @@ -0,0 +1,26 @@ + +SET(PreferencePackTemplates_Files + Arch_Colors.cfg + Console_Colors.cfg + Draft_Colors.cfg + Editor_Colors.cfg + Path_Colors.cfg + Sketcher_Colors.cfg + Start_Colors.cfg + TechDraw_Colors.cfg + Window_Colors.cfg +) + +ADD_CUSTOM_TARGET(PreferencePackTemplates_data ALL + SOURCES ${PreferencePackTemplates_Files} +) + +fc_copy_sources(PreferencePackTemplates_data "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Gui/PreferencePackTemplates/Appearance" + ${PreferencePackTemplates_Files}) + +INSTALL( + FILES + ${PreferencePackTemplates_Files} + DESTINATION + ${CMAKE_INSTALL_DATADIR}/Gui/PreferencePackTemplates/Appearance +) \ No newline at end of file diff --git a/src/Gui/PreferencePackTemplates/Appearance/Console_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/Console_Colors.cfg new file mode 100644 index 0000000000..7e4f4c2d20 --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/Console_Colors.cfg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Appearance/Draft_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/Draft_Colors.cfg new file mode 100644 index 0000000000..7f68a8e53c --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/Draft_Colors.cfg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Appearance/Editor_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/Editor_Colors.cfg new file mode 100644 index 0000000000..93ecd1cff1 --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/Editor_Colors.cfg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Appearance/Path_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/Path_Colors.cfg new file mode 100644 index 0000000000..0780cf0255 --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/Path_Colors.cfg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Appearance/Sketcher_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/Sketcher_Colors.cfg new file mode 100644 index 0000000000..44b4d9f535 --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/Sketcher_Colors.cfg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Appearance/Start_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/Start_Colors.cfg new file mode 100644 index 0000000000..2465eae672 --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/Start_Colors.cfg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Appearance/TechDraw_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/TechDraw_Colors.cfg new file mode 100644 index 0000000000..7a1525ad91 --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/TechDraw_Colors.cfg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Appearance/Window_Colors.cfg b/src/Gui/PreferencePackTemplates/Appearance/Window_Colors.cfg new file mode 100644 index 0000000000..3821bc48a2 --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Appearance/Window_Colors.cfg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/Behavior/CMakeLists.txt b/src/Gui/PreferencePackTemplates/Behavior/CMakeLists.txt new file mode 100644 index 0000000000..7a6174cc9e --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Behavior/CMakeLists.txt @@ -0,0 +1,18 @@ + +SET(PreferencePackBehaviorTemplates_Files + Main_window_layout.cfg +) + +ADD_CUSTOM_TARGET(PreferencePackBehaviorTemplates_data ALL + SOURCES ${PreferencePackBehaviorTemplates_Files} +) + +fc_copy_sources(PreferencePackBehaviorTemplates_data "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Gui/PreferencePackTemplates/Behavior" + ${PreferencePackBehaviorTemplates_Files}) + +INSTALL( + FILES + ${PreferencePackBehaviorTemplates_Files} + DESTINATION + ${CMAKE_INSTALL_DATADIR}/Gui/PreferencePackTemplates/Behavior +) \ No newline at end of file diff --git a/src/Gui/PreferencePackTemplates/Behavior/Main_window_layout.cfg b/src/Gui/PreferencePackTemplates/Behavior/Main_window_layout.cfg new file mode 100644 index 0000000000..1e9c8d023c --- /dev/null +++ b/src/Gui/PreferencePackTemplates/Behavior/Main_window_layout.cfg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePackTemplates/CMakeLists.txt b/src/Gui/PreferencePackTemplates/CMakeLists.txt new file mode 100644 index 0000000000..b5c71577cf --- /dev/null +++ b/src/Gui/PreferencePackTemplates/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(Appearance) +add_subdirectory(Behavior) \ No newline at end of file diff --git a/src/Gui/PreferencePacks/CMakeLists.txt b/src/Gui/PreferencePacks/CMakeLists.txt new file mode 100644 index 0000000000..e7c235b9e0 --- /dev/null +++ b/src/Gui/PreferencePacks/CMakeLists.txt @@ -0,0 +1,28 @@ + +SET(PreferencePacks_Files +"package.xml" +) + +SET(PreferencePacks_Directories +"FreeCAD Classic Colors" +) + +ADD_CUSTOM_TARGET(PreferencePacks_data ALL +SOURCES ${PreferencePacks_Files} ${PreferencePacks_Directories} +) + +FILE(COPY ${PreferencePacks_Files} ${PreferencePacks_Directories} DESTINATION "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Gui/PreferencePacks") + +INSTALL( +FILES + ${PreferencePacks_Files} +DESTINATION + ${CMAKE_INSTALL_DATADIR}/Gui/PreferencePacks +) + +INSTALL( +DIRECTORY + ${PreferencePacks_Directories} +DESTINATION + ${CMAKE_INSTALL_DATADIR}/Gui/PreferencePacks +) \ No newline at end of file diff --git a/src/Gui/PreferencePacks/FreeCAD Classic Colors/FreeCAD Classic Colors.cfg b/src/Gui/PreferencePacks/FreeCAD Classic Colors/FreeCAD Classic Colors.cfg new file mode 100644 index 0000000000..c18797a155 --- /dev/null +++ b/src/Gui/PreferencePacks/FreeCAD Classic Colors/FreeCAD Classic Colors.cfg @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/PreferencePacks/package.xml b/src/Gui/PreferencePacks/package.xml new file mode 100644 index 0000000000..4791afca43 --- /dev/null +++ b/src/Gui/PreferencePacks/package.xml @@ -0,0 +1,19 @@ + + + Built-In Preference Packs + Preference Packs included with the FreeCAD distribution + 1.0.0 + No Maintainer + LGPL2 + https://github.com/FreeCAD/FreeCAD + + + + FreeCAD Classic Colors + FreeCAD default colors for core app and included Mods. + 1.0.0 + appearance + + + +