add mixbus profile which removes the tearoffs
[ardour.git] / gtk2_ardour / theme_manager.cc
1 /*
2     Copyright (C) 2000-2007 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 <cmath>
21 #include <iostream>
22 #include <fstream>
23 #include <errno.h>
24
25 #include "fix_carbon.h"
26
27 #include <gtkmm/stock.h>
28 #include <gtkmm/settings.h>
29
30 #include "gtkmm2ext/gtk_ui.h"
31 #include "gtkmm2ext/cell_renderer_color_selector.h"
32 #include "gtkmm2ext/utils.h"
33
34 #include "pbd/file_utils.h"
35 #include "pbd/compose.h"
36
37 #include "ardour/filesystem_paths.h"
38
39 #include "canvas/wave_view.h"
40
41 #include "ardour_button.h"
42 #include "theme_manager.h"
43 #include "rgb_macros.h"
44 #include "ardour_ui.h"
45 #include "global_signals.h"
46 #include "utils.h"
47
48 #include "i18n.h"
49
50 using namespace std;
51 using namespace Gtk;
52 using namespace PBD;
53 using namespace ARDOUR;
54 using namespace ARDOUR_UI_UTILS;
55
56 namespace ARDOUR_UI_UTILS {
57         sigc::signal<void> ColorsChanged;
58         sigc::signal<void,uint32_t> ColorChanged;
59 }
60
61 ThemeManager::ThemeManager()
62         : ArdourWindow (_("Theme Manager"))
63         , dark_button (_("Dark Theme"))
64         , light_button (_("Light Theme"))
65         , reset_button (_("Restore Defaults"))
66         , flat_buttons (_("Draw \"flat\" buttons"))
67         , region_color_button (_("Color regions using their track's color"))
68         , show_clipping_button (_("Show waveform clipping"))
69         , waveform_gradient_depth (0, 1.0, 0.05)
70         , waveform_gradient_depth_label (_("Waveforms color gradient depth"))
71         , timeline_item_gradient_depth (0, 1.0, 0.05)
72         , timeline_item_gradient_depth_label (_("Timeline item gradient depth"))
73         , all_dialogs (_("All floating windows are dialogs"))
74         , icon_set_label (_("Icon Set"))
75 {
76         set_title (_("Theme Manager"));
77
78         color_list = TreeStore::create (columns);
79         color_display.set_model (color_list);
80         color_display.append_column (_("Object"), columns.name);
81
82         Gtkmm2ext::CellRendererColorSelector* color_renderer = manage (new Gtkmm2ext::CellRendererColorSelector);
83         TreeViewColumn* color_column = manage (new TreeViewColumn (_("Color"), *color_renderer));
84         color_column->add_attribute (color_renderer->property_color(), columns.gdkcolor);
85
86         color_display.append_column (*color_column);
87
88         color_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
89         color_display.get_column (0)->set_expand (true);
90         color_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
91         color_display.get_column (1)->set_expand (false);
92         color_display.set_reorderable (false);
93         color_display.get_selection()->set_mode (SELECTION_NONE);
94         color_display.set_headers_visible (true);
95
96         scroller.add (color_display);
97         scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
98
99         RadioButton::Group group = dark_button.get_group();
100         light_button.set_group(group);
101         theme_selection_hbox.set_homogeneous(false);
102         theme_selection_hbox.pack_start (dark_button);
103         theme_selection_hbox.pack_start (light_button);
104
105         Gtk::VBox* vbox = Gtk::manage (new Gtk::VBox ());
106         vbox->set_homogeneous (false);
107         vbox->pack_start (theme_selection_hbox, PACK_SHRINK);
108         vbox->pack_start (reset_button, PACK_SHRINK);
109 #ifndef __APPLE__
110         vbox->pack_start (all_dialogs, PACK_SHRINK);
111 #endif
112         vbox->pack_start (flat_buttons, PACK_SHRINK);
113         vbox->pack_start (region_color_button, PACK_SHRINK);
114         vbox->pack_start (show_clipping_button, PACK_SHRINK);
115
116         Gtk::HBox* hbox;
117
118         vector<string> icon_sets = ::get_icon_sets ();
119
120         if (icon_sets.size() > 1) {
121                 Gtkmm2ext::set_popdown_strings (icon_set_dropdown, icon_sets);
122                 icon_set_dropdown.set_active_text (ARDOUR_UI::config()->get_icon_set());
123
124                 hbox = Gtk::manage (new Gtk::HBox());
125                 hbox->set_spacing (6);
126                 hbox->pack_start (icon_set_label, false, false);
127                 hbox->pack_start (icon_set_dropdown, true, true);
128                 vbox->pack_start (*hbox, PACK_SHRINK);
129         }
130
131         
132         hbox = Gtk::manage (new Gtk::HBox());
133         hbox->set_spacing (6);
134         hbox->pack_start (waveform_gradient_depth, true, true);
135         hbox->pack_start (waveform_gradient_depth_label, false, false);
136         vbox->pack_start (*hbox, PACK_SHRINK);
137
138         hbox = Gtk::manage (new Gtk::HBox());
139         hbox->set_spacing (6);
140         hbox->pack_start (timeline_item_gradient_depth, true, true);
141         hbox->pack_start (timeline_item_gradient_depth_label, false, false);
142         vbox->pack_start (*hbox, PACK_SHRINK);
143
144         vbox->pack_start (scroller);
145
146         vbox->show_all ();
147
148         add (*vbox);
149
150         waveform_gradient_depth.set_update_policy (Gtk::UPDATE_DELAYED);
151         timeline_item_gradient_depth.set_update_policy (Gtk::UPDATE_DELAYED);
152         
153         color_display.signal_button_press_event().connect (sigc::mem_fun (*this, &ThemeManager::button_press_event), false);
154
155         color_dialog.get_colorsel()->set_has_opacity_control (true);
156         color_dialog.get_colorsel()->set_has_palette (true);
157
158         flat_buttons.set_active (ARDOUR_UI::config()->get_flat_buttons());
159         region_color_button.set_active (ARDOUR_UI::config()->get_color_regions_using_track_color());
160         show_clipping_button.set_active (ARDOUR_UI::config()->get_show_waveform_clipping());
161
162         color_dialog.get_ok_button()->signal_clicked().connect (sigc::bind (sigc::mem_fun (color_dialog, &Gtk::Dialog::response), RESPONSE_ACCEPT));
163         color_dialog.get_cancel_button()->signal_clicked().connect (sigc::bind (sigc::mem_fun (color_dialog, &Gtk::Dialog::response), RESPONSE_CANCEL));
164         dark_button.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_dark_theme_button_toggled));
165         light_button.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_light_theme_button_toggled));
166         reset_button.signal_clicked().connect (sigc::mem_fun (*this, &ThemeManager::reset_canvas_colors));
167         flat_buttons.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_flat_buttons_toggled));
168         region_color_button.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_region_color_toggled));
169         show_clipping_button.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_show_clip_toggled));
170         waveform_gradient_depth.signal_value_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_waveform_gradient_depth_change));
171         timeline_item_gradient_depth.signal_value_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_timeline_item_gradient_depth_change));
172         all_dialogs.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_all_dialogs_toggled));
173         icon_set_dropdown.signal_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_icon_set_changed));
174
175         Gtkmm2ext::UI::instance()->set_tip (all_dialogs, 
176                                             string_compose (_("Mark all floating windows to be type \"Dialog\" rather than using \"Utility\" for some.\n"
177                                                               "This may help with some window managers. This requires a restart of %1 to take effect"),
178                                                             PROGRAM_NAME));
179
180         set_size_request (-1, 400);
181         setup_theme ();
182 }
183
184 ThemeManager::~ThemeManager()
185 {
186 }
187
188 int
189 ThemeManager::save (string /*path*/)
190 {
191         return 0;
192 }
193
194 bool
195 ThemeManager::button_press_event (GdkEventButton* ev)
196 {
197         TreeIter iter;
198         TreeModel::Path path;
199         TreeViewColumn* column;
200         int cellx;
201         int celly;
202
203         ColorVariable<uint32_t> *ccvar;
204
205         if (!color_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
206                 return false;
207         }
208
209         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
210         case 0:
211                 /* allow normal processing to occur */
212                 return false;
213
214         case 1: /* color */
215                 if ((iter = color_list->get_iter (path))) {
216
217                         ColorVariable<uint32_t>* var = (*iter)[columns.pVar];
218                         if (!var) {
219                                 /* parent row, do nothing */
220                                 return false;
221                         }
222
223                         int r,g, b, a;
224                         uint32_t rgba = (*iter)[columns.rgba];
225                         Gdk::Color color;
226
227                         UINT_TO_RGBA (rgba, &r, &g, &b, &a);
228                         color.set_rgb_p (r / 255.0, g / 255.0, b / 255.0);
229                         color_dialog.get_colorsel()->set_previous_color (color);
230                         color_dialog.get_colorsel()->set_current_color (color);
231                         color_dialog.get_colorsel()->set_previous_alpha (a * 256);
232                         color_dialog.get_colorsel()->set_current_alpha (a * 256);
233
234                         ResponseType result = (ResponseType) color_dialog.run();
235
236                         switch (result) {
237                         case RESPONSE_CANCEL:
238                                 break;
239                         case RESPONSE_ACCEPT:
240                                 color = color_dialog.get_colorsel()->get_current_color();
241                                 a = color_dialog.get_colorsel()->get_current_alpha();
242                                 r = (int) floor (color.get_red_p() * 255.0);
243                                 g = (int) floor (color.get_green_p() * 255.0);
244                                 b = (int) floor (color.get_blue_p() * 255.0);
245
246                                 rgba = RGBA_TO_UINT(r,g,b,a>>8);
247                                 (*iter)[columns.rgba] = rgba;
248                                 (*iter)[columns.gdkcolor] = color;
249
250                                 ccvar = (*iter)[columns.pVar];
251                                 ccvar->set(rgba);
252                                 /* mark dirty ... */
253                                 ARDOUR_UI::config()->set_dirty ();
254                                 /* but save it immediately */
255                                 ARDOUR_UI::config()->save_state ();
256
257                                 ColorsChanged(); //EMIT SIGNAL
258                                 break;
259
260                         default:
261                                 break;
262
263                         }
264
265                         color_dialog.hide ();
266                 }
267                 return true;
268
269         default:
270                 break;
271         }
272
273         return false;
274 }
275
276 void
277 load_rc_file (const string& filename, bool themechange)
278 {
279         std::string rc_file_path;
280
281         if (!find_file (ardour_config_search_path(), filename, rc_file_path)) {
282                 warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
283                                            filename, ardour_config_search_path().to_string(), PROGRAM_NAME)
284                                 << endmsg;
285                 return;
286         }
287
288         info << "Loading ui configuration file " << rc_file_path << endmsg;
289
290         Gtkmm2ext::UI::instance()->load_rcfile (rc_file_path, themechange);
291 }
292
293 /* hmm, this is a problem. the profile doesn't
294    exist when the theme manager is constructed
295    and toggles buttons during "normal" GTK setup.
296
297    a better solution will be to make all Profile
298    methods static or something.
299
300    XXX FIX ME
301 */
302
303 #define HACK_PROFILE_IS_SAE() (getenv("ARDOUR_SAE")!=0)
304
305 void
306 ThemeManager::on_flat_buttons_toggled ()
307 {
308         ARDOUR_UI::config()->set_flat_buttons (flat_buttons.get_active());
309         ARDOUR_UI::config()->set_dirty ();
310         ArdourButton::set_flat_buttons (flat_buttons.get_active());
311         /* force a redraw */
312         gtk_rc_reset_styles (gtk_settings_get_default());
313 }
314
315 void
316 ThemeManager::on_region_color_toggled ()
317 {
318         ARDOUR_UI::config()->set_color_regions_using_track_color (region_color_button.get_active());
319         ARDOUR_UI::config()->set_dirty ();
320 }
321
322 void
323 ThemeManager::on_show_clip_toggled ()
324 {
325         ARDOUR_UI::config()->set_show_waveform_clipping (show_clipping_button.get_active());
326         ARDOUR_UI::config()->set_dirty ();
327 }
328
329 void
330 ThemeManager::on_all_dialogs_toggled ()
331 {
332         ARDOUR_UI::config()->set_all_floating_windows_are_dialogs (all_dialogs.get_active());
333         ARDOUR_UI::config()->set_dirty ();
334 }
335
336 void
337 ThemeManager::on_waveform_gradient_depth_change ()
338 {
339         double v = waveform_gradient_depth.get_value();
340
341         ARDOUR_UI::config()->set_waveform_gradient_depth (v);
342         ARDOUR_UI::config()->set_dirty ();
343         ArdourCanvas::WaveView::set_global_gradient_depth (v);
344 }
345
346 void
347 ThemeManager::on_timeline_item_gradient_depth_change ()
348 {
349         double v = timeline_item_gradient_depth.get_value();
350
351         ARDOUR_UI::config()->set_timeline_item_gradient_depth (v);
352         ARDOUR_UI::config()->set_dirty ();
353 }
354
355 void
356 ThemeManager::on_icon_set_changed ()
357 {
358         string new_set = icon_set_dropdown.get_active_text();
359         ARDOUR_UI::config()->set_icon_set (new_set);
360 }
361
362 void
363 ThemeManager::on_dark_theme_button_toggled()
364 {
365         if (!dark_button.get_active()) return;
366
367         if (HACK_PROFILE_IS_SAE()){
368                 ARDOUR_UI::config()->set_ui_rc_file("ardour3_ui_dark_sae.rc");
369         } else {
370                 ARDOUR_UI::config()->set_ui_rc_file("ardour3_ui_dark.rc");
371         }
372         ARDOUR_UI::config()->set_dirty ();
373
374         load_rc_file (ARDOUR_UI::config()->get_ui_rc_file(), true);
375 }
376
377 void
378 ThemeManager::on_light_theme_button_toggled()
379 {
380         if (!light_button.get_active()) return;
381
382         if (HACK_PROFILE_IS_SAE()){
383                 ARDOUR_UI::config()->set_ui_rc_file("ardour3_ui_light_sae.rc");
384         } else {
385                 ARDOUR_UI::config()->set_ui_rc_file("ardour3_ui_light.rc");
386         }
387
388         load_rc_file (ARDOUR_UI::config()->get_ui_rc_file(), true);
389 }
390
391 void
392 ThemeManager::setup_theme ()
393 {
394         int r, g, b, a;
395
396         color_list->clear();
397
398         for (std::map<std::string,ColorVariable<uint32_t> *>::iterator i = ARDOUR_UI::config()->canvas_colors.begin(); i != ARDOUR_UI::config()->canvas_colors.end(); i++) {
399
400
401                 ColorVariable<uint32_t>* var = i->second;
402
403                 TreeModel::Children rows = color_list->children();
404                 TreeModel::Row row;
405                 string::size_type colon;
406
407                 if ((colon = var->name().find (':')) != string::npos) {
408
409                         /* this is supposed to be a child node, so find the
410                          * parent 
411                          */
412
413                         string parent = var->name().substr (0, colon);
414                         TreeModel::iterator ri;
415
416                         for (ri = rows.begin(); ri != rows.end(); ++ri) {
417                                 string s = (*ri)[columns.name];
418                                 if (s == parent) {
419                                         break;
420                                 }
421                         }
422
423                         if (ri == rows.end()) {
424                                 /* not found, add the parent as new top level row */
425                                 row = *(color_list->append());
426                                 row[columns.name] = parent;
427                                 row[columns.pVar] = 0;
428                                 
429                                 /* now add the child as a child of this one */
430
431                                 row = *(color_list->insert (row->children().end()));
432                                 row[columns.name] = var->name().substr (colon+1);
433                         } else {
434                                 row = *(color_list->insert ((*ri)->children().end()));
435                                 row[columns.name] = var->name().substr (colon+1);
436                         }
437
438                 } else {
439                         /* add as a child */
440                         row = *(color_list->append());
441                         row[columns.name] = var->name();
442                 }
443
444                 Gdk::Color col;
445                 uint32_t rgba = var->get();
446                 UINT_TO_RGBA (rgba, &r, &g, &b, &a);
447                 //cerr << (*i)->name() << " == " << hex << rgba << ": " << hex << r << " " << hex << g << " " << hex << b << endl;
448                 col.set_rgb_p (r / 255.0, g / 255.0, b / 255.0);
449
450                 row[columns.pVar] = var;
451                 row[columns.rgba] = rgba;
452                 row[columns.gdkcolor] = col;
453         }
454
455         ColorsChanged.emit();
456
457         bool env_defined = false;
458         string rcfile = Glib::getenv("ARDOUR3_UI_RC", env_defined);
459
460         if(!env_defined) {
461                 rcfile = ARDOUR_UI::config()->get_ui_rc_file();
462         }
463
464         if (rcfile == "ardour3_ui_dark.rc" || rcfile == "ardour3_ui_dark_sae.rc") {
465                 dark_button.set_active();
466         } else if (rcfile == "ardour3_ui_light.rc" || rcfile == "ardour3_ui_light_sae.rc") {
467                 light_button.set_active();
468         }
469         
470         flat_buttons.set_active (ARDOUR_UI::config()->get_flat_buttons());
471         waveform_gradient_depth.set_value (ARDOUR_UI::config()->get_waveform_gradient_depth());
472         timeline_item_gradient_depth.set_value (ARDOUR_UI::config()->get_timeline_item_gradient_depth());
473         all_dialogs.set_active (ARDOUR_UI::config()->get_all_floating_windows_are_dialogs());
474         
475         load_rc_file(rcfile, false);
476 }
477
478 void
479 ThemeManager::reset_canvas_colors()
480 {
481         ARDOUR_UI::config()->load_defaults();
482         setup_theme ();
483         /* mark dirty ... */
484         ARDOUR_UI::config()->set_dirty ();
485         /* but save it immediately */
486         ARDOUR_UI::config()->save_state ();
487 }
488