remove debug output
[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 <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
33 #include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
34
35 #include "pbd/gstdio_compat.h"
36 #include "pbd/unwind.h"
37 #include <glibmm/miscutils.h>
38
39 #include <gtkmm/settings.h>
40
41 #include "pbd/convert.h"
42 #include "pbd/failed_constructor.h"
43 #include "pbd/xml++.h"
44 #include "pbd/file_utils.h"
45 #include "pbd/locale_guard.h"
46 #include "pbd/error.h"
47 #include "pbd/stacktrace.h"
48
49 #include "gtkmm2ext/rgb_macros.h"
50 #include "gtkmm2ext/gtk_ui.h"
51
52 #include "ardour/filesystem_paths.h"
53 #include "ardour/search_paths.h"
54 #include "ardour/utils.h"
55
56 #include "ui_config.h"
57
58 #include "i18n.h"
59
60 using namespace std;
61 using namespace PBD;
62 using namespace ARDOUR;
63 using namespace ArdourCanvas;
64
65 static const char* ui_config_file_name = "ui_config";
66 static const char* default_ui_config_file_name = "default_ui_config";
67
68 static const double hue_width = 18.0;
69 std::string UIConfiguration::color_file_suffix = X_(".colors");
70
71 UIConfiguration&
72 UIConfiguration::instance ()
73 {
74         static UIConfiguration s_instance;
75         return s_instance;
76 }
77
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
101 UIConfiguration::~UIConfiguration ()
102 {
103 }
104
105 void
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
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 ();
131         }
132
133         save_state ();
134 }
135
136 void
137 UIConfiguration::reset_gtk_theme ()
138 {
139         stringstream ss;
140
141         ss << "gtk_color_scheme = \"" << hex;
142
143         for (ColorAliases::iterator g = color_aliases.begin(); g != color_aliases.end(); ++g) {
144
145                 if (g->first.find ("gtk_") == 0) {
146                         const string gtk_name = g->first.substr (4);
147                         ss << gtk_name << ":#" << std::setw (6) << setfill ('0') << (color (g->second) >> 8) << ';';
148                 }
149         }
150
151         ss << '"' << dec << endl;
152
153         /* reset GTK color scheme */
154
155         Gtk::Settings::get_default()->property_gtk_color_scheme() = ss.str();
156 }
157
158 void
159 UIConfiguration::reset_dpi ()
160 {
161         long val = get_font_scale();
162         set_pango_fontsize ();
163         /* Xft rendering */
164
165         gtk_settings_set_long_property (gtk_settings_get_default(),
166                                         "gtk-xft-dpi", val, "ardour");
167         DPIReset(); //Emit Signal
168 }
169
170 void
171 UIConfiguration::set_pango_fontsize ()
172 {
173         long val = get_font_scale();
174
175         /* FT2 rendering - used by GnomeCanvas, sigh */
176
177 #ifndef PLATFORM_WINDOWS
178         pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_new(), val/1024, val/1024);
179 #endif
180
181         /* Cairo rendering, in case there is any */
182
183         pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
184 }
185
186 float
187 UIConfiguration::get_ui_scale ()
188 {
189         return get_font_scale () / 102400.;
190 }
191
192 void
193 UIConfiguration::map_parameters (boost::function<void (std::string)>& functor)
194 {
195 #undef  UI_CONFIG_VARIABLE
196 #define UI_CONFIG_VARIABLE(Type,var,Name,value) functor (Name);
197 #include "ui_config_vars.h"
198 #undef  UI_CONFIG_VARIABLE
199 }
200
201 int
202 UIConfiguration::pre_gui_init ()
203 {
204 #ifdef CAIRO_SUPPORTS_FORCE_BUGGY_GRADIENTS_ENVIRONMENT_VARIABLE
205         if (get_buggy_gradients()) {
206                 g_setenv ("FORCE_BUGGY_GRADIENTS", "1", 1);
207         }
208 #endif
209 #ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
210         if (get_cairo_image_surface()) {
211                 g_setenv ("ARDOUR_IMAGE_SURFACE", "1", 1);
212         }
213 #endif
214         return 0;
215 }
216
217 UIConfiguration*
218 UIConfiguration::post_gui_init ()
219 {
220         load_color_theme ();
221         return this;
222 }
223
224 int
225 UIConfiguration::load_defaults ()
226 {
227         std::string rcfile;
228         int ret = -1;
229
230         if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile) ) {
231                 XMLTree tree;
232
233                 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
234
235                 if (!tree.read (rcfile.c_str())) {
236                         error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
237                 } else {
238                         if (set_state (*tree.root(), Stateful::loading_state_version)) {
239                                 error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
240                         } else {
241                                 _dirty = false;
242                                 ret = 0;
243                         }
244                 }
245
246         } else {
247                 warning << string_compose (_("Could not find default UI configuration file %1"), default_ui_config_file_name) << endmsg;
248         }
249
250
251         if (ret == 0) {
252                 /* reload color theme */
253                 load_color_theme (false);
254         }
255
256         return ret;
257 }
258
259 int
260 UIConfiguration::load_color_theme (bool allow_own)
261 {
262         std::string cfile;
263         string basename;
264         bool found = false;
265         /* ColorsChanged() will trigger a  parameter_changed () which
266          * in turn calls save_state()
267          */
268         PBD::Unwinder<uint32_t> uw (block_save, block_save + 1);
269
270         if (allow_own) {
271                 basename = "my-";
272                 basename += color_file.get();
273                 basename += color_file_suffix;
274
275                 if (find_file (theme_search_path(), basename, cfile)) {
276                         found = true;
277                 }
278         }
279
280         if (!found) {
281                 basename = color_file.get();
282                 basename += color_file_suffix;
283
284                 if (find_file (theme_search_path(), basename, cfile)) {
285                         found = true;
286                 }
287         }
288
289         if (found) {
290
291                 XMLTree tree;
292
293                 info << string_compose (_("Loading color file %1"), cfile) << endmsg;
294
295                 if (!tree.read (cfile.c_str())) {
296                         error << string_compose(_("cannot read color file \"%1\""), cfile) << endmsg;
297                         return -1;
298                 }
299
300                 if (set_state (*tree.root(), Stateful::loading_state_version)) {
301                         error << string_compose(_("color file \"%1\" not loaded successfully."), cfile) << endmsg;
302                         return -1;
303                 }
304
305                 ColorsChanged ();
306         } else {
307                 warning << string_compose (_("Color file %1 not found"), basename) << endmsg;
308         }
309
310         return 0;
311 }
312
313 int
314 UIConfiguration::store_color_theme ()
315 {
316         XMLNode* root;
317         LocaleGuard lg;
318
319         root = new XMLNode("Ardour");
320
321         XMLNode* parent = new XMLNode (X_("Colors"));
322         for (Colors::const_iterator i = colors.begin(); i != colors.end(); ++i) {
323                 XMLNode* node = new XMLNode (X_("Color"));
324                 node->add_property (X_("name"), i->first);
325                 stringstream ss;
326                 ss << "0x" << setw (8) << setfill ('0') << hex << i->second;
327                 node->add_property (X_("value"), ss.str());
328                 parent->add_child_nocopy (*node);
329         }
330         root->add_child_nocopy (*parent);
331
332         parent = new XMLNode (X_("ColorAliases"));
333         for (ColorAliases::const_iterator i = color_aliases.begin(); i != color_aliases.end(); ++i) {
334                 XMLNode* node = new XMLNode (X_("ColorAlias"));
335                 node->add_property (X_("name"), i->first);
336                 node->add_property (X_("alias"), i->second);
337                 parent->add_child_nocopy (*node);
338         }
339         root->add_child_nocopy (*parent);
340
341         parent = new XMLNode (X_("Modifiers"));
342         for (Modifiers::const_iterator i = modifiers.begin(); i != modifiers.end(); ++i) {
343                 XMLNode* node = new XMLNode (X_("Modifier"));
344                 node->add_property (X_("name"), i->first);
345                 node->add_property (X_("modifier"), i->second.to_string());
346                 parent->add_child_nocopy (*node);
347         }
348         root->add_child_nocopy (*parent);
349
350         XMLTree tree;
351         std::string colorfile = Glib::build_filename (user_config_directory(), (string ("my-") + color_file.get() + color_file_suffix));
352
353         tree.set_root (root);
354
355         if (!tree.write (colorfile.c_str())){
356                 error << string_compose (_("Color file %1 not saved"), colorfile) << endmsg;
357                 return -1;
358         }
359
360         return 0;
361 }
362
363 int
364 UIConfiguration::load_state ()
365 {
366         LocaleGuard lg; // a single guard for all 3 configs
367         bool found = false;
368
369         std::string rcfile;
370
371         if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile)) {
372                 XMLTree tree;
373                 found = true;
374
375                 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
376
377                 if (!tree.read (rcfile.c_str())) {
378                         error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
379                         return -1;
380                 }
381
382                 if (set_state (*tree.root(), Stateful::loading_state_version)) {
383                         error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
384                         return -1;
385                 }
386         }
387
388         if (find_file (ardour_config_search_path(), ui_config_file_name, rcfile)) {
389                 XMLTree tree;
390                 found = true;
391
392                 info << string_compose (_("Loading user ui configuration file %1"), rcfile) << endmsg;
393
394                 if (!tree.read (rcfile)) {
395                         error << string_compose(_("cannot read ui configuration file \"%1\""), rcfile) << endmsg;
396                         return -1;
397                 }
398
399                 if (set_state (*tree.root(), Stateful::loading_state_version)) {
400                         error << string_compose(_("user ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
401                         return -1;
402                 }
403
404                 _dirty = false;
405         }
406
407         if (!found) {
408                 error << _("could not find any ui configuration file, canvas will look broken.") << endmsg;
409         }
410
411         return 0;
412 }
413
414 int
415 UIConfiguration::save_state()
416 {
417         if (block_save != 0) {
418                 return -1;
419         }
420
421         if (_dirty) {
422                 std::string rcfile = Glib::build_filename (user_config_directory(), ui_config_file_name);
423
424                 XMLTree tree;
425
426                 tree.set_root (&get_state());
427
428                 if (!tree.write (rcfile.c_str())){
429                         error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
430                         return -1;
431                 }
432
433                 _dirty = false;
434         }
435
436         if (aliases_modified || colors_modified || modifiers_modified) {
437
438                 if (store_color_theme ()) {
439                         error << string_compose (_("Color file %1 not saved"), color_file.get()) << endmsg;
440                         return -1;
441                 }
442
443                 aliases_modified = false;
444                 colors_modified = false;
445                 modifiers_modified = false;
446         }
447
448
449         return 0;
450 }
451
452 XMLNode&
453 UIConfiguration::get_state ()
454 {
455         XMLNode* root;
456         LocaleGuard lg;
457
458         root = new XMLNode("Ardour");
459
460         root->add_child_nocopy (get_variables ("UI"));
461         root->add_child_nocopy (get_variables ("Canvas"));
462
463         if (_extra_xml) {
464                 root->add_child_copy (*_extra_xml);
465         }
466
467         return *root;
468 }
469
470 XMLNode&
471 UIConfiguration::get_variables (std::string which_node)
472 {
473         XMLNode* node;
474         LocaleGuard lg;
475
476         node = new XMLNode (which_node);
477
478 #undef  UI_CONFIG_VARIABLE
479 #undef  CANVAS_FONT_VARIABLE
480 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
481 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
482 #include "ui_config_vars.h"
483 #include "canvas_vars.h"
484 #undef  UI_CONFIG_VARIABLE
485 #undef  CANVAS_FONT_VARIABLE
486
487         return *node;
488 }
489
490 int
491 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
492 {
493         LocaleGuard lg;
494         /* this can load a generic UI configuration file or a colors file */
495
496         if (root.name() != "Ardour") {
497                 return -1;
498         }
499
500         Stateful::save_extra_xml (root);
501
502         XMLNodeList nlist = root.children();
503         XMLNodeConstIterator niter;
504         XMLNode *node;
505
506         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
507
508                 node = *niter;
509
510                 if (node->name() == "Canvas" ||  node->name() == "UI") {
511                         set_variables (*node);
512
513                 }
514         }
515
516         XMLNode* colors = find_named_node (root, X_("Colors"));
517
518         if (colors) {
519                 load_colors (*colors);
520         }
521
522         XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
523
524         if (aliases) {
525                 load_color_aliases (*aliases);
526         }
527
528         XMLNode* modifiers = find_named_node (root, X_("Modifiers"));
529
530         if (modifiers) {
531                 load_modifiers (*modifiers);
532         }
533
534         return 0;
535 }
536
537 void
538 UIConfiguration::load_color_aliases (XMLNode const & node)
539 {
540         XMLNodeList const nlist = node.children();
541         XMLNodeConstIterator niter;
542         XMLProperty const *name;
543         XMLProperty const *alias;
544
545         color_aliases.clear ();
546
547         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
548                 XMLNode const * child = *niter;
549                 if (child->name() != X_("ColorAlias")) {
550                         continue;
551                 }
552                 name = child->property (X_("name"));
553                 alias = child->property (X_("alias"));
554
555                 if (name && alias) {
556                         color_aliases.insert (make_pair (name->value(), alias->value()));
557                 }
558         }
559 }
560
561 void
562 UIConfiguration::load_colors (XMLNode const & node)
563 {
564         XMLNodeList const nlist = node.children();
565         XMLNodeConstIterator niter;
566         XMLProperty const *name;
567         XMLProperty const *color;
568
569         colors.clear ();
570
571         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
572                 XMLNode const * child = *niter;
573                 if (child->name() != X_("Color")) {
574                         continue;
575                 }
576                 name = child->property (X_("name"));
577                 color = child->property (X_("value"));
578
579                 if (name && color) {
580                         ArdourCanvas::Color c;
581                         c = strtoul (color->value().c_str(), 0, 16);
582                         colors.insert (make_pair (name->value(), c));
583                 }
584         }
585 }
586
587 void
588 UIConfiguration::load_modifiers (XMLNode const & node)
589 {
590         PBD::LocaleGuard lg;
591         XMLNodeList const nlist = node.children();
592         XMLNodeConstIterator niter;
593         XMLProperty const *name;
594         XMLProperty const *mod;
595
596         modifiers.clear ();
597
598         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
599                 XMLNode const * child = *niter;
600                 if (child->name() != X_("Modifier")) {
601                         continue;
602                 }
603
604                 name = child->property (X_("name"));
605                 mod = child->property (X_("modifier"));
606
607                 if (name && mod) {
608                         SVAModifier svam (mod->value());
609                         modifiers.insert (make_pair (name->value(), svam));
610                 }
611         }
612 }
613
614 void
615 UIConfiguration::set_variables (const XMLNode& node)
616 {
617 #undef  UI_CONFIG_VARIABLE
618 #define UI_CONFIG_VARIABLE(Type,var,name,val) if (var.set_from_node (node)) { ParameterChanged (name); }
619 #define CANVAS_FONT_VARIABLE(var,name)        if (var.set_from_node (node)) { ParameterChanged (name); }
620 #include "ui_config_vars.h"
621 #include "canvas_vars.h"
622 #undef  UI_CONFIG_VARIABLE
623 #undef  CANVAS_FONT_VARIABLE
624 }
625
626 ArdourCanvas::SVAModifier
627 UIConfiguration::modifier (string const & name) const
628 {
629         Modifiers::const_iterator m = modifiers.find (name);
630         if (m != modifiers.end()) {
631                 return m->second;
632         }
633         return SVAModifier ();
634 }
635
636 ArdourCanvas::Color
637 UIConfiguration::color_mod (std::string const & colorname, std::string const & modifiername) const
638 {
639         return HSV (color (colorname)).mod (modifier (modifiername)).color ();
640 }
641
642 ArdourCanvas::Color
643 UIConfiguration::color_mod (const ArdourCanvas::Color& color, std::string const & modifiername) const
644 {
645         return HSV (color).mod (modifier (modifiername)).color ();
646 }
647
648 ArdourCanvas::Color
649 UIConfiguration::color (const std::string& name, bool* failed) const
650 {
651         ColorAliases::const_iterator e = color_aliases.find (name);
652
653         if (failed) {
654                 *failed = false;
655         }
656
657         if (e != color_aliases.end ()) {
658                 Colors::const_iterator rc = colors.find (e->second);
659                 if (rc != colors.end()) {
660                         return rc->second;
661                 }
662         } else {
663                 /* not an alias, try directly */
664                 Colors::const_iterator rc = colors.find (name);
665                 if (rc != colors.end()) {
666                         return rc->second;
667                 }
668         }
669
670         if (!failed) {
671                 /* only show this message if the caller wasn't interested in
672                    the fail status.
673                 */
674                 cerr << string_compose (_("Color %1 not found"), name) << endl;
675         }
676
677         if (failed) {
678                 *failed = true;
679         }
680
681         return rgba_to_color ((g_random_int()%256)/255.0,
682                               (g_random_int()%256)/255.0,
683                               (g_random_int()%256)/255.0,
684                               0xff);
685 }
686
687 Color
688 UIConfiguration::quantized (Color c) const
689 {
690         HSV hsv (c);
691         hsv.h = hue_width * (round (hsv.h/hue_width));
692         return hsv.color ();
693 }
694
695 void
696 UIConfiguration::set_color (string const& name, ArdourCanvas::Color color)
697 {
698         Colors::iterator i = colors.find (name);
699         if (i == colors.end()) {
700                 return;
701         }
702         i->second = color;
703         colors_modified = true;
704
705         ColorsChanged (); /* EMIT SIGNAL */
706 }
707
708 void
709 UIConfiguration::set_alias (string const & name, string const & alias)
710 {
711         ColorAliases::iterator i = color_aliases.find (name);
712         if (i == color_aliases.end()) {
713                 return;
714         }
715
716         i->second = alias;
717         aliases_modified = true;
718
719         ColorsChanged (); /* EMIT SIGNAL */
720 }
721
722 void
723 UIConfiguration::set_modifier (string const & name, SVAModifier svam)
724 {
725         Modifiers::iterator m = modifiers.find (name);
726
727         if (m == modifiers.end()) {
728                 return;
729         }
730
731         m->second = svam;
732         modifiers_modified = true;
733
734         ColorsChanged (); /* EMIT SIGNAL */
735 }
736
737 void
738 UIConfiguration::load_rc_file (bool themechange, bool allow_own)
739 {
740         string basename = ui_rc_file.get();
741         std::string rc_file_path;
742
743         if (!find_file (ardour_config_search_path(), basename, rc_file_path)) {
744                 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
745                                            basename, ardour_config_search_path().to_string(), PROGRAM_NAME)
746                                 << endmsg;
747                 return;
748         }
749
750         info << "Loading ui configuration file " << rc_file_path << endmsg;
751
752         Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);
753 }