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 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 palette.insert (make_pair (f->first, HSV (f->second->get())));
189 for (map<string,HSV>::iterator f = palette.begin(); f != palette.end(); ++f) {
192 HSV fixed (f->second);
194 if (fixed.is_gray() || variable.is_gray()) {
195 /* at least one is achromatic; HSV::distance() will do
198 d = fixed.distance (variable);
200 /* chromatic: compare ONLY hue because our task is
201 to pick the HUE closest and then compute
202 a modifier. We want to keep the number of
203 hues low, and by computing perceptual distance
204 we end up finding colors that are to each
205 other without necessarily be close in hue.
207 d = fabs (variable.h - fixed.h);
210 if (d < shortest_distance) {
212 closest_name = f->first;
213 shortest_distance = d;
217 /* we now know the closest color of the fixed colors to
218 this variable color. Compute the HSV diff and
219 use it to redefine the variable color in terms of the
223 HSV delta = variable.delta (closest);
225 /* quantize hue delta so we don't end up with many subtle hues caused
226 * by original color choices
229 delta.h = hue_width * (round (delta.h/hue_width));
231 return RelativeHSV (closest_name, delta);
235 UIConfiguration::map_parameters (boost::function<void (std::string)>& functor)
237 #undef UI_CONFIG_VARIABLE
238 #define UI_CONFIG_VARIABLE(Type,var,Name,value) functor (Name);
239 #include "ui_config_vars.h"
240 #undef UI_CONFIG_VARIABLE
244 UIConfiguration::load_defaults ()
249 if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile) ) {
253 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
255 if (!tree.read (rcfile.c_str())) {
256 error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
260 if (set_state (*tree.root(), Stateful::loading_state_version)) {
261 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
268 ARDOUR_UI_UTILS::ColorsChanged ();
274 UIConfiguration::load_state ()
280 if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile)) {
284 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
286 if (!tree.read (rcfile.c_str())) {
287 error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
291 if (set_state (*tree.root(), Stateful::loading_state_version)) {
292 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
299 if (find_file (ardour_config_search_path(), ui_config_file_name, rcfile)) {
303 info << string_compose (_("Loading user ui configuration file %1"), rcfile) << endmsg;
305 if (!tree.read (rcfile)) {
306 error << string_compose(_("cannot read ui configuration file \"%1\""), rcfile) << endmsg;
310 if (set_state (*tree.root(), Stateful::loading_state_version)) {
311 error << string_compose(_("user ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
319 error << _("could not find any ui configuration file, canvas will look broken.") << endmsg;
322 ARDOUR_UI_UTILS::ColorsChanged ();
328 UIConfiguration::save_state()
336 std::string rcfile(user_config_directory());
337 rcfile = Glib::build_filename (rcfile, ui_config_file_name);
339 if (rcfile.length()) {
340 tree.set_root (&get_state());
341 if (!tree.write (rcfile.c_str())){
342 error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
353 UIConfiguration::get_state ()
356 LocaleGuard lg (X_("POSIX"));
358 root = new XMLNode("Ardour");
360 root->add_child_nocopy (get_variables ("UI"));
361 root->add_child_nocopy (get_variables ("Canvas"));
363 if (derived_modified) {
367 if (aliases_modified) {
368 XMLNode* parent = new XMLNode (X_("ColorAliases"));
369 for (ColorAliases::const_iterator i = color_aliases.begin(); i != color_aliases.end(); ++i) {
370 XMLNode* node = new XMLNode (X_("ColorAlias"));
371 node->add_property (X_("name"), i->first);
372 node->add_property (X_("alias"), i->second);
373 parent->add_child_nocopy (*node);
375 root->add_child_nocopy (*parent);
379 root->add_child_copy (*_extra_xml);
386 UIConfiguration::get_variables (std::string which_node)
389 LocaleGuard lg (X_("POSIX"));
391 node = new XMLNode (which_node);
393 #undef UI_CONFIG_VARIABLE
394 #undef CANVAS_FONT_VARIABLE
395 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
396 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
397 #include "ui_config_vars.h"
398 #include "canvas_vars.h"
399 #undef UI_CONFIG_VARIABLE
400 #undef CANVAS_FONT_VARIABLE
406 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
408 if (root.name() != "Ardour") {
412 Stateful::save_extra_xml (root);
414 XMLNodeList nlist = root.children();
415 XMLNodeConstIterator niter;
418 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
422 if (node->name() == "Canvas" || node->name() == "UI") {
423 set_variables (*node);
428 XMLNode* relative = find_named_node (root, X_("RelativeColors"));
431 // load_relative_colors (*relative);
435 XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
438 load_color_aliases (*aliases);
445 UIConfiguration::load_color_aliases (XMLNode const & node)
447 XMLNodeList const nlist = node.children();
448 XMLNodeConstIterator niter;
449 XMLProperty const *name;
450 XMLProperty const *alias;
452 color_aliases.clear ();
454 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
455 if ((*niter)->name() != X_("ColorAlias")) {
458 name = (*niter)->property (X_("name"));
459 alias = (*niter)->property (X_("alias"));
462 color_aliases.insert (make_pair (name->value(), alias->value()));
470 UIConfiguration::load_relative_colors (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_("RelativeColor")) {
483 name = (*niter)->property (X_("name"));
484 alias = (*niter)->property (X_("alias"));
487 color_aliases.insert (make_pair (name->value(), alias->value()));
494 UIConfiguration::set_variables (const XMLNode& node)
496 #undef UI_CONFIG_VARIABLE
497 #define UI_CONFIG_VARIABLE(Type,var,name,val) \
498 if (var.set_from_node (node)) { \
499 ParameterChanged (name); \
501 #define CANVAS_FONT_VARIABLE(var,name) \
502 if (var.set_from_node (node)) { \
503 ParameterChanged (name); \
505 #include "ui_config_vars.h"
506 #include "canvas_vars.h"
507 #undef UI_CONFIG_VARIABLE
508 #undef CANVAS_FONT_VARIABLE
510 /* Reset base colors */
512 #undef CANVAS_BASE_COLOR
513 #define CANVAS_BASE_COLOR(var,name,val) \
514 var.set_from_node (node);
515 #include "base_colors.h"
516 #undef CANVAS_BASE_COLOR
521 UIConfiguration::set_dirty ()
527 UIConfiguration::dirty () const
529 return _dirty || aliases_modified || derived_modified;
533 UIConfiguration::base_color_by_name (const std::string& name) const
535 map<std::string,ColorVariable<Color>* >::const_iterator i = configurable_colors.find (name);
537 if (i != configurable_colors.end()) {
538 return i->second->get();
541 #if 0 // yet unsed experimental style postfix
542 /* Idea: use identical colors but different font/sizes
543 * for variants of the same 'widget'.
546 * set_name("mute button"); // in route_ui.cc
547 * set_name("mute button small"); // in mixer_strip.cc
549 * ardour3_widget_list.rc:
550 * widget "*mute button" style:highest "small_button"
551 * widget "*mute button small" style:highest "very_small_text"
553 * both use color-schema of defined in
554 * BUTTON_VARS(MuteButton, "mute button")
556 * (in this particular example the widgets should be packed
557 * vertically shinking the mixer strip ones are currently not)
559 const size_t name_len = name.size();
560 const size_t name_sep = name.find(':');
561 for (i = configurable_colors.begin(); i != configurable_colors.end(), name_sep != string::npos; ++i) {
562 const size_t cmp_len = i->first.size();
563 const size_t cmp_sep = i->first.find(':');
564 if (cmp_len >= name_len || cmp_sep == string::npos) continue;
565 if (name.substr(name_sep) != i->first.substr(cmp_sep)) continue;
566 if (name.substr(0, cmp_sep) != i->first.substr(0, cmp_sep)) continue;
567 return i->second->get();
571 cerr << string_compose (_("Base Color %1 not found"), name) << endl;
572 return RGBA_TO_UINT (g_random_int()%256,g_random_int()%256,g_random_int()%256,0xff);
576 UIConfiguration::color (const std::string& name) const
578 map<string,string>::const_iterator e = color_aliases.find (name);
580 if (e != color_aliases.end ()) {
581 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (e->second);
582 if (rc != relative_colors.end()) {
583 return rc->second.get();
586 /* not an alias, try directly */
587 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (name);
588 if (rc != relative_colors.end()) {
589 return rc->second.get();
593 cerr << string_compose (_("Color %1 not found"), name) << endl;
595 return rgba_to_color ((g_random_int()%256)/255.0,
596 (g_random_int()%256)/255.0,
597 (g_random_int()%256)/255.0,
602 UIConfiguration::RelativeHSV::get() const
604 HSV base (UIConfiguration::instance()->base_color_by_name (base_color));
606 /* this operation is a little wierd. because of the way we originally
607 * computed the alpha specification for the modifiers used here
608 * we need to reset base's alpha to zero before adding the modifier.
611 HSV self (base + modifier);
613 if (quantized_hue >= 0.0) {
614 self.h = quantized_hue;
621 UIConfiguration::quantized (Color c) const
624 hsv.h = hue_width * (round (hsv.h/hue_width));
629 UIConfiguration::reset_relative (const string& name, const RelativeHSV& rhsv)
631 RelativeColors::iterator i = relative_colors.find (name);
633 if (i == relative_colors.end()) {
638 derived_modified = true;
640 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
646 UIConfiguration::set_alias (string const & name, string const & alias)
648 ColorAliases::iterator i = color_aliases.find (name);
649 if (i == color_aliases.end()) {
654 aliases_modified = true;
656 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
662 UIConfiguration::load_rc_file (const string& filename, bool themechange)
664 std::string rc_file_path;
666 if (!find_file (ardour_config_search_path(), filename, rc_file_path)) {
667 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
668 filename, ardour_config_search_path().to_string(), PROGRAM_NAME)
673 info << "Loading ui configuration file " << rc_file_path << endmsg;
675 Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);