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