1 /*
2  * Copyright (C) 2008-2014 David Robillard <d@drobilla.net>
3  * Copyright (C) 2008-2016 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2009-2010 Carl Hetherington <carl@carlh.net>
5  * Copyright (C) 2012-2016 Tim Mayberry <mojofunk@gmail.com>
6  * Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include <iostream>
24 #include <sstream>
25 #include <unistd.h>
26 #include <cstdlib>
27 #include <cstdio> /* for snprintf, grrr */
28 
29 #include <cairo/cairo.h>
30 
31 #include <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
32 #include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
33 
34 #include <glibmm/miscutils.h>
35 
36 #include <gtkmm/settings.h>
37 
38 #include "pbd/convert.h"
39 #include "pbd/error.h"
40 #include "pbd/failed_constructor.h"
41 #include "pbd/file_utils.h"
42 #include "pbd/gstdio_compat.h"
43 #include "pbd/unwind.h"
44 #include "pbd/xml++.h"
45 
46 #include "ardour/filesystem_paths.h"
47 #include "ardour/search_paths.h"
48 #include "ardour/revision.h"
49 #include "ardour/utils.h"
50 #include "ardour/types_convert.h"
51 
52 #include "gtkmm2ext/rgb_macros.h"
53 #include "gtkmm2ext/gtk_ui.h"
54 
55 #include "ui_config.h"
56 
57 #include "pbd/i18n.h"
58 
59 using namespace std;
60 using namespace PBD;
61 using namespace ARDOUR;
62 using namespace Gtkmm2ext;
63 
64 static const char* ui_config_file_name = "ui_config";
65 static const char* default_ui_config_file_name = "default_ui_config";
66 
67 static const double hue_width = 18.0;
68 std::string UIConfiguration::color_file_suffix = X_(".colors");
69 
70 UIConfiguration&
instance()71 UIConfiguration::instance ()
72 {
73 	static UIConfiguration s_instance;
74 	_instance = &s_instance;
75 	return s_instance;
76 }
77 
UIConfiguration()78 UIConfiguration::UIConfiguration ()
79 	:
80 #undef  UI_CONFIG_VARIABLE
81 #define UI_CONFIG_VARIABLE(Type,var,name,val) var (name,val),
82 #define CANVAS_FONT_VARIABLE(var,name) var (name),
83 #include "ui_config_vars.h"
84 #include "canvas_vars.h"
85 #undef  UI_CONFIG_VARIABLE
86 #undef  CANVAS_FONT_VARIABLE
87 
88 	_dirty (false),
89 	aliases_modified (false),
90 	colors_modified (false),
91 	modifiers_modified (false),
92 	block_save (0)
93 {
94 	load_state();
95 
96 	ColorsChanged.connect (boost::bind (&UIConfiguration::colors_changed, this));
97 
98 	ParameterChanged.connect (sigc::mem_fun (*this, &UIConfiguration::parameter_changed));
99 }
100 
~UIConfiguration()101 UIConfiguration::~UIConfiguration ()
102 {
103 }
104 
105 void
colors_changed()106 UIConfiguration::colors_changed ()
107 {
108 	reset_gtk_theme ();
109 
110 	/* In theory, one of these ought to work:
111 
112 	   gtk_rc_reparse_all_for_settings (gtk_settings_get_default(), true);
113 	   gtk_rc_reset_styles (gtk_settings_get_default());
114 
115 	   but in practice, neither of them do. So just reload the current
116 	   GTK RC file, which causes a reset of all styles and a redraw
117 	*/
118 
119 	parameter_changed ("ui-rc-file");
120 }
121 
122 void
parameter_changed(string param)123 UIConfiguration::parameter_changed (string param)
124 {
125 	_dirty = true;
126 
127 	if (param == "ui-rc-file") {
128 		load_rc_file (true);
129 	} else if (param == "color-file") {
130 		load_color_theme (true);
131 	}
132 
133 	save_state ();
134 }
135 
136 void
reset_gtk_theme()137 UIConfiguration::reset_gtk_theme ()
138 {
139 	std::string color_scheme_string("gtk_color_scheme = \"");
140 
141 	for (ColorAliases::iterator g = color_aliases.begin(); g != color_aliases.end(); ++g) {
142 
143 		if (g->first.find ("gtk_") == 0) {
144 			const string gtk_name = g->first.substr (4);
145 			Gtkmm2ext::Color a_color = color (g->second);
146 
147 			color_scheme_string += gtk_name + ":#" + color_to_hex_string_no_alpha (a_color) + ';';
148 		}
149 	}
150 
151 	color_scheme_string += '"';
152 
153 	/* reset GTK color scheme */
154 
155 	Gtk::Settings::get_default()->property_gtk_color_scheme() = color_scheme_string;
156 }
157 
158 void
reset_dpi()159 UIConfiguration::reset_dpi ()
160 {
161 	long val = get_font_scale();
162 
163 	/* FT2 rendering - used by GnomeCanvas, sigh */
164 
165 #ifndef PLATFORM_WINDOWS
166 	pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_new(), val/1024, val/1024); // XXX pango_ft2_font_map_new leaks
167 #endif
168 
169 	/* Cairo rendering, in case there is any */
170 
171 	pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
172 
173 	/* Xft rendering */
174 
175 	gtk_settings_set_long_property (gtk_settings_get_default(),
176 					"gtk-xft-dpi", val, "ardour");
177 	DPIReset(); //Emit Signal
178 }
179 
180 float
get_ui_scale()181 UIConfiguration::get_ui_scale ()
182 {
183 	return get_font_scale () / 102400.;
184 }
185 
186 void
map_parameters(boost::function<void (std::string)> & functor)187 UIConfiguration::map_parameters (boost::function<void (std::string)>& functor)
188 {
189 #undef  UI_CONFIG_VARIABLE
190 #define UI_CONFIG_VARIABLE(Type,var,Name,value) functor (Name);
191 #include "ui_config_vars.h"
192 #undef  UI_CONFIG_VARIABLE
193 }
194 
195 int
pre_gui_init()196 UIConfiguration::pre_gui_init ()
197 {
198 #ifdef CAIRO_SUPPORTS_FORCE_BUGGY_GRADIENTS_ENVIRONMENT_VARIABLE
199 	if (get_buggy_gradients()) {
200 		g_setenv ("FORCE_BUGGY_GRADIENTS", "1", 1);
201 	}
202 #endif
203 #ifndef USE_CAIRO_IMAGE_SURFACE
204 	if (get_cairo_image_surface()) {
205 		g_setenv ("ARDOUR_IMAGE_SURFACE", "1", 1);
206 	}
207 #endif
208 	return 0;
209 }
210 
211 UIConfiguration*
post_gui_init()212 UIConfiguration::post_gui_init ()
213 {
214 	load_color_theme (true);
215 	return this;
216 }
217 
218 int
load_defaults()219 UIConfiguration::load_defaults ()
220 {
221 	std::string rcfile;
222 	int ret = -1;
223 
224 	if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile) ) {
225 		XMLTree tree;
226 
227 		info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
228 
229 		if (!tree.read (rcfile.c_str())) {
230 			error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
231 		} else {
232 			if (set_state (*tree.root(), Stateful::loading_state_version)) {
233 				error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
234 			} else {
235 				_dirty = false;
236 				ret = 0;
237 			}
238 		}
239 
240 	} else {
241 		warning << string_compose (_("Could not find default UI configuration file %1"), default_ui_config_file_name) << endmsg;
242 	}
243 
244 	if (ret == 0) {
245 		/* reload color theme */
246 		load_color_theme (false);
247 	}
248 
249 	return ret;
250 }
251 
252 std::string
color_file_name(bool use_my,bool with_version) const253 UIConfiguration::color_file_name (bool use_my, bool with_version) const
254 {
255 	string basename;
256 
257 	if (use_my) {
258 		basename += "my-";
259 	}
260 
261 	std::string color_name = color_file.get();
262 	size_t sep = color_name.find_first_of("-");
263 	if (sep != string::npos) {
264 		color_name = color_name.substr (0, sep);
265 	}
266 
267 	basename += color_name;
268 	basename += "-";
269 	basename += downcase(std::string(PROGRAM_NAME));
270 
271 	std::string rev (revision);
272 	std::size_t pos = rev.find_first_of("-");
273 
274 	if (with_version && pos != string::npos && pos > 0) {
275 		basename += "-";
276 		basename += rev.substr (0, pos); // COLORFILE_VERSION - program major.minor
277 	}
278 
279 	basename += color_file_suffix;
280 	return basename;
281 }
282 
283 int
load_color_file(string const & path)284 UIConfiguration::load_color_file (string const & path)
285 {
286 	XMLTree tree;
287 
288 	info << string_compose (_("Loading color file %1"), path) << endmsg;
289 
290 	if (!tree.read (path.c_str())) {
291 		error << string_compose(_("cannot read color file \"%1\""), path) << endmsg;
292 		return -1;
293 	}
294 
295 	if (set_state (*tree.root(), Stateful::loading_state_version)) {
296 		error << string_compose(_("color file \"%1\" not loaded successfully."), path) << endmsg;
297 		return -1;
298 	}
299 
300 	return 0;
301 }
302 
303 int
load_color_theme(bool allow_own)304 UIConfiguration::load_color_theme (bool allow_own)
305 {
306 	std::string cfile;
307 	bool found = false;
308 	/* ColorsChanged() will trigger a  parameter_changed () which
309 	 * in turn calls save_state()
310 	 */
311 	PBD::Unwinder<uint32_t> uw (block_save, block_save + 1);
312 
313 	if (find_file (theme_search_path(), color_file_name (false, true), cfile)) {
314 		found = true;
315 	}
316 
317 	if (!found) {
318 		if (find_file (theme_search_path(), color_file_name (false, false), cfile)) {
319 			found = true;
320 		}
321 	}
322 
323 	if (!found) {
324 		warning << string_compose (_("Color file for %1 not found along %2"), color_file.get(), theme_search_path().to_string()) << endmsg;
325 		return -1;
326 	}
327 
328 	(void) load_color_file (cfile);
329 
330 	if (allow_own) {
331 
332 		found = false;
333 
334 		PBD::Searchpath sp (user_config_directory());
335 
336 		/* user's own color files never have the program name in them */
337 
338 		if (find_file (sp, color_file_name (true, true), cfile)) {
339 			found = true;
340 		}
341 
342 		if (!found) {
343 			if (find_file (sp, color_file_name (true, false), cfile)) {
344 				found = true;
345 			}
346 		}
347 
348 		if (found) {
349 			(void) load_color_file (cfile);
350 		}
351 
352 	}
353 
354 	ColorsChanged ();
355 
356 	return 0;
357 }
358 
359 int
store_color_theme()360 UIConfiguration::store_color_theme ()
361 {
362 	XMLNode* root;
363 
364 	root = new XMLNode("Ardour");
365 
366 	XMLNode* parent = new XMLNode (X_("Colors"));
367 	for (Colors::const_iterator i = colors.begin(); i != colors.end(); ++i) {
368 		XMLNode* node = new XMLNode (X_("Color"));
369 		node->set_property (X_("name"), i->first);
370 		node->set_property (X_("value"), color_to_hex_string (i->second));
371 		parent->add_child_nocopy (*node);
372 	}
373 	root->add_child_nocopy (*parent);
374 
375 	parent = new XMLNode (X_("ColorAliases"));
376 	for (ColorAliases::const_iterator i = color_aliases.begin(); i != color_aliases.end(); ++i) {
377 		XMLNode* node = new XMLNode (X_("ColorAlias"));
378 		node->set_property (X_("name"), i->first);
379 		node->set_property (X_("alias"), i->second);
380 		parent->add_child_nocopy (*node);
381 	}
382 	root->add_child_nocopy (*parent);
383 
384 	parent = new XMLNode (X_("Modifiers"));
385 	for (Modifiers::const_iterator i = modifiers.begin(); i != modifiers.end(); ++i) {
386 		XMLNode* node = new XMLNode (X_("Modifier"));
387 		node->set_property (X_("name"), i->first);
388 		node->set_property (X_("modifier"), i->second.to_string());
389 		parent->add_child_nocopy (*node);
390 	}
391 	root->add_child_nocopy (*parent);
392 
393 	XMLTree tree;
394 	std::string colorfile = Glib::build_filename (user_config_directory(), color_file_name (true, true));;
395 
396 	tree.set_root (root);
397 
398 	if (!tree.write (colorfile.c_str())){
399 		error << string_compose (_("Color file %1 not saved"), colorfile) << endmsg;
400 		return -1;
401 	}
402 
403 	return 0;
404 }
405 
406 int
load_state()407 UIConfiguration::load_state ()
408 {
409 	bool found = false;
410 
411 	std::string rcfile;
412 
413 	if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile)) {
414 		XMLTree tree;
415 		found = true;
416 
417 		info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
418 
419 		if (!tree.read (rcfile.c_str())) {
420 			error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
421 			return -1;
422 		}
423 
424 		if (set_state (*tree.root(), Stateful::loading_state_version)) {
425 			error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
426 			return -1;
427 		}
428 	}
429 
430 	if (find_file (ardour_config_search_path(), ui_config_file_name, rcfile)) {
431 		XMLTree tree;
432 		found = true;
433 
434 		info << string_compose (_("Loading user ui configuration file %1"), rcfile) << endmsg;
435 
436 		if (!tree.read (rcfile)) {
437 			error << string_compose(_("cannot read ui configuration file \"%1\""), rcfile) << endmsg;
438 			return -1;
439 		}
440 
441 		if (set_state (*tree.root(), Stateful::loading_state_version)) {
442 			error << string_compose(_("user ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
443 			return -1;
444 		}
445 
446 		_dirty = false;
447 	}
448 
449 	if (!found) {
450 		error << _("could not find any ui configuration file, canvas will look broken.") << endmsg;
451 	}
452 
453 	return 0;
454 }
455 
456 int
save_state()457 UIConfiguration::save_state()
458 {
459 	if (block_save != 0) {
460 		return -1;
461 	}
462 
463 	if (_dirty) {
464 		std::string rcfile = Glib::build_filename (user_config_directory(), ui_config_file_name);
465 
466 		XMLTree tree;
467 
468 		tree.set_root (&get_state());
469 
470 		if (!tree.write (rcfile.c_str())){
471 			error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
472 			return -1;
473 		}
474 
475 		_dirty = false;
476 	}
477 
478 	if (aliases_modified || colors_modified || modifiers_modified) {
479 
480 		if (store_color_theme ()) {
481 			error << string_compose (_("Color file %1 not saved"), color_file.get()) << endmsg;
482 			return -1;
483 		}
484 
485 		aliases_modified = false;
486 		colors_modified = false;
487 		modifiers_modified = false;
488 	}
489 
490 
491 	return 0;
492 }
493 
494 XMLNode&
get_state()495 UIConfiguration::get_state ()
496 {
497 	XMLNode* root;
498 
499 	root = new XMLNode("Ardour");
500 
501 	root->add_child_nocopy (get_variables ("UI"));
502 	root->add_child_nocopy (get_variables ("Canvas"));
503 
504 	if (_extra_xml) {
505 		root->add_child_copy (*_extra_xml);
506 	}
507 
508 	return *root;
509 }
510 
511 XMLNode&
get_variables(std::string which_node)512 UIConfiguration::get_variables (std::string which_node)
513 {
514 	XMLNode* node;
515 
516 	node = new XMLNode (which_node);
517 
518 #undef  UI_CONFIG_VARIABLE
519 #undef  CANVAS_FONT_VARIABLE
520 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
521 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
522 #include "ui_config_vars.h"
523 #include "canvas_vars.h"
524 #undef  UI_CONFIG_VARIABLE
525 #undef  CANVAS_FONT_VARIABLE
526 
527 	return *node;
528 }
529 
530 int
set_state(const XMLNode & root,int)531 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
532 {
533 	/* this can load a generic UI configuration file or a colors file */
534 
535 	if (root.name() != "Ardour") {
536 		return -1;
537 	}
538 
539 	Stateful::save_extra_xml (root);
540 
541 	XMLNodeList nlist = root.children();
542 	XMLNodeConstIterator niter;
543 	XMLNode *node;
544 
545 	for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
546 
547 		node = *niter;
548 
549 		if (node->name() == "Canvas" ||  node->name() == "UI") {
550 			set_variables (*node);
551 
552 		}
553 	}
554 
555 	XMLNode* colors = find_named_node (root, X_("Colors"));
556 
557 	if (colors) {
558 		load_colors (*colors);
559 	}
560 
561 	XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
562 
563 	if (aliases) {
564 		load_color_aliases (*aliases);
565 	}
566 
567 	XMLNode* modifiers = find_named_node (root, X_("Modifiers"));
568 
569 	if (modifiers) {
570 		load_modifiers (*modifiers);
571 	}
572 
573 	return 0;
574 }
575 
576 void
load_color_aliases(XMLNode const & node)577 UIConfiguration::load_color_aliases (XMLNode const & node)
578 {
579 	XMLNodeList const nlist = node.children();
580 	XMLNodeConstIterator niter;
581 	XMLProperty const *name;
582 	XMLProperty const *alias;
583 
584 	for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
585 		XMLNode const * child = *niter;
586 		if (child->name() != X_("ColorAlias")) {
587 			continue;
588 		}
589 		name = child->property (X_("name"));
590 		alias = child->property (X_("alias"));
591 
592 		if (name && alias) {
593 			color_aliases[name->value()] = alias->value();
594 		}
595 	}
596 }
597 
598 void
load_colors(XMLNode const & node)599 UIConfiguration::load_colors (XMLNode const & node)
600 {
601 	XMLNodeList const nlist = node.children();
602 	XMLNodeConstIterator niter;
603 	XMLProperty const *name;
604 	XMLProperty const *color;
605 
606 	/* don't clear colors, so that we can load > 1 color file and have
607 	   the subsequent ones overwrite the later ones.
608 	*/
609 
610 	for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
611 		XMLNode const * child = *niter;
612 		if (child->name() != X_("Color")) {
613 			continue;
614 		}
615 		name = child->property (X_("name"));
616 		color = child->property (X_("value"));
617 
618 		if (name && color) {
619 			Gtkmm2ext::Color c;
620 			c = strtoul (color->value().c_str(), 0, 16);
621 			/* insert or replace color name definition */
622 			colors[name->value()] =  c;
623 		}
624 	}
625 }
626 
627 void
load_modifiers(XMLNode const & node)628 UIConfiguration::load_modifiers (XMLNode const & node)
629 {
630 	XMLNodeList const nlist = node.children();
631 	XMLNodeConstIterator niter;
632 	XMLProperty const *name;
633 	XMLProperty const *mod;
634 
635 	for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
636 		XMLNode const * child = *niter;
637 		if (child->name() != X_("Modifier")) {
638 			continue;
639 		}
640 
641 		name = child->property (X_("name"));
642 		mod = child->property (X_("modifier"));
643 
644 		if (name && mod) {
645 			SVAModifier svam (mod->value());
646 			modifiers[name->value()] = svam;
647 		}
648 	}
649 }
650 
651 void
set_variables(const XMLNode & node)652 UIConfiguration::set_variables (const XMLNode& node)
653 {
654 #undef  UI_CONFIG_VARIABLE
655 #define UI_CONFIG_VARIABLE(Type,var,name,val) if (var.set_from_node (node)) { ParameterChanged (name); }
656 #define CANVAS_FONT_VARIABLE(var,name)        if (var.set_from_node (node)) { ParameterChanged (name); }
657 #include "ui_config_vars.h"
658 #include "canvas_vars.h"
659 #undef  UI_CONFIG_VARIABLE
660 #undef  CANVAS_FONT_VARIABLE
661 }
662 
663 Gtkmm2ext::SVAModifier
modifier(string const & name) const664 UIConfiguration::modifier (string const & name) const
665 {
666 	Modifiers::const_iterator m = modifiers.find (name);
667 	if (m != modifiers.end()) {
668 		return m->second;
669 	}
670 	return SVAModifier ();
671 }
672 
673 Gtkmm2ext::Color
color_mod(std::string const & colorname,std::string const & modifiername) const674 UIConfiguration::color_mod (std::string const & colorname, std::string const & modifiername) const
675 {
676 	return HSV (color (colorname)).mod (modifier (modifiername)).color ();
677 }
678 
679 Gtkmm2ext::Color
color_mod(const Gtkmm2ext::Color & color,std::string const & modifiername) const680 UIConfiguration::color_mod (const Gtkmm2ext::Color& color, std::string const & modifiername) const
681 {
682 	return HSV (color).mod (modifier (modifiername)).color ();
683 }
684 
685 Gtkmm2ext::Color
color(const std::string & name,bool * failed) const686 UIConfiguration::color (const std::string& name, bool* failed) const
687 {
688 	ColorAliases::const_iterator e = color_aliases.find (name);
689 
690 	if (failed) {
691 		*failed = false;
692 	}
693 
694 	if (e != color_aliases.end ()) {
695 		Colors::const_iterator rc = colors.find (e->second);
696 		if (rc != colors.end()) {
697 			return rc->second;
698 		}
699 	} else {
700 		/* not an alias, try directly */
701 		Colors::const_iterator rc = colors.find (name);
702 		if (rc != colors.end()) {
703 			return rc->second;
704 		}
705 	}
706 
707 	if (!failed) {
708 		/* only show this message if the caller wasn't interested in
709 		   the fail status.
710 		*/
711 		cerr << string_compose (_("Color %1 not found"), name) << endl;
712 	}
713 
714 	if (failed) {
715 		*failed = true;
716 	}
717 
718 	return rgba_to_color ((g_random_int()%256)/255.0,
719 			      (g_random_int()%256)/255.0,
720 			      (g_random_int()%256)/255.0,
721 			      0xff);
722 }
723 
724 Color
quantized(Color c) const725 UIConfiguration::quantized (Color c) const
726 {
727 	HSV hsv (c);
728 	hsv.h = hue_width * (round (hsv.h/hue_width));
729 	return hsv.color ();
730 }
731 
732 void
set_color(string const & name,Gtkmm2ext::Color color)733 UIConfiguration::set_color (string const& name, Gtkmm2ext::Color color)
734 {
735 	Colors::iterator i = colors.find (name);
736 	if (i == colors.end()) {
737 		return;
738 	}
739 	i->second = color;
740 	colors_modified = true;
741 
742 	ColorsChanged (); /* EMIT SIGNAL */
743 }
744 
745 void
set_alias(string const & name,string const & alias)746 UIConfiguration::set_alias (string const & name, string const & alias)
747 {
748 	ColorAliases::iterator i = color_aliases.find (name);
749 	if (i == color_aliases.end()) {
750 		return;
751 	}
752 
753 	i->second = alias;
754 	aliases_modified = true;
755 
756 	ColorsChanged (); /* EMIT SIGNAL */
757 }
758 
759 void
set_modifier(string const & name,SVAModifier svam)760 UIConfiguration::set_modifier (string const & name, SVAModifier svam)
761 {
762 	Modifiers::iterator m = modifiers.find (name);
763 
764 	if (m == modifiers.end()) {
765 		return;
766 	}
767 
768 	m->second = svam;
769 	modifiers_modified = true;
770 
771 	ColorsChanged (); /* EMIT SIGNAL */
772 }
773 
774 void
load_rc_file(bool themechange,bool allow_own)775 UIConfiguration::load_rc_file (bool themechange, bool allow_own)
776 {
777 	string basename = ui_rc_file.get();
778 	std::string rc_file_path;
779 
780 	if (!find_file (ardour_config_search_path(), basename, rc_file_path)) {
781 		warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
782                                            basename, ardour_config_search_path().to_string(), PROGRAM_NAME)
783 				<< endmsg;
784 		return;
785 	}
786 
787 	info << string_compose (_("Loading ui configuration file %1"), rc_file_path) << endmsg;
788 
789 	Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);
790 }
791 
792 std::string
color_to_hex_string(Gtkmm2ext::Color c)793 UIConfiguration::color_to_hex_string (Gtkmm2ext::Color c)
794 {
795 	char buf[16];
796 	int retval = g_snprintf (buf, sizeof(buf), "%08x", c);
797 
798 	if (retval < 0 || retval >= (int)sizeof(buf)) {
799 		assert(false);
800 	}
801 	return buf;
802 }
803 
804 std::string
color_to_hex_string_no_alpha(Gtkmm2ext::Color c)805 UIConfiguration::color_to_hex_string_no_alpha (Gtkmm2ext::Color c)
806 {
807 	c >>= 8; // shift/remove alpha
808 	char buf[16];
809 	int retval = g_snprintf (buf, sizeof(buf), "%06x", c);
810 
811 	if (retval < 0 || retval >= (int)sizeof(buf)) {
812 		assert(false);
813 	}
814 	return buf;
815 }
816