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