2 Copyright (C) 1999-2014 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include <cstdio> /* for snprintf, grrr */
26 #include <glibmm/miscutils.h>
27 #include <glib/gstdio.h>
29 #include "pbd/failed_constructor.h"
30 #include "pbd/xml++.h"
31 #include "pbd/file_utils.h"
32 #include "pbd/error.h"
33 #include "pbd/stacktrace.h"
35 #include "gtkmm2ext/rgb_macros.h"
36 #include "gtkmm2ext/gtk_ui.h"
38 #include "ardour/filesystem_paths.h"
40 #include "ardour_ui.h"
41 #include "global_signals.h"
42 #include "ui_config.h"
48 using namespace ARDOUR;
49 using namespace ArdourCanvas;
51 static const char* ui_config_file_name = "ui_config";
52 static const char* default_ui_config_file_name = "default_ui_config";
53 UIConfiguration* UIConfiguration::_instance = 0;
55 static const double hue_width = 18.0;
57 UIConfiguration::UIConfiguration ()
59 #undef UI_CONFIG_VARIABLE
60 #define UI_CONFIG_VARIABLE(Type,var,name,val) var (name,val),
61 #define CANVAS_FONT_VARIABLE(var,name) var (name),
62 #include "ui_config_vars.h"
63 #include "canvas_vars.h"
64 #undef UI_CONFIG_VARIABLE
65 #undef CANVAS_FONT_VARIABLE
67 /* initialize all the base colors using default
68 colors for now. these will be reset when/if
69 we load the UI config file.
72 #undef CANVAS_BASE_COLOR
73 #define CANVAS_BASE_COLOR(var,name,val) var (name,quantized (val)),
74 #include "base_colors.h"
75 #undef CANVAS_BASE_COLOR
78 aliases_modified (false),
79 derived_modified (false),
80 _saved_state_node (""),
81 _saved_state_version (-1)
86 /* pack all base colors into the configurable color map so that
87 derived colors can use them.
90 #undef CANVAS_BASE_COLOR
91 #define CANVAS_BASE_COLOR(var,name,color) configurable_colors.insert (make_pair (name,&var));
92 #include "base_colors.h"
93 #undef CANVAS_BASE_COLOR
96 #define CANVAS_COLOR(var,name,base,modifier) relative_colors.insert (make_pair (name, RelativeHSV (base,modifier)));
101 #define COLOR_ALIAS(var,name,alias) color_aliases.insert (make_pair (name,alias));
102 #include "color_aliases.h"
107 ARDOUR_UI_UTILS::ColorsChanged.connect (boost::bind (&UIConfiguration::colors_changed, this));
109 ParameterChanged.connect (sigc::mem_fun (*this, &UIConfiguration::parameter_changed));
111 /* force GTK theme setting, so that loading an RC file will work */
116 UIConfiguration::~UIConfiguration ()
121 UIConfiguration::colors_changed ()
127 /* In theory, one of these ought to work:
129 gtk_rc_reparse_all_for_settings (gtk_settings_get_default(), true);
130 gtk_rc_reset_styles (gtk_settings_get_default());
132 but in practice, neither of them do. So just reload the current
133 GTK RC file, which causes a reset of all styles and a redraw
136 parameter_changed ("ui-rc_file");
140 UIConfiguration::parameter_changed (string param)
144 if (param == "ui-rc-file") {
145 load_rc_file (get_ui_rc_file(), true);
152 UIConfiguration::reset_gtk_theme ()
156 ss << "gtk_color_scheme = \"" << hex;
158 for (ColorAliases::iterator g = color_aliases.begin(); g != color_aliases.end(); ++g) {
160 if (g->first.find ("gtk_") == 0) {
161 ColorAliases::const_iterator a = color_aliases.find (g->first);
162 const string gtk_name = g->first.substr (4);
163 ss << gtk_name << ":#" << std::setw (6) << setfill ('0') << (color (g->second) >> 8) << ';';
167 ss << '"' << dec << endl;
169 /* reset GTK color scheme */
171 Gtk::Settings::get_default()->property_gtk_color_scheme() = ss.str();
174 UIConfiguration::RelativeHSV
175 UIConfiguration::color_as_relative_hsv (Color c)
179 double shortest_distance = DBL_MAX;
182 map<string,ColorVariable<Color>*>::iterator f;
183 std::map<std::string,HSV> palette;
185 for (f = configurable_colors.begin(); f != configurable_colors.end(); ++f) {
186 /* Do not include any specialized base colors in the palette
187 we use to do comparisons
190 if (f->first.find ("color") == 0) {
191 palette.insert (make_pair (f->first, HSV (f->second->get())));
195 for (map<string,HSV>::iterator f = palette.begin(); f != palette.end(); ++f) {
198 HSV fixed (f->second);
200 if (fixed.is_gray() || variable.is_gray()) {
201 /* at least one is achromatic; HSV::distance() will do
204 d = fixed.distance (variable);
206 /* chromatic: compare ONLY hue because our task is
207 to pick the HUE closest and then compute
208 a modifier. We want to keep the number of
209 hues low, and by computing perceptual distance
210 we end up finding colors that are to each
211 other without necessarily be close in hue.
213 d = fabs (variable.h - fixed.h);
216 if (d < shortest_distance) {
218 closest_name = f->first;
219 shortest_distance = d;
223 /* we now know the closest color of the fixed colors to
224 this variable color. Compute the HSV diff and
225 use it to redefine the variable color in terms of the
229 HSV delta = variable.delta (closest);
231 /* quantize hue delta so we don't end up with many subtle hues caused
232 * by original color choices
235 delta.h = hue_width * (round (delta.h/hue_width));
237 return RelativeHSV (closest_name, delta);
241 UIConfiguration::color_as_alias (Color c)
244 double shortest_distance = DBL_MAX;
247 for (RelativeColors::const_iterator a = relative_colors.begin(); a != relative_colors.end(); ++a) {
248 HSV hsv (a->second.get());
249 double d = hsv.distance (target);
250 if (d < shortest_distance) {
251 shortest_distance = d;
258 UIConfiguration::map_parameters (boost::function<void (std::string)>& functor)
260 #undef UI_CONFIG_VARIABLE
261 #define UI_CONFIG_VARIABLE(Type,var,Name,value) functor (Name);
262 #include "ui_config_vars.h"
263 #undef UI_CONFIG_VARIABLE
267 UIConfiguration::load_defaults ()
272 if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile) ) {
276 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
278 if (!tree.read (rcfile.c_str())) {
279 error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
283 if (set_state (*tree.root(), Stateful::loading_state_version)) {
284 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
290 ARDOUR_UI_UTILS::ColorsChanged ();
292 warning << string_compose (_("Could not find default UI configuration file %1"), default_ui_config_file_name) << endmsg;
299 UIConfiguration::load_state ()
305 if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile)) {
309 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
311 if (!tree.read (rcfile.c_str())) {
312 error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
316 if (set_state (*tree.root(), Stateful::loading_state_version)) {
317 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
324 if (find_file (ardour_config_search_path(), ui_config_file_name, rcfile)) {
328 info << string_compose (_("Loading user ui configuration file %1"), rcfile) << endmsg;
330 if (!tree.read (rcfile)) {
331 error << string_compose(_("cannot read ui configuration file \"%1\""), rcfile) << endmsg;
335 if (set_state (*tree.root(), Stateful::loading_state_version)) {
336 error << string_compose(_("user ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
344 error << _("could not find any ui configuration file, canvas will look broken.") << endmsg;
347 ARDOUR_UI_UTILS::ColorsChanged ();
353 UIConfiguration::save_state()
361 std::string rcfile(user_config_directory());
362 rcfile = Glib::build_filename (rcfile, ui_config_file_name);
364 if (rcfile.length()) {
365 tree.set_root (&get_state());
366 if (!tree.write (rcfile.c_str())){
367 error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
378 UIConfiguration::get_state ()
381 LocaleGuard lg (X_("POSIX"));
383 root = new XMLNode("Ardour");
385 root->add_child_nocopy (get_variables ("UI"));
386 root->add_child_nocopy (get_variables ("Canvas"));
388 if (derived_modified) {
392 if (aliases_modified) {
393 XMLNode* parent = new XMLNode (X_("ColorAliases"));
394 for (ColorAliases::const_iterator i = color_aliases.begin(); i != color_aliases.end(); ++i) {
395 XMLNode* node = new XMLNode (X_("ColorAlias"));
396 node->add_property (X_("name"), i->first);
397 node->add_property (X_("alias"), i->second);
398 parent->add_child_nocopy (*node);
400 root->add_child_nocopy (*parent);
404 root->add_child_copy (*_extra_xml);
411 UIConfiguration::get_variables (std::string which_node)
414 LocaleGuard lg (X_("POSIX"));
416 node = new XMLNode (which_node);
418 #undef UI_CONFIG_VARIABLE
419 #undef CANVAS_FONT_VARIABLE
420 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
421 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
422 #include "ui_config_vars.h"
423 #include "canvas_vars.h"
424 #undef UI_CONFIG_VARIABLE
425 #undef CANVAS_FONT_VARIABLE
431 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
433 if (root.name() != "Ardour") {
437 Stateful::save_extra_xml (root);
439 XMLNodeList nlist = root.children();
440 XMLNodeConstIterator niter;
443 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
447 if (node->name() == "Canvas" || node->name() == "UI") {
448 set_variables (*node);
453 XMLNode* relative = find_named_node (root, X_("RelativeColors"));
456 // load_relative_colors (*relative);
460 XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
463 load_color_aliases (*aliases);
470 UIConfiguration::load_color_aliases (XMLNode const & node)
472 XMLNodeList const nlist = node.children();
473 XMLNodeConstIterator niter;
474 XMLProperty const *name;
475 XMLProperty const *alias;
477 color_aliases.clear ();
479 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
480 if ((*niter)->name() != X_("ColorAlias")) {
483 name = (*niter)->property (X_("name"));
484 alias = (*niter)->property (X_("alias"));
487 color_aliases.insert (make_pair (name->value(), alias->value()));
495 UIConfiguration::load_relative_colors (XMLNode const & node)
497 XMLNodeList const nlist = node.children();
498 XMLNodeConstIterator niter;
499 XMLProperty const *name;
500 XMLProperty const *alias;
502 color_aliases.clear ();
504 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
505 if ((*niter)->name() != X_("RelativeColor")) {
508 name = (*niter)->property (X_("name"));
509 alias = (*niter)->property (X_("alias"));
512 color_aliases.insert (make_pair (name->value(), alias->value()));
519 UIConfiguration::set_variables (const XMLNode& node)
521 #undef UI_CONFIG_VARIABLE
522 #define UI_CONFIG_VARIABLE(Type,var,name,val) \
523 if (var.set_from_node (node)) { \
524 ParameterChanged (name); \
526 #define CANVAS_FONT_VARIABLE(var,name) \
527 if (var.set_from_node (node)) { \
528 ParameterChanged (name); \
530 #include "ui_config_vars.h"
531 #include "canvas_vars.h"
532 #undef UI_CONFIG_VARIABLE
533 #undef CANVAS_FONT_VARIABLE
535 /* Reset base colors */
537 #undef CANVAS_BASE_COLOR
538 #define CANVAS_BASE_COLOR(var,name,val) \
539 var.set_from_node (node);
540 #include "base_colors.h"
541 #undef CANVAS_BASE_COLOR
546 UIConfiguration::set_dirty ()
552 UIConfiguration::dirty () const
554 return _dirty || aliases_modified || derived_modified;
558 UIConfiguration::base_color_by_name (const std::string& name) const
560 map<std::string,ColorVariable<Color>* >::const_iterator i = configurable_colors.find (name);
562 if (i != configurable_colors.end()) {
563 return i->second->get();
566 cerr << string_compose (_("Base Color %1 not found"), name) << endl;
567 return RGBA_TO_UINT (g_random_int()%256,g_random_int()%256,g_random_int()%256,0xff);
571 UIConfiguration::color (const std::string& name) const
573 map<string,string>::const_iterator e = color_aliases.find (name);
575 if (e != color_aliases.end ()) {
576 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (e->second);
577 if (rc != relative_colors.end()) {
578 return rc->second.get();
581 /* not an alias, try directly */
582 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (name);
583 if (rc != relative_colors.end()) {
584 return rc->second.get();
588 cerr << string_compose (_("Color %1 not found"), name) << endl;
590 return rgba_to_color ((g_random_int()%256)/255.0,
591 (g_random_int()%256)/255.0,
592 (g_random_int()%256)/255.0,
597 UIConfiguration::RelativeHSV::get() const
599 HSV base (UIConfiguration::instance()->base_color_by_name (base_color));
601 /* this operation is a little wierd. because of the way we originally
602 * computed the alpha specification for the modifiers used here
603 * we need to reset base's alpha to zero before adding the modifier.
606 HSV self (base + modifier);
608 if (quantized_hue >= 0.0) {
609 self.h = quantized_hue;
616 UIConfiguration::quantized (Color c) const
619 hsv.h = hue_width * (round (hsv.h/hue_width));
624 UIConfiguration::reset_relative (const string& name, const RelativeHSV& rhsv)
626 RelativeColors::iterator i = relative_colors.find (name);
628 if (i == relative_colors.end()) {
633 derived_modified = true;
635 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
641 UIConfiguration::set_alias (string const & name, string const & alias)
643 ColorAliases::iterator i = color_aliases.find (name);
644 if (i == color_aliases.end()) {
649 aliases_modified = true;
651 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
657 UIConfiguration::load_rc_file (const string& filename, bool themechange)
659 std::string rc_file_path;
661 if (!find_file (ardour_config_search_path(), filename, rc_file_path)) {
662 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
663 filename, ardour_config_search_path().to_string(), PROGRAM_NAME)
668 info << "Loading ui configuration file " << rc_file_path << endmsg;
670 Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);
673 std::ostream& operator<< (std::ostream& o, const UIConfiguration::RelativeHSV& rhsv)
675 return o << rhsv.base_color << " + HSV(" << rhsv.modifier << ")";