remove base color concept from UIConfiguration and ThemeManager.
[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         XMLTree tree;
267         std::string colorfile = Glib::build_filename (user_config_directory(), (string ("my-") + color_file.get() + ".colors"));
268         
269         tree.set_root (root);
270
271         if (!tree.write (colorfile.c_str())){
272                 error << string_compose (_("Color file %1 not saved"), colorfile) << endmsg;
273                 return -1;
274         }
275
276         return 0;
277 }
278
279 int
280 UIConfiguration::load_state ()
281 {
282         bool found = false;
283
284         std::string rcfile;
285
286         if (find_file (ardour_config_search_path(), default_ui_config_file_name, rcfile)) {
287                 XMLTree tree;
288                 found = true;
289
290                 info << string_compose (_("Loading default ui configuration file %1"), rcfile) << endmsg;
291
292                 if (!tree.read (rcfile.c_str())) {
293                         error << string_compose(_("cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
294                         return -1;
295                 }
296
297                 if (set_state (*tree.root(), Stateful::loading_state_version)) {
298                         error << string_compose(_("default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
299                         return -1;
300                 }
301         }
302
303         if (find_file (ardour_config_search_path(), ui_config_file_name, rcfile)) {
304                 XMLTree tree;
305                 found = true;
306
307                 info << string_compose (_("Loading user ui configuration file %1"), rcfile) << endmsg;
308
309                 if (!tree.read (rcfile)) {
310                         error << string_compose(_("cannot read ui configuration file \"%1\""), rcfile) << endmsg;
311                         return -1;
312                 }
313
314                 if (set_state (*tree.root(), Stateful::loading_state_version)) {
315                         error << string_compose(_("user ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
316                         return -1;
317                 }
318
319                 _dirty = false;
320         }
321
322         if (!found) {
323                 error << _("could not find any ui configuration file, canvas will look broken.") << endmsg;
324         }
325
326         return 0;
327 }
328
329 int
330 UIConfiguration::save_state()
331 {
332
333         if (_dirty) {
334                 std::string rcfile = Glib::build_filename (user_config_directory(), ui_config_file_name);
335                 
336                 XMLTree tree;
337
338                 tree.set_root (&get_state());
339
340                 if (!tree.write (rcfile.c_str())){
341                         error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
342                         return -1;
343                 }
344
345                 _dirty = false;
346         }
347
348         if (aliases_modified || colors_modified) {
349
350                 if (store_color_theme ()) {
351                         error << string_compose (_("Color file %1 not saved"), color_file.get()) << endmsg;
352                         return -1;
353                 }
354
355                 aliases_modified = false;
356                 colors_modified = false;
357         }
358         
359
360         return 0;
361 }
362
363 XMLNode&
364 UIConfiguration::get_state ()
365 {
366         XMLNode* root;
367         LocaleGuard lg (X_("POSIX"));
368
369         root = new XMLNode("Ardour");
370
371         root->add_child_nocopy (get_variables ("UI"));
372         root->add_child_nocopy (get_variables ("Canvas"));
373
374         if (_extra_xml) {
375                 root->add_child_copy (*_extra_xml);
376         }
377
378         return *root;
379 }
380
381 XMLNode&
382 UIConfiguration::get_variables (std::string which_node)
383 {
384         XMLNode* node;
385         LocaleGuard lg (X_("POSIX"));
386
387         node = new XMLNode (which_node);
388
389 #undef  UI_CONFIG_VARIABLE
390 #undef  CANVAS_FONT_VARIABLE
391 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
392 #define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
393 #include "ui_config_vars.h"
394 #include "canvas_vars.h"
395 #undef  UI_CONFIG_VARIABLE
396 #undef  CANVAS_FONT_VARIABLE
397
398         return *node;
399 }
400
401 int
402 UIConfiguration::set_state (const XMLNode& root, int /*version*/)
403 {
404         /* this can load a generic UI configuration file or a colors file */
405
406         if (root.name() != "Ardour") {
407                 return -1;
408         }
409
410         Stateful::save_extra_xml (root);
411
412         XMLNodeList nlist = root.children();
413         XMLNodeConstIterator niter;
414         XMLNode *node;
415
416         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
417
418                 node = *niter;
419
420                 if (node->name() == "Canvas" ||  node->name() == "UI") {
421                         set_variables (*node);
422
423                 }
424         }
425
426         XMLNode* colors = find_named_node (root, X_("Colors"));
427
428         if (colors) {
429                 load_colors (*colors);
430         }
431
432         XMLNode* aliases = find_named_node (root, X_("ColorAliases"));
433
434         if (aliases) {
435                 load_color_aliases (*aliases);
436         }
437
438         return 0;
439 }
440
441 void
442 UIConfiguration::load_color_aliases (XMLNode const & node)
443 {
444         XMLNodeList const nlist = node.children();
445         XMLNodeConstIterator niter;
446         XMLProperty const *name;
447         XMLProperty const *alias;
448         
449         color_aliases.clear ();
450
451         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
452                 if ((*niter)->name() != X_("ColorAlias")) {
453                         continue;
454                 }
455                 name = (*niter)->property (X_("name"));
456                 alias = (*niter)->property (X_("alias"));
457
458                 if (name && alias) {
459                         color_aliases.insert (make_pair (name->value(), alias->value()));
460                 }
461         }
462 }
463
464 void
465 UIConfiguration::load_colors (XMLNode const & node)
466 {
467         XMLNodeList const nlist = node.children();
468         XMLNodeConstIterator niter;
469         XMLProperty const *name;
470         XMLProperty const *color;
471         
472         colors.clear ();
473
474         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
475                 if ((*niter)->name() != X_("Color")) {
476                         continue;
477                 }
478                 name = (*niter)->property (X_("name"));
479                 color = (*niter)->property (X_("value"));
480
481                 if (name && color) {
482                         ArdourCanvas::Color c;
483                         c = strtol (color->value().c_str(), 0, 16);
484                         colors.insert (make_pair (name->value(), c));
485                 }
486         }
487 }
488
489 void
490 UIConfiguration::set_variables (const XMLNode& node)
491 {
492 #undef  UI_CONFIG_VARIABLE
493 #define UI_CONFIG_VARIABLE(Type,var,name,val) if (var.set_from_node (node)) { ParameterChanged (name); }
494 #define CANVAS_FONT_VARIABLE(var,name)        if (var.set_from_node (node)) { ParameterChanged (name); }
495 #include "ui_config_vars.h"
496 #include "canvas_vars.h"
497 #undef  UI_CONFIG_VARIABLE
498 #undef  CANVAS_FONT_VARIABLE
499 }
500
501 ArdourCanvas::Color
502 UIConfiguration::color (const std::string& name, bool* failed) const
503 {
504         ColorAliases::const_iterator e = color_aliases.find (name);
505
506         if (failed) {
507                 *failed = false;
508         }
509         
510         if (e != color_aliases.end ()) {
511                 Colors::const_iterator rc = colors.find (e->second);
512                 if (rc != colors.end()) {
513                         return rc->second;
514                 }
515         } else {
516                 /* not an alias, try directly */
517                 Colors::const_iterator rc = colors.find (name);
518                 if (rc != colors.end()) {
519                         return rc->second;
520                 }
521         }
522         
523         if (!failed) {
524                 /* only show this message if the caller wasn't interested in
525                    the fail status.
526                 */
527                 cerr << string_compose (_("Color %1 not found"), name) << endl;
528         }
529
530         if (failed) {
531                 *failed = true;
532         }
533         
534         return rgba_to_color ((g_random_int()%256)/255.0,
535                               (g_random_int()%256)/255.0,
536                               (g_random_int()%256)/255.0,
537                               0xff);
538 }
539
540 Color
541 UIConfiguration::quantized (Color c) const
542 {
543         HSV hsv (c);
544         hsv.h = hue_width * (round (hsv.h/hue_width));
545         return hsv.color ();
546 }
547
548 void
549 UIConfiguration::set_color (string const& name, ArdourCanvas::Color color)
550 {
551         Colors::iterator i = colors.find (name);
552         if (i == colors.end()) {
553                 return;
554         }
555         i->second = color;
556         colors_modified = true;
557
558         ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
559 }
560
561 void
562 UIConfiguration::set_alias (string const & name, string const & alias)
563 {
564         ColorAliases::iterator i = color_aliases.find (name);
565         if (i == color_aliases.end()) {
566                 return;
567         }
568
569         i->second = alias;
570         aliases_modified = true;
571
572         ARDOUR_UI_UTILS::ColorsChanged (); /* EMIT SIGNAL */
573 }
574
575 void
576 UIConfiguration::load_rc_file (bool themechange, bool allow_own)
577 {
578         string basename = ui_rc_file.get();
579         std::string rc_file_path;
580
581         if (!find_file (ardour_config_search_path(), basename, rc_file_path)) {
582                 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
583                                            basename, ardour_config_search_path().to_string(), PROGRAM_NAME)
584                                 << endmsg;
585                 return;
586         }
587
588         info << "Loading ui configuration file " << rc_file_path << endmsg;
589
590         Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);
591 }
592
593