missing template dir patch from tim, fix extend selection to track when track is...
[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     $Id$
19 */
20
21 #include <cstdlib>
22 #include <cmath>
23 #include <cassert>
24
25 #include <algorithm>
26 #include <string>
27 #include <vector>
28
29 #include <sigc++/bind.h>
30
31 #include <pbd/error.h>
32 #include <pbd/stl_delete.h>
33 #include <pbd/memento_command.h>
34
35 #include <gtkmm2ext/gtk_ui.h>
36 #include <gtkmm2ext/selector.h>
37 #include <gtkmm2ext/stop_signal.h>
38 #include <gtkmm2ext/bindable_button.h>
39 #include <gtkmm2ext/utils.h>
40
41 #include <ardour/audioplaylist.h>
42 #include <ardour/audio_diskstream.h>
43 #include <ardour/insert.h>
44 #include <ardour/location.h>
45 #include <ardour/panner.h>
46 #include <ardour/playlist.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         if (is_audio_track()) {
112
113                 controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
114                 controls_base_selected_name = "AudioTrackControlsBaseSelected";
115                 controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
116
117                 /* ask for notifications of any new RegionViews */
118                 _view->RegionViewAdded.connect (mem_fun(*this, &AudioTimeAxisView::region_view_added));
119                 _view->attach ();
120
121         } else { /* bus */
122
123                 controls_ebox.set_name ("AudioBusControlsBaseUnselected");
124                 controls_base_selected_name = "AudioBusControlsBaseSelected";
125                 controls_base_unselected_name = "AudioBusControlsBaseUnselected";
126         }
127
128         post_construct ();
129 }
130
131 AudioTimeAxisView::~AudioTimeAxisView ()
132 {
133 }
134
135 AudioStreamView*
136 AudioTimeAxisView::audio_view()
137 {
138         return dynamic_cast<AudioStreamView*>(_view);
139 }
140
141 guint32
142 AudioTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
143 {
144         ensure_xml_node ();
145         xml_node->add_property ("shown_editor", "yes");
146                 
147         return TimeAxisView::show_at (y, nth, parent);
148 }
149
150 void
151 AudioTimeAxisView::hide ()
152 {
153         ensure_xml_node ();
154         xml_node->add_property ("shown_editor", "no");
155
156         TimeAxisView::hide ();
157 }
158
159 void
160 AudioTimeAxisView::set_state (const XMLNode& node)
161 {
162         const XMLProperty *prop;
163         
164         TimeAxisView::set_state (node);
165         
166         if ((prop = node.property ("shown_editor")) != 0) {
167                 if (prop->value() == "no") {
168                         _marked_for_display = false;
169                 } else {
170                         _marked_for_display = true;
171                 }
172         } else {
173                 _marked_for_display = true;
174         }
175         
176         XMLNodeList nlist = node.children();
177         XMLNodeConstIterator niter;
178         XMLNode *child_node;
179         
180         
181         show_gain_automation = false;
182         show_pan_automation  = false;
183         
184         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
185                 child_node = *niter;
186
187                 if (child_node->name() == "gain") {
188                         XMLProperty *prop=child_node->property ("shown");
189                         
190                         if (prop != 0) {
191                                 if (prop->value() == "yes") {
192                                         show_gain_automation = true;
193                                 }
194                         }
195                         continue;
196                 }
197                 
198                 if (child_node->name() == "pan") {
199                         XMLProperty *prop=child_node->property ("shown");
200                         
201                         if (prop != 0) {
202                                 if (prop->value() == "yes") {
203                                         show_pan_automation = true;
204                                 }                       
205                         }
206                         continue;
207                 }
208         }
209 }
210
211 void
212 AudioTimeAxisView::build_automation_action_menu ()
213 {
214         using namespace Menu_Helpers;
215
216         RouteTimeAxisView::build_automation_action_menu ();
217
218         MenuList& automation_items = automation_action_menu->items();
219         
220         automation_items.push_back (SeparatorElem());
221
222         automation_items.push_back (CheckMenuElem (_("Fader"), 
223                                                    mem_fun(*this, &AudioTimeAxisView::toggle_gain_track)));
224         gain_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
225         gain_automation_item->set_active(show_gain_automation);
226
227         automation_items.push_back (CheckMenuElem (_("Pan"),
228                                                    mem_fun(*this, &AudioTimeAxisView::toggle_pan_track)));
229         pan_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
230         pan_automation_item->set_active(show_pan_automation);
231         
232 }
233
234 void
235 AudioTimeAxisView::append_extra_display_menu_items ()
236 {
237         using namespace Menu_Helpers;
238
239         MenuList& items = display_menu->items();
240
241         // crossfade stuff
242         items.push_back (MenuElem (_("Hide all crossfades"), mem_fun(*this, &AudioTimeAxisView::hide_all_xfades)));
243         items.push_back (MenuElem (_("Show all crossfades"), mem_fun(*this, &AudioTimeAxisView::show_all_xfades)));
244
245         // waveform menu
246         Menu *waveform_menu = manage(new Menu);
247         MenuList& waveform_items = waveform_menu->items();
248         waveform_menu->set_name ("ArdourContextMenu");
249         
250         waveform_items.push_back (CheckMenuElem (_("Show waveforms"), mem_fun(*this, &AudioTimeAxisView::toggle_waveforms)));
251         waveform_item = static_cast<CheckMenuItem *> (&waveform_items.back());
252         ignore_toggle = true;
253         waveform_item->set_active (editor.show_waveforms());
254         ignore_toggle = false;
255
256         waveform_items.push_back (SeparatorElem());
257         
258         RadioMenuItem::Group group;
259         
260         waveform_items.push_back (RadioMenuElem (group, _("Traditional"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Traditional)));
261         traditional_item = static_cast<RadioMenuItem *> (&waveform_items.back());
262
263         waveform_items.push_back (RadioMenuElem (group, _("Rectified"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Rectified)));
264         rectified_item = static_cast<RadioMenuItem *> (&waveform_items.back());
265
266         waveform_items.push_back (SeparatorElem());
267         
268         RadioMenuItem::Group group2;
269
270         waveform_items.push_back (RadioMenuElem (group2, _("Linear"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_scale), LinearWaveform)));
271         linearscale_item = static_cast<RadioMenuItem *> (&waveform_items.back());
272
273         waveform_items.push_back (RadioMenuElem (group2, _("Logarithmic"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_scale), LogWaveform)));
274         logscale_item = static_cast<RadioMenuItem *> (&waveform_items.back());
275
276         // setting initial item state
277         AudioStreamView* asv = audio_view();
278         if (asv) {
279                 ignore_toggle = true;
280                 if (asv->get_waveform_shape() == Rectified) 
281                         rectified_item->set_active(true);
282                 else traditional_item->set_active(true);
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 (color_map[cAutomationLine]);
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                         /* first line is a nice orange */
452                         line->set_line_color (color_map[cLeftPanAutomationLine]);
453                 } else {
454                         /* second line is a nice blue */
455                         line->set_line_color (color_map[cRightPanAutomationLine]);
456                 }
457
458                 pan_track->add_line (*line);
459         }
460 }
461                 
462 void
463 AudioTimeAxisView::toggle_gain_track ()
464 {
465
466         bool showit = gain_automation_item->get_active();
467
468         if (showit != gain_track->marked_for_display()) {
469                 if (showit) {
470                         gain_track->set_marked_for_display (true);
471                         gain_track->canvas_display->show();
472                         gain_track->get_state_node()->add_property ("shown", X_("yes"));
473                 } else {
474                         gain_track->set_marked_for_display (false);
475                         gain_track->hide ();
476                         gain_track->get_state_node()->add_property ("shown", X_("no"));
477                 }
478
479                 /* now trigger a redisplay */
480                 
481                 if (!no_redraw) {
482                          _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
483                 }
484         }
485 }
486
487 void
488 AudioTimeAxisView::gain_hidden ()
489 {
490         gain_track->get_state_node()->add_property (X_("shown"), X_("no"));
491
492         if (gain_automation_item && !_hidden) {
493                 gain_automation_item->set_active (false);
494         }
495
496          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
497 }
498
499 void
500 AudioTimeAxisView::toggle_pan_track ()
501 {
502         bool showit = pan_automation_item->get_active();
503
504         if (showit != pan_track->marked_for_display()) {
505                 if (showit) {
506                         pan_track->set_marked_for_display (true);
507                         pan_track->canvas_display->show();
508                         pan_track->get_state_node()->add_property ("shown", X_("yes"));
509                 } else {
510                         pan_track->set_marked_for_display (false);
511                         pan_track->hide ();
512                         pan_track->get_state_node()->add_property ("shown", X_("no"));
513                 }
514
515                 /* now trigger a redisplay */
516                 
517                 if (!no_redraw) {
518                          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
519                 }
520         }
521 }
522
523 void
524 AudioTimeAxisView::pan_hidden ()
525 {
526         pan_track->get_state_node()->add_property ("shown", "no");
527
528         if (pan_automation_item && !_hidden) {
529                 pan_automation_item->set_active (false);
530         }
531
532          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
533 }
534
535 void
536 AudioTimeAxisView::show_all_automation ()
537 {
538         no_redraw = true;
539
540         pan_automation_item->set_active (true);
541         gain_automation_item->set_active (true);
542         
543         RouteTimeAxisView::show_all_automation ();
544
545         no_redraw = false;
546
547          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
548 }
549
550 void
551 AudioTimeAxisView::show_existing_automation ()
552 {
553         no_redraw = true;
554
555         pan_automation_item->set_active (true);
556         gain_automation_item->set_active (true);
557
558         RouteTimeAxisView::show_existing_automation ();
559
560         no_redraw = false;
561
562          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
563 }
564
565 void
566 AudioTimeAxisView::hide_all_automation ()
567 {
568         no_redraw = true;
569
570         pan_automation_item->set_active (false);
571         gain_automation_item->set_active (false);
572
573         RouteTimeAxisView::hide_all_automation();
574
575         no_redraw = false;
576          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
577 }
578
579 void
580 AudioTimeAxisView::show_all_xfades ()
581 {
582         AudioStreamView* asv = audio_view();
583
584         if (asv) {
585                 asv->show_all_xfades ();
586         }
587 }
588
589 void
590 AudioTimeAxisView::hide_all_xfades ()
591 {
592         AudioStreamView* asv = audio_view();
593         
594         if (asv) {
595                 asv->hide_all_xfades ();
596         }
597 }
598
599 void
600 AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi)
601 {
602         AudioStreamView* asv = audio_view();
603         AudioRegionView* rv;
604
605         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
606                 asv->hide_xfades_involving (*rv);
607         }
608 }
609
610 void
611 AudioTimeAxisView::reveal_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->reveal_xfades_involving (*rv);
618         }
619 }
620
621 void
622 AudioTimeAxisView::route_active_changed ()
623 {
624         RouteTimeAxisView::route_active_changed ();
625
626         if (is_audio_track()) {
627                 if (_route->active()) {
628                         controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
629                         controls_base_selected_name = "AudioTrackControlsBaseSelected";
630                         controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
631                 } else {
632                         controls_ebox.set_name ("AudioTrackControlsBaseInactiveUnselected");
633                         controls_base_selected_name = "AudioTrackControlsBaseInactiveSelected";
634                         controls_base_unselected_name = "AudioTrackControlsBaseInactiveUnselected";
635                 }
636         } else {
637                 if (_route->active()) {
638                         controls_ebox.set_name ("BusControlsBaseUnselected");
639                         controls_base_selected_name = "BusControlsBaseSelected";
640                         controls_base_unselected_name = "BusControlsBaseUnselected";
641                 } else {
642                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
643                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
644                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
645                 }
646         }
647 }
648
649 XMLNode* 
650 AudioTimeAxisView::get_child_xml_node (const string & childname)
651 {
652         return RouteUI::get_child_xml_node (childname);
653 }
654