[dasher: 131/217] Add a shared XmlSettingsStore class and include support for it the Gtk version. When the --config=<f
- From: Patrick Welche <pwelche src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dasher: 131/217] Add a shared XmlSettingsStore class and include support for it the Gtk version. When the --config=<f
- Date: Sat, 27 Feb 2016 12:11:20 +0000 (UTC)
commit 84bf9a4dfdd83ef1758cd2ae6bbf982712fb1c68
Author: lbaudoin <lbaudoin google com>
Date: Mon Nov 23 19:52:59 2015 -0700
Add a shared XmlSettingsStore class and include support for it the Gtk version.
When the --config=<file> command line option is used the settings will be read from and saved to the XML
file specified.
If the file does not exist it will be created, if the '--config' option is not used the Gnome settings
are used.
Src/DasherCore/Makefile.am | 4 +-
Src/DasherCore/SettingsStore.h | 3 +-
Src/DasherCore/XmlSettingsStore.cpp | 202 +++++++++++++++++++++++++++++++++++
Src/DasherCore/XmlSettingsStore.h | 65 +++++++++++
Src/Gtk2/DasherControl.cpp | 5 +-
Src/Gtk2/DasherControl.h | 5 +-
Src/Gtk2/GtkDasherControl.cpp | 28 +++++-
Src/Gtk2/dasher_main.h | 10 +-
Src/main.cc | 22 +++--
9 files changed, 320 insertions(+), 24 deletions(-)
---
diff --git a/Src/DasherCore/Makefile.am b/Src/DasherCore/Makefile.am
index de21e23..5ce63c9 100644
--- a/Src/DasherCore/Makefile.am
+++ b/Src/DasherCore/Makefile.am
@@ -6,7 +6,9 @@ libdasherprefs_la_SOURCES = \
Parameters.h \
Parameters.cpp \
SettingsStore.cpp \
- SettingsStore.h
+ SettingsStore.h \
+ XmlSettingsStore.h \
+ XmlSettingsStore.cpp
libdashercore_la_SOURCES = \
AbstractXMLParser.cpp \
diff --git a/Src/DasherCore/SettingsStore.h b/Src/DasherCore/SettingsStore.h
index 08d5e5d..69b0cd0 100644
--- a/Src/DasherCore/SettingsStore.h
+++ b/Src/DasherCore/SettingsStore.h
@@ -39,8 +39,7 @@ public:
CSettingsStore();
- virtual ~CSettingsStore() {
- };
+ virtual ~CSettingsStore() = default;
// New functions for event driven interface
diff --git a/Src/DasherCore/XmlSettingsStore.cpp b/Src/DasherCore/XmlSettingsStore.cpp
new file mode 100644
index 0000000..f9a6914
--- /dev/null
+++ b/Src/DasherCore/XmlSettingsStore.cpp
@@ -0,0 +1,202 @@
+#include "XmlSettingsStore.h"
+
+#include <iostream>
+#include <fstream>
+#include <string.h>
+#include <algorithm>
+
+namespace Dasher {
+namespace {
+
+template <typename T>
+bool Read(const std::map<std::string, T> values, const std::string& key,
+ T* value) {
+ auto i = values.find(key);
+ if (i == values.end()) {
+ return false;
+ }
+ *value = i->second;
+ return true;
+}
+
+} // namespace
+
+XmlSettingsStore::XmlSettingsStore(const std::string& filename,
+ CMessageDisplay* pDisplay)
+ : AbstractXMLParser(pDisplay), filename_(filename) {}
+
+bool XmlSettingsStore::Load() {
+ bool result = true;
+ std::ifstream f(filename_);
+ if (f.good()) {
+ f.close();
+ if (!ParseFile(filename_, true /* user */)) {
+ m_pMsgs->Message("Failed to load the XML settings", true /* interrupt */);
+ result = false;
+ }
+ } else {
+ m_pMsgs->FormatMessageWithString("XML File not found: ", filename_.c_str());
+ result = false;
+ }
+ // Load all the settings or create defaults for the ones that don't exist.
+ // The superclass 'ParseFile' saves default settings if not found.
+ mode_ = EXPLICIT_SAVE;
+ LoadPersistent();
+ mode_ = SAVE_IMMEDIATELY;
+ return result;
+}
+
+bool XmlSettingsStore::LoadSetting(const std::string& key, bool* value) {
+ return Read(boolean_settings_, key, value);
+}
+
+bool XmlSettingsStore::LoadSetting(const std::string& key, long* value) {
+ return Read(long_settings_, key, value);
+}
+
+bool XmlSettingsStore::LoadSetting(const std::string& key, std::string* value) {
+ return Read(string_settings_, key, value);
+}
+
+void XmlSettingsStore::SaveSetting(const std::string& key, bool value) {
+ boolean_settings_[key] = value;
+ SaveIfNeeded();
+}
+
+void XmlSettingsStore::SaveSetting(const std::string& key, long value) {
+ long_settings_[key] = value;
+ SaveIfNeeded();
+}
+
+void XmlSettingsStore::SaveSetting(const std::string& key,
+ const std::string& value) {
+ string_settings_[key] = value;
+ SaveIfNeeded();
+}
+
+void XmlSettingsStore::SaveIfNeeded() {
+ modified_ = true;
+ if (mode_ == SAVE_IMMEDIATELY) {
+ Save();
+ }
+}
+
+bool XmlSettingsStore::Save() {
+ if (!modified_) {
+ return true;
+ }
+ try {
+ modified_ = false;
+ std::ofstream out;
+ out.open(filename_, std::ios::out | std::ios::trunc);
+ out << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
+ out << "<settings>\n";
+ for (const auto& p : long_settings_) {
+ out << "<long name=\"" << p.first << "\" value=\"" << p.second
+ << "\"/>\n";
+ }
+ for (const auto& p : boolean_settings_) {
+ std::string value = p.second ? "True" : "False";
+ out << "<bool name=\"" << p.first << "\" value=\"" << value << "\"/>\n";
+ }
+ for (const auto& p : string_settings_) {
+ std::string attribute = p.second;
+ XML_Escape(attribute, true /* attribute */);
+ out << "<string name=\"" << p.first << "\" value=\"" << attribute
+ << "\"/>\n";
+ }
+ out << "</settings>\n";
+ out.close();
+ } catch (...) {
+ // TODO(localize).
+ m_pMsgs->Message("Failed to save the settings", true /* interrupt */);
+ return false;
+ }
+ return true;
+}
+
+bool XmlSettingsStore::GetNameAndValue(const XML_Char** attributes,
+ std::string* name, std::string* value) {
+ bool found_name = false, found_value = false;
+ for (; *attributes != nullptr; attributes += 2) {
+ if (strcmp(attributes[0], "value") == 0) {
+ if (found_value) {
+ m_pMsgs->Message(
+ "XML configuration: the 'value' attribute can only be present "
+ "once in a tag",
+ true /* interrupt */);
+ return false;
+ }
+ *value = attributes[1];
+ found_value = true;
+ } else if (strcmp(attributes[0], "name") == 0) {
+ if (found_name) {
+ m_pMsgs->Message(
+ "XML configuration: the 'name' attribute can only be present "
+ "once in a tag",
+ true /* interrupt */);
+ return false;
+ }
+ *name = attributes[1];
+ if (name->empty()) {
+ m_pMsgs->Message(
+ "XML configuration: the 'name' attribute can not be empty.",
+ true /* interrupt */);
+ return false;
+ }
+ found_name = true;
+ } else {
+ m_pMsgs->FormatMessageWithString(
+ "XML configuration: invalid attribute: %s", *attributes);
+ return false;
+ }
+ }
+ if (!found_name || !found_value) {
+ m_pMsgs->Message("XML configuration: missing name or value in a tag.",
+ true /* interrupt */);
+ return false;
+ }
+ return true;
+}
+
+void XmlSettingsStore::XmlStartHandler(const XML_Char* element_name,
+ const XML_Char** attributes) {
+ std::string element = element_name;
+ if (element == "settings") {
+ return;
+ }
+ std::string name, value;
+ if (!GetNameAndValue(attributes, &name, &value)) {
+ return;
+ }
+ if (element == "string") {
+ string_settings_[name] = value;
+ } else if (element == "long") {
+ errno = 0;
+ long v = std::strtol(value.c_str(), nullptr, 0 /* base */);
+ if (errno != 0) {
+ m_pMsgs->FormatMessageWith2Strings(
+ "XML configuration: invalid numeric value '%s' for '%s'",
+ value.c_str(), name.c_str());
+ }
+ long_settings_[name] = v;
+ } else if (element == "bool") {
+
+ if (strcasecmp(value.c_str(), "true") == 0) {
+ boolean_settings_[name] = true;
+ } else if (strcasecmp(value.c_str(), "false") == 0) {
+ boolean_settings_[name] = false;
+ } else {
+ m_pMsgs->FormatMessageWith2Strings(
+ "XML configuration: boolean value should be 'true' or 'false' found "
+ "%s = '%s'",
+ name.c_str(), value.c_str());
+ }
+ } else {
+ m_pMsgs->FormatMessageWithString("XML configuration: unknown tag '%s'",
+ element.c_str());
+ }
+}
+
+void XmlSettingsStore::XmlEndHandler(const XML_Char* ) {}
+} // namespace Dasher
diff --git a/Src/DasherCore/XmlSettingsStore.h b/Src/DasherCore/XmlSettingsStore.h
new file mode 100644
index 0000000..37c892b
--- /dev/null
+++ b/Src/DasherCore/XmlSettingsStore.h
@@ -0,0 +1,65 @@
+#ifndef XML_SETTING_STORE_H_
+#define XML_SETTING_STORE_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string>
+#include <map>
+
+#include "SettingsStore.h"
+#include "AbstractXMLParser.h"
+
+namespace Dasher {
+
+// This class is not thread-safe.
+class XmlSettingsStore : public Dasher::CSettingsStore, AbstractXMLParser {
+ public:
+ XmlSettingsStore(const std::string& filename, CMessageDisplay* pDisplay);
+ ~XmlSettingsStore() override = default;
+ // Load the XML file and fills in the default values needed.
+ // Returns true on success.
+ bool Load();
+ // Saves the XML file, returns true on success.
+ bool Save();
+
+ private:
+ bool LoadSetting(const std::string& Key, bool* Value) override;
+ bool LoadSetting(const std::string& Key, long* Value) override;
+ bool LoadSetting(const std::string& Key, std::string* Value) override;
+
+ void SaveSetting(const std::string& Key, bool Value) override;
+ void SaveSetting(const std::string& Key, long Value) override;
+ void SaveSetting(const std::string& Key, const std::string& Value) override;
+
+ void XmlStartHandler(const XML_Char* name, const XML_Char** atts) override;
+ virtual void XmlEndHandler(const XML_Char* name) override;
+
+ // Parses the tag attributes expecting exactly one 'value' and one 'name'
+ // attribute.
+ bool GetNameAndValue(const XML_Char** attributes, std::string* name,
+ std::string* value);
+
+ // Save if the mode is 'SAVE_IMMEDIATELY', otherwise just set 'modified_' to
+ // true.
+ void SaveIfNeeded();
+
+ enum Mode {
+ // Save each time 'SaveSetting' is called.
+ SAVE_IMMEDIATELY,
+ // Save only when 'Save' is called.
+ EXPLICIT_SAVE
+ };
+
+ Mode mode_ = EXPLICIT_SAVE;
+ std::string filename_;
+ bool modified_ = false;
+ std::map<std::string, bool> boolean_settings_;
+ std::map<std::string, long> long_settings_;
+ std::map<std::string, std::string> string_settings_;
+};
+
+} // namespace Dasher
+
+#endif // XML_SETTING_STORE_H_
diff --git a/Src/Gtk2/DasherControl.cpp b/Src/Gtk2/DasherControl.cpp
index 80d9074..d49daa3 100644
--- a/Src/Gtk2/DasherControl.cpp
+++ b/Src/Gtk2/DasherControl.cpp
@@ -39,8 +39,9 @@ extern "C" gint canvas_expose_event(GtkWidget *widget, GdkEventExpose *event, gp
static bool g_iTimeoutID = 0;
// CDasherControl class definitions
-CDasherControl::CDasherControl(GtkVBox *pVBox, GtkDasherControl *pDasherControl)
- : CDashIntfScreenMsgs(new CGnomeSettingsStore()) {
+CDasherControl::CDasherControl(GtkVBox *pVBox, GtkDasherControl *pDasherControl,
+ CSettingsStore* settings)
+ : CDashIntfScreenMsgs(settings) {
m_pScreen = NULL;
m_pDasherControl = pDasherControl;
diff --git a/Src/Gtk2/DasherControl.h b/Src/Gtk2/DasherControl.h
index 7eb9b67..8279f7c 100644
--- a/Src/Gtk2/DasherControl.h
+++ b/Src/Gtk2/DasherControl.h
@@ -48,8 +48,9 @@ public:
/// \param pDasherControl Pointer to the GObject wrapper. This is
/// needed so that we can emit signals from the GObject.
///
-
- CDasherControl(GtkVBox * pVbox, GtkDasherControl * pDasherControl);
+ // The CDasherControl object takes ownership of 'settings'.
+ CDasherControl(GtkVBox * pVbox, GtkDasherControl * pDasherControl,
+ CSettingsStore* settings);
~CDasherControl();
// Event handlers
diff --git a/Src/Gtk2/GtkDasherControl.cpp b/Src/Gtk2/GtkDasherControl.cpp
index 28bdd76..5dca279 100644
--- a/Src/Gtk2/GtkDasherControl.cpp
+++ b/Src/Gtk2/GtkDasherControl.cpp
@@ -18,6 +18,7 @@
// along with Dasher; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#include "../DasherCore/XmlSettingsStore.h"
#include "../Common/Common.h"
#include "DasherControl.h"
@@ -25,6 +26,10 @@
#include "custom_marshal.h"
#include "dasher_editor.h"
+// TODO: find a better way to get access to the command line arguments.
+extern gchar* g_xml_file_location;
+
+
struct _GtkDasherControlPrivate {
CDasherControl *pControl;
DasherEditor *pEditor;
@@ -106,11 +111,30 @@ gtk_dasher_control_class_init(GtkDasherControlClass *pClass) {
// pClass->key_release_event = gtk_dasher_control_default_key_release_handler;
}
+class XmlErrorDisplay : public CMessageDisplay {
+ public:
+ void Message(const std::string &strText, bool bInterrupt) override {
+ // TODO: decide if a pop-up dialog should be shown instead.
+ fputs(strText.c_str(), stderr);
+ fputs("\n", stderr);
+ }
+};
+
static void
gtk_dasher_control_init(GtkDasherControl *pDasherControl) {
GtkDasherControlPrivate *pPrivate = GTK_DASHER_CONTROL_GET_PRIVATE(pDasherControl);
-
- pPrivate->pControl = new CDasherControl(&(pDasherControl->box), pDasherControl);
+ static XmlErrorDisplay display;
+ Dasher::CSettingsStore* settings;
+ if (g_xml_file_location == nullptr) {
+ settings = new CGnomeSettingsStore();
+ } else {
+ auto xml_settings = new XmlSettingsStore(g_xml_file_location, &display);
+ xml_settings->Load();
+ // Save the defaults if needed.
+ xml_settings->Save();
+ settings = xml_settings;
+ }
+ pPrivate->pControl = new CDasherControl(&(pDasherControl->box), pDasherControl, settings);
// g_signal_connect(G_OBJECT(pDasherControl), "key-press-event",
G_CALLBACK(gtk_dasher_control_default_key_press_handler), pPrivate->pControl);
// g_signal_connect(G_OBJECT(pDasherControl), "key-release-event",
G_CALLBACK(gtk_dasher_control_default_key_release_handler), pPrivate->pControl);
diff --git a/Src/Gtk2/dasher_main.h b/Src/Gtk2/dasher_main.h
index 61d3de6..46099ef 100644
--- a/Src/Gtk2/dasher_main.h
+++ b/Src/Gtk2/dasher_main.h
@@ -30,12 +30,10 @@ struct _DasherMainClass {
void (*realized)(DasherMain *pDasherMain);
};
-typedef struct _SCommandLine SCommandLine;
-
-struct _SCommandLine {
- gchar *szFilename;
- gchar *szAppStyle;
- gchar *szOptions;
+struct SCommandLine {
+ gchar *szFilename = nullptr;
+ gchar *szAppStyle = nullptr;
+ gchar *szOptions = nullptr;
};
DasherMain *dasher_main_new(int *argc, char ***argv, SCommandLine *pCommandLine);
diff --git a/Src/main.cc b/Src/main.cc
index 460a528..4c0fbe2 100644
--- a/Src/main.cc
+++ b/Src/main.cc
@@ -133,6 +133,7 @@ void clean_up();
// }
// }
+gchar* g_xml_file_location = nullptr;
extern "C" gint main_key_snooper(GtkWidget *pWidget, GdkEventKey *pEvent, gpointer pUserData);
@@ -152,10 +153,6 @@ int main(int argc, char *argv[]) {
SCommandLine sCommandLine;
- sCommandLine.szFilename = NULL;
- sCommandLine.szAppStyle = NULL;
- sCommandLine.szOptions = NULL;
-
gboolean do_option_help = false;
static const GOptionEntry options[] = {
// {"timedata", 'w', 0, G_OPTION_ARG_NONE, &timedata, "Write basic timing information to stdout",
NULL},
@@ -166,18 +163,22 @@ int main(int argc, char *argv[]) {
{"appstyle", 'a', 0, G_OPTION_ARG_STRING, &(sCommandLine.szAppStyle), N_("Application style
(traditional, direct, compose or fullscreen)"), "traditional"},
// Note to translators: This is the help string for "--options"
{"options", 'o', 0, G_OPTION_ARG_STRING, &(sCommandLine.szOptions), N_("Override stored options"), NULL},
- // Note to translators: This is the help string for "--help-options"
+ {"config", 'c', 0, G_OPTION_ARG_STRING, &g_xml_file_location, N_("XML configuration file name"), NULL},
+ // Note to translators: This is the help string for "--help-options"
{"help-options", 0, 0, G_OPTION_ARG_NONE, &do_option_help, N_("Describe \"--options\"."), NULL},
{NULL}
};
//parse command line options
- GOptionContext *goptcontext;
// Note to translators: This is the "--help" description of dasher.
- goptcontext = g_option_context_new(_("- A text input application honouring accessibility"));
+ GOptionContext *goptcontext = g_option_context_new(_("- A text input application honouring
accessibility"));
g_option_context_add_main_entries(goptcontext, options, GETTEXT_PACKAGE);
- g_option_context_add_group(goptcontext, gtk_get_option_group (TRUE));
- g_option_context_parse(goptcontext, &argc, &argv, NULL);
+ g_option_context_add_group(goptcontext, gtk_get_option_group(TRUE));
+ GError *error = nullptr;
+ if (!g_option_context_parse(goptcontext, &argc, &argv, &error)) {
+ g_print("option parsing failed: %s\n", error->message);
+ exit (1);
+ }
// TODO: Check what happens here when goption has done its stuff
@@ -236,6 +237,9 @@ int main(int argc, char *argv[]) {
// 11.
clean_up();
+ if (g_xml_file_location != nullptr)
+ g_free(g_xml_file_location);
+
return 0;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]