Implement a platform abstraction for settings.

This commit mostly just changes the settings code to be in line with
the rest of the platform abstractions, although it also fixes some
settings names to be consistent with others, and uses native bool
types where applicable.

This commit also makes settings-related operations much less
wasteful, not that it should matter.
This commit is contained in:
whitequark 2018-07-16 10:37:41 +00:00
parent f324477dd0
commit eb5501ecd6
16 changed files with 526 additions and 502 deletions

View File

@ -283,7 +283,7 @@ void GraphicsWindow::PopulateMainMenu() {
for(const Locale &locale : Locales()) {
localeMenu->AddItem(locale.displayName, [&]() {
SetLocale(locale.Culture());
CnfFreezeString(locale.Culture(), "Locale");
Platform::GetSettings()->FreezeString("Locale", locale.Culture());
SS.UpdateWindowTitles();
PopulateMainMenu();

View File

@ -17,17 +17,6 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *) {
// Nothing to do for now.
}
void SolveSpace::CnfFreezeInt(uint32_t, const std::string &)
{
abort();
}
uint32_t SolveSpace::CnfThawInt(uint32_t, const std::string &)
{
abort();
return 0;
}
void SolveSpace::DoMessageBox(const char *, int, int, bool)
{
abort();

View File

@ -19,49 +19,6 @@ static NSString* Wrap(const std::string &s) {
return [NSString stringWithUTF8String:s.c_str()];
}
/* Settings */
namespace SolveSpace {
void CnfFreezeInt(uint32_t val, const std::string &key) {
[[NSUserDefaults standardUserDefaults]
setInteger:val forKey:Wrap(key)];
}
uint32_t CnfThawInt(uint32_t val, const std::string &key) {
NSString *nsKey = Wrap(key);
if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey])
return [[NSUserDefaults standardUserDefaults] integerForKey:nsKey];
return val;
}
void CnfFreezeFloat(float val, const std::string &key) {
[[NSUserDefaults standardUserDefaults]
setFloat:val forKey:Wrap(key)];
}
float CnfThawFloat(float val, const std::string &key) {
NSString *nsKey = Wrap(key);
if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey])
return [[NSUserDefaults standardUserDefaults] floatForKey:nsKey];
return val;
}
void CnfFreezeString(const std::string &val, const std::string &key) {
[[NSUserDefaults standardUserDefaults]
setObject:Wrap(val)
forKey:Wrap(key)];
}
std::string CnfThawString(const std::string &val, const std::string &key) {
NSString *nsKey = Wrap(key);
if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey]) {
NSString *nsNewVal = [[NSUserDefaults standardUserDefaults] stringForKey:nsKey];
return [nsNewVal UTF8String];
}
return val;
}
};
/* Save/load */
bool SolveSpace::GetOpenFile(Platform::Path *filename, const std::string &defExtension,

View File

@ -57,111 +57,6 @@ std::string Title(const std::string &s) {
return "SolveSpace - " + s;
}
/* Settings */
/* Why not just use GSettings? Two reasons. It doesn't allow to easily see
whether the setting had the default value, and it requires to install
a schema globally. */
static json_object *settings = NULL;
static std::string CnfPrepare() {
// Refer to http://standards.freedesktop.org/basedir-spec/latest/
std::string dir;
if(getenv("XDG_CONFIG_HOME")) {
dir = std::string(getenv("XDG_CONFIG_HOME")) + "/solvespace";
} else if(getenv("HOME")) {
dir = std::string(getenv("HOME")) + "/.config/solvespace";
} else {
dbp("neither XDG_CONFIG_HOME nor HOME are set");
return "";
}
struct stat st;
if(stat(dir.c_str(), &st)) {
if(errno == ENOENT) {
if(mkdir(dir.c_str(), 0777)) {
dbp("cannot mkdir %s: %s", dir.c_str(), strerror(errno));
return "";
}
} else {
dbp("cannot stat %s: %s", dir.c_str(), strerror(errno));
return "";
}
} else if(!S_ISDIR(st.st_mode)) {
dbp("%s is not a directory", dir.c_str());
return "";
}
return dir + "/settings.json";
}
static void CnfLoad() {
std::string path = CnfPrepare();
if(path.empty())
return;
if(settings)
json_object_put(settings); // deallocate
settings = json_object_from_file(path.c_str());
if(!settings) {
if(errno != ENOENT)
dbp("cannot load settings: %s", strerror(errno));
settings = json_object_new_object();
}
}
static void CnfSave() {
std::string path = CnfPrepare();
if(path.empty())
return;
/* json-c <0.12 has the first argument non-const here */
if(json_object_to_file_ext((char*) path.c_str(), settings, JSON_C_TO_STRING_PRETTY))
dbp("cannot save settings: %s", strerror(errno));
}
void CnfFreezeInt(uint32_t val, const std::string &key) {
struct json_object *jval = json_object_new_int(val);
json_object_object_add(settings, key.c_str(), jval);
CnfSave();
}
uint32_t CnfThawInt(uint32_t val, const std::string &key) {
struct json_object *jval;
if(json_object_object_get_ex(settings, key.c_str(), &jval))
return json_object_get_int(jval);
else return val;
}
void CnfFreezeFloat(float val, const std::string &key) {
struct json_object *jval = json_object_new_double(val);
json_object_object_add(settings, key.c_str(), jval);
CnfSave();
}
float CnfThawFloat(float val, const std::string &key) {
struct json_object *jval;
if(json_object_object_get_ex(settings, key.c_str(), &jval))
return json_object_get_double(jval);
else return val;
}
void CnfFreezeString(const std::string &val, const std::string &key) {
struct json_object *jval = json_object_new_string(val.c_str());
json_object_object_add(settings, key.c_str(), jval);
CnfSave();
}
std::string CnfThawString(const std::string &val, const std::string &key) {
struct json_object *jval;
if(json_object_object_get_ex(settings, key.c_str(), &jval))
return json_object_get_string(jval);
return val;
}
/* Save/load */
static std::string ConvertFilters(std::string active, const FileFilter ssFilters[],
@ -202,12 +97,12 @@ bool GetOpenFile(Platform::Path *filename, const std::string &activeOrEmpty,
chooser.set_filename(filename->raw);
chooser.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
chooser.add_button(_("_Open"), Gtk::RESPONSE_OK);
chooser.set_current_folder(CnfThawString("", "FileChooserPath"));
chooser.set_current_folder(Platform::GetSettings()->ThawString("FileChooserPath"));
ConvertFilters(activeOrEmpty, filters, &chooser);
if(chooser.run() == Gtk::RESPONSE_OK) {
CnfFreezeString(chooser.get_current_folder(), "FileChooserPath");
Platform::GetSettings()->FreezeString("FileChooserPath", chooser.get_current_folder());
*filename = Platform::Path::From(chooser.get_filename());
return true;
} else {
@ -250,7 +145,7 @@ bool GetSaveFile(Platform::Path *filename, const std::string &defExtension,
std::string activeExtension = ConvertFilters(defExtension, filters, &chooser);
if(filename->IsEmpty()) {
chooser.set_current_folder(CnfThawString("", "FileChooserPath"));
chooser.set_current_folder(Platform::GetSettings()->ThawString("FileChooserPath"));
chooser.set_current_name(std::string(_("untitled")) + "." + activeExtension);
} else {
chooser.set_current_folder(filename->Parent().raw);
@ -263,7 +158,7 @@ bool GetSaveFile(Platform::Path *filename, const std::string &defExtension,
connect(sigc::bind(sigc::ptr_fun(&ChooserFilterChanged), &chooser));
if(chooser.run() == Gtk::RESPONSE_OK) {
CnfFreezeString(chooser.get_current_folder(), "FileChooserPath");
Platform::GetSettings()->FreezeString("FileChooserPath", chooser.get_current_folder());
*filename = Platform::Path::From(chooser.get_filename());
return true;
} else {
@ -458,8 +353,6 @@ int main(int argc, char** argv) {
gdk_window_add_filter(NULL, GdkSpnavFilter, NULL);
#endif
CnfLoad();
const char* const* langNames = g_get_language_names();
while(*langNames) {
if(SetLocale(*langNames++)) break;

View File

@ -8,6 +8,10 @@
namespace SolveSpace {
namespace Platform {
//-----------------------------------------------------------------------------
// Keyboard events
//-----------------------------------------------------------------------------
std::string AcceleratorDescription(const KeyboardEvent &accel) {
std::string label;
if(accel.controlDown) {
@ -45,5 +49,25 @@ std::string AcceleratorDescription(const KeyboardEvent &accel) {
return label;
}
//-----------------------------------------------------------------------------
// Settings
//-----------------------------------------------------------------------------
void Settings::FreezeBool(const std::string &key, bool value) {
FreezeInt(key, (int)value);
}
bool Settings::ThawBool(const std::string &key, bool defaultValue) {
return (bool)ThawInt(key, (int)defaultValue);
}
void Settings::FreezeColor(const std::string &key, RgbaColor value) {
FreezeInt(key, value.ToPackedInt());
}
RgbaColor Settings::ThawColor(const std::string &key, RgbaColor defaultValue) {
return RgbaColor::FromPackedInt(ThawInt(key, defaultValue.ToPackedInt()));
}
}
}

View File

@ -76,6 +76,32 @@ std::string AcceleratorDescription(const KeyboardEvent &accel);
// Interfaces
//-----------------------------------------------------------------------------
// A native settings store.
class Settings {
public:
virtual ~Settings() {}
virtual void FreezeInt(const std::string &key, uint32_t value) = 0;
virtual uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) = 0;
virtual void FreezeFloat(const std::string &key, double value) = 0;
virtual double ThawFloat(const std::string &key, double defaultValue = 0.0) = 0;
virtual void FreezeString(const std::string &key, const std::string &value) = 0;
virtual std::string ThawString(const std::string &key,
const std::string &defaultValue = "") = 0;
virtual void FreezeBool(const std::string &key, bool value);
virtual bool ThawBool(const std::string &key, bool defaultValue = false);
virtual void FreezeColor(const std::string &key, RgbaColor value);
virtual RgbaColor ThawColor(const std::string &key, RgbaColor defaultValue);
};
typedef std::shared_ptr<Settings> SettingsRef;
SettingsRef GetSettings();
// A native single-shot timer.
class Timer {
public:
@ -191,8 +217,8 @@ public:
virtual void GetContentSize(double *width, double *height) = 0;
virtual void SetMinContentSize(double width, double height) = 0;
virtual void FreezePosition(const std::string &key) = 0;
virtual void ThawPosition(const std::string &key) = 0;
virtual void FreezePosition(SettingsRef settings, const std::string &key) = 0;
virtual void ThawPosition(SettingsRef settings, const std::string &key) = 0;
virtual void SetCursor(Cursor cursor) = 0;
virtual void SetTooltip(const std::string &text) = 0;

View File

@ -4,6 +4,11 @@
// Copyright 2018 whitequark
//-----------------------------------------------------------------------------
#include "solvespace.h"
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <json-c/json_object.h>
#include <json-c/json_util.h>
#include <glibmm/main.h>
#include <gtkmm/box.h>
#include <gtkmm/checkmenuitem.h>
@ -20,6 +25,128 @@
namespace SolveSpace {
namespace Platform {
//-----------------------------------------------------------------------------
// Settings
//-----------------------------------------------------------------------------
class SettingsImplGtk : public Settings {
public:
// Why aren't we using GSettings? Two reasons. It doesn't allow to easily see whether
// the setting had the default value, and it requires to install a schema globally.
Path _path;
json_object *_json = NULL;
static Path GetConfigPath() {
Path configHome;
if(getenv("XDG_CONFIG_HOME")) {
configHome = Path::From(getenv("XDG_CONFIG_HOME"));
} else if(getenv("HOME")) {
configHome = Path::From(getenv("HOME")).Join(".config");
} else {
dbp("neither XDG_CONFIG_HOME nor HOME are set");
return Path::From("");
}
if(!configHome.IsEmpty()) {
configHome = configHome.Join("solvespace");
}
const char *configHomeC = configHome.raw.c_str();
struct stat st;
if(stat(configHomeC, &st)) {
if(errno == ENOENT) {
if(mkdir(configHomeC, 0777)) {
dbp("cannot mkdir %s: %s", configHomeC, strerror(errno));
return Path::From("");
}
} else {
dbp("cannot stat %s: %s", configHomeC, strerror(errno));
return Path::From("");
}
} else if(!S_ISDIR(st.st_mode)) {
dbp("%s is not a directory", configHomeC);
return Path::From("");
}
return configHome.Join("settings.json");
}
SettingsImplGtk() {
_path = GetConfigPath();
if(_path.IsEmpty()) {
dbp("settings will not be saved");
} else {
_json = json_object_from_file(_path.raw.c_str());
if(!_json && errno != ENOENT) {
dbp("cannot load settings: %s", strerror(errno));
}
}
if(_json == NULL) {
_json = json_object_new_object();
}
}
~SettingsImplGtk() {
if(!_path.IsEmpty()) {
// json-c <0.12 has the first argument non-const
if(json_object_to_file_ext((char *)_path.raw.c_str(), _json,
JSON_C_TO_STRING_PRETTY)) {
dbp("cannot save settings: %s", strerror(errno));
}
}
json_object_put(_json);
}
void FreezeInt(const std::string &key, uint32_t value) override {
struct json_object *jsonValue = json_object_new_int(value);
json_object_object_add(_json, key.c_str(), jsonValue);
}
uint32_t ThawInt(const std::string &key, uint32_t defaultValue) override {
struct json_object *jsonValue;
if(json_object_object_get_ex(_json, key.c_str(), &jsonValue)) {
return json_object_get_int(jsonValue);
}
return defaultValue;
}
void FreezeFloat(const std::string &key, double value) override {
struct json_object *jsonValue = json_object_new_double(value);
json_object_object_add(_json, key.c_str(), jsonValue);
}
double ThawFloat(const std::string &key, double defaultValue) override {
struct json_object *jsonValue;
if(json_object_object_get_ex(_json, key.c_str(), &jsonValue)) {
return json_object_get_double(jsonValue);
}
return defaultValue;
}
void FreezeString(const std::string &key, const std::string &value) override {
struct json_object *jsonValue = json_object_new_string(value.c_str());
json_object_object_add(_json, key.c_str(), jsonValue);
}
std::string ThawString(const std::string &key,
const std::string &defaultValue = "") override {
struct json_object *jsonValue;
if(json_object_object_get_ex(_json, key.c_str(), &jsonValue)) {
return json_object_get_string(jsonValue);
}
return defaultValue;
}
};
SettingsRef GetSettings() {
static std::shared_ptr<SettingsImplGtk> settings;
if(!settings) {
settings = std::make_shared<SettingsImplGtk>();
}
return settings;
}
//-----------------------------------------------------------------------------
// Timers
//-----------------------------------------------------------------------------
@ -721,7 +848,7 @@ public:
gtkWindow.get_gl_widget().set_size_request(width, height);
}
void FreezePosition(const std::string &key) override {
void FreezePosition(SettingsRef settings, const std::string &key) override {
if(!gtkWindow.is_visible()) return;
int left, top, width, height;
@ -729,27 +856,27 @@ public:
gtkWindow.get_size(width, height);
bool isMaximized = gtkWindow.is_maximized();
CnfFreezeInt(left, key + "_left");
CnfFreezeInt(top, key + "_top");
CnfFreezeInt(width, key + "_width");
CnfFreezeInt(height, key + "_height");
CnfFreezeInt(isMaximized, key + "_maximized");
settings->FreezeInt(key + "_Left", left);
settings->FreezeInt(key + "_Top", top);
settings->FreezeInt(key + "_Width", width);
settings->FreezeInt(key + "_Height", height);
settings->FreezeBool(key + "_Maximized", isMaximized);
}
void ThawPosition(const std::string &key) override {
void ThawPosition(SettingsRef settings, const std::string &key) override {
int left, top, width, height;
gtkWindow.get_position(left, top);
gtkWindow.get_size(width, height);
left = CnfThawInt(left, key + "_left");
top = CnfThawInt(top, key + "_top");
width = CnfThawInt(width, key + "_width");
height = CnfThawInt(height, key + "_height");
left = settings->ThawInt(key + "_Left", left);
top = settings->ThawInt(key + "_Top", top);
width = settings->ThawInt(key + "_Width", width);
height = settings->ThawInt(key + "_Height", height);
gtkWindow.move(left, top);
gtkWindow.resize(width, height);
if(CnfThawInt(false, key + "_maximized")) {
if(settings->ThawBool(key + "_Maximized", false)) {
gtkWindow.maximize();
}
}

View File

@ -52,6 +52,72 @@ static NSString* Wrap(const std::string &s) {
namespace SolveSpace {
namespace Platform {
//-----------------------------------------------------------------------------
// Settings
//-----------------------------------------------------------------------------
class SettingsImplCocoa : public Settings {
public:
NSUserDefaults *userDefaults;
SettingsImplCocoa() {
userDefaults = [NSUserDefaults standardUserDefaults];
}
void FreezeInt(const std::string &key, uint32_t value) override {
[userDefaults setInteger:value forKey:Wrap(key)];
}
uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) override {
NSString *nsKey = Wrap(key);
if([userDefaults objectForKey:nsKey]) {
return [userDefaults integerForKey:nsKey];
}
return defaultValue;
}
void FreezeBool(const std::string &key, bool value) override {
[userDefaults setBool:value forKey:Wrap(key)];
}
bool ThawBool(const std::string &key, bool defaultValue = false) override {
NSString *nsKey = Wrap(key);
if([userDefaults objectForKey:nsKey]) {
return [userDefaults boolForKey:nsKey];
}
return defaultValue;
}
void FreezeFloat(const std::string &key, double value) override {
[userDefaults setDouble:value forKey:Wrap(key)];
}
double ThawFloat(const std::string &key, double defaultValue = 0.0) override {
NSString *nsKey = Wrap(key);
if([userDefaults objectForKey:nsKey]) {
return [userDefaults doubleForKey:nsKey];
}
return defaultValue;
}
void FreezeString(const std::string &key, const std::string &value) override {
[userDefaults setObject:Wrap(value) forKey:Wrap(key)];
}
std::string ThawString(const std::string &key,
const std::string &defaultValue = "") override {
NSObject *nsValue = [userDefaults objectForKey:Wrap(key)];
if(nsValue && [nsValue isKindOfClass:[NSString class]]) {
return [(NSString *)nsValue UTF8String];
}
return defaultValue;
}
};
SettingsRef GetSettings() {
return std::make_shared<SettingsImplCocoa>();
}
//-----------------------------------------------------------------------------
// Timers
//-----------------------------------------------------------------------------
@ -805,11 +871,11 @@ public:
[nsWindow setContentSize:nsMinSize];
}
void FreezePosition(const std::string &key) override {
void FreezePosition(SettingsRef _settings, const std::string &key) override {
[nsWindow saveFrameUsingName:Wrap(key)];
}
void ThawPosition(const std::string &key) override {
void ThawPosition(SettingsRef _settings, const std::string &key) override {
[nsWindow setFrameUsingName:Wrap(key)];
}

View File

@ -18,6 +18,38 @@ std::shared_ptr<ViewportCanvas> CreateRenderer() {
namespace Platform {
//-----------------------------------------------------------------------------
// Settings
//-----------------------------------------------------------------------------
class SettingsImplDummy : public Settings {
public:
void FreezeInt(const std::string &key, uint32_t value) {}
uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) {
return defaultValue;
}
void FreezeFloat(const std::string &key, double value) {}
double ThawFloat(const std::string &key, double defaultValue = 0.0) {
return defaultValue;
}
void FreezeString(const std::string &key, const std::string &value) {}
std::string ThawString(const std::string &key,
const std::string &defaultValue = "") {
return defaultValue;
}
};
SettingsRef GetSettings() {
static std::shared_ptr<SettingsImplDummy> settings =
std::make_shared<SettingsImplDummy>();
return settings;
}
//-----------------------------------------------------------------------------
// Timers
//-----------------------------------------------------------------------------
@ -62,76 +94,6 @@ void Exit() {
}
//-----------------------------------------------------------------------------
// Settings
//-----------------------------------------------------------------------------
class Setting {
public:
enum class Type {
Undefined,
Int,
Float,
String
};
Type type;
int valueInt;
float valueFloat;
std::string valueString;
void CheckType(Type expectedType) {
ssassert(type == Setting::Type::Undefined ||
type == expectedType, "Wrong setting type");
type = expectedType;
}
};
std::map<std::string, Setting> settings;
void CnfFreezeInt(uint32_t val, const std::string &key) {
Setting &setting = settings[key];
setting.CheckType(Setting::Type::Int);
setting.valueInt = val;
}
uint32_t CnfThawInt(uint32_t val, const std::string &key) {
if(settings.find(key) != settings.end()) {
Setting &setting = settings[key];
setting.CheckType(Setting::Type::Int);
val = setting.valueInt;
}
return val;
}
void CnfFreezeFloat(float val, const std::string &key) {
Setting &setting = settings[key];
setting.CheckType(Setting::Type::Float);
setting.valueFloat = val;
}
float CnfThawFloat(float val, const std::string &key) {
if(settings.find(key) != settings.end()) {
Setting &setting = settings[key];
setting.CheckType(Setting::Type::Float);
val = setting.valueFloat;
}
return val;
}
void CnfFreezeString(const std::string &val, const std::string &key) {
Setting &setting = settings[key];
setting.CheckType(Setting::Type::String);
setting.valueString = val;
}
std::string CnfThawString(const std::string &val, const std::string &key) {
std::string ret = val;
if(settings.find(key) != settings.end()) {
Setting &setting = settings[key];
setting.CheckType(Setting::Type::String);
ret = setting.valueString;
}
return ret;
}
//-----------------------------------------------------------------------------
// Dialogs
//-----------------------------------------------------------------------------

View File

@ -102,6 +102,87 @@ static int Clamp(int x, int a, int b) {
return max(a, min(x, b));
}
//-----------------------------------------------------------------------------
// Settings
//-----------------------------------------------------------------------------
class SettingsImplWin32 : public Settings {
public:
HKEY hKey = NULL;
HKEY GetKey() {
if(hKey == NULL) {
sscheck(RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\SolveSpace", 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &hKey, NULL));
}
return hKey;
}
~SettingsImplWin32() {
if(hKey != NULL) {
sscheck(RegCloseKey(hKey));
}
}
void FreezeInt(const std::string &key, uint32_t value) {
sscheck(RegSetValueExW(GetKey(), &Widen(key)[0], 0,
REG_DWORD, (const BYTE *)&value, sizeof(value)));
}
uint32_t ThawInt(const std::string &key, uint32_t defaultValue) {
DWORD value;
DWORD type, length = sizeof(value);
LSTATUS result = RegQueryValueEx(GetKey(), &Widen(key)[0], 0,
&type, (BYTE *)&value, &length);
if(result == ERROR_SUCCESS && type == REG_DWORD) {
return value;
}
return defaultValue;
}
void FreezeFloat(const std::string &key, double value) {
sscheck(RegSetValueExW(GetKey(), &Widen(key)[0], 0,
REG_QWORD, (const BYTE *)&value, sizeof(value)));
}
double ThawFloat(const std::string &key, double defaultValue) {
double value;
DWORD type, length = sizeof(value);
LSTATUS result = RegQueryValueEx(GetKey(), &Widen(key)[0], 0,
&type, (BYTE *)&value, &length);
if(result == ERROR_SUCCESS && type == REG_QWORD) {
return value;
}
return defaultValue;
}
void FreezeString(const std::string &key, const std::string &value) {
ssassert(value.length() == strlen(value.c_str()),
"illegal null byte in middle of a string setting");
std::wstring valueW = Widen(value);
sscheck(RegSetValueExW(GetKey(), &Widen(key)[0], 0,
REG_SZ, (const BYTE *)&valueW[0], (valueW.length() + 1) * 2));
}
std::string ThawString(const std::string &key, const std::string &defaultValue) {
DWORD type, length = 0;
LSTATUS result = RegQueryValueEx(GetKey(), &Widen(key)[0], 0,
&type, NULL, &length);
if(result == ERROR_SUCCESS && type == REG_SZ) {
std::wstring valueW;
valueW.resize(length / 2 - 1);
sscheck(RegQueryValueEx(GetKey(), &Widen(key)[0], 0,
&type, (BYTE *)&valueW[0], &length));
return Narrow(valueW);
}
return defaultValue;
}
};
SettingsRef GetSettings() {
return std::make_shared<SettingsImplWin32>();
}
//-----------------------------------------------------------------------------
// Timers
//-----------------------------------------------------------------------------
@ -960,28 +1041,28 @@ public:
}
}
void FreezePosition(const std::string &key) override {
void FreezePosition(SettingsRef settings, const std::string &key) override {
sscheck(GetWindowPlacement(hWindow, &placement));
BOOL isMaximized;
sscheck(isMaximized = IsZoomed(hWindow));
RECT rc = placement.rcNormalPosition;
CnfFreezeInt(rc.left, key + "_left");
CnfFreezeInt(rc.right, key + "_right");
CnfFreezeInt(rc.top, key + "_top");
CnfFreezeInt(rc.bottom, key + "_bottom");
CnfFreezeInt(isMaximized, key + "_maximized");
settings->FreezeInt(key + "_Left", rc.left);
settings->FreezeInt(key + "_Right", rc.right);
settings->FreezeInt(key + "_Top", rc.top);
settings->FreezeInt(key + "_Bottom", rc.bottom);
settings->FreezeBool(key + "_Maximized", isMaximized);
}
void ThawPosition(const std::string &key) override {
void ThawPosition(SettingsRef settings, const std::string &key) override {
sscheck(GetWindowPlacement(hWindow, &placement));
RECT rc = placement.rcNormalPosition;
rc.left = CnfThawInt(rc.left, key + "_left");
rc.right = CnfThawInt(rc.right, key + "_right");
rc.top = CnfThawInt(rc.top, key + "_top");
rc.bottom = CnfThawInt(rc.bottom, key + "_bottom");
rc.left = settings->ThawInt(key + "_Left", rc.left);
rc.right = settings->ThawInt(key + "_Right", rc.right);
rc.top = settings->ThawInt(key + "_Top", rc.top);
rc.bottom = settings->ThawInt(key + "_Bottom", rc.bottom);
MONITORINFO mi;
mi.cbSize = sizeof(mi);
@ -999,7 +1080,7 @@ public:
sscheck(SendMessageW(hWindow, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&rc));
placement.flags = 0;
if(CnfThawInt(false, key + "_maximized")) {
if(settings->ThawBool(key + "_Maximized", false)) {
placement.showCmd = SW_SHOWMAXIMIZED;
} else {
placement.showCmd = SW_SHOW;

View File

@ -177,102 +177,6 @@ void SolveSpace::OpenWebsite(const char *url) {
L"open", Widen(url).c_str(), NULL, NULL, SW_SHOWNORMAL);
}
//-----------------------------------------------------------------------------
// Helpers so that we can read/write registry keys from the platform-
// independent code.
//-----------------------------------------------------------------------------
static HKEY GetRegistryKey()
{
HKEY Software;
if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software", 0,
KEY_ALL_ACCESS, &Software) != ERROR_SUCCESS)
return NULL;
HKEY SolveSpace;
if(RegCreateKeyExW(Software, L"SolveSpace", 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &SolveSpace, NULL) != ERROR_SUCCESS)
return NULL;
RegCloseKey(Software);
return SolveSpace;
}
void SolveSpace::CnfFreezeInt(uint32_t val, const std::string &name)
{
HKEY SolveSpace = GetRegistryKey();
RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
REG_DWORD, (const BYTE*) &val, sizeof(DWORD));
RegCloseKey(SolveSpace);
}
void SolveSpace::CnfFreezeFloat(float val, const std::string &name)
{
static_assert(sizeof(float) == sizeof(DWORD),
"sizes of float and DWORD must match");
HKEY SolveSpace = GetRegistryKey();
RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
REG_DWORD, (const BYTE*) &val, sizeof(DWORD));
RegCloseKey(SolveSpace);
}
void SolveSpace::CnfFreezeString(const std::string &str, const std::string &name)
{
HKEY SolveSpace = GetRegistryKey();
std::wstring strW = Widen(str);
RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
REG_SZ, (const BYTE*) &strW[0], (strW.length() + 1) * 2);
RegCloseKey(SolveSpace);
}
uint32_t SolveSpace::CnfThawInt(uint32_t val, const std::string &name)
{
HKEY SolveSpace = GetRegistryKey();
DWORD type, newval, len = sizeof(DWORD);
LONG result = RegQueryValueEx(SolveSpace, &Widen(name)[0], NULL,
&type, (BYTE*) &newval, &len);
RegCloseKey(SolveSpace);
if(result == ERROR_SUCCESS && type == REG_DWORD)
return newval;
else
return val;
}
float SolveSpace::CnfThawFloat(float val, const std::string &name)
{
HKEY SolveSpace = GetRegistryKey();
DWORD type, len = sizeof(DWORD);
float newval;
LONG result = RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
&type, (BYTE*) &newval, &len);
RegCloseKey(SolveSpace);
if(result == ERROR_SUCCESS && type == REG_DWORD)
return newval;
else
return val;
}
std::string SolveSpace::CnfThawString(const std::string &val, const std::string &name)
{
HKEY SolveSpace = GetRegistryKey();
DWORD type, len;
if(RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
&type, NULL, &len) != ERROR_SUCCESS || type != REG_SZ) {
RegCloseKey(SolveSpace);
return val;
}
std::wstring newval;
newval.resize(len / 2 - 1);
if(RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
NULL, (BYTE*) &newval[0], &len) != ERROR_SUCCESS) {
RegCloseKey(SolveSpace);
return val;
}
RegCloseKey(SolveSpace);
return Narrow(newval);
}
//-----------------------------------------------------------------------------
// Common dialog routines, to open or save a file.
//-----------------------------------------------------------------------------

View File

@ -834,7 +834,7 @@ public:
static void CreateAllDefaultStyles();
static void CreateDefaultStyle(hStyle h);
static void FillDefaultStyle(Style *s, const Default *d = NULL, bool factory = false);
static void FreezeDefaultStyles();
static void FreezeDefaultStyles(Platform::SettingsRef settings);
static void LoadFactoryDefaults();
static void AssignSelectionToStyle(uint32_t v);

View File

@ -16,95 +16,97 @@ void SolveSpaceUI::Init() {
dbp("%s", LoadString("banner.txt").data());
#endif
Platform::SettingsRef settings = Platform::GetSettings();
SS.tangentArcRadius = 10.0;
// Then, load the registry settings.
// Default list of colors for the model material
modelColor[0] = CnfThawColor(RGBi(150, 150, 150), "ModelColor_0");
modelColor[1] = CnfThawColor(RGBi(100, 100, 100), "ModelColor_1");
modelColor[2] = CnfThawColor(RGBi( 30, 30, 30), "ModelColor_2");
modelColor[3] = CnfThawColor(RGBi(150, 0, 0), "ModelColor_3");
modelColor[4] = CnfThawColor(RGBi( 0, 100, 0), "ModelColor_4");
modelColor[5] = CnfThawColor(RGBi( 0, 80, 80), "ModelColor_5");
modelColor[6] = CnfThawColor(RGBi( 0, 0, 130), "ModelColor_6");
modelColor[7] = CnfThawColor(RGBi( 80, 0, 80), "ModelColor_7");
modelColor[0] = settings->ThawColor("ModelColor_0", RGBi(150, 150, 150));
modelColor[1] = settings->ThawColor("ModelColor_1", RGBi(100, 100, 100));
modelColor[2] = settings->ThawColor("ModelColor_2", RGBi( 30, 30, 30));
modelColor[3] = settings->ThawColor("ModelColor_3", RGBi(150, 0, 0));
modelColor[4] = settings->ThawColor("ModelColor_4", RGBi( 0, 100, 0));
modelColor[5] = settings->ThawColor("ModelColor_5", RGBi( 0, 80, 80));
modelColor[6] = settings->ThawColor("ModelColor_6", RGBi( 0, 0, 130));
modelColor[7] = settings->ThawColor("ModelColor_7", RGBi( 80, 0, 80));
// Light intensities
lightIntensity[0] = CnfThawFloat(1.0f, "LightIntensity_0");
lightIntensity[1] = CnfThawFloat(0.5f, "LightIntensity_1");
lightIntensity[0] = settings->ThawFloat("LightIntensity_0", 1.0f);
lightIntensity[1] = settings->ThawFloat("LightIntensity_1", 0.5f);
ambientIntensity = 0.3; // no setting for that yet
// Light positions
lightDir[0].x = CnfThawFloat(-1.0f, "LightDir_0_Right" );
lightDir[0].y = CnfThawFloat( 1.0f, "LightDir_0_Up" );
lightDir[0].z = CnfThawFloat( 0.0f, "LightDir_0_Forward" );
lightDir[1].x = CnfThawFloat( 1.0f, "LightDir_1_Right" );
lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up" );
lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward" );
lightDir[0].x = settings->ThawFloat("LightDir_0_Right", -1.0f);
lightDir[0].y = settings->ThawFloat("LightDir_0_Up", 1.0f);
lightDir[0].z = settings->ThawFloat("LightDir_0_Forward", 0.0f);
lightDir[1].x = settings->ThawFloat("LightDir_1_Right", 1.0f);
lightDir[1].y = settings->ThawFloat("LightDir_1_Up", 0.0f);
lightDir[1].z = settings->ThawFloat("LightDir_1_Forward", 0.0f);
exportMode = false;
// Chord tolerance
chordTol = CnfThawFloat(0.5f, "ChordTolerancePct");
chordTol = settings->ThawFloat("ChordTolerancePct", 0.5f);
// Max pwl segments to generate
maxSegments = CnfThawInt(10, "MaxSegments");
maxSegments = settings->ThawInt("MaxSegments", 10);
// Chord tolerance
exportChordTol = CnfThawFloat(0.1f, "ExportChordTolerance");
exportChordTol = settings->ThawFloat("ExportChordTolerance", 0.1f);
// Max pwl segments to generate
exportMaxSegments = CnfThawInt(64, "ExportMaxSegments");
exportMaxSegments = settings->ThawInt("ExportMaxSegments", 64);
// View units
viewUnits = (Unit)CnfThawInt((uint32_t)Unit::MM, "ViewUnits");
viewUnits = (Unit)settings->ThawInt("ViewUnits", (uint32_t)Unit::MM);
// Number of digits after the decimal point
afterDecimalMm = CnfThawInt(2, "AfterDecimalMm");
afterDecimalInch = CnfThawInt(3, "AfterDecimalInch");
afterDecimalMm = settings->ThawInt("AfterDecimalMm", 2);
afterDecimalInch = settings->ThawInt("AfterDecimalInch", 3);
// Camera tangent (determines perspective)
cameraTangent = CnfThawFloat(0.3f/1e3f, "CameraTangent");
cameraTangent = settings->ThawFloat("CameraTangent", 0.3f/1e3f);
// Grid spacing
gridSpacing = CnfThawFloat(5.0f, "GridSpacing");
gridSpacing = settings->ThawFloat("GridSpacing", 5.0f);
// Export scale factor
exportScale = CnfThawFloat(1.0f, "ExportScale");
exportScale = settings->ThawFloat("ExportScale", 1.0f);
// Export offset (cutter radius comp)
exportOffset = CnfThawFloat(0.0f, "ExportOffset");
exportOffset = settings->ThawFloat("ExportOffset", 0.0f);
// Rewrite exported colors close to white into black (assuming white bg)
fixExportColors = CnfThawBool(true, "FixExportColors");
fixExportColors = settings->ThawBool("FixExportColors", true);
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
drawBackFaces = CnfThawBool(true, "DrawBackFaces");
drawBackFaces = settings->ThawBool("DrawBackFaces", true);
// Check that contours are closed and not self-intersecting
checkClosedContour = CnfThawBool(true, "CheckClosedContour");
checkClosedContour = settings->ThawBool("CheckClosedContour", true);
// Draw closed polygons areas
showContourAreas = CnfThawBool(false, "ShowContourAreas");
showContourAreas = settings->ThawBool("ShowContourAreas", false);
// Export shaded triangles in a 2d view
exportShadedTriangles = CnfThawBool(true, "ExportShadedTriangles");
exportShadedTriangles = settings->ThawBool("ExportShadedTriangles", true);
// Export pwl curves (instead of exact) always
exportPwlCurves = CnfThawBool(false, "ExportPwlCurves");
exportPwlCurves = settings->ThawBool("ExportPwlCurves", false);
// Background color on-screen
backgroundColor = CnfThawColor(RGBi(0, 0, 0), "BackgroundColor");
backgroundColor = settings->ThawColor("BackgroundColor", RGBi(0, 0, 0));
// Whether export canvas size is fixed or derived from bbox
exportCanvasSizeAuto = CnfThawBool(true, "ExportCanvasSizeAuto");
exportCanvasSizeAuto = settings->ThawBool("ExportCanvasSizeAuto", true);
// Margins for automatic canvas size
exportMargin.left = CnfThawFloat(5.0f, "ExportMargin_Left");
exportMargin.right = CnfThawFloat(5.0f, "ExportMargin_Right");
exportMargin.bottom = CnfThawFloat(5.0f, "ExportMargin_Bottom");
exportMargin.top = CnfThawFloat(5.0f, "ExportMargin_Top");
exportMargin.left = settings->ThawFloat("ExportMargin_Left", 5.0f);
exportMargin.right = settings->ThawFloat("ExportMargin_Right", 5.0f);
exportMargin.bottom = settings->ThawFloat("ExportMargin_Bottom", 5.0f);
exportMargin.top = settings->ThawFloat("ExportMargin_Top", 5.0f);
// Dimensions for fixed canvas size
exportCanvas.width = CnfThawFloat(100.0f, "ExportCanvas_Width");
exportCanvas.height = CnfThawFloat(100.0f, "ExportCanvas_Height");
exportCanvas.dx = CnfThawFloat( 5.0f, "ExportCanvas_Dx");
exportCanvas.dy = CnfThawFloat( 5.0f, "ExportCanvas_Dy");
exportCanvas.width = settings->ThawFloat("ExportCanvas_Width", 100.0f);
exportCanvas.height = settings->ThawFloat("ExportCanvas_Height", 100.0f);
exportCanvas.dx = settings->ThawFloat("ExportCanvas_Dx", 5.0f);
exportCanvas.dy = settings->ThawFloat("ExportCanvas_Dy", 5.0f);
// Extra parameters when exporting G code
gCode.depth = CnfThawFloat(10.0f, "GCode_Depth");
gCode.passes = CnfThawInt(1, "GCode_Passes");
gCode.feed = CnfThawFloat(10.0f, "GCode_Feed");
gCode.plungeFeed = CnfThawFloat(10.0f, "GCode_PlungeFeed");
gCode.depth = settings->ThawFloat("GCode_Depth", 10.0f);
gCode.passes = settings->ThawInt("GCode_Passes", 1);
gCode.feed = settings->ThawFloat("GCode_Feed", 10.0f);
gCode.plungeFeed = settings->ThawFloat("GCode_PlungeFeed", 10.0f);
// Show toolbar in the graphics window
showToolbar = CnfThawBool(true, "ShowToolbar");
showToolbar = settings->ThawBool("ShowToolbar", true);
// Recent files menus
for(size_t i = 0; i < MAX_RECENT; i++) {
std::string rawPath = CnfThawString("", "RecentFile_" + std::to_string(i));
std::string rawPath = settings->ThawString("RecentFile_" + std::to_string(i), "");
if(rawPath.empty()) continue;
recentFiles.push_back(Platform::Path::From(rawPath));
}
// Autosave timer
autosaveInterval = CnfThawInt(5, "AutosaveInterval");
autosaveInterval = settings->ThawInt("AutosaveInterval", 5);
// Locale
std::string locale = CnfThawString("", "Locale");
std::string locale = settings->ThawString("Locale", "");
if(!locale.empty()) {
SetLocale(locale);
}
@ -129,9 +131,9 @@ void SolveSpaceUI::Init() {
AfterNewFile();
if(TW.window && GW.window) {
TW.window->ThawPosition("TextWindow");
TW.window->ThawPosition(settings, "TextWindow");
GW.window->ThawPosition(settings, "GraphicsWindow");
TW.window->SetVisible(true);
GW.window->ThawPosition("GraphicsWindow");
GW.window->SetVisible(true);
GW.window->Focus();
}
@ -169,8 +171,10 @@ bool SolveSpaceUI::Load(const Platform::Path &filename) {
}
void SolveSpaceUI::Exit() {
GW.window->FreezePosition("GraphicsWindow");
TW.window->FreezePosition("TextWindow");
Platform::SettingsRef settings = Platform::GetSettings();
GW.window->FreezePosition(settings, "GraphicsWindow");
TW.window->FreezePosition(settings, "TextWindow");
// Recent files
for(size_t i = 0; i < MAX_RECENT; i++) {
@ -178,80 +182,80 @@ void SolveSpaceUI::Exit() {
if(recentFiles.size() > i) {
rawPath = recentFiles[i].raw;
}
CnfFreezeString(rawPath, "RecentFile_" + std::to_string(i));
settings->FreezeString("RecentFile_" + std::to_string(i), rawPath);
}
// Model colors
for(size_t i = 0; i < MODEL_COLORS; i++)
CnfFreezeColor(modelColor[i], "ModelColor_" + std::to_string(i));
settings->FreezeColor("ModelColor_" + std::to_string(i), modelColor[i]);
// Light intensities
CnfFreezeFloat((float)lightIntensity[0], "LightIntensity_0");
CnfFreezeFloat((float)lightIntensity[1], "LightIntensity_1");
settings->FreezeFloat("LightIntensity_0", (float)lightIntensity[0]);
settings->FreezeFloat("LightIntensity_1", (float)lightIntensity[1]);
// Light directions
CnfFreezeFloat((float)lightDir[0].x, "LightDir_0_Right");
CnfFreezeFloat((float)lightDir[0].y, "LightDir_0_Up");
CnfFreezeFloat((float)lightDir[0].z, "LightDir_0_Forward");
CnfFreezeFloat((float)lightDir[1].x, "LightDir_1_Right");
CnfFreezeFloat((float)lightDir[1].y, "LightDir_1_Up");
CnfFreezeFloat((float)lightDir[1].z, "LightDir_1_Forward");
settings->FreezeFloat("LightDir_0_Right", (float)lightDir[0].x);
settings->FreezeFloat("LightDir_0_Up", (float)lightDir[0].y);
settings->FreezeFloat("LightDir_0_Forward", (float)lightDir[0].z);
settings->FreezeFloat("LightDir_1_Right", (float)lightDir[1].x);
settings->FreezeFloat("LightDir_1_Up", (float)lightDir[1].y);
settings->FreezeFloat("LightDir_1_Forward", (float)lightDir[1].z);
// Chord tolerance
CnfFreezeFloat((float)chordTol, "ChordTolerancePct");
settings->FreezeFloat("ChordTolerancePct", (float)chordTol);
// Max pwl segments to generate
CnfFreezeInt((uint32_t)maxSegments, "MaxSegments");
settings->FreezeInt("MaxSegments", (uint32_t)maxSegments);
// Export Chord tolerance
CnfFreezeFloat((float)exportChordTol, "ExportChordTolerance");
settings->FreezeFloat("ExportChordTolerance", (float)exportChordTol);
// Export Max pwl segments to generate
CnfFreezeInt((uint32_t)exportMaxSegments, "ExportMaxSegments");
settings->FreezeInt("ExportMaxSegments", (uint32_t)exportMaxSegments);
// View units
CnfFreezeInt((uint32_t)viewUnits, "ViewUnits");
settings->FreezeInt("ViewUnits", (uint32_t)viewUnits);
// Number of digits after the decimal point
CnfFreezeInt((uint32_t)afterDecimalMm, "AfterDecimalMm");
CnfFreezeInt((uint32_t)afterDecimalInch, "AfterDecimalInch");
settings->FreezeInt("AfterDecimalMm", (uint32_t)afterDecimalMm);
settings->FreezeInt("AfterDecimalInch", (uint32_t)afterDecimalInch);
// Camera tangent (determines perspective)
CnfFreezeFloat((float)cameraTangent, "CameraTangent");
settings->FreezeFloat("CameraTangent", (float)cameraTangent);
// Grid spacing
CnfFreezeFloat(gridSpacing, "GridSpacing");
settings->FreezeFloat("GridSpacing", gridSpacing);
// Export scale
CnfFreezeFloat(exportScale, "ExportScale");
settings->FreezeFloat("ExportScale", exportScale);
// Export offset (cutter radius comp)
CnfFreezeFloat(exportOffset, "ExportOffset");
settings->FreezeFloat("ExportOffset", exportOffset);
// Rewrite exported colors close to white into black (assuming white bg)
CnfFreezeBool(fixExportColors, "FixExportColors");
settings->FreezeBool("FixExportColors", fixExportColors);
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
CnfFreezeBool(drawBackFaces, "DrawBackFaces");
settings->FreezeBool("DrawBackFaces", drawBackFaces);
// Draw closed polygons areas
CnfFreezeBool(showContourAreas, "ShowContourAreas");
settings->FreezeBool("ShowContourAreas", showContourAreas);
// Check that contours are closed and not self-intersecting
CnfFreezeBool(checkClosedContour, "CheckClosedContour");
settings->FreezeBool("CheckClosedContour", checkClosedContour);
// Export shaded triangles in a 2d view
CnfFreezeBool(exportShadedTriangles, "ExportShadedTriangles");
settings->FreezeBool("ExportShadedTriangles", exportShadedTriangles);
// Export pwl curves (instead of exact) always
CnfFreezeBool(exportPwlCurves, "ExportPwlCurves");
settings->FreezeBool("ExportPwlCurves", exportPwlCurves);
// Background color on-screen
CnfFreezeColor(backgroundColor, "BackgroundColor");
settings->FreezeColor("BackgroundColor", backgroundColor);
// Whether export canvas size is fixed or derived from bbox
CnfFreezeBool(exportCanvasSizeAuto, "ExportCanvasSizeAuto");
settings->FreezeBool("ExportCanvasSizeAuto", exportCanvasSizeAuto);
// Margins for automatic canvas size
CnfFreezeFloat(exportMargin.left, "ExportMargin_Left");
CnfFreezeFloat(exportMargin.right, "ExportMargin_Right");
CnfFreezeFloat(exportMargin.bottom, "ExportMargin_Bottom");
CnfFreezeFloat(exportMargin.top, "ExportMargin_Top");
settings->FreezeFloat("ExportMargin_Left", exportMargin.left);
settings->FreezeFloat("ExportMargin_Right", exportMargin.right);
settings->FreezeFloat("ExportMargin_Bottom", exportMargin.bottom);
settings->FreezeFloat("ExportMargin_Top", exportMargin.top);
// Dimensions for fixed canvas size
CnfFreezeFloat(exportCanvas.width, "ExportCanvas_Width");
CnfFreezeFloat(exportCanvas.height, "ExportCanvas_Height");
CnfFreezeFloat(exportCanvas.dx, "ExportCanvas_Dx");
CnfFreezeFloat(exportCanvas.dy, "ExportCanvas_Dy");
settings->FreezeFloat("ExportCanvas_Width", exportCanvas.width);
settings->FreezeFloat("ExportCanvas_Height", exportCanvas.height);
settings->FreezeFloat("ExportCanvas_Dx", exportCanvas.dx);
settings->FreezeFloat("ExportCanvas_Dy", exportCanvas.dy);
// Extra parameters when exporting G code
CnfFreezeFloat(gCode.depth, "GCode_Depth");
CnfFreezeInt(gCode.passes, "GCode_Passes");
CnfFreezeFloat(gCode.feed, "GCode_Feed");
CnfFreezeFloat(gCode.plungeFeed, "GCode_PlungeFeed");
settings->FreezeFloat("GCode_Depth", gCode.depth);
settings->FreezeInt("GCode_Passes", gCode.passes);
settings->FreezeFloat("GCode_Feed", gCode.feed);
settings->FreezeFloat("GCode_PlungeFeed", gCode.plungeFeed);
// Show toolbar in the graphics window
CnfFreezeBool(showToolbar, "ShowToolbar");
settings->FreezeBool("ShowToolbar", showToolbar);
// Autosave timer
CnfFreezeInt(autosaveInterval, "AutosaveInterval");
settings->FreezeInt("AutosaveInterval", autosaveInterval);
// And the default styles, colors and line widths and such.
Style::FreezeDefaultStyles();
Style::FreezeDefaultStyles(settings);
Platform::Exit();
}
@ -444,6 +448,8 @@ void SolveSpaceUI::UpdateWindowTitles() {
}
void SolveSpaceUI::MenuFile(Command id) {
Platform::SettingsRef settings = Platform::GetSettings();
switch(id) {
case Command::NEW:
if(!SS.OkayToStartNewFile()) break;
@ -480,9 +486,10 @@ void SolveSpaceUI::MenuFile(Command id) {
case Command::EXPORT_VIEW: {
Platform::Path exportFile = SS.saveFile;
if(!GetSaveFile(&exportFile, CnfThawString("", "ViewExportFormat"),
if(!GetSaveFile(&exportFile,
Platform::GetSettings()->ThawString("ViewExportFormat"),
VectorFileFilter)) break;
CnfFreezeString(exportFile.Extension(), "ViewExportFormat");
settings->FreezeString("ViewExportFormat", exportFile.Extension());
// If the user is exporting something where it would be
// inappropriate to include the constraints, then warn.
@ -502,9 +509,10 @@ void SolveSpaceUI::MenuFile(Command id) {
case Command::EXPORT_WIREFRAME: {
Platform::Path exportFile = SS.saveFile;
if(!GetSaveFile(&exportFile, CnfThawString("", "WireframeExportFormat"),
if(!GetSaveFile(&exportFile,
Platform::GetSettings()->ThawString("WireframeExportFormat"),
Vector3dFileFilter)) break;
CnfFreezeString(exportFile.Extension(), "WireframeExportFormat");
settings->FreezeString("WireframeExportFormat", exportFile.Extension());
SS.ExportViewOrWireframeTo(exportFile, /*exportWireframe*/true);
break;
@ -512,9 +520,10 @@ void SolveSpaceUI::MenuFile(Command id) {
case Command::EXPORT_SECTION: {
Platform::Path exportFile = SS.saveFile;
if(!GetSaveFile(&exportFile, CnfThawString("", "SectionExportFormat"),
if(!GetSaveFile(&exportFile,
Platform::GetSettings()->ThawString("SectionExportFormat"),
VectorFileFilter)) break;
CnfFreezeString(exportFile.Extension(), "SectionExportFormat");
settings->FreezeString("SectionExportFormat", exportFile.Extension());
SS.ExportSectionTo(exportFile);
break;
@ -522,9 +531,10 @@ void SolveSpaceUI::MenuFile(Command id) {
case Command::EXPORT_MESH: {
Platform::Path exportFile = SS.saveFile;
if(!GetSaveFile(&exportFile, CnfThawString("", "MeshExportFormat"),
if(!GetSaveFile(&exportFile,
Platform::GetSettings()->ThawString("MeshExportFormat"),
MeshFileFilter)) break;
CnfFreezeString(exportFile.Extension(), "MeshExportFormat");
settings->FreezeString("MeshExportFormat", exportFile.Extension());
SS.ExportMeshTo(exportFile);
break;
@ -532,9 +542,10 @@ void SolveSpaceUI::MenuFile(Command id) {
case Command::EXPORT_SURFACES: {
Platform::Path exportFile = SS.saveFile;
if(!GetSaveFile(&exportFile, CnfThawString("", "SurfacesExportFormat"),
if(!GetSaveFile(&exportFile,
Platform::GetSettings()->ThawString("SurfacesExportFormat"),
SurfaceFileFilter)) break;
CnfFreezeString(exportFile.Extension(), "SurfacesExportFormat");
settings->FreezeString("SurfacesExportFormat", exportFile.Extension());
StepFileWriter sfw = {};
sfw.ExportSurfacesTo(exportFile);
@ -543,9 +554,10 @@ void SolveSpaceUI::MenuFile(Command id) {
case Command::IMPORT: {
Platform::Path importFile;
if(!GetOpenFile(&importFile, CnfThawString("", "ImportFormat"),
if(!GetOpenFile(&importFile,
Platform::GetSettings()->ThawString("ImportFormat"),
ImportableFileFilter)) break;
CnfFreezeString(importFile.Extension(), "ImportFormat");
settings->FreezeString("ImportFormat", importFile.Extension());
if(importFile.HasExtension("dxf")) {
ImportDxf(importFile);

View File

@ -173,13 +173,6 @@ void dbp(const char *str, ...);
void SetMousePointerToHand(bool yes);
void DoMessageBox(const char *str, int rows, int cols, bool error);
void CnfFreezeInt(uint32_t val, const std::string &name);
void CnfFreezeFloat(float val, const std::string &name);
void CnfFreezeString(const std::string &val, const std::string &name);
std::string CnfThawString(const std::string &val, const std::string &name);
uint32_t CnfThawInt(uint32_t val, const std::string &name);
float CnfThawFloat(float val, const std::string &name);
std::vector<std::string> InitPlatform(int argc, char **argv);
void *AllocTemporary(size_t n);
@ -263,10 +256,6 @@ void MultMatrix(double *mata, double *matb, double *matr);
int64_t GetMilliseconds();
void Message(const char *str, ...);
void Error(const char *str, ...);
void CnfFreezeBool(bool v, const std::string &name);
void CnfFreezeColor(RgbaColor v, const std::string &name);
bool CnfThawBool(bool v, const std::string &name);
RgbaColor CnfThawColor(RgbaColor v, const std::string &name);
class System {
public:

View File

@ -81,12 +81,18 @@ void Style::CreateDefaultStyle(hStyle h) {
}
void Style::FillDefaultStyle(Style *s, const Default *d, bool factory) {
Platform::SettingsRef settings = Platform::GetSettings();
if(d == NULL) d = &Defaults[0];
s->color = (factory) ? d->color : CnfThawColor(d->color, CnfColor(d->cnfPrefix));
s->width = (factory) ? d->width : CnfThawFloat((float)(d->width), CnfWidth(d->cnfPrefix));
s->color = (factory)
? d->color
: settings->ThawColor(CnfColor(d->cnfPrefix), d->color);
s->width = (factory)
? d->width
: settings->ThawFloat(CnfWidth(d->cnfPrefix), (float)(d->width));
s->widthAs = UnitsAs::PIXELS;
s->textHeight = (factory) ? 11.5
: CnfThawFloat(11.5, CnfTextHeight(d->cnfPrefix));
: settings->ThawFloat(CnfTextHeight(d->cnfPrefix), 11.5);
s->textHeightAs = UnitsAs::PIXELS;
s->textOrigin = TextOrigin::NONE;
s->textAngle = 0;
@ -109,12 +115,12 @@ void Style::LoadFactoryDefaults() {
SS.backgroundColor = RGBi(0, 0, 0);
}
void Style::FreezeDefaultStyles() {
void Style::FreezeDefaultStyles(Platform::SettingsRef settings) {
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
CnfFreezeColor(Color(d->h), CnfColor(d->cnfPrefix));
CnfFreezeFloat((float)Width(d->h), CnfWidth(d->cnfPrefix));
CnfFreezeFloat((float)TextHeight(d->h), CnfTextHeight(d->cnfPrefix));
settings->FreezeColor(CnfColor(d->cnfPrefix), Color(d->h));
settings->FreezeFloat(CnfWidth(d->cnfPrefix), (float)Width(d->h));
settings->FreezeFloat(CnfTextHeight(d->cnfPrefix), (float)TextHeight(d->h));
}
}

View File

@ -173,18 +173,6 @@ void SolveSpace::Message(const char *str, ...)
va_end(f);
}
void SolveSpace::CnfFreezeBool(bool v, const std::string &name)
{ CnfFreezeInt(v ? 1 : 0, name); }
void SolveSpace::CnfFreezeColor(RgbaColor v, const std::string &name)
{ CnfFreezeInt(v.ToPackedInt(), name); }
bool SolveSpace::CnfThawBool(bool v, const std::string &name)
{ return CnfThawInt(v ? 1 : 0, name) != 0; }
RgbaColor SolveSpace::CnfThawColor(RgbaColor v, const std::string &name)
{ return RgbaColor::FromPackedInt(CnfThawInt(v.ToPackedInt(), name)); }
//-----------------------------------------------------------------------------
// Solve a mostly banded matrix. In a given row, there are LEFT_OF_DIAG
// elements to the left of the diagonal element, and RIGHT_OF_DIAG elements to