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()
332 PBD::stacktrace (cerr, 20);
338 std::string rcfile(user_config_directory());
339 rcfile = Glib::build_filename (rcfile, ui_config_file_name);
341 // this test seems bogus?
342 if (rcfile.length()) {
343 tree.set_root (&get_state());
344 if (!tree.write (rcfile.c_str())){
345 error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
356 UIConfiguration::get_state ()
359 LocaleGuard lg (X_("POSIX"));
361 root = new XMLNode("Ardour");
363 root->add_child_nocopy (get_variables ("UI"));
364 root->add_child_nocopy (get_variables ("Canvas"));
366 if (derived_modified) {
370 if (aliases_modified) {
371 XMLNode* parent = new XMLNode (X_("ColorAliases"));
372 for (ColorAliases::const_iterator i = color_aliases.begin(); i != color_aliases.end(); ++i) {
373 XMLNode* node = new XMLNode (X_("ColorAlias"));
374 node->add_property (X_("name"), i->first);
375 node->add_property (X_("alias"), i->second);
376 parent->add_child_nocopy (*node);
378 root->add_child_nocopy (*parent);
382 root->add_child_copy (*_extra_xml);
389 UIConfiguration::get_variables (std::string which_node)
392 LocaleGuard lg (X_("POSIX"));
394 node = new XMLNode (which_node);
396 #undef UI_CONFIG_VARIABLE
397 #undef CANVAS_FONT_VARIABLE
398 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
399 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
400 #include "ui_config_vars.h"
401 #include "canvas_vars.h"
402 #undef UI_CONFIG_VARIABLE
403 #undef CANVAS_FONT_VARIABLE
409 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
411 if (root.name() != "Ardour") {
415 Stateful::save_extra_xml (root);
417 XMLNodeList nlist = root.children();
418 XMLNodeConstIterator niter;
421 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
425 if (node->name() == "Canvas" || node->name() == "UI") {
426 set_variables (*node);
431 XMLNode* relative = find_named_node (root, X_("RelativeColors"));
434 // load_relative_colors (*relative);
438 XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
441 load_color_aliases (*aliases);
448 UIConfiguration::load_color_aliases (XMLNode const & node)
450 XMLNodeList const nlist = node.children();
451 XMLNodeConstIterator niter;
452 XMLProperty const *name;
453 XMLProperty const *alias;
455 color_aliases.clear ();
457 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
458 if ((*niter)->name() != X_("ColorAlias")) {
461 name = (*niter)->property (X_("name"));
462 alias = (*niter)->property (X_("alias"));
465 color_aliases.insert (make_pair (name->value(), alias->value()));
473 UIConfiguration::load_relative_colors (XMLNode const & node)
475 XMLNodeList const nlist = node.children();
476 XMLNodeConstIterator niter;
477 XMLProperty const *name;
478 XMLProperty const *alias;
480 color_aliases.clear ();
482 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
483 if ((*niter)->name() != X_("RelativeColor")) {
486 name = (*niter)->property (X_("name"));
487 alias = (*niter)->property (X_("alias"));
490 color_aliases.insert (make_pair (name->value(), alias->value()));
497 UIConfiguration::set_variables (const XMLNode& node)
499 #undef UI_CONFIG_VARIABLE
500 #define UI_CONFIG_VARIABLE(Type,var,name,val) \
501 if (var.set_from_node (node)) { \
502 ParameterChanged (name); \
504 #define CANVAS_FONT_VARIABLE(var,name) \
505 if (var.set_from_node (node)) { \
506 ParameterChanged (name); \
508 #include "ui_config_vars.h"
509 #include "canvas_vars.h"
510 #undef UI_CONFIG_VARIABLE
511 #undef CANVAS_FONT_VARIABLE
513 /* Reset base colors */
515 #undef CANVAS_BASE_COLOR
516 #define CANVAS_BASE_COLOR(var,name,val) \
517 var.set_from_node (node);
518 #include "base_colors.h"
519 #undef CANVAS_BASE_COLOR
524 UIConfiguration::set_dirty ()
530 UIConfiguration::dirty () const
532 return _dirty || aliases_modified || derived_modified;
536 UIConfiguration::base_color_by_name (const std::string& name) const
538 map<std::string,ColorVariable<Color>* >::const_iterator i = configurable_colors.find (name);
540 if (i != configurable_colors.end()) {
541 return i->second->get();
544 #if 0 // yet unsed experimental style postfix
545 /* Idea: use identical colors but different font/sizes
546 * for variants of the same 'widget'.
549 * set_name("mute button"); // in route_ui.cc
550 * set_name("mute button small"); // in mixer_strip.cc
552 * ardour3_widget_list.rc:
553 * widget "*mute button" style:highest "small_button"
554 * widget "*mute button small" style:highest "very_small_text"
556 * both use color-schema of defined in
557 * BUTTON_VARS(MuteButton, "mute button")
559 * (in this particular example the widgets should be packed
560 * vertically shinking the mixer strip ones are currently not)
562 const size_t name_len = name.size();
563 const size_t name_sep = name.find(':');
564 for (i = configurable_colors.begin(); i != configurable_colors.end(), name_sep != string::npos; ++i) {
565 const size_t cmp_len = i->first.size();
566 const size_t cmp_sep = i->first.find(':');
567 if (cmp_len >= name_len || cmp_sep == string::npos) continue;
568 if (name.substr(name_sep) != i->first.substr(cmp_sep)) continue;
569 if (name.substr(0, cmp_sep) != i->first.substr(0, cmp_sep)) continue;
570 return i->second->get();
574 cerr << string_compose (_("Base Color %1 not found"), name) << endl;
575 return RGBA_TO_UINT (g_random_int()%256,g_random_int()%256,g_random_int()%256,0xff);
579 UIConfiguration::color (const std::string& name) const
581 map<string,string>::const_iterator e = color_aliases.find (name);
583 if (e != color_aliases.end ()) {
584 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (e->second);
585 if (rc != relative_colors.end()) {
586 return rc->second.get();
589 /* not an alias, try directly */
590 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (name);
591 if (rc != relative_colors.end()) {
592 return rc->second.get();
596 cerr << string_compose (_("Color %1 not found"), name) << endl;
598 return rgba_to_color ((g_random_int()%256)/255.0,
599 (g_random_int()%256)/255.0,
600 (g_random_int()%256)/255.0,
605 UIConfiguration::RelativeHSV::get() const
607 HSV base (UIConfiguration::instance()->base_color_by_name (base_color));
609 /* this operation is a little wierd. because of the way we originally
610 * computed the alpha specification for the modifiers used here
611 * we need to reset base's alpha to zero before adding the modifier.
614 HSV self (base + modifier);
616 if (quantized_hue >= 0.0) {
617 self.h = quantized_hue;
624 UIConfiguration::quantized (Color c) const
627 hsv.h = hue_width * (round (hsv.h/hue_width));
632 UIConfiguration::reset_relative (const string& name, const RelativeHSV& rhsv)
634 RelativeColors::iterator i = relative_colors.find (name);
636 if (i == relative_colors.end()) {
641 derived_modified = true;
643 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
649 UIConfiguration::set_alias (string const & name, string const & alias)
651 ColorAliases::iterator i = color_aliases.find (name);
652 if (i == color_aliases.end()) {
657 aliases_modified = true;
659 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
665 UIConfiguration::load_rc_file (const string& filename, bool themechange)
667 std::string rc_file_path;
669 if (!find_file (ardour_config_search_path(), filename, rc_file_path)) {
670 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
671 filename, ardour_config_search_path().to_string(), PROGRAM_NAME)
676 info << "Loading ui configuration file " << rc_file_path << endmsg;
678 Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);