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()));
468 UIConfiguration::load_relative_colors (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_("RelativeColor")) {
481 name = (*niter)->property (X_("name"));
482 alias = (*niter)->property (X_("alias"));
485 color_aliases.insert (make_pair (name->value(), alias->value()));
492 UIConfiguration::set_variables (const XMLNode& node)
494 #undef UI_CONFIG_VARIABLE
495 #define UI_CONFIG_VARIABLE(Type,var,name,val) \
496 if (var.set_from_node (node)) { \
497 ParameterChanged (name); \
499 #define CANVAS_FONT_VARIABLE(var,name) \
500 if (var.set_from_node (node)) { \
501 ParameterChanged (name); \
503 #include "ui_config_vars.h"
504 #include "canvas_vars.h"
505 #undef UI_CONFIG_VARIABLE
506 #undef CANVAS_FONT_VARIABLE
508 /* Reset base colors */
510 #undef CANVAS_BASE_COLOR
511 #define CANVAS_BASE_COLOR(var,name,val) \
512 var.set_from_node (node);
513 #include "base_colors.h"
514 #undef CANVAS_BASE_COLOR
519 UIConfiguration::set_dirty ()
525 UIConfiguration::dirty () const
527 return _dirty || aliases_modified || derived_modified;
531 UIConfiguration::base_color_by_name (const std::string& name) const
533 map<std::string,ColorVariable<Color>* >::const_iterator i = configurable_colors.find (name);
535 if (i != configurable_colors.end()) {
536 return i->second->get();
539 #if 0 // yet unsed experimental style postfix
540 /* Idea: use identical colors but different font/sizes
541 * for variants of the same 'widget'.
544 * set_name("mute button"); // in route_ui.cc
545 * set_name("mute button small"); // in mixer_strip.cc
547 * ardour3_widget_list.rc:
548 * widget "*mute button" style:highest "small_button"
549 * widget "*mute button small" style:highest "very_small_text"
551 * both use color-schema of defined in
552 * BUTTON_VARS(MuteButton, "mute button")
554 * (in this particular example the widgets should be packed
555 * vertically shinking the mixer strip ones are currently not)
557 const size_t name_len = name.size();
558 const size_t name_sep = name.find(':');
559 for (i = configurable_colors.begin(); i != configurable_colors.end(), name_sep != string::npos; ++i) {
560 const size_t cmp_len = i->first.size();
561 const size_t cmp_sep = i->first.find(':');
562 if (cmp_len >= name_len || cmp_sep == string::npos) continue;
563 if (name.substr(name_sep) != i->first.substr(cmp_sep)) continue;
564 if (name.substr(0, cmp_sep) != i->first.substr(0, cmp_sep)) continue;
565 return i->second->get();
569 cerr << string_compose (_("Base Color %1 not found"), name) << endl;
570 return RGBA_TO_UINT (g_random_int()%256,g_random_int()%256,g_random_int()%256,0xff);
574 UIConfiguration::color (const std::string& name) const
576 map<string,string>::const_iterator e = color_aliases.find (name);
578 if (e != color_aliases.end ()) {
579 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (e->second);
580 if (rc != relative_colors.end()) {
581 return rc->second.get();
584 /* not an alias, try directly */
585 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (name);
586 if (rc != relative_colors.end()) {
587 return rc->second.get();
591 cerr << string_compose (_("Color %1 not found"), name) << endl;
593 return rgba_to_color ((g_random_int()%256)/255.0,
594 (g_random_int()%256)/255.0,
595 (g_random_int()%256)/255.0,
600 UIConfiguration::RelativeHSV::get() const
602 HSV base (UIConfiguration::instance()->base_color_by_name (base_color));
604 /* this operation is a little wierd. because of the way we originally
605 * computed the alpha specification for the modifiers used here
606 * we need to reset base's alpha to zero before adding the modifier.
609 HSV self (base + modifier);
611 if (quantized_hue >= 0.0) {
612 self.h = quantized_hue;
619 UIConfiguration::quantized (Color c) const
622 hsv.h = hue_width * (round (hsv.h/hue_width));
627 UIConfiguration::reset_relative (const string& name, const RelativeHSV& rhsv)
629 RelativeColors::iterator i = relative_colors.find (name);
631 if (i == relative_colors.end()) {
636 derived_modified = true;
638 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
644 UIConfiguration::set_alias (string const & name, string const & alias)
646 ColorAliases::iterator i = color_aliases.find (name);
647 if (i == color_aliases.end()) {
652 aliases_modified = true;
654 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
660 UIConfiguration::load_rc_file (const string& filename, bool themechange)
662 std::string rc_file_path;
664 if (!find_file (ardour_config_search_path(), filename, rc_file_path)) {
665 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
666 filename, ardour_config_search_path().to_string(), PROGRAM_NAME)
671 info << "Loading ui configuration file " << rc_file_path << endmsg;
673 Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);