Major Theme Manager changes, see ardour-dev
[ardour.git] / gtk2_ardour / audio_time_axis.cc
1 /*
2     Copyright (C) 2000-2006 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 <cstdlib>
21 #include <cmath>
22 #include <cassert>
23
24 #include <algorithm>
25 #include <string>
26 #include <vector>
27
28 #include <sigc++/bind.h>
29
30 #include <pbd/error.h>
31 #include <pbd/stl_delete.h>
32 #include <pbd/memento_command.h>
33
34 #include <gtkmm2ext/gtk_ui.h>
35 #include <gtkmm2ext/selector.h>
36 #include <gtkmm2ext/stop_signal.h>
37 #include <gtkmm2ext/bindable_button.h>
38 #include <gtkmm2ext/utils.h>
39
40 #include <ardour/audioplaylist.h>
41 #include <ardour/audio_diskstream.h>
42 #include <ardour/insert.h>
43 #include <ardour/location.h>
44 #include <ardour/panner.h>
45 #include <ardour/playlist.h>
46 #include <ardour/session.h>
47 #include <ardour/session_playlist.h>
48 #include <ardour/utils.h>
49
50 #include "ardour_ui.h"
51 #include "audio_time_axis.h"
52 #include "automation_gain_line.h"
53 #include "automation_pan_line.h"
54 #include "canvas_impl.h"
55 #include "crossfade_view.h"
56 #include "enums.h"
57 #include "gain_automation_time_axis.h"
58 #include "keyboard.h"
59 #include "pan_automation_time_axis.h"
60 #include "playlist_selector.h"
61 #include "prompter.h"
62 #include "public_editor.h"
63 #include "audio_region_view.h"
64 #include "simplerect.h"
65 #include "audio_streamview.h"
66 #include "utils.h"
67
68 #include <ardour/audio_track.h>
69
70 #include "i18n.h"
71
72 using namespace ARDOUR;
73 using namespace PBD;
74 using namespace Gtk;
75 using namespace Editing;
76
77
78 AudioTimeAxisView::AudioTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
79         : AxisView(sess)
80         , RouteTimeAxisView(ed, sess, rt, canvas)
81 {
82         // Make sure things are sane...
83         assert(!is_track() || is_audio_track());
84
85         subplugin_menu.set_name ("ArdourContextMenu");
86         gain_track = 0;
87         pan_track = 0;
88         waveform_item = 0;
89         pan_automation_item = 0;
90         gain_automation_item = 0;
91
92         _view = new AudioStreamView (*this);
93
94         add_gain_automation_child ();
95         add_pan_automation_child ();
96
97         ignore_toggle = false;
98
99         mute_button->set_active (false);
100         solo_button->set_active (false);
101         
102         if (is_audio_track())
103                 controls_ebox.set_name ("AudioTimeAxisViewControlsBaseUnselected");
104         else // bus
105                 controls_ebox.set_name ("AudioBusControlsBaseUnselected");
106
107         /* map current state of the route */
108
109         redirects_changed (0);
110         reset_redirect_automation_curves ();
111
112         ensure_xml_node ();
113
114         set_state (*xml_node);
115         
116         _route->panner().Changed.connect (mem_fun(*this, &AudioTimeAxisView::update_pans));
117
118         update_control_names ();
119
120         if (is_audio_track()) {
121
122                 /* ask for notifications of any new RegionViews */
123                 _view->RegionViewAdded.connect (mem_fun(*this, &AudioTimeAxisView::region_view_added));
124                 _view->attach ();
125         }
126
127         post_construct ();
128 }
129
130 AudioTimeAxisView::~AudioTimeAxisView ()
131 {
132 }
133
134 AudioStreamView*
135 AudioTimeAxisView::audio_view()
136 {
137         return dynamic_cast<AudioStreamView*>(_view);
138 }
139
140 guint32
141 AudioTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
142 {
143         ensure_xml_node ();
144         xml_node->add_property ("shown_editor", "yes");
145                 
146         return TimeAxisView::show_at (y, nth, parent);
147 }
148
149 void
150 AudioTimeAxisView::hide ()
151 {
152         ensure_xml_node ();
153         xml_node->add_property ("shown_editor", "no");
154
155         TimeAxisView::hide ();
156 }
157
158 void
159 AudioTimeAxisView::set_state (const XMLNode& node)
160 {
161         const XMLProperty *prop;
162         
163         TimeAxisView::set_state (node);
164         
165         if ((prop = node.property ("shown_editor")) != 0) {
166                 if (prop->value() == "no") {
167                         _marked_for_display = false;
168                 } else {
169                         _marked_for_display = true;
170                 }
171         } else {
172                 _marked_for_display = true;
173         }
174         
175         XMLNodeList nlist = node.children();
176         XMLNodeConstIterator niter;
177         XMLNode *child_node;
178         
179         
180         show_gain_automation = false;
181         show_pan_automation  = false;
182         
183         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
184                 child_node = *niter;
185
186                 if (child_node->name() == "gain") {
187                         XMLProperty *prop=child_node->property ("shown");
188                         
189                         if (prop != 0) {
190                                 if (prop->value() == "yes") {
191                                         show_gain_automation = true;
192                                 }
193                         }
194                         continue;
195                 }
196                 
197                 if (child_node->name() == "pan") {
198                         XMLProperty *prop=child_node->property ("shown");
199                         
200                         if (prop != 0) {
201                                 if (prop->value() == "yes") {
202                                         show_pan_automation = true;
203                                 }                       
204                         }
205                         continue;
206                 }
207         }
208 }
209
210 void
211 AudioTimeAxisView::build_automation_action_menu ()
212 {
213         using namespace Menu_Helpers;
214
215         RouteTimeAxisView::build_automation_action_menu ();
216
217         MenuList& automation_items = automation_action_menu->items();
218         
219         automation_items.push_back (SeparatorElem());
220
221         automation_items.push_back (CheckMenuElem (_("Fader"), 
222                                                    mem_fun(*this, &AudioTimeAxisView::toggle_gain_track)));
223         gain_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
224         gain_automation_item->set_active(show_gain_automation);
225
226         automation_items.push_back (CheckMenuElem (_("Pan"),
227                                                    mem_fun(*this, &AudioTimeAxisView::toggle_pan_track)));
228         pan_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
229         pan_automation_item->set_active(show_pan_automation);
230         
231 }
232
233 void
234 AudioTimeAxisView::append_extra_display_menu_items ()
235 {
236         using namespace Menu_Helpers;
237
238         MenuList& items = display_menu->items();
239
240         // crossfade stuff
241         items.push_back (MenuElem (_("Hide all crossfades"), mem_fun(*this, &AudioTimeAxisView::hide_all_xfades)));
242         items.push_back (MenuElem (_("Show all crossfades"), mem_fun(*this, &AudioTimeAxisView::show_all_xfades)));
243
244         // waveform menu
245         Menu *waveform_menu = manage(new Menu);
246         MenuList& waveform_items = waveform_menu->items();
247         waveform_menu->set_name ("ArdourContextMenu");
248         
249         waveform_items.push_back (CheckMenuElem (_("Show waveforms"), mem_fun(*this, &AudioTimeAxisView::toggle_waveforms)));
250         waveform_item = static_cast<CheckMenuItem *> (&waveform_items.back());
251         ignore_toggle = true;
252         waveform_item->set_active (editor.show_waveforms());
253         ignore_toggle = false;
254
255         waveform_items.push_back (SeparatorElem());
256         
257         RadioMenuItem::Group group;
258         
259         waveform_items.push_back (RadioMenuElem (group, _("Traditional"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Traditional)));
260         traditional_item = static_cast<RadioMenuItem *> (&waveform_items.back());
261
262         waveform_items.push_back (RadioMenuElem (group, _("Rectified"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Rectified)));
263         rectified_item = static_cast<RadioMenuItem *> (&waveform_items.back());
264
265         waveform_items.push_back (SeparatorElem());
266         
267         RadioMenuItem::Group group2;
268
269         waveform_items.push_back (RadioMenuElem (group2, _("Linear"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_scale), LinearWaveform)));
270         linearscale_item = static_cast<RadioMenuItem *> (&waveform_items.back());
271
272         waveform_items.push_back (RadioMenuElem (group2, _("Logarithmic"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_scale), LogWaveform)));
273         logscale_item = static_cast<RadioMenuItem *> (&waveform_items.back());
274
275         // setting initial item state
276         AudioStreamView* asv = audio_view();
277         if (asv) {
278                 ignore_toggle = true;
279                 if (asv->get_waveform_shape() == Rectified) 
280                         rectified_item->set_active(true);
281                 else traditional_item->set_active(true);
282
283                 if (asv->get_waveform_scale() == LogWaveform) 
284                         logscale_item->set_active(true);
285                 else linearscale_item->set_active(true);
286                 ignore_toggle = false;
287         }
288
289         items.push_back (MenuElem (_("Waveform"), *waveform_menu));
290
291
292         Menu *layers_menu = manage(new Menu);
293         MenuList &layers_items = layers_menu->items();
294         layers_menu->set_name("ArdourContextMenu");
295
296         RadioMenuItem::Group layers_group;
297         
298         layers_items.push_back(RadioMenuElem (layers_group, _("Overlaid"), bind (mem_fun (*this, &AudioTimeAxisView::set_layer_display), Overlaid)));
299         layers_items.push_back(RadioMenuElem (layers_group, _("Stacked"), bind (mem_fun (*this, &AudioTimeAxisView::set_layer_display), Stacked)));
300
301         items.push_back (MenuElem (_("Layers"), *layers_menu));
302 }
303
304 void
305 AudioTimeAxisView::toggle_waveforms ()
306 {
307         AudioStreamView* asv = audio_view();
308         assert(asv);
309
310         if (asv && waveform_item && !ignore_toggle) {
311                 asv->set_show_waveforms (waveform_item->get_active());
312         }
313 }
314
315 void
316 AudioTimeAxisView::set_show_waveforms (bool yn)
317 {
318         AudioStreamView* asv = audio_view();
319         assert(asv);
320
321         if (waveform_item) {
322                 waveform_item->set_active (yn);
323         } else {
324                 asv->set_show_waveforms (yn);
325         }
326 }
327
328 void
329 AudioTimeAxisView::set_show_waveforms_recording (bool yn)
330 {
331         AudioStreamView* asv = audio_view();
332
333         if (asv) {
334                 asv->set_show_waveforms_recording (yn);
335         }
336 }
337
338 void
339 AudioTimeAxisView::set_waveform_shape (WaveformShape shape)
340 {
341         AudioStreamView* asv = audio_view();
342
343         if (asv && !ignore_toggle) {
344                 asv->set_waveform_shape (shape);
345         }
346
347         map_frozen ();
348 }       
349
350 void
351 AudioTimeAxisView::set_waveform_scale (WaveformScale scale)
352 {
353         AudioStreamView* asv = audio_view();
354
355         if (asv && !ignore_toggle) {
356                 asv->set_waveform_scale (scale);
357         }
358
359         map_frozen ();
360 }       
361
362 void
363 AudioTimeAxisView::add_gain_automation_child ()
364 {
365         XMLProperty* prop;
366         AutomationLine* line;
367
368         gain_track = new GainAutomationTimeAxisView (_session,
369                                                      _route,
370                                                      editor,
371                                                      *this,
372                                                      parent_canvas,
373                                                      _("gain"),
374                                                      _route->gain_automation_curve());
375         
376         line = new AutomationGainLine ("automation gain",
377                                        _session,
378                                        *gain_track,
379                                        *gain_track->canvas_display,
380                                        _route->gain_automation_curve());
381
382         line->set_line_color (Config->canvasvar_AutomationLine.get());
383         
384
385         gain_track->add_line (*line);
386
387         add_child (gain_track);
388
389         gain_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::gain_hidden));
390
391         bool hideit = true;
392         
393         XMLNode* node;
394
395         if ((node = gain_track->get_state_node()) != 0) {
396                 if  ((prop = node->property ("shown")) != 0) {
397                         if (prop->value() == "yes") {
398                                 hideit = false;
399                         }
400                 } 
401         }
402
403         if (hideit) {
404                 gain_track->hide ();
405         }
406 }
407
408 void
409 AudioTimeAxisView::add_pan_automation_child ()
410 {
411         XMLProperty* prop;
412
413         pan_track = new PanAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, _("pan"));
414
415         update_pans ();
416         
417         add_child (pan_track);
418
419         pan_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::pan_hidden));
420
421         ensure_xml_node ();
422         bool hideit = true;
423         
424         XMLNode* node;
425
426         if ((node = pan_track->get_state_node()) != 0) {
427                 if ((prop = node->property ("shown")) != 0) {
428                         if (prop->value() == "yes") {
429                                 hideit = false;
430                         }
431                 } 
432         }
433
434         if (hideit) {
435                 pan_track->hide ();
436         }
437 }
438
439 void
440 AudioTimeAxisView::update_pans ()
441 {
442         Panner::iterator p;
443         
444         pan_track->clear_lines ();
445         
446         /* we don't draw lines for "greater than stereo" panning.
447          */
448
449         if (_route->n_outputs().n_audio() > 2) {
450                 return;
451         }
452
453         for (p = _route->panner().begin(); p != _route->panner().end(); ++p) {
454
455                 AutomationLine* line;
456
457                 line = new AutomationPanLine ("automation pan", _session, *pan_track,
458                                               *pan_track->canvas_display, 
459                                               (*p)->automation());
460
461                 if (p == _route->panner().begin()) {
462                         /* first line is a nice orange */
463                         line->set_line_color (Config->canvasvar_AutomationLine.get());
464                 } else {
465                         /* second line is a nice blue */
466                         line->set_line_color (Config->canvasvar_AutomationLine.get());
467                 }
468
469                 pan_track->add_line (*line);
470         }
471 }
472                 
473 void
474 AudioTimeAxisView::toggle_gain_track ()
475 {
476
477         bool showit = gain_automation_item->get_active();
478
479         if (showit != gain_track->marked_for_display()) {
480                 if (showit) {
481                         gain_track->set_marked_for_display (true);
482                         gain_track->canvas_display->show();
483                         gain_track->get_state_node()->add_property ("shown", X_("yes"));
484                 } else {
485                         gain_track->set_marked_for_display (false);
486                         gain_track->hide ();
487                         gain_track->get_state_node()->add_property ("shown", X_("no"));
488                 }
489
490                 /* now trigger a redisplay */
491                 
492                 if (!no_redraw) {
493                          _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
494                 }
495         }
496 }
497
498 void
499 AudioTimeAxisView::gain_hidden ()
500 {
501         gain_track->get_state_node()->add_property (X_("shown"), X_("no"));
502
503         if (gain_automation_item && !_hidden) {
504                 gain_automation_item->set_active (false);
505         }
506
507          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
508 }
509
510 void
511 AudioTimeAxisView::toggle_pan_track ()
512 {
513         bool showit = pan_automation_item->get_active();
514
515         if (showit != pan_track->marked_for_display()) {
516                 if (showit) {
517                         pan_track->set_marked_for_display (true);
518                         pan_track->canvas_display->show();
519                         pan_track->get_state_node()->add_property ("shown", X_("yes"));
520                 } else {
521                         pan_track->set_marked_for_display (false);
522                         pan_track->hide ();
523                         pan_track->get_state_node()->add_property ("shown", X_("no"));
524                 }
525
526                 /* now trigger a redisplay */
527                 
528                 if (!no_redraw) {
529                          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
530                 }
531         }
532 }
533
534 void
535 AudioTimeAxisView::pan_hidden ()
536 {
537         pan_track->get_state_node()->add_property ("shown", "no");
538
539         if (pan_automation_item && !_hidden) {
540                 pan_automation_item->set_active (false);
541         }
542
543          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
544 }
545
546 void
547 AudioTimeAxisView::show_all_automation ()
548 {
549         no_redraw = true;
550
551         pan_automation_item->set_active (true);
552         gain_automation_item->set_active (true);
553         
554         RouteTimeAxisView::show_all_automation ();
555
556         no_redraw = false;
557
558          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
559 }
560
561 void
562 AudioTimeAxisView::show_existing_automation ()
563 {
564         no_redraw = true;
565
566         pan_automation_item->set_active (true);
567         gain_automation_item->set_active (true);
568
569         RouteTimeAxisView::show_existing_automation ();
570
571         no_redraw = false;
572
573          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
574 }
575
576 void
577 AudioTimeAxisView::hide_all_automation ()
578 {
579         no_redraw = true;
580
581         pan_automation_item->set_active (false);
582         gain_automation_item->set_active (false);
583
584         RouteTimeAxisView::hide_all_automation();
585
586         no_redraw = false;
587          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
588 }
589
590 void
591 AudioTimeAxisView::show_all_xfades ()
592 {
593         AudioStreamView* asv = audio_view();
594
595         if (asv) {
596                 asv->show_all_xfades ();
597         }
598 }
599
600 void
601 AudioTimeAxisView::hide_all_xfades ()
602 {
603         AudioStreamView* asv = audio_view();
604         
605         if (asv) {
606                 asv->hide_all_xfades ();
607         }
608 }
609
610 void
611 AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi)
612 {
613         AudioStreamView* asv = audio_view();
614         AudioRegionView* rv;
615
616         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
617                 asv->hide_xfades_involving (*rv);
618         }
619 }
620
621 void
622 AudioTimeAxisView::reveal_dependent_views (TimeAxisViewItem& tavi)
623 {
624         AudioStreamView* asv = audio_view();
625         AudioRegionView* rv;
626
627         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
628                 asv->reveal_xfades_involving (*rv);
629         }
630 }
631
632 void
633 AudioTimeAxisView::route_active_changed ()
634 {
635         RouteTimeAxisView::route_active_changed ();
636         update_control_names ();
637 }
638
639
640 /**
641  *    Set up the names of the controls so that they are coloured
642  *    correctly depending on whether this route is inactive or
643  *    selected.
644  */
645
646 void
647 AudioTimeAxisView::update_control_names ()
648 {
649         if (is_audio_track()) {
650                 if (_route->active()) {
651                         controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
652                         controls_base_selected_name = "AudioTrackControlsBaseSelected";
653                         controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
654                 } else {
655                         controls_ebox.set_name ("AudioTrackControlsBaseInactiveUnselected");
656                         controls_base_selected_name = "AudioTrackControlsBaseInactiveSelected";
657                         controls_base_unselected_name = "AudioTrackControlsBaseInactiveUnselected";
658                 }
659         } else {
660                 if (_route->active()) {
661                         controls_ebox.set_name ("BusControlsBaseUnselected");
662                         controls_base_selected_name = "BusControlsBaseSelected";
663                         controls_base_unselected_name = "BusControlsBaseUnselected";
664                 } else {
665                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
666                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
667                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
668                 }
669         }
670 }
671
672 XMLNode* 
673 AudioTimeAxisView::get_child_xml_node (const string & childname)
674 {
675         return RouteUI::get_child_xml_node (childname);
676 }
677
678 void
679 AudioTimeAxisView::set_layer_display (LayerDisplay d)
680 {
681         AudioStreamView* asv = audio_view ();
682         if (asv) {
683                 asv->set_layer_display (d);
684         }
685 }