summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-03-30 23:31:36 +0000
committerDavid Robillard <d@drobilla.net>2014-03-30 23:31:36 +0000
commit77448bc7507c26b964a9159fa1e4035487ccc326 (patch)
tree2b849fa16e97826d0ee279559d7db4126af1f66c /src
parent2a37c8279d54e41242dca7ffb9c5019d56b01145 (diff)
downloadpatchage-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.cpp17
-rw-r--r--src/Patchage.hpp3
-rw-r--r--src/StateManager.cpp274
-rw-r--r--src/StateManager.hpp11
-rw-r--r--src/patchage.ui10
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 &lt;nedko@arnaudov.name&gt;</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>