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