From b6376fc247490134827c51c554a6108ab9f5a898 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 22 Nov 2012 03:17:28 +0000 Subject: Configuration file saving. Automatically save and restore GUI settings (last patch directory, human names, etc.). git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@4848 a436a847-0d15-0410-975c-d299462d15a1 --- src/Configuration.cpp | 150 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 119 insertions(+), 31 deletions(-) (limited to 'src/Configuration.cpp') diff --git a/src/Configuration.cpp b/src/Configuration.cpp index b3f69c4a..60f41597 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -14,6 +14,10 @@ along with Ingen. If not, see . */ +#include +#include +#include + #include #include @@ -22,6 +26,7 @@ #include "ingen/Configuration.hpp" #include "raul/fmt.hpp" #include "sord/sordmm.hpp" +#include "sratom/sratom.h" #define NS_INGEN "http://drobilla.net/ns/ingen#" @@ -44,23 +49,23 @@ Configuration::Configuration(Forge& forge) " ingen -egl foo.ingen # Run an engine and a GUI and load a graph") , _max_name_length(0) { - add("clientPort", "client-port", 'C', "Client port", forge.Int, Raul::Atom()); - add("connect", "connect", 'c', "Connect to engine URI", forge.String, forge.alloc("unix:///tmp/ingen.sock")); - add("engine", "engine", 'e', "Run (JACK) engine", forge.Bool, forge.make(false)); - add("enginePort", "engine-port", 'E', "Engine listen port", forge.Int, forge.make(16180)); - add("socket", "socket", 'S', "Engine socket path", forge.String, forge.alloc("/tmp/ingen.sock")); - add("gui", "gui", 'g', "Launch the GTK graphical interface", forge.Bool, forge.make(false)); - add("", "help", 'h', "Print this help message", forge.Bool, forge.make(false)); - add("jackName", "jack-name", 'n', "JACK name", forge.String, forge.alloc("ingen")); - add("jackServer", "jack-server", 's', "JACK server name", forge.String, forge.alloc("")); - add("uuid", "uuid", 'u', "JACK session UUID", forge.String, Raul::Atom()); - add("load", "load", 'l', "Load graph", forge.String, Raul::Atom()); - add("path", "path", 'L', "Target path for loaded graph", forge.String, Raul::Atom()); - add("queueSize", "queue-size", 'q', "Event queue size", forge.Int, forge.make(4096)); - add("run", "run", 'r', "Run script", forge.String, Raul::Atom()); - add("humanNames", "human-names", 0, "Show human names in GUI", forge.Bool, forge.make(true)); - add("portLabels", "port-labels", 0, "Show port labels in GUI", forge.Bool, forge.make(true)); - add("graphDirectory", "graph-directory", 0, "Default directory for opening graphs", forge.String, Raul::Atom()); + add("clientPort", "client-port", 'C', "Client port", SESSION, forge.Int, Raul::Atom()); + add("connect", "connect", 'c', "Connect to engine URI", SESSION, forge.String, forge.alloc("unix:///tmp/ingen.sock")); + add("engine", "engine", 'e', "Run (JACK) engine", SESSION, forge.Bool, forge.make(false)); + add("enginePort", "engine-port", 'E', "Engine listen port", SESSION, forge.Int, forge.make(16180)); + add("socket", "socket", 'S', "Engine socket path", SESSION, forge.String, forge.alloc("/tmp/ingen.sock")); + add("gui", "gui", 'g', "Launch the GTK graphical interface", SESSION, forge.Bool, forge.make(false)); + add("", "help", 'h', "Print this help message", SESSION, forge.Bool, forge.make(false)); + add("jackName", "jack-name", 'n', "JACK name", SESSION, forge.String, forge.alloc("ingen")); + add("jackServer", "jack-server", 's', "JACK server name", GLOBAL, forge.String, forge.alloc("")); + add("uuid", "uuid", 'u', "JACK session UUID", SESSION, forge.String, Raul::Atom()); + add("load", "load", 'l', "Load graph", SESSION, forge.String, Raul::Atom()); + add("path", "path", 'L', "Target path for loaded graph", SESSION, forge.String, Raul::Atom()); + add("queueSize", "queue-size", 'q', "Event queue size", GLOBAL, forge.Int, forge.make(4096)); + add("run", "run", 'r', "Run script", SESSION, forge.String, Raul::Atom()); + add("humanNames", "human-names", 0, "Show human names in GUI", GUI, forge.Bool, forge.make(true)); + add("portLabels", "port-labels", 0, "Show port labels in GUI", GUI, forge.Bool, forge.make(true)); + add("graphDirectory", "graph-directory", 0, "Default directory for opening graphs", GUI, forge.String, Raul::Atom()); } Configuration& @@ -68,12 +73,13 @@ Configuration::add(const std::string& key, const std::string& name, char letter, const std::string& desc, + Scope scope, const Raul::Atom::TypeID type, const Raul::Atom& value) { assert(value.type() == type || value.type() == 0); _max_name_length = std::max(_max_name_length, name.length()); - _options.insert(make_pair(name, Option(name, letter, desc, type, value))); + _options.insert(make_pair(name, Option(key, name, letter, desc, scope, type, value))); if (!key.empty()) { _keys.insert(make_pair(key, name)); } @@ -106,7 +112,7 @@ Configuration::print_usage(const std::string& program, std::ostream& os) int Configuration::set_value_from_string(Configuration::Option& option, const std::string& value) - throw (Configuration::CommandLineError) + throw (Configuration::OptionError) { if (option.type == _forge.Int) { char* endptr = NULL; @@ -114,7 +120,7 @@ Configuration::set_value_from_string(Configuration::Option& option, if (endptr && *endptr == '\0') { option.value = _forge.make(intval); } else { - throw CommandLineError( + throw OptionError( (Raul::fmt("option `%1%' has non-integer value `%2%'") % option.name % value).str()); } @@ -125,7 +131,7 @@ Configuration::set_value_from_string(Configuration::Option& option, option.value = _forge.make(bool(!strcmp(value.c_str(), "true"))); assert(option.value.type() == _forge.Bool); } else { - throw CommandLineError( + throw OptionError( (Raul::fmt("bad option type `%1%'") % option.name).str()); } return EXIT_SUCCESS; @@ -133,7 +139,7 @@ Configuration::set_value_from_string(Configuration::Option& option, /** Parse command line arguments. */ void -Configuration::parse(int argc, char** argv) throw (Configuration::CommandLineError) +Configuration::parse(int argc, char** argv) throw (Configuration::OptionError) { for (int i = 1; i < argc; ++i) { if (argv[i][0] != '-' || !strcmp(argv[i], "-")) { @@ -142,14 +148,14 @@ Configuration::parse(int argc, char** argv) throw (Configuration::CommandLineErr const std::string name = std::string(argv[i]).substr(2); Options::iterator o = _options.find(name); if (o == _options.end()) { - throw CommandLineError( + throw OptionError( (Raul::fmt("unrecognized option `%1%'") % name).str()); } if (o->second.type == _forge.Bool) { o->second.value = _forge.make(true); } else { if (++i >= argc) - throw CommandLineError( + throw OptionError( (Raul::fmt("missing value for `%1'") % name).str()); set_value_from_string(o->second, argv[i]); } @@ -159,12 +165,12 @@ Configuration::parse(int argc, char** argv) throw (Configuration::CommandLineErr char letter = argv[i][j]; ShortNames::iterator n = _short_names.find(letter); if (n == _short_names.end()) - throw CommandLineError( + throw OptionError( (Raul::fmt("unrecognized option `%1%'") % letter).str()); Options::iterator o = _options.find(n->second); if (j < len - 1) { if (o->second.type != _forge.Bool) - throw CommandLineError( + throw OptionError( (Raul::fmt("missing value for `%1%'") % letter).str()); o->second.value = _forge.make(true); } else { @@ -172,7 +178,7 @@ Configuration::parse(int argc, char** argv) throw (Configuration::CommandLineErr o->second.value = _forge.make(true); } else { if (++i >= argc) - throw CommandLineError( + throw OptionError( (Raul::fmt("missing value for `%1%'") % letter).str()); set_value_from_string(o->second, argv[i]); } @@ -212,14 +218,96 @@ Configuration::load(const std::string& path) } } } - + serd_node_free(&node); serd_env_free(env); return true; } +std::string +Configuration::save(URIMap& uri_map, + const std::string& app, + const std::string& filename, + unsigned scopes) + throw (FileError) +{ + // Save to file if it is absolute, otherwise save to user config dir + std::string path = filename; + if (!Glib::path_is_absolute(path)) { + path = Glib::build_filename(Glib::get_user_config_dir(), app, filename); + } + + // Create parent directories if necessary + const std::string dir = Glib::path_get_dirname(path); + if (g_mkdir_with_parents(dir.c_str(), 0755) < 0) { + throw FileError((Raul::fmt("Error creating directory %1% (%2%)") + % dir % strerror(errno)).str()); + } + + // Attempt to open file for writing + FILE* file = fopen(path.c_str(), "w"); + if (!file) { + throw FileError((Raul::fmt("Failed to open file %1% (%2%)") + % path % strerror(errno)).str()); + } + + // Use the file's URI as the base URI + SerdURI base_uri; + SerdNode base = serd_node_new_file_uri( + (const uint8_t*)path.c_str(), NULL, &base_uri, true); + + // Create environment with ingen prefix + SerdEnv* env = serd_env_new(&base); + serd_env_set_prefix_from_strings( + env, (const uint8_t*)"ingen", (const uint8_t*)NS_INGEN); + + // Create Turtle writer + SerdWriter* writer = serd_writer_new( + SERD_TURTLE, + (SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED), + env, + &base_uri, + serd_file_sink, + file); + + // Write a prefix directive for each prefix in the environment + serd_env_foreach(env, (SerdPrefixSink)serd_writer_set_prefix, writer); + + // Create an atom serialiser and connect it to the Turtle writer + Sratom* sratom = sratom_new(&uri_map.urid_map_feature()->urid_map); + sratom_set_pretty_numbers(sratom, true); + sratom_set_sink(sratom, (const char*)base.buf, + (SerdStatementSink)serd_writer_write_statement, NULL, + writer); + + // Write a statement for each valid option + for (Options::iterator o = _options.begin(); o != _options.end(); ++o) { + const Raul::Atom& value = o->second.value; + if (!(o->second.scope & scopes) || + o->second.key.empty() || + !value.is_valid()) { + continue; + } + + const std::string key(std::string("ingen:") + o->second.key); + SerdNode pred = serd_node_from_string( + SERD_CURIE, (const uint8_t*)key.c_str()); + sratom_write(sratom, &uri_map.urid_unmap_feature()->urid_unmap, 0, + &base, &pred, value.type(), value.size(), value.get_body()); + } + + sratom_free(sratom); + serd_writer_free(writer); + serd_env_free(env); + serd_node_free(&base); + fclose(file); + + return path; +} + std::list -Configuration::load_default(const std::string& app, const std::string& file) +Configuration::load_default(const std::string& app, + const std::string& filename) { std::list loaded; @@ -227,14 +315,14 @@ Configuration::load_default(const std::string& app, const std::string& file) for (std::vector::const_iterator i = dirs.begin(); i != dirs.end(); ++i) { - const std::string path = Glib::build_filename(*i, app, file); + const std::string path = Glib::build_filename(*i, app, filename); if (load(path)) { loaded.push_back(path); } } const std::string path = Glib::build_filename( - Glib::get_user_config_dir(), app, file); + Glib::get_user_config_dir(), app, filename); if (load(path)) { loaded.push_back(path); } -- cgit v1.2.1