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;
291 ARDOUR_UI_UTILS::ColorsChanged ();
297 UIConfiguration::load_state ()
303 if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile)) {
307 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
309 if (!tree.read (rcfile.c_str())) {
310 error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
314 if (set_state (*tree.root(), Stateful::loading_state_version)) {
315 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
322 if (find_file (ardour_config_search_path(), ui_config_file_name, rcfile)) {
326 info << string_compose (_("Loading user ui configuration file %1"), rcfile) << endmsg;
328 if (!tree.read (rcfile)) {
329 error << string_compose(_("cannot read ui configuration file \"%1\""), rcfile) << endmsg;
333 if (set_state (*tree.root(), Stateful::loading_state_version)) {
334 error << string_compose(_("user ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
342 error << _("could not find any ui configuration file, canvas will look broken.") << endmsg;
345 ARDOUR_UI_UTILS::ColorsChanged ();
351 UIConfiguration::save_state()
359 std::string rcfile(user_config_directory());
360 rcfile = Glib::build_filename (rcfile, ui_config_file_name);
362 if (rcfile.length()) {
363 tree.set_root (&get_state());
364 if (!tree.write (rcfile.c_str())){
365 error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
376 UIConfiguration::get_state ()
379 LocaleGuard lg (X_("POSIX"));
381 root = new XMLNode("Ardour");
383 root->add_child_nocopy (get_variables ("UI"));
384 root->add_child_nocopy (get_variables ("Canvas"));
386 if (derived_modified) {
390 if (aliases_modified) {
391 XMLNode* parent = new XMLNode (X_("ColorAliases"));
392 for (ColorAliases::const_iterator i = color_aliases.begin(); i != color_aliases.end(); ++i) {
393 XMLNode* node = new XMLNode (X_("ColorAlias"));
394 node->add_property (X_("name"), i->first);
395 node->add_property (X_("alias"), i->second);
396 parent->add_child_nocopy (*node);
398 root->add_child_nocopy (*parent);
402 root->add_child_copy (*_extra_xml);
409 UIConfiguration::get_variables (std::string which_node)
412 LocaleGuard lg (X_("POSIX"));
414 node = new XMLNode (which_node);
416 #undef UI_CONFIG_VARIABLE
417 #undef CANVAS_FONT_VARIABLE
418 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
419 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
420 #include "ui_config_vars.h"
421 #include "canvas_vars.h"
422 #undef UI_CONFIG_VARIABLE
423 #undef CANVAS_FONT_VARIABLE
429 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
431 if (root.name() != "Ardour") {
435 Stateful::save_extra_xml (root);
437 XMLNodeList nlist = root.children();
438 XMLNodeConstIterator niter;
441 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
445 if (node->name() == "Canvas" || node->name() == "UI") {
446 set_variables (*node);
451 XMLNode* relative = find_named_node (root, X_("RelativeColors"));
454 // load_relative_colors (*relative);
458 XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
461 load_color_aliases (*aliases);
468 UIConfiguration::load_color_aliases (XMLNode const & node)
470 XMLNodeList const nlist = node.children();
471 XMLNodeConstIterator niter;
472 XMLProperty const *name;
473 XMLProperty const *alias;
475 color_aliases.clear ();
477 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
478 if ((*niter)->name() != X_("ColorAlias")) {
481 name = (*niter)->property (X_("name"));
482 alias = (*niter)->property (X_("alias"));
485 color_aliases.insert (make_pair (name->value(), alias->value()));
493 UIConfiguration::load_relative_colors (XMLNode const & node)
495 XMLNodeList const nlist = node.children();
496 XMLNodeConstIterator niter;
497 XMLProperty const *name;
498 XMLProperty const *alias;
500 color_aliases.clear ();
502 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
503 if ((*niter)->name() != X_("RelativeColor")) {
506 name = (*niter)->property (X_("name"));
507 alias = (*niter)->property (X_("alias"));
510 color_aliases.insert (make_pair (name->value(), alias->value()));
517 UIConfiguration::set_variables (const XMLNode& node)
519 #undef UI_CONFIG_VARIABLE
520 #define UI_CONFIG_VARIABLE(Type,var,name,val) \
521 if (var.set_from_node (node)) { \
522 ParameterChanged (name); \
524 #define CANVAS_FONT_VARIABLE(var,name) \
525 if (var.set_from_node (node)) { \
526 ParameterChanged (name); \
528 #include "ui_config_vars.h"
529 #include "canvas_vars.h"
530 #undef UI_CONFIG_VARIABLE
531 #undef CANVAS_FONT_VARIABLE
533 /* Reset base colors */
535 #undef CANVAS_BASE_COLOR
536 #define CANVAS_BASE_COLOR(var,name,val) \
537 var.set_from_node (node);
538 #include "base_colors.h"
539 #undef CANVAS_BASE_COLOR
544 UIConfiguration::set_dirty ()
550 UIConfiguration::dirty () const
552 return _dirty || aliases_modified || derived_modified;
556 UIConfiguration::base_color_by_name (const std::string& name) const
558 map<std::string,ColorVariable<Color>* >::const_iterator i = configurable_colors.find (name);
560 if (i != configurable_colors.end()) {
561 return i->second->get();
564 cerr << string_compose (_("Base Color %1 not found"), name) << endl;
565 return RGBA_TO_UINT (g_random_int()%256,g_random_int()%256,g_random_int()%256,0xff);
569 UIConfiguration::color (const std::string& name) const
571 map<string,string>::const_iterator e = color_aliases.find (name);
573 if (e != color_aliases.end ()) {
574 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (e->second);
575 if (rc != relative_colors.end()) {
576 return rc->second.get();
579 /* not an alias, try directly */
580 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (name);
581 if (rc != relative_colors.end()) {
582 return rc->second.get();
586 cerr << string_compose (_("Color %1 not found"), name) << endl;
588 return rgba_to_color ((g_random_int()%256)/255.0,
589 (g_random_int()%256)/255.0,
590 (g_random_int()%256)/255.0,
595 UIConfiguration::RelativeHSV::get() const
597 HSV base (UIConfiguration::instance()->base_color_by_name (base_color));
599 /* this operation is a little wierd. because of the way we originally
600 * computed the alpha specification for the modifiers used here
601 * we need to reset base's alpha to zero before adding the modifier.
604 HSV self (base + modifier);
606 if (quantized_hue >= 0.0) {
607 self.h = quantized_hue;
614 UIConfiguration::quantized (Color c) const
617 hsv.h = hue_width * (round (hsv.h/hue_width));
622 UIConfiguration::reset_relative (const string& name, const RelativeHSV& rhsv)
624 RelativeColors::iterator i = relative_colors.find (name);
626 if (i == relative_colors.end()) {
631 derived_modified = true;
633 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
639 UIConfiguration::set_alias (string const & name, string const & alias)
641 ColorAliases::iterator i = color_aliases.find (name);
642 if (i == color_aliases.end()) {
647 aliases_modified = true;
649 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
655 UIConfiguration::load_rc_file (const string& filename, bool themechange)
657 std::string rc_file_path;
659 if (!find_file (ardour_config_search_path(), filename, rc_file_path)) {
660 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
661 filename, ardour_config_search_path().to_string(), PROGRAM_NAME)
666 info << "Loading ui configuration file " << rc_file_path << endmsg;
668 Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);
671 std::ostream& operator<< (std::ostream& o, const UIConfiguration::RelativeHSV& rhsv)
673 return o << rhsv.base_color << " + HSV(" << rhsv.modifier << ")";