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