[GUI] Add PreferencePack support

Preference Packs are collections of preferences that can be applied en
mass to the user's current setup. Any preference that can be stored in
user.cfg can be stored in a preference pack, and they are designed to be
easy to distribute.

Support is also added for saving a subset of current preferences into a
new preference pack in order to facilitate easy creation of new
"themes", etc.
This commit is contained in:
Chris Hennes
2021-04-19 12:19:50 -05:00
parent 30fd2ce5b8
commit 858e88afe1
29 changed files with 1858 additions and 32 deletions

View File

@@ -44,6 +44,7 @@
#endif
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/filesystem.hpp>
#include <QtOpenGL.h>
#if defined(HAVE_QT5_OPENGL)
#include <QWindow>
@@ -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<Gui::BaseView*> 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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::Dialog::DlgCreateNewPreferencePack</class>
<widget class="QDialog" name="Gui::Dialog::DlgCreateNewPreferencePack">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>580</width>
<height>520</height>
</rect>
</property>
<property name="windowTitle">
<string>Create New Preference Pack</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
</layout>
</item>
<item>
<widget class="QTreeWidget" name="treeWidget">
<attribute name="headerMinimumSectionSize">
<number>50</number>
</attribute>
<attribute name="headerDefaultSectionSize">
<number>250</number>
</attribute>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>Property group templates</string>
</property>
</column>
<column>
<property name="text">
<string>Template Type</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Gui::Dialog::DlgCreateNewPreferencePack</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Gui::Dialog::DlgCreateNewPreferencePack</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,153 @@
/***************************************************************************
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
* *
* 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 <QPushButton>
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<Gui::PreferencePackManager::TemplateFile>& 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<Gui::PreferencePackManager::TemplateFile> DlgCreateNewPreferencePackImp::selectedTemplates() const
{
std::vector<Gui::PreferencePackManager::TemplateFile> 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<Gui::PreferencePackManager::TemplateFile>())
results.push_back(child->data(0, TemplateRole).value<Gui::PreferencePackManager::TemplateFile>());
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"

View File

@@ -0,0 +1,78 @@
/***************************************************************************
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
* *
* 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 <memory>
#include <QDialog>
#include <QRegExpValidator>
#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<PreferencePackManager::TemplateFile> &availableTemplates);
std::vector<PreferencePackManager::TemplateFile> 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_DlgCreateNewPreferencePack> ui;
std::map<std::string, QTreeWidgetItem*> _groups;
std::vector<PreferencePackManager::TemplateFile> _templates;
QRegExpValidator _nameValidator;
};
} // namespace Dialog
} // namespace Gui
#endif // GUI_DIALOG_DLGCREATENEWTHEMEIMP_H

View File

@@ -14,7 +14,16 @@
<string>General</string>
</property>
<layout class="QGridLayout">
<property name="margin">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
@@ -32,7 +41,16 @@
<string>Language</string>
</property>
<layout class="QGridLayout">
<property name="margin">
<property name="leftMargin">
<number>11</number>
</property>
<property name="topMargin">
<number>11</number>
</property>
<property name="rightMargin">
<number>11</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<property name="spacing">
@@ -43,7 +61,16 @@
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -65,6 +92,146 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Preference Packs</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QTableWidget" name="PreferencePacks">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>75</number>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>16</number>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>24</number>
</attribute>
<column>
<property name="text">
<string>Name</string>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
</column>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="ApplyPreferencePack">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Run</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SaveNewPreferencePack">
<property name="text">
<string>Save new...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ManagePreferencePacks">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Manage...</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="GroupBox3">
<property name="title">
@@ -74,7 +241,16 @@
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>11</number>
</property>
<property name="topMargin">
<number>11</number>
</property>
<property name="rightMargin">
<number>11</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<item>
@@ -82,7 +258,16 @@
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -131,7 +316,16 @@
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -152,7 +346,16 @@ See the FreeCAD Wiki for details about the image.</string>
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -176,7 +379,16 @@ See the FreeCAD Wiki for details about the image.</string>
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -201,7 +413,16 @@ this according to your screen size or personal taste</string>
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -214,7 +435,7 @@ this according to your screen size or personal taste</string>
<item>
<widget class="QComboBox" name="treeMode">
<property name="toolTip">
<string>Customize how tree view is shown in the panel (restart required).
<string>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</string>
<string>Start up</string>
</property>
<layout class="QGridLayout">
<property name="margin">
<property name="leftMargin">
<number>11</number>
</property>
<property name="topMargin">
<number>11</number>
</property>
<property name="rightMargin">
<number>11</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<property name="spacing">
@@ -262,7 +492,16 @@ display the splash screen</string>
</item>
<item row="0" column="0">
<layout class="QGridLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
@@ -294,7 +533,16 @@ after FreeCAD launches</string>
<string>Python console</string>
</property>
<layout class="QGridLayout" name="_4">
<property name="margin">
<property name="leftMargin">
<number>11</number>
</property>
<property name="topMargin">
<number>11</number>
</property>
<property name="rightMargin">
<number>11</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<property name="spacing">
@@ -323,19 +571,6 @@ horizontal space in Python console</string>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>352</width>
<height>221</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
@@ -353,11 +588,6 @@ horizontal space in Python console</string>
<extends>QSpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefComboBox</class>
<extends>QComboBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>Languages</tabstop>

View File

@@ -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<DlgCreateNewPreferencePackImp>(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<PreferencePackManager::TemplateFile> 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<DlgPreferencesImp*> (this->window());
if (parentDialog)
parentDialog->reload();
}
}
}
}
#include "moc_DlgGeneralImp.cpp"

View File

@@ -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_DlgGeneral> ui;
std::unique_ptr<DlgCreateNewPreferencePackImp> newPreferencePackDialog;
};
} // namespace Dialog

View File

@@ -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<PreferencePage*>(tabWidget->widget(j));
if (page)
page->loadSettings();
}
}
applyChanges();
}
#include "moc_DlgPreferencesImp.cpp"

View File

@@ -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:

View File

@@ -0,0 +1,454 @@
/***************************************************************************
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
* *
* 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 <memory>
# include <string_view>
# include <mutex>
#endif
#include <boost/filesystem.hpp>
#include <QDir>
#include "PreferencePackManager.h"
#include "App/Metadata.h"
#include "Base/Parameter.h"
#include "Base/Interpreter.h"
#include "Base/Console.h"
#include <App/Application.h>
#include <ctime> // 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<std::mutex> 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<std::string> PreferencePackManager::preferencePackNames(PreferencePack::Type type) const
{
std::lock_guard<std::mutex> lock(_mutex);
std::vector<std::string> 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<std::mutex> 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<ParameterGrp> templateGroup, const std::string& path, Base::Reference<ParameterGrp> 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<TemplateFile>& templates)
{
if (templates.empty())
return;
std::lock_guard<std::mutex> 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<App::Metadata> metadata;
if (fs::exists(savedPreferencePacksDirectory / "package.xml")) {
metadata = std::make_unique<App::Metadata>(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<App::Metadata>();
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<fs::path> scanForTemplateFolders(const std::string& groupName, const fs::path& entry)
{
// From this location, find the folder(s) called "PreferencePackTemplates"
std::vector<fs::path> 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<PreferencePackManager::TemplateFile> scanForTemplateFiles(const std::string& groupName, const fs::path& entry)
{
auto templateFolders = scanForTemplateFolders(groupName, entry);
std::vector<PreferencePackManager::TemplateFile> templateFiles;
for (const auto& dir : templateFolders) {
auto templateDirs = std::vector<std::pair<fs::path, PreferencePack::Type>>({
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::TemplateFile> PreferencePackManager::templateFiles(bool rescan)
{
std::lock_guard<std::mutex> 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 (...) {}
}
}
}
}

View File

@@ -0,0 +1,190 @@
/***************************************************************************
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
* *
* 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 <vector>
#include <string>
#include <mutex>
#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<std::string> 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<TemplateFile>& templates);
std::vector<TemplateFile> templateFiles(bool rescan = false);
private:
void FindPreferencePacksInPackage(const boost::filesystem::path& mod);
void BackupCurrentConfig() const;
void DeleteOldBackups() const;
std::vector<boost::filesystem::path> _preferencePackPaths;
std::vector<TemplateFile> _templateFiles;
std::map<std::string, PreferencePack> _preferencePacks;
mutable std::mutex _mutex;
};
}
Q_DECLARE_METATYPE(Gui::PreferencePackManager::TemplateFile) // So it can be used with QVariant
#endif

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="Mod">
<FCParamGroup Name="Arch">
<FCUInt Name="WallColor" Value="3604403967"/>
<FCUInt Name="StructureColor" Value="2527705855"/>
<FCUInt Name="RebarColor" Value="3111475967"/>
<FCUInt Name="WindowColor" Value="556614399"/>
<FCUInt Name="WindowGlassColor" Value="1572326399"/>
<FCUInt Name="PanelColor" Value="3416289279"/>
<FCUInt Name="ColorHelpers" Value="674321151"/>
<FCUInt Name="defaultSpaceColor" Value="4280090879"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -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
)

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="OutputWindow">
<FCUInt Name="colorText" Value="4294967295"/>
<FCUInt Name="colorLogging" Value="3117350911"/>
<FCUInt Name="colorWarning" Value="4290999551"/>
<FCUInt Name="colorError" Value="4287006463"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="Mod">
<FCParamGroup Name="Draft">
<FCUInt Name="constructioncolor" Value="746455039"/>
<FCInt Name="gridTransparency" Value="0"/>
<FCUInt Name="gridColor" Value="842157055"/>
<FCUInt Name="snapcolor" Value="4294967295"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="Editor">
<FCUInt Name="Text" Value="4294967040"/>
<FCUInt Name="Bookmark" Value="15921664"/>
<FCUInt Name="Breakpoint" Value="3992977408"/>
<FCUInt Name="Keyword" Value="195493632"/>
<FCUInt Name="Comment" Value="14483456"/>
<FCUInt Name="Block comment" Value="2526452224"/>
<FCUInt Name="Number" Value="1625816832"/>
<FCUInt Name="String" Value="4283782400"/>
<FCUInt Name="Character" Value="4281611264"/>
<FCUInt Name="Class name" Value="4003397632"/>
<FCUInt Name="Define name" Value="3784704000"/>
<FCUInt Name="Operator" Value="3553875968"/>
<FCUInt Name="Python output" Value="3705447680"/>
<FCUInt Name="Python error" Value="4287203584"/>
<FCUInt Name="Current line highlight" Value="774778368"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="Mod">
<FCParamGroup Name="Path">
<FCUInt Name="DefaultNormalPathColor" Value="11141375"/>
<FCUInt Name="DefaultRapidPathColor" Value="2852126975"/>
<FCUInt Name="DefaultPathMarkerColor" Value="1442775295"/>
<FCUInt Name="DefaultExtentsColor" Value="3823363071"/>
<FCUInt Name="DefaultProbePathColor" Value="4294903295"/>
<FCUInt Name="DefaultHighlightPathColor" Value="4286382335"/>
<FCUInt Name="DefaultBBoxSelectionColor" Value="3372220415"/>
<FCUInt Name="DefaultBBoxNormalColor" Value="4294967295"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="View">
<FCUInt Name="SketchEdgeColor" Value="4294967295"/>
<FCUInt Name="SketchVertexColor" Value="4294967295"/>
<FCUInt Name="EditedEdgeColor" Value="4294967295"/>
<FCUInt Name="EditedVertexColor" Value="4280680703"/>
<FCUInt Name="ConstructionColor" Value="56575"/>
<FCUInt Name="ExternalColor" Value="3425924095"/>
<FCUInt Name="InvalidSketchColor" Value="4285333759"/>
<FCUInt Name="FullyConstrainedColor" Value="16711935"/>
<FCUInt Name="InternalAlignedGeoColor" Value="2998042623"/>
<FCUInt Name="FullyConstraintElementColor" Value="2161156351"/>
<FCUInt Name="FullyConstraintConstructionElementColor" Value="2410282495"/>
<FCUInt Name="FullyConstraintInternalAlignmentColor" Value="3739142399"/>
<FCUInt Name="FullyConstraintConstructionPointColor" Value="4287987967"/>
<FCUInt Name="ConstrainedIcoColor" Value="4280680703"/>
<FCUInt Name="NonDrivingConstrDimColor" Value="2555903"/>
<FCUInt Name="ConstrainedDimColor" Value="4280680703"/>
<FCUInt Name="ExprBasedConstrDimColor" Value="4286523135"/>
<FCUInt Name="DeactivatedConstrDimColor" Value="2139062271"/>
<FCUInt Name="CursorTextColor" Value="65535"/>
<FCUInt Name="CursorCrosshairColor" Value="4294967295"/>
<FCUInt Name="CreateLineColor" Value="3435973887"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="Mod">
<FCParamGroup Name="Start">
<FCUInt Name="BackgroundColor1" Value="1414812927"/>
<FCUInt Name="BackgroundTextColor" Value="4294967295"/>
<FCUInt Name="PageColor" Value="690563583"/>
<FCUInt Name="PageTextColor" Value="3789677055"/>
<FCUInt Name="BoxColor" Value="1296911871"/>
<FCUInt Name="LinkColor" Value="3099197439"/>
<FCUInt Name="BackgroundColor2" Value="2141107711"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="Mod">
<FCParamGroup Name="TechDraw">
<FCParamGroup Name="Decorations">
<FCUInt Name="SectionColor" Value="255"/>
<FCUInt Name="CenterColor" Value="255"/>
<FCUInt Name="VertexColor" Value="255"/>
<FCUInt Name="HighlightColor" Value="255"/>
</FCParamGroup>
<FCParamGroup Name="Colors">
<FCUInt Name="Hatch" Value="255"/>
<FCUInt Name="Background" Value="3553874943"/>
<FCUInt Name="PreSelectColor" Value="4294902015"/>
<FCUInt Name="HiddenColor" Value="255"/>
<FCUInt Name="SelectColor" Value="16711935"/>
<FCUInt Name="NormalColor" Value="255"/>
<FCUInt Name="CutSurfaceColor" Value="3553874943"/>
<FCUInt Name="GeomHatch" Value="255"/>
<FCUInt Name="FaceColor" Value="4294967295"/>
<FCBool Name="ClearFace" Value="1"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="View">
<FCUInt Name="BacklightColor" Value="3755991039"/>
<FCUInt Name="BackgroundColor" Value="336897023"/>
<FCUInt Name="BackgroundColor2" Value="1701144063"/>
<FCUInt Name="BackgroundColor3" Value="757935615"/>
<FCUInt Name="BackgroundColor4" Value="1869583359"/>
<FCBool Name="Simple" Value="0"/>
<FCBool Name="Gradient" Value="1"/>
<FCBool Name="UseBackgroundColorMid" Value="0"/>
<FCUInt Name="HighlightColor" Value="4294907903"/>
<FCUInt Name="SelectionColor" Value="704588287"/>
<FCUInt Name="DefaultShapeColor" Value="3435973887"/>
<FCBool Name="RandomColor" Value="0"/>
</FCParamGroup>
<FCParamGroup Name="TreeView">
<FCUInt Name="TreeEditColor" Value="4042260735"/>
<FCUInt Name="TreeActiveColor" Value="3537037823"/>
</FCParamGroup>
<FCParamGroup Name="MainWindow">
<FCBool Name="TiledBackground" Value="0"/>
<FCText Name="StyleSheet"></FCText>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -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
)

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="MainWindow">
<FCParamGroup Name="DockWindows">
<FCBool Name="Std_SelectionView" Value="0"/>
<FCBool Name="Std_ComboView" Value="1"/>
<FCBool Name="Std_ReportView" Value="0"/>
<FCBool Name="Std_PythonView" Value="0"/>
<FCBool Name="Std_TreeView" Value="0"/>
<FCBool Name="Std_PropertyView" Value="0"/>
<FCBool Name="Std_DAGView" Value="0"/>
</FCParamGroup>
<FCParamGroup Name="Toolbars">
<FCBool Name="File" Value="1"/>
<FCBool Name="Workbench" Value="1"/>
<FCBool Name="Macro" Value="1"/>
<FCBool Name="View" Value="1"/>
<FCBool Name="Structure" Value="1"/>
<FCBool Name="Navigation" Value="1"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,2 @@
add_subdirectory(Appearance)
add_subdirectory(Behavior)

View File

@@ -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
)

View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<FCParameters>
<FCParamGroup Name="Root">
<FCParamGroup Name="BaseApp">
<FCParamGroup Name="Preferences">
<FCParamGroup Name="Mod">
<FCParamGroup Name="Arch">
<FCUInt Name="WallColor" Value="3604403967"/>
<FCUInt Name="StructureColor" Value="2527705855"/>
<FCUInt Name="RebarColor" Value="3111475967"/>
<FCUInt Name="WindowColor" Value="556614399"/>
<FCUInt Name="WindowGlassColor" Value="1572326399"/>
<FCUInt Name="PanelColor" Value="3416289279"/>
<FCUInt Name="ColorHelpers" Value="674321151"/>
<FCUInt Name="defaultSpaceColor" Value="4280090879"/>
</FCParamGroup>
<FCParamGroup Name="Draft">
<FCInt Name="gridTransparency" Value="0"/>
<FCUInt Name="constructioncolor" Value="746455039"/>
<FCUInt Name="gridColor" Value="842157055"/>
<FCUInt Name="snapcolor" Value="4294967295"/>
</FCParamGroup>
<FCParamGroup Name="Path">
<FCUInt Name="DefaultNormalPathColor" Value="11141375"/>
<FCUInt Name="DefaultRapidPathColor" Value="2852126975"/>
<FCUInt Name="DefaultPathMarkerColor" Value="1442775295"/>
<FCUInt Name="DefaultExtentsColor" Value="3823363071"/>
<FCUInt Name="DefaultProbePathColor" Value="4294903295"/>
<FCUInt Name="DefaultHighlightPathColor" Value="4286382335"/>
<FCUInt Name="DefaultBBoxSelectionColor" Value="3372220415"/>
<FCUInt Name="DefaultBBoxNormalColor" Value="4294967295"/>
</FCParamGroup>
<FCParamGroup Name="Start">
<FCUInt Name="BackgroundColor1" Value="1331197183"/>
<FCUInt Name="BackgroundTextColor" Value="4294703103"/>
<FCUInt Name="PageColor" Value="4294967295"/>
<FCUInt Name="PageTextColor" Value="255"/>
<FCUInt Name="BoxColor" Value="3722305023"/>
<FCUInt Name="LinkColor" Value="65535"/>
<FCUInt Name="BackgroundColor2" Value="2141107711"/>
</FCParamGroup>
<FCParamGroup Name="TechDraw">
<FCParamGroup Name="Decorations">
<FCUInt Name="SectionColor" Value="255"/>
<FCUInt Name="CenterColor" Value="255"/>
<FCUInt Name="VertexColor" Value="255"/>
<FCUInt Name="HighlightColor" Value="255"/>
</FCParamGroup>
<FCParamGroup Name="Colors">
<FCBool Name="ClearFace" Value="1"/>
<FCUInt Name="Hatch" Value="255"/>
<FCUInt Name="Background" Value="3553874943"/>
<FCUInt Name="PreSelectColor" Value="4294902015"/>
<FCUInt Name="HiddenColor" Value="255"/>
<FCUInt Name="SelectColor" Value="16711935"/>
<FCUInt Name="NormalColor" Value="255"/>
<FCUInt Name="CutSurfaceColor" Value="3553874943"/>
<FCUInt Name="GeomHatch" Value="255"/>
<FCUInt Name="FaceColor" Value="4294967295"/>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
<FCParamGroup Name="OutputWindow">
<FCUInt Name="colorText" Value="255"/>
<FCUInt Name="colorLogging" Value="65535"/>
<FCUInt Name="colorWarning" Value="4289331455"/>
<FCUInt Name="colorError" Value="4278190335"/>
</FCParamGroup>
<FCParamGroup Name="Editor">
<FCUInt Name="Text" Value="0"/>
<FCUInt Name="Bookmark" Value="16776960"/>
<FCUInt Name="Breakpoint" Value="4278190080"/>
<FCUInt Name="Keyword" Value="65280"/>
<FCUInt Name="Comment" Value="11141120"/>
<FCUInt Name="Block comment" Value="2694882304"/>
<FCUInt Name="Number" Value="65280"/>
<FCUInt Name="String" Value="4278190080"/>
<FCUInt Name="Character" Value="4278190080"/>
<FCUInt Name="Class name" Value="4289331200"/>
<FCUInt Name="Define name" Value="4289331200"/>
<FCUInt Name="Operator" Value="2694882304"/>
<FCUInt Name="Python output" Value="2863300352"/>
<FCUInt Name="Python error" Value="4278190080"/>
<FCUInt Name="Current line highlight" Value="3772833792"/>
</FCParamGroup>
<FCParamGroup Name="View">
<FCUInt Name="SketchEdgeColor" Value="4294967295"/>
<FCUInt Name="SketchVertexColor" Value="4294967295"/>
<FCUInt Name="EditedEdgeColor" Value="4294967295"/>
<FCUInt Name="EditedVertexColor" Value="4280680703"/>
<FCUInt Name="ConstructionColor" Value="56575"/>
<FCUInt Name="ExternalColor" Value="3425924095"/>
<FCUInt Name="InvalidSketchColor" Value="4285333759"/>
<FCUInt Name="FullyConstrainedColor" Value="16711935"/>
<FCUInt Name="InternalAlignedGeoColor" Value="2998042623"/>
<FCUInt Name="FullyConstraintElementColor" Value="2161156351"/>
<FCUInt Name="FullyConstraintConstructionElementColor" Value="2410282495"/>
<FCUInt Name="FullyConstraintInternalAlignmentColor" Value="3739142399"/>
<FCUInt Name="FullyConstraintConstructionPointColor" Value="4287987967"/>
<FCUInt Name="ConstrainedIcoColor" Value="4280680703"/>
<FCUInt Name="NonDrivingConstrDimColor" Value="2555903"/>
<FCUInt Name="ConstrainedDimColor" Value="4280680703"/>
<FCUInt Name="ExprBasedConstrDimColor" Value="4286523135"/>
<FCUInt Name="DeactivatedConstrDimColor" Value="2139062271"/>
<FCUInt Name="CursorTextColor" Value="65535"/>
<FCUInt Name="CursorCrosshairColor" Value="4294967295"/>
<FCUInt Name="CreateLineColor" Value="3435973887"/>
<FCBool Name="Simple" Value="0"/>
<FCBool Name="Gradient" Value="1"/>
<FCBool Name="UseBackgroundColorMid" Value="0"/>
<FCBool Name="RandomColor" Value="0"/>
<FCUInt Name="BacklightColor" Value="4294967295"/>
<FCUInt Name="BackgroundColor" Value="336897023"/>
<FCUInt Name="BackgroundColor2" Value="859006463"/>
<FCUInt Name="BackgroundColor3" Value="2543299327"/>
<FCUInt Name="BackgroundColor4" Value="1869583359"/>
<FCUInt Name="HighlightColor" Value="3789624575"/>
<FCUInt Name="SelectionColor" Value="481107199"/>
<FCUInt Name="DefaultShapeColor" Value="3435973887"/>
</FCParamGroup>
<FCParamGroup Name="TreeView">
<FCUInt Name="TreeEditColor" Value="4294902015"/>
<FCUInt Name="TreeActiveColor" Value="3873898495"/>
</FCParamGroup>
<FCParamGroup Name="MainWindow">
<FCBool Name="TiledBackground" Value="0"/>
<FCText Name="StyleSheet"></FCText>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParamGroup>
</FCParameters>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<package format="1">
<name>Built-In Preference Packs</name>
<description>Preference Packs included with the FreeCAD distribution</description>
<version>1.0.0</version>
<maintainer email="email@freecadweb.org">No Maintainer</maintainer>
<license file="../../LICENSE">LGPL2</license>
<url type="repository">https://github.com/FreeCAD/FreeCAD</url>
<content>
<preferencepack>
<name>FreeCAD Classic Colors</name>
<description>FreeCAD default colors for core app and included Mods.</description>
<version>1.0.0</version>
<type>appearance</type>
</preferencepack>
</content>
</package>