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