display active peak-builds
[ardour.git] / gtk2_ardour / ui_config.cc
1 /*
2     Copyright (C) 1999-2014 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #if !defined USE_CAIRO_IMAGE_SURFACE && !defined NDEBUG
21 #define OPTIONAL_CAIRO_IMAGE_SURFACE
22 #endif
23
24 #include <iostream>
25 #include <sstream>
26 #include <unistd.h>
27 #include <cstdlib>
28 #include <cstdio> /* for snprintf, grrr */
29
30 #include <cairo/cairo.h>
31
32 #include <glibmm/miscutils.h>
33 #include <glib/gstdio.h>
34
35 #include "pbd/convert.h"
36 #include "pbd/failed_constructor.h"
37 #include "pbd/xml++.h"
38 #include "pbd/file_utils.h"
39 #include "pbd/error.h"
40 #include "pbd/stacktrace.h"
41
42 #include "gtkmm2ext/rgb_macros.h"
43 #include "gtkmm2ext/gtk_ui.h"
44
45 #include "ardour/filesystem_paths.h"
46
47 #include "ardour_ui.h"
48 #include "global_signals.h"
49 #include "ui_config.h"
50
51 #include "i18n.h"
52
53 using namespace std;
54 using namespace PBD;
55 using namespace ARDOUR;
56 using namespace ArdourCanvas;
57
58 static const char* ui_config_file_name = "ui_config";
59 static const char* default_ui_config_file_name = "default_ui_config";
60 UIConfiguration* UIConfiguration::_instance = 0;
61
62 static const double hue_width = 18.0;
63
64 UIConfiguration::UIConfiguration ()
65         :
66 #undef  UI_CONFIG_VARIABLE
67 #define UI_CONFIG_VARIABLE(Type,var,name,val) var (name,val),
68 #define CANVAS_FONT_VARIABLE(var,name) var (name),
69 #include "ui_config_vars.h"
70 #include "canvas_vars.h"
71 #undef  UI_CONFIG_VARIABLE
72 #undef  CANVAS_FONT_VARIABLE
73
74         _dirty (false),
75         aliases_modified (false),
76         colors_modified (false),
77         modifiers_modified (false),
78         block_save (0)
79 {
80         _instance = this;
81
82         load_state();
83
84         ARDOUR_UI_UTILS::ColorsChanged.connect (boost::bind (&UIConfiguration::colors_changed, this));
85
86         ParameterChanged.connect (sigc::mem_fun (*this, &UIConfiguration::parameter_changed));
87 }
88
89 UIConfiguration::~UIConfiguration ()
90 {
91 }
92
93 void
94 UIConfiguration::colors_changed ()
95 {
96         reset_gtk_theme ();
97
98         /* In theory, one of these ought to work:
99
100            gtk_rc_reparse_all_for_settings (gtk_settings_get_default(), true);
101            gtk_rc_reset_styles (gtk_settings_get_default());
102
103            but in practice, neither of them do. So just reload the current
104            GTK RC file, which causes a reset of all styles and a redraw
105         */
106
107         parameter_changed ("ui-rc-file");
108 }
109
110 void
111 UIConfiguration::parameter_changed (string param)
112 {
113         _dirty = true;
114         
115         if (param == "ui-rc-file") {
116                 load_rc_file (true);
117         } else if (param == "color-file") {
118                 load_color_theme ();
119         }
120
121         save_state ();
122 }
123
124 void
125 UIConfiguration::reset_gtk_theme ()
126 {
127         stringstream ss;
128
129         ss << "gtk_color_scheme = \"" << hex;
130         
131         for (ColorAliases::iterator g = color_aliases.begin(); g != color_aliases.end(); ++g) {
132                 
133                 if (g->first.find ("gtk_") == 0) {
134                         ColorAliases::const_iterator a = color_aliases.find (g->first);
135                         const string gtk_name = g->first.substr (4);
136                         ss << gtk_name << ":#" << std::setw (6) << setfill ('0') << (color (g->second) >> 8) << ';';
137                 }
138         }
139
140         ss << '"' << dec << endl;
141
142         /* reset GTK color scheme */
143
144         Gtk::Settings::get_default()->property_gtk_color_scheme() = ss.str();
145 }
146         
147 void
148 UIConfiguration::map_parameters (boost::function<void (std::string)>& functor)
149 {
150 #undef  UI_CONFIG_VARIABLE
151 #define UI_CONFIG_VARIABLE(Type,var,Name,value) functor (Name);
152 #include "ui_config_vars.h"
153 #undef  UI_CONFIG_VARIABLE
154 }
155
156 int
157 UIConfiguration::pre_gui_init ()
158 {
159 #ifdef CAIRO_SUPPORTS_FORCE_BUGGY_GRADIENTS_ENVIRONMENT_VARIABLE
160         if (get_buggy_gradients()) {
161                 g_setenv ("FORCE_BUGGY_GRADIENTS", "1", 1);
162         }
163 #endif
164 #ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
165         if (get_cairo_image_surface()) {
166                 g_setenv ("ARDOUR_IMAGE_SURFACE", "1", 1);
167         }
168 #endif
169         return 0;
170 }
171
172 UIConfiguration*
173 UIConfiguration::post_gui_init ()
174 {
175         load_color_theme ();
176         return this;
177 }
178
179 int
180 UIConfiguration::load_defaults ()
181 {
182         std::string rcfile;
183         int ret = -1;
184         
185         if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile) ) {
186                 XMLTree tree;
187
188                 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
189
190                 if (!tree.read (rcfile.c_str())) {
191                         error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
192                 } else {
193                         if (set_state (*tree.root(), Stateful::loading_state_version)) {
194                                 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
195                         } else {
196                                 _dirty = false;
197                                 ret = 0;
198                         }
199                 }
200
201         } else {
202                 warning << string_compose (_("Could not find default UI configuration file %1"), default_ui_config_file_name) << endmsg;
203         }
204
205
206         if (ret == 0) {
207                 /* reload color theme */
208                 load_color_theme (false);
209                 ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
210         }
211
212         return ret;
213 }
214
215 int
216 UIConfiguration::load_color_theme (bool allow_own)
217 {
218         std::string cfile;
219         string basename;
220         bool found = false;
221
222         if (allow_own) {
223                 basename = "my-";
224                 basename += color_file.get();
225                 basename += ".colors";
226
227                 if (find_file (ardour_config_search_path(), basename, cfile)) {
228                         found = true;
229                 }
230         }
231
232         if (!found) {
233                 basename = color_file.get();
234                 basename += ".colors";
235         
236                 if (find_file (ardour_config_search_path(), basename, cfile)) {
237                         found = true;
238                 }
239         }
240
241         if (found) {
242
243                 XMLTree tree;
244                 
245                 info << string_compose (_("Loading color file %1"), cfile) << endmsg;
246
247                 if (!tree.read (cfile.c_str())) {
248                         error << string_compose(_("cannot read color file \"%1\""), cfile) << endmsg;
249                         return -1;
250                 }
251
252                 if (set_state (*tree.root(), Stateful::loading_state_version)) {
253                         error << string_compose(_("color file \"%1\" not loaded successfully."), cfile) << endmsg;
254                         return -1;
255                 }
256
257                 ARDOUR_UI_UTILS::ColorsChanged ();
258         } else {
259                 warning << string_compose (_("Color file %1 not found"), basename) << endmsg;
260         }
261
262         return 0;
263 }
264
265 int
266 UIConfiguration::store_color_theme ()
267 {
268         XMLNode* root;
269         LocaleGuard lg (X_("C"));
270
271         root = new XMLNode("Ardour");
272
273         XMLNode* parent = new XMLNode (X_("Colors"));
274         for (Colors::const_iterator i = colors.begin(); i != colors.end(); ++i) {
275                 XMLNode* node = new XMLNode (X_("Color"));
276                 node->add_property (X_("name"), i->first);
277                 stringstream ss;
278                 ss << "0x" << setw (8) << setfill ('0') << hex << i->second;
279                 node->add_property (X_("value"), ss.str());
280                 parent->add_child_nocopy (*node);
281         }
282         root->add_child_nocopy (*parent);
283         
284         parent = new XMLNode (X_("ColorAliases"));
285         for (ColorAliases::const_iterator i = color_aliases.begin(); i != color_aliases.end(); ++i) {
286                 XMLNode* node = new XMLNode (X_("ColorAlias"));
287                 node->add_property (X_("name"), i->first);
288                 node->add_property (X_("alias"), i->second);
289                 parent->add_child_nocopy (*node);
290         }
291         root->add_child_nocopy (*parent);
292
293         parent = new XMLNode (X_("Modifiers"));
294         for (Modifiers::const_iterator i = modifiers.begin(); i != modifiers.end(); ++i) {
295                 XMLNode* node = new XMLNode (X_("Modifier"));
296                 node->add_property (X_("name"), i->first);
297                 node->add_property (X_("modifier"), i->second.to_string());
298                 parent->add_child_nocopy (*node);
299         }
300         root->add_child_nocopy (*parent);
301
302         XMLTree tree;
303         std::string colorfile = Glib::build_filename (user_config_directory(), (string ("my-") + color_file.get() + ".colors"));
304         
305         tree.set_root (root);
306
307         if (!tree.write (colorfile.c_str())){
308                 error << string_compose (_("Color file %1 not saved"), colorfile) << endmsg;
309                 return -1;
310         }
311
312         return 0;
313 }
314
315 int
316 UIConfiguration::load_state ()
317 {
318         bool found = false;
319
320         std::string rcfile;
321
322         if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile)) {
323                 XMLTree tree;
324                 found = true;
325
326                 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
327
328                 if (!tree.read (rcfile.c_str())) {
329                         error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
330                         return -1;
331                 }
332
333                 if (set_state (*tree.root(), Stateful::loading_state_version)) {
334                         error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
335                         return -1;
336                 }
337         }
338
339         if (find_file (ardour_config_search_path(), ui_config_file_name, rcfile)) {
340                 XMLTree tree;
341                 found = true;
342
343                 info << string_compose (_("Loading user ui configuration file %1"), rcfile) << endmsg;
344
345                 if (!tree.read (rcfile)) {
346                         error << string_compose(_("cannot read ui configuration file \"%1\""), rcfile) << endmsg;
347                         return -1;
348                 }
349
350                 if (set_state (*tree.root(), Stateful::loading_state_version)) {
351                         error << string_compose(_("user ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
352                         return -1;
353                 }
354
355                 _dirty = false;
356         }
357
358         if (!found) {
359                 error << _("could not find any ui configuration file, canvas will look broken.") << endmsg;
360         }
361
362         return 0;
363 }
364
365 int
366 UIConfiguration::save_state()
367 {
368
369         if (_dirty) {
370                 std::string rcfile = Glib::build_filename (user_config_directory(), ui_config_file_name);
371                 
372                 XMLTree tree;
373
374                 tree.set_root (&get_state());
375
376                 if (!tree.write (rcfile.c_str())){
377                         error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
378                         return -1;
379                 }
380
381                 _dirty = false;
382         }
383
384         if (aliases_modified || colors_modified || modifiers_modified) {
385
386                 if (store_color_theme ()) {
387                         error << string_compose (_("Color file %1 not saved"), color_file.get()) << endmsg;
388                         return -1;
389                 }
390
391                 aliases_modified = false;
392                 colors_modified = false;
393                 modifiers_modified = false;
394         }
395         
396
397         return 0;
398 }
399
400 XMLNode&
401 UIConfiguration::get_state ()
402 {
403         XMLNode* root;
404         LocaleGuard lg (X_("C"));
405
406         root = new XMLNode("Ardour");
407
408         root->add_child_nocopy (get_variables ("UI"));
409         root->add_child_nocopy (get_variables ("Canvas"));
410
411         if (_extra_xml) {
412                 root->add_child_copy (*_extra_xml);
413         }
414
415         return *root;
416 }
417
418 XMLNode&
419 UIConfiguration::get_variables (std::string which_node)
420 {
421         XMLNode* node;
422         LocaleGuard lg (X_("C"));
423
424         node = new XMLNode (which_node);
425
426 #undef  UI_CONFIG_VARIABLE
427 #undef  CANVAS_FONT_VARIABLE
428 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
429 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
430 #include "ui_config_vars.h"
431 #include "canvas_vars.h"
432 #undef  UI_CONFIG_VARIABLE
433 #undef  CANVAS_FONT_VARIABLE
434
435         return *node;
436 }
437
438 int
439 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
440 {
441         /* this can load a generic UI configuration file or a colors file */
442
443         if (root.name() != "Ardour") {
444                 return -1;
445         }
446
447         Stateful::save_extra_xml (root);
448
449         XMLNodeList nlist = root.children();
450         XMLNodeConstIterator niter;
451         XMLNode *node;
452
453         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
454
455                 node = *niter;
456
457                 if (node->name() == "Canvas" ||  node->name() == "UI") {
458                         set_variables (*node);
459
460                 }
461         }
462
463         XMLNode* colors = find_named_node (root, X_("Colors"));
464
465         if (colors) {
466                 load_colors (*colors);
467         }
468
469         XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
470
471         if (aliases) {
472                 load_color_aliases (*aliases);
473         }
474
475         XMLNode* modifiers = find_named_node (root, X_("Modifiers"));
476
477         if (modifiers) {
478                 load_modifiers (*modifiers);
479         }
480
481         return 0;
482 }
483
484 void
485 UIConfiguration::load_color_aliases (XMLNode const & node)
486 {
487         XMLNodeList const nlist = node.children();
488         XMLNodeConstIterator niter;
489         XMLProperty const *name;
490         XMLProperty const *alias;
491         
492         color_aliases.clear ();
493
494         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
495                 if ((*niter)->name() != X_("ColorAlias")) {
496                         continue;
497                 }
498                 name = (*niter)->property (X_("name"));
499                 alias = (*niter)->property (X_("alias"));
500
501                 if (name && alias) {
502                         color_aliases.insert (make_pair (name->value(), alias->value()));
503                 }
504         }
505 }
506
507 void
508 UIConfiguration::load_colors (XMLNode const & node)
509 {
510         XMLNodeList const nlist = node.children();
511         XMLNodeConstIterator niter;
512         XMLProperty const *name;
513         XMLProperty const *color;
514         
515         colors.clear ();
516
517         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
518                 if ((*niter)->name() != X_("Color")) {
519                         continue;
520                 }
521                 name = (*niter)->property (X_("name"));
522                 color = (*niter)->property (X_("value"));
523
524                 if (name && color) {
525                         ArdourCanvas::Color c;
526                         c = strtoul (color->value().c_str(), 0, 16);
527                         colors.insert (make_pair (name->value(), c));
528                 }
529         }
530 }
531
532 void
533 UIConfiguration::load_modifiers (XMLNode const & node)
534 {
535         PBD::LocaleGuard lg ("C");
536         XMLNodeList const nlist = node.children();
537         XMLNodeConstIterator niter;
538         XMLProperty const *name;
539         XMLProperty const *mod;
540         
541         modifiers.clear ();
542         
543         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
544                 if ((*niter)->name() != X_("Modifier")) {
545                         continue;
546                 }
547
548                 name = (*niter)->property (X_("name"));
549                 mod = (*niter)->property (X_("modifier"));
550
551                 if (name && mod) {
552                         SVAModifier svam (mod->value());
553                         modifiers.insert (make_pair (name->value(), svam));
554                 }
555         }
556 }
557
558 void
559 UIConfiguration::set_variables (const XMLNode& node)
560 {
561 #undef  UI_CONFIG_VARIABLE
562 #define UI_CONFIG_VARIABLE(Type,var,name,val) if (var.set_from_node (node)) { ParameterChanged (name); }
563 #define CANVAS_FONT_VARIABLE(var,name)        if (var.set_from_node (node)) { ParameterChanged (name); }
564 #include "ui_config_vars.h"
565 #include "canvas_vars.h"
566 #undef  UI_CONFIG_VARIABLE
567 #undef  CANVAS_FONT_VARIABLE
568 }
569
570 ArdourCanvas::SVAModifier
571 UIConfiguration::modifier (string const & name) const
572 {
573         Modifiers::const_iterator m = modifiers.find (name);
574         if (m != modifiers.end()) {
575                 return m->second;
576         }
577         return SVAModifier ();
578 }
579
580 ArdourCanvas::Color
581 UIConfiguration::color_mod (std::string const & colorname, std::string const & modifiername) const
582 {
583         return HSV (color (colorname)).mod (modifier (modifiername)).color ();
584 }
585
586 ArdourCanvas::Color
587 UIConfiguration::color_mod (const ArdourCanvas::Color& color, std::string const & modifiername) const
588 {
589         return HSV (color).mod (modifier (modifiername)).color ();
590 }
591
592 ArdourCanvas::Color
593 UIConfiguration::color (const std::string& name, bool* failed) const
594 {
595         ColorAliases::const_iterator e = color_aliases.find (name);
596
597         if (failed) {
598                 *failed = false;
599         }
600         
601         if (e != color_aliases.end ()) {
602                 Colors::const_iterator rc = colors.find (e->second);
603                 if (rc != colors.end()) {
604                         return rc->second;
605                 }
606         } else {
607                 /* not an alias, try directly */
608                 Colors::const_iterator rc = colors.find (name);
609                 if (rc != colors.end()) {
610                         return rc->second;
611                 }
612         }
613         
614         if (!failed) {
615                 /* only show this message if the caller wasn't interested in
616                    the fail status.
617                 */
618                 cerr << string_compose (_("Color %1 not found"), name) << endl;
619         }
620
621         if (failed) {
622                 *failed = true;
623         }
624         
625         return rgba_to_color ((g_random_int()%256)/255.0,
626                               (g_random_int()%256)/255.0,
627                               (g_random_int()%256)/255.0,
628                               0xff);
629 }
630
631 Color
632 UIConfiguration::quantized (Color c) const
633 {
634         HSV hsv (c);
635         hsv.h = hue_width * (round (hsv.h/hue_width));
636         return hsv.color ();
637 }
638
639 void
640 UIConfiguration::set_color (string const& name, ArdourCanvas::Color color)
641 {
642         Colors::iterator i = colors.find (name);
643         if (i == colors.end()) {
644                 return;
645         }
646         i->second = color;
647         colors_modified = true;
648
649         ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
650 }
651
652 void
653 UIConfiguration::set_alias (string const & name, string const & alias)
654 {
655         ColorAliases::iterator i = color_aliases.find (name);
656         if (i == color_aliases.end()) {
657                 return;
658         }
659
660         i->second = alias;
661         aliases_modified = true;
662
663         ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
664 }
665
666 void
667 UIConfiguration::set_modifier (string const & name, SVAModifier svam)
668 {
669         Modifiers::iterator m = modifiers.find (name);
670
671         if (m == modifiers.end()) {
672                 return;
673         }
674
675         m->second = svam;
676         modifiers_modified = true;
677
678         ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
679 }
680
681 void
682 UIConfiguration::load_rc_file (bool themechange, bool allow_own)
683 {
684         string basename = ui_rc_file.get();
685         std::string rc_file_path;
686
687         if (!find_file (ardour_config_search_path(), basename, rc_file_path)) {
688                 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
689                                            basename, ardour_config_search_path().to_string(), PROGRAM_NAME)
690                                 << endmsg;
691                 return;
692         }
693
694         info << "Loading ui configuration file " << rc_file_path << endmsg;
695
696         Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);
697 }
698
699