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