diff options
author | David Robillard <d@drobilla.net> | 2014-03-30 23:31:36 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2014-03-30 23:31:36 +0000 |
commit | 77448bc7507c26b964a9159fa1e4035487ccc326 (patch) | |
tree | 2b849fa16e97826d0ee279559d7db4126af1f66c /src | |
parent | 2a37c8279d54e41242dca7ffb9c5019d56b01145 (diff) | |
download | patchage-77448bc7507c26b964a9159fa1e4035487ccc326.tar.gz patchage-77448bc7507c26b964a9159fa1e4035487ccc326.tar.bz2 patchage-77448bc7507c26b964a9159fa1e4035487ccc326.zip |
Rewrite configuration system.
Use standard XDG paths for configuration (fix #142).
Save settings automatically on exit.
git-svn-id: http://svn.drobilla.net/lad/trunk/patchage@5347 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r-- | src/Patchage.cpp | 17 | ||||
-rw-r--r-- | src/Patchage.hpp | 3 | ||||
-rw-r--r-- | src/StateManager.cpp | 274 | ||||
-rw-r--r-- | src/StateManager.hpp | 11 | ||||
-rw-r--r-- | src/patchage.ui | 10 |
5 files changed, 157 insertions, 158 deletions
diff --git a/src/Patchage.cpp b/src/Patchage.cpp index e76a8d5..4531cad 100644 --- a/src/Patchage.cpp +++ b/src/Patchage.cpp @@ -97,7 +97,6 @@ Patchage::Patchage(int argc, char** argv) , INIT_WIDGET(_menu_open_session) , INIT_WIDGET(_menu_save_session) , INIT_WIDGET(_menu_save_close_session) - , INIT_WIDGET(_menu_store_positions) , INIT_WIDGET(_menu_view_arrange) , INIT_WIDGET(_menu_view_messages) , INIT_WIDGET(_menu_view_refresh) @@ -120,8 +119,6 @@ Patchage::Patchage(int argc, char** argv) , _alsa_driver_autoattach(true) #endif { - _settings_filename = getenv("HOME"); - _settings_filename += "/.patchagerc"; _state_manager = new StateManager(); _canvas = boost::shared_ptr<PatchageCanvas>(new PatchageCanvas(this, 1600*2, 1200*2)); @@ -182,8 +179,6 @@ Patchage::Patchage(int argc, char** argv) _menu_alsa_disconnect->set_sensitive(false); #endif - _menu_store_positions->signal_activate().connect( - sigc::mem_fun(this, &Patchage::on_store_positions)); _menu_file_quit->signal_activate().connect( sigc::mem_fun(this, &Patchage::on_quit)); _menu_draw->signal_activate().connect( @@ -225,7 +220,7 @@ Patchage::Patchage(int argc, char** argv) _canvas->widget().show(); _main_win->present(); - _state_manager->load(_settings_filename); + _state_manager->load(); _main_win->resize( static_cast<int>(_state_manager->get_window_size().x), @@ -276,6 +271,9 @@ Patchage::Patchage(int argc, char** argv) Patchage::~Patchage() { + store_window_location(); + _state_manager->save(); + #if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS) delete _jack_driver; #endif @@ -714,13 +712,6 @@ Patchage::on_show_messages() _messages_win->present(); } -void -Patchage::on_store_positions() -{ - store_window_location(); - _state_manager->save(_settings_filename); -} - bool Patchage::on_scroll(GdkEventScroll* ev) { diff --git a/src/Patchage.hpp b/src/Patchage.hpp index 5a09e74..ece59b0 100644 --- a/src/Patchage.hpp +++ b/src/Patchage.hpp @@ -129,8 +129,6 @@ protected: Gtk::Main* _gtk_main; - std::string _settings_filename; - Widget<Gtk::AboutDialog> _about_win; Widget<Gtk::ScrolledWindow> _main_scrolledwin; Widget<Gtk::Window> _main_win; @@ -145,7 +143,6 @@ protected: Widget<Gtk::MenuItem> _menu_open_session; Widget<Gtk::MenuItem> _menu_save_session; Widget<Gtk::MenuItem> _menu_save_close_session; - Widget<Gtk::MenuItem> _menu_store_positions; Widget<Gtk::MenuItem> _menu_view_arrange; Widget<Gtk::MenuItem> _menu_view_messages; Widget<Gtk::MenuItem> _menu_view_refresh; diff --git a/src/StateManager.cpp b/src/StateManager.cpp index d059cb7..62d9ea7 100644 --- a/src/StateManager.cpp +++ b/src/StateManager.cpp @@ -14,19 +14,18 @@ * along with Patchage. If not, see <http://www.gnu.org/licenses/>. */ +#include <ctype.h> #include <stdlib.h> #include <fstream> #include <iostream> +#include <limits> #include <stdexcept> +#include <vector> #include "StateManager.hpp" #include "Patchage.hpp" -using std::endl; -using std::map; -using std::string; - StateManager::StateManager() : _window_location(0, 0) , _window_size(640, 480) @@ -35,65 +34,49 @@ StateManager::StateManager() } bool -StateManager::get_module_location(const string& name, ModuleType type, Coord& loc) +StateManager::get_module_location(const std::string& name, ModuleType type, Coord& loc) { - map<string, ModuleSettings>::const_iterator i = _module_settings.find(name); - if (i == _module_settings.end()) + std::map<std::string, ModuleSettings>::const_iterator i = _module_settings.find(name); + if (i == _module_settings.end()) { return false; + } const ModuleSettings& settings = (*i).second; - - switch (type) { - case Input: - if (settings.input_location) { - loc = *settings.input_location; - return true; - } - break; - case Output: - if (settings.output_location) { - loc = *settings.output_location; - return true; - } - break; - case InputOutput: - if (settings.inout_location) { - loc = *settings.inout_location; - return true; - } - break; - default: - throw std::runtime_error("Invalid module type"); + if (type == Input && settings.input_location) { + loc = *settings.input_location; + } else if (type == Output && settings.output_location) { + loc = *settings.output_location; + } else if (type == InputOutput && settings.inout_location) { + loc = *settings.inout_location; + } else { + return false; } - return false; + return true; } void -StateManager::set_module_location(const string& name, ModuleType type, Coord loc) +StateManager::set_module_location(const std::string& name, ModuleType type, Coord loc) { -retry: - map<string, ModuleSettings>::iterator i = _module_settings.find(name); + std::map<std::string, ModuleSettings>::iterator i = _module_settings.find(name); if (i == _module_settings.end()) { - // no mapping exists, insert new element and set its split type, then retry to retrieve reference to it - _module_settings[name].split = type != InputOutput; - goto retry; + i = _module_settings.insert( + std::make_pair(name, ModuleSettings(type != InputOutput))).first; } - ModuleSettings& settings_ref = (*i).second; - + ModuleSettings& settings = (*i).second; switch (type) { case Input: - settings_ref.input_location = loc; + settings.input_location = loc; break; case Output: - settings_ref.output_location = loc; + settings.output_location = loc; break; case InputOutput: - settings_ref.inout_location = loc; + settings.inout_location = loc; break; default: - throw std::runtime_error("Invalid module type"); + break; // shouldn't reach here } } @@ -103,142 +86,179 @@ retry: * to allow driver's to request terminal ports get split by default). */ bool -StateManager::get_module_split(const string& name, bool default_val) const +StateManager::get_module_split(const std::string& name, bool default_val) const { - map<string, ModuleSettings>::const_iterator i = _module_settings.find(name); - if (i == _module_settings.end()) + std::map<std::string, ModuleSettings>::const_iterator i = _module_settings.find(name); + if (i == _module_settings.end()) { return default_val; + } return (*i).second.split; } void -StateManager::set_module_split(const string& name, bool split) +StateManager::set_module_split(const std::string& name, bool split) { _module_settings[name].split = split; } -void -StateManager::load(const string& filename) +/** Return a vector of filenames in descending order by preference. */ +static std::vector<std::string> +get_filenames() { - _module_settings.clear(); + std::vector<std::string> filenames; + std::string prefix; - std::ifstream is; - is.open(filename.c_str(), std::ios::in); + const char* xdg_config_home = getenv("XDG_CONFIG_HOME"); + const char* home = getenv("HOME"); - if ( ! is.good()) - return; + // XDG spec + if (xdg_config_home) { + filenames.push_back(std::string(xdg_config_home) + "/patchagerc"); + } else if (home) { + filenames.push_back(std::string(home) + "/.config/patchagerc"); + } - std::cout << "Loading configuration file " << filename << std::endl; + // Old location + if (home) { + filenames.push_back(std::string(home) + "/.patchagerc"); + } - string s; + // Current directory (bundle or last-ditch effort) + filenames.push_back("patchagerc"); - is >> s; - if (s == "window_location") { - is >> s; - _window_location.x = atoi(s.c_str()); - is >> s; - _window_location.y = atoi(s.c_str()); - } + return filenames; +} - is >> s; - if (s == "window_size") { - is >> s; - _window_size.x = atoi(s.c_str()); - is >> s; - _window_size.y = atoi(s.c_str()); +void +StateManager::load() +{ + // Try to find a readable configuration file + const std::vector<std::string> filenames = get_filenames(); + std::ifstream file; + for (size_t i = 0; i < filenames.size(); ++i) { + file.open(filenames[i].c_str(), std::ios::in); + if (file.good()) { + std::cout << "Loading configuration from " << filenames[i] << std::endl; + break; + } } - is >> s; - if (s != "zoom_level") { - std::string msg = "Corrupt settings file: expected \"zoom_level\", found \""; - msg.append(s).append("\""); - throw std::runtime_error(msg); + if (!file.good()) { + std::cout << "No configuration file present" << std::endl; + return; } - is >> s; - _zoom = atof(s.c_str()); - - Coord loc; - ModuleType type; - string name; - - while (1) { - is >> s; - if (is.eof()) break; + _module_settings.clear(); + while (file.good()) { + std::string key; + if (file.peek() == '\"') { + /* Old versions ommitted the module_position key and listed + positions starting with module name in quotes. */ + key = "module_position"; + } else { + file >> key; + } - // Old versions didn't quote, so need to support both :/ - if (s[0] == '\"') { - if (s.length() > 1 && s[s.length()-1] == '\"') { - name = s.substr(1, s.length()-2); + if (key == "window_location") { + file >> _window_location.x >> _window_location.y; + } else if (key == "window_size") { + file >> _window_size.x >> _window_size.y; + } else if (key == "zoom_level") { + file >> _zoom; + } else if (key == "module_position" || key[0] == '\"') { + + Coord loc; + std::string name; + file.ignore(1, '\"'); + std::getline(file, name, '\"'); + + ModuleType type; + std::string type_str; + file >> type_str; + if (type_str == "input") { + type = Input; + } else if (type_str == "output") { + type = Output; + } else if (type_str == "inputoutput") { + type = InputOutput; } else { - name = s.substr(1); - is >> s; - while (s[s.length()-1] != '\"') { - name.append(" ").append(s); - is >> s; - } - name.append(" ").append(s.substr(0, s.length()-1)); + std::cerr << "error: bad position type `" << type_str + << "' for module `" << name << "'" << std::endl; + file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); + continue; } - } else { - name = s; - } - is >> s; - if (s == "input") type = Input; - else if (s == "output") type = Output; - else if (s == "inputoutput") type = InputOutput; - else throw std::runtime_error("Corrupt settings file."); + file >> loc.x; + file >> loc.y; - is >> s; - loc.x = atoi(s.c_str()); - is >> s; - loc.y = atoi(s.c_str()); + set_module_location(name, type, loc); + } else { + std::cerr << "warning: unknown configuration key `" << key << "'" + << std::endl; + file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); + } - set_module_location(name, type, loc); + // Skip trailing whitespace, including newline + while (file.good() && isspace(file.peek())) { + file.ignore(1); + } } - is.close(); + file.close(); } static inline void -write_module_settings_entry( - std::ofstream& os, - const string& name, - const char * type, - const Coord& loc) +write_module_position(std::ofstream& os, + const std::string& name, + const char* type, + const Coord& loc) { - os << "\"" << name << "\"" << " " << type << " " << loc.x << " " << loc.y << std::endl; + os << "module_position \"" << name << "\"" + << " " << type << " " << loc.x << " " << loc.y << std::endl; } void -StateManager::save(const string& filename) +StateManager::save() { - std::ofstream os; - os.open(filename.c_str(), std::ios::out); + // Try to find a writable configuration file + const std::vector<std::string> filenames = get_filenames(); + std::ofstream file; + for (size_t i = 0; i < filenames.size(); ++i) { + file.open(filenames[i].c_str(), std::ios::out); + if (file.good()) { + std::cout << "Writing configuration to " << filenames[i] << std::endl; + break; + } + } + + if (!file.good()) { + std::cout << "Unable to open configuration file to write" << std::endl; + return; + } - os << "window_location " << _window_location.x << " " << _window_location.y << std::endl; - os << "window_size " << _window_size.x << " " << _window_size.y << std::endl; - os << "zoom_level " << _zoom << std::endl; + file << "window_location " << _window_location.x << " " << _window_location.y << std::endl; + file << "window_size " << _window_size.x << " " << _window_size.y << std::endl; + file << "zoom_level " << _zoom << std::endl; - for (map<string, ModuleSettings>::iterator i = _module_settings.begin(); - i != _module_settings.end(); ++i) { + for (std::map<std::string, ModuleSettings>::iterator i = _module_settings.begin(); + i != _module_settings.end(); ++i) { const ModuleSettings& settings = (*i).second; - const string& name = (*i).first; + const std::string& name = (*i).first; if (settings.split) { if (settings.input_location && settings.output_location) { - write_module_settings_entry(os, name, "input", *settings.input_location); - write_module_settings_entry(os, name, "output", *settings.output_location); + write_module_position(file, name, "input", *settings.input_location); + write_module_position(file, name, "output", *settings.output_location); } } else { if (settings.input_location && settings.inout_location) { - write_module_settings_entry(os, name, "inputoutput", *settings.inout_location); + write_module_position(file, name, "inputoutput", *settings.inout_location); } } } - os.close(); + file.close(); } float diff --git a/src/StateManager.hpp b/src/StateManager.hpp index 3df4d35..d8c1b4b 100644 --- a/src/StateManager.hpp +++ b/src/StateManager.hpp @@ -38,8 +38,8 @@ class StateManager public: StateManager(); - void load(const std::string& filename); - void save(const std::string& filename); + void load(); + void save(); bool get_module_location(const std::string& name, ModuleType type, Coord& loc); void set_module_location(const std::string& name, ModuleType type, Coord loc); @@ -59,14 +59,15 @@ public: private: struct ModuleSettings { - ModuleSettings() : split(false) {} + ModuleSettings(bool s=false) : split(s) {} boost::optional<Coord> input_location; boost::optional<Coord> output_location; boost::optional<Coord> inout_location; - bool split; + bool split; }; - std::map<std::string,ModuleSettings> _module_settings; + std::map<std::string, ModuleSettings> _module_settings; + Coord _window_location; Coord _window_size; float _zoom; diff --git a/src/patchage.ui b/src/patchage.ui index c34fa32..2836109 100644 --- a/src/patchage.ui +++ b/src/patchage.ui @@ -764,16 +764,6 @@ Nedko Arnaudov <nedko@arnaudov.name></property> </object> </child> <child> - <object class="GtkImageMenuItem" id="menu_store_positions"> - <property name="label">Save Positions</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_stock">False</property> - <signal name="activate" handler="on_save_settings1_activate" swapped="no"/> - </object> - </child> - <child> <object class="GtkSeparatorMenuItem" id="menu_file_draw_sep"> <property name="visible">True</property> <property name="can_focus">False</property> |