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
68 base_modified (false),
69 aliases_modified (false),
70 derived_modified (false),
75 /* pack all base colors into the configurable color map so that
76 derived colors can use them.
79 #undef CANVAS_BASE_COLOR
80 #define CANVAS_BASE_COLOR(var,name,color) base_colors.insert (make_pair (name,color));
81 #include "base_colors.h"
82 #undef CANVAS_BASE_COLOR
85 #define CANVAS_COLOR(var,name,base,modifier) relative_colors.insert (make_pair (name, RelativeHSV (base,modifier)));
90 #define COLOR_ALIAS(var,name,alias) color_aliases.insert (make_pair (name,alias));
91 #include "color_aliases.h"
96 ARDOUR_UI_UTILS::ColorsChanged.connect (boost::bind (&UIConfiguration::colors_changed, this));
98 ParameterChanged.connect (sigc::mem_fun (*this, &UIConfiguration::parameter_changed));
100 /* force GTK theme setting, so that loading an RC file will work */
105 UIConfiguration::~UIConfiguration ()
110 UIConfiguration::colors_changed ()
114 /* In theory, one of these ought to work:
116 gtk_rc_reparse_all_for_settings (gtk_settings_get_default(), true);
117 gtk_rc_reset_styles (gtk_settings_get_default());
119 but in practice, neither of them do. So just reload the current
120 GTK RC file, which causes a reset of all styles and a redraw
123 parameter_changed ("ui-rc-file");
127 UIConfiguration::parameter_changed (string param)
131 if (param == "ui-rc-file") {
133 } else if (param == "color-file") {
135 } else if (param == "base-color") { /* one of many */
136 base_modified = true;
137 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
144 UIConfiguration::reset_gtk_theme ()
148 ss << "gtk_color_scheme = \"" << hex;
150 for (ColorAliases::iterator g = color_aliases.begin(); g != color_aliases.end(); ++g) {
152 if (g->first.find ("gtk_") == 0) {
153 ColorAliases::const_iterator a = color_aliases.find (g->first);
154 const string gtk_name = g->first.substr (4);
155 ss << gtk_name << ":#" << std::setw (6) << setfill ('0') << (color (g->second) >> 8) << ';';
159 ss << '"' << dec << endl;
161 /* reset GTK color scheme */
163 Gtk::Settings::get_default()->property_gtk_color_scheme() = ss.str();
166 UIConfiguration::RelativeHSV
167 UIConfiguration::color_as_relative_hsv (Color c)
171 double shortest_distance = DBL_MAX;
174 BaseColors::iterator f;
175 std::map<std::string,HSV> palette;
177 for (f = base_colors.begin(); f != base_colors.end(); ++f) {
178 /* Do not include any specialized base colors in the palette
179 we use to do comparisons (e.g. meter colors)
182 if (f->first.find ("color") == 0) {
183 palette.insert (make_pair (f->first, HSV (f->second)));
187 for (map<string,HSV>::iterator f = palette.begin(); f != palette.end(); ++f) {
190 HSV fixed (f->second);
192 if (fixed.is_gray() || variable.is_gray()) {
193 /* at least one is achromatic; HSV::distance() will do
196 d = fixed.distance (variable);
198 /* chromatic: compare ONLY hue because our task is
199 to pick the HUE closest and then compute
200 a modifier. We want to keep the number of
201 hues low, and by computing perceptual distance
202 we end up finding colors that are to each
203 other without necessarily be close in hue.
205 d = fabs (variable.h - fixed.h);
208 if (d < shortest_distance) {
210 closest_name = f->first;
211 shortest_distance = d;
215 /* we now know the closest color of the fixed colors to
216 this variable color. Compute the HSV diff and
217 use it to redefine the variable color in terms of the
221 HSV delta = variable.delta (closest);
223 /* quantize hue delta so we don't end up with many subtle hues caused
224 * by original color choices
227 delta.h = hue_width * (round (delta.h/hue_width));
229 return RelativeHSV (closest_name, delta);
233 UIConfiguration::color_as_alias (Color c)
236 double shortest_distance = DBL_MAX;
239 for (RelativeColors::const_iterator a = relative_colors.begin(); a != relative_colors.end(); ++a) {
240 HSV hsv (a->second.get());
241 double d = hsv.distance (target);
242 if (d < shortest_distance) {
243 shortest_distance = d;
250 UIConfiguration::map_parameters (boost::function<void (std::string)>& functor)
252 #undef UI_CONFIG_VARIABLE
253 #define UI_CONFIG_VARIABLE(Type,var,Name,value) functor (Name);
254 #include "ui_config_vars.h"
255 #undef UI_CONFIG_VARIABLE
259 UIConfiguration::load_defaults ()
264 if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile) ) {
267 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
269 if (!tree.read (rcfile.c_str())) {
270 error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
272 if (set_state (*tree.root(), Stateful::loading_state_version)) {
273 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
281 warning << string_compose (_("Could not find default UI configuration file %1"), default_ui_config_file_name) << endmsg;
285 /* reload color theme */
286 load_color_theme (false);
287 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
294 UIConfiguration::load_color_theme (bool allow_own)
302 basename += color_file.get();
303 basename += ".colors";
305 if (find_file (ardour_config_search_path(), basename, cfile)) {
311 basename = color_file.get();
312 basename += ".colors";
314 if (find_file (ardour_config_search_path(), basename, cfile)) {
323 info << string_compose (_("Loading color file %1"), cfile) << endmsg;
325 if (!tree.read (cfile.c_str())) {
326 error << string_compose(_("cannot read color file \"%1\""), cfile) << endmsg;
330 if (set_state (*tree.root(), Stateful::loading_state_version)) {
331 error << string_compose(_("color file \"%1\" not loaded successfully."), cfile) << endmsg;
335 ARDOUR_UI_UTILS::ColorsChanged ();
337 warning << string_compose (_("Color file %1 not found"), basename) << endmsg;
344 UIConfiguration::store_color_theme ()
347 LocaleGuard lg (X_("POSIX"));
349 root = new XMLNode("Ardour");
351 XMLNode* parent = new XMLNode (X_("RelativeColors"));
352 for (RelativeColors::const_iterator i = relative_colors.begin(); i != relative_colors.end(); ++i) {
353 XMLNode* node = new XMLNode (X_("RelativeColor"));
354 node->add_property (X_("name"), i->first);
355 node->add_property (X_("base"), i->second.base_color);
356 node->add_property (X_("modifier"), i->second.modifier.to_string());
357 parent->add_child_nocopy (*node);
359 root->add_child_nocopy (*parent);
362 parent = new XMLNode (X_("ColorAliases"));
363 for (ColorAliases::const_iterator i = color_aliases.begin(); i != color_aliases.end(); ++i) {
364 XMLNode* node = new XMLNode (X_("ColorAlias"));
365 node->add_property (X_("name"), i->first);
366 node->add_property (X_("alias"), i->second);
367 parent->add_child_nocopy (*node);
369 root->add_child_nocopy (*parent);
372 std::string colorfile = Glib::build_filename (user_config_directory(), (string ("my-") + color_file.get() + ".colors"));
374 tree.set_root (root);
376 if (!tree.write (colorfile.c_str())){
377 error << string_compose (_("Color file %1 not saved"), colorfile) << endmsg;
385 UIConfiguration::load_state ()
391 if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile)) {
395 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
397 if (!tree.read (rcfile.c_str())) {
398 error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
402 if (set_state (*tree.root(), Stateful::loading_state_version)) {
403 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
408 if (find_file (ardour_config_search_path(), ui_config_file_name, rcfile)) {
412 info << string_compose (_("Loading user ui configuration file %1"), rcfile) << endmsg;
414 if (!tree.read (rcfile)) {
415 error << string_compose(_("cannot read ui configuration file \"%1\""), rcfile) << endmsg;
419 if (set_state (*tree.root(), Stateful::loading_state_version)) {
420 error << string_compose(_("user ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
428 error << _("could not find any ui configuration file, canvas will look broken.") << endmsg;
435 UIConfiguration::save_state()
439 std::string rcfile = Glib::build_filename (user_config_directory(), ui_config_file_name);
443 tree.set_root (&get_state());
445 if (!tree.write (rcfile.c_str())){
446 error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
453 if (base_modified || aliases_modified || derived_modified) {
455 if (store_color_theme ()) {
456 error << string_compose (_("Color file %1 not saved"), color_file.get()) << endmsg;
460 base_modified = false;
461 aliases_modified = false;
462 derived_modified = false;
470 UIConfiguration::get_state ()
473 LocaleGuard lg (X_("POSIX"));
475 root = new XMLNode("Ardour");
477 root->add_child_nocopy (get_variables ("UI"));
478 root->add_child_nocopy (get_variables ("Canvas"));
481 root->add_child_copy (*_extra_xml);
488 UIConfiguration::get_variables (std::string which_node)
491 LocaleGuard lg (X_("POSIX"));
493 node = new XMLNode (which_node);
495 #undef UI_CONFIG_VARIABLE
496 #undef CANVAS_FONT_VARIABLE
497 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
498 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
499 #include "ui_config_vars.h"
500 #include "canvas_vars.h"
501 #undef UI_CONFIG_VARIABLE
502 #undef CANVAS_FONT_VARIABLE
508 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
510 /* this can load a generic UI configuration file or a colors file */
512 if (root.name() != "Ardour") {
516 Stateful::save_extra_xml (root);
518 XMLNodeList nlist = root.children();
519 XMLNodeConstIterator niter;
522 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
526 if (node->name() == "Canvas" || node->name() == "UI") {
527 set_variables (*node);
532 XMLNode* base = find_named_node (root, X_("BaseColors"));
535 load_base_colors (*base);
539 XMLNode* relative = find_named_node (root, X_("RelativeColors"));
542 load_relative_colors (*relative);
546 XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
549 load_color_aliases (*aliases);
556 UIConfiguration::load_base_colors (XMLNode const &)
562 UIConfiguration::load_color_aliases (XMLNode const & node)
564 XMLNodeList const nlist = node.children();
565 XMLNodeConstIterator niter;
566 XMLProperty const *name;
567 XMLProperty const *alias;
569 color_aliases.clear ();
571 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
572 if ((*niter)->name() != X_("ColorAlias")) {
575 name = (*niter)->property (X_("name"));
576 alias = (*niter)->property (X_("alias"));
579 color_aliases.insert (make_pair (name->value(), alias->value()));
585 UIConfiguration::load_relative_colors (XMLNode const & node)
587 XMLNodeList const nlist = node.children();
588 XMLNodeConstIterator niter;
589 XMLProperty const *name;
590 XMLProperty const *base;
591 XMLProperty const *modifier;
593 relative_colors.clear ();
595 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
596 if ((*niter)->name() != X_("RelativeColor")) {
599 name = (*niter)->property (X_("name"));
600 base = (*niter)->property (X_("base"));
601 modifier = (*niter)->property (X_("modifier"));
603 if (name && base && modifier) {
604 RelativeHSV rhsv (base->value(), HSV (modifier->value()));
605 relative_colors.insert (make_pair (name->value(), rhsv));
612 UIConfiguration::set_variables (const XMLNode& node)
614 #undef UI_CONFIG_VARIABLE
615 #define UI_CONFIG_VARIABLE(Type,var,name,val) if (var.set_from_node (node)) { ParameterChanged (name); }
616 #define CANVAS_FONT_VARIABLE(var,name) if (var.set_from_node (node)) { ParameterChanged (name); }
617 #include "ui_config_vars.h"
618 #include "canvas_vars.h"
619 #undef UI_CONFIG_VARIABLE
620 #undef CANVAS_FONT_VARIABLE
624 UIConfiguration::base_color_by_name (const std::string& name) const
626 BaseColors::const_iterator i = base_colors.find (name);
628 if (i != base_colors.end()) {
632 cerr << string_compose (_("Base Color %1 not found"), name) << endl;
633 return RGBA_TO_UINT (g_random_int()%256,g_random_int()%256,g_random_int()%256,0xff);
637 UIConfiguration::color (const std::string& name, bool* failed) const
639 map<string,string>::const_iterator e = color_aliases.find (name);
645 if (e != color_aliases.end ()) {
646 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (e->second);
647 if (rc != relative_colors.end()) {
648 return rc->second.get();
651 /* not an alias, try directly */
652 map<string,RelativeHSV>::const_iterator rc = relative_colors.find (name);
653 if (rc != relative_colors.end()) {
654 return rc->second.get();
658 cerr << string_compose (_("Color %1 not found"), name) << endl;
664 return rgba_to_color ((g_random_int()%256)/255.0,
665 (g_random_int()%256)/255.0,
666 (g_random_int()%256)/255.0,
671 UIConfiguration::RelativeHSV::get() const
673 HSV base (UIConfiguration::instance()->base_color_by_name (base_color));
675 /* this operation is a little wierd. because of the way we originally
676 * computed the alpha specification for the modifiers used here
677 * we need to reset base's alpha to zero before adding the modifier.
680 HSV self (base + modifier);
682 if (quantized_hue >= 0.0) {
683 self.h = quantized_hue;
690 UIConfiguration::quantized (Color c) const
693 hsv.h = hue_width * (round (hsv.h/hue_width));
698 UIConfiguration::set_base (string const& name, ArdourCanvas::Color color)
700 BaseColors::iterator i = base_colors.find (name);
701 if (i == base_colors.end()) {
705 base_modified = true;
707 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
711 UIConfiguration::set_relative (const string& name, const RelativeHSV& rhsv)
713 RelativeColors::iterator i = relative_colors.find (name);
715 if (i == relative_colors.end()) {
720 derived_modified = true;
722 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
726 UIConfiguration::set_alias (string const & name, string const & alias)
728 ColorAliases::iterator i = color_aliases.find (name);
729 if (i == color_aliases.end()) {
734 aliases_modified = true;
736 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
740 UIConfiguration::load_rc_file (bool themechange, bool allow_own)
742 string basename = ui_rc_file.get();
743 std::string rc_file_path;
745 if (!find_file (ardour_config_search_path(), basename, rc_file_path)) {
746 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
747 basename, ardour_config_search_path().to_string(), PROGRAM_NAME)
752 info << "Loading ui configuration file " << rc_file_path << endmsg;
754 Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);
757 std::ostream& operator<< (std::ostream& o, const UIConfiguration::RelativeHSV& rhsv)
759 return o << rhsv.base_color << " + HSV(" << rhsv.modifier << ")";