6ce54f788210119078c3041ccb3def1e3d0723f4
[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/audio_diskstream.h"
41 #include "ardour/audioplaylist.h"
42 #include "ardour/event_type_map.h"
43 #include "ardour/location.h"
44 #include "ardour/panner.h"
45 #include "ardour/playlist.h"
46 #include "ardour/processor.h"
47 #include "ardour/profile.h"
48 #include "ardour/session.h"
49 #include "ardour/session_playlist.h"
50 #include "ardour/utils.h"
51
52 #include "ardour_ui.h"
53 #include "audio_time_axis.h"
54 #include "automation_line.h"
55 #include "canvas_impl.h"
56 #include "crossfade_view.h"
57 #include "enums.h"
58 #include "automation_time_axis.h"
59 #include "keyboard.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 std;
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         waveform_item = 0;
87
88         _view = new AudioStreamView (*this);
89
90         ignore_toggle = false;
91
92         mute_button->set_active (false);
93         solo_button->set_active (false);
94         
95         if (is_audio_track()) {
96                 controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
97         } else { // bus
98                 controls_ebox.set_name ("AudioBusControlsBaseUnselected");
99         }
100
101         ensure_xml_node ();
102
103         set_state (*xml_node);
104
105         /* if set_state above didn't create a gain automation child, we need to make one */
106         if (automation_track (GainAutomation) == 0) {
107                 create_automation_child (GainAutomation, false);
108         }
109         
110         if (_route->panner()) {
111                 _route->panner()->Changed.connect (bind (
112                                 mem_fun(*this, &AudioTimeAxisView::ensure_pan_views),
113                                 false));
114         }
115
116         /* map current state of the route */
117
118         processors_changed ();
119         reset_processor_automation_curves ();
120         ensure_pan_views (false);
121         update_control_names ();
122
123         if (is_audio_track()) {
124
125                 /* ask for notifications of any new RegionViews */
126                 _view->RegionViewAdded.connect (mem_fun(*this, &AudioTimeAxisView::region_view_added));
127
128                 if (!_editor.have_idled()) {
129                         /* first idle will do what we need */
130                 } else {
131                         first_idle ();
132                 } 
133
134         } else {
135                 post_construct ();
136         }
137 }
138
139 AudioTimeAxisView::~AudioTimeAxisView ()
140 {
141 }
142
143 void
144 AudioTimeAxisView::first_idle ()
145 {
146         _view->attach ();
147         post_construct ();
148 }
149
150 AudioStreamView*
151 AudioTimeAxisView::audio_view()
152 {
153         return dynamic_cast<AudioStreamView*>(_view);
154 }
155
156 guint32
157 AudioTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
158 {
159         ensure_xml_node ();
160         xml_node->add_property ("shown-editor", "yes");
161                 
162         return TimeAxisView::show_at (y, nth, parent);
163 }
164
165 void
166 AudioTimeAxisView::hide ()
167 {
168         ensure_xml_node ();
169         xml_node->add_property ("shown-editor", "no");
170
171         TimeAxisView::hide ();
172 }
173
174
175 void
176 AudioTimeAxisView::append_extra_display_menu_items ()
177 {
178         using namespace Menu_Helpers;
179
180         MenuList& items = display_menu->items();
181
182         // crossfade stuff
183         if (!Profile->get_sae()) {
184                 items.push_back (MenuElem (_("Hide all crossfades"), mem_fun(*this, &AudioTimeAxisView::hide_all_xfades)));
185                 items.push_back (MenuElem (_("Show all crossfades"), mem_fun(*this, &AudioTimeAxisView::show_all_xfades)));
186         }
187
188         // waveform menu
189         Menu *waveform_menu = manage(new Menu);
190         MenuList& waveform_items = waveform_menu->items();
191         waveform_menu->set_name ("ArdourContextMenu");
192         
193         waveform_items.push_back (CheckMenuElem (_("Show waveforms"), mem_fun(*this, &AudioTimeAxisView::toggle_waveforms)));
194         waveform_item = static_cast<CheckMenuItem *> (&waveform_items.back());
195         ignore_toggle = true;
196         waveform_item->set_active (_editor.show_waveforms());
197         ignore_toggle = false;
198
199         waveform_items.push_back (SeparatorElem());
200         
201         RadioMenuItem::Group group;
202         
203         waveform_items.push_back (RadioMenuElem (group, _("Traditional"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Traditional)));
204         traditional_item = static_cast<RadioMenuItem *> (&waveform_items.back());
205
206         if (!Profile->get_sae()) {
207                 waveform_items.push_back (RadioMenuElem (group, _("Rectified"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Rectified)));
208                 rectified_item = static_cast<RadioMenuItem *> (&waveform_items.back());
209         } else {
210                 rectified_item = 0;
211         }
212
213         waveform_items.push_back (SeparatorElem());
214         
215         RadioMenuItem::Group group2;
216
217         waveform_items.push_back (RadioMenuElem (group2, _("Linear"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_scale), LinearWaveform)));
218         linearscale_item = static_cast<RadioMenuItem *> (&waveform_items.back());
219
220         waveform_items.push_back (RadioMenuElem (group2, _("Logarithmic"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_scale), LogWaveform)));
221         logscale_item = static_cast<RadioMenuItem *> (&waveform_items.back());
222
223         // setting initial item state
224         AudioStreamView* asv = audio_view();
225         if (asv) {
226                 ignore_toggle = true;
227                 if (asv->get_waveform_shape() == Rectified && rectified_item) {
228                         rectified_item->set_active(true);
229                 } else {
230                         traditional_item->set_active(true);
231                 }
232
233                 if (asv->get_waveform_scale() == LogWaveform) 
234                         logscale_item->set_active(true);
235                 else linearscale_item->set_active(true);
236                 ignore_toggle = false;
237         }
238
239         items.push_back (MenuElem (_("Waveform"), *waveform_menu));
240
241 }
242         
243 Gtk::Menu*
244 AudioTimeAxisView::build_mode_menu()
245 {
246         using namespace Menu_Helpers;
247
248         Menu* mode_menu = manage (new Menu);
249         MenuList& items = mode_menu->items();
250         mode_menu->set_name ("ArdourContextMenu");
251
252         RadioMenuItem::Group mode_group;
253
254         items.push_back (RadioMenuElem (mode_group, _("Normal"),
255                                 bind (mem_fun (*this, &AudioTimeAxisView::set_track_mode), ARDOUR::Normal)));
256         normal_track_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
257
258         items.push_back (RadioMenuElem (mode_group, _("Non Overlapping"),
259                                 bind (mem_fun (*this, &AudioTimeAxisView::set_track_mode), ARDOUR::NonLayered)));
260         non_layered_track_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
261
262         items.push_back (RadioMenuElem (mode_group, _("Tape"),
263                                 bind (mem_fun (*this, &AudioTimeAxisView::set_track_mode), ARDOUR::Destructive)));
264         destructive_track_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
265
266         switch (track()->mode()) {
267                 case ARDOUR::Destructive:
268                         destructive_track_mode_item->set_active ();
269                         break;
270                 case ARDOUR::NonLayered:
271                         non_layered_track_mode_item->set_active ();
272                         break;
273                 case ARDOUR::Normal:
274                         normal_track_mode_item->set_active ();
275                         break;
276         }
277
278         return mode_menu;
279 }
280
281 void
282 AudioTimeAxisView::toggle_waveforms ()
283 {
284         AudioStreamView* asv = audio_view();
285         assert(asv);
286
287         if (asv && waveform_item && !ignore_toggle) {
288                 asv->set_show_waveforms (waveform_item->get_active());
289         }
290 }
291
292 void
293 AudioTimeAxisView::set_show_waveforms (bool yn)
294 {
295         AudioStreamView* asv = audio_view();
296         assert(asv);
297
298         if (waveform_item) {
299                 waveform_item->set_active (yn);
300         } else {
301                 asv->set_show_waveforms (yn);
302         }
303 }
304
305 void
306 AudioTimeAxisView::set_show_waveforms_recording (bool yn)
307 {
308         AudioStreamView* asv = audio_view();
309
310         if (asv) {
311                 asv->set_show_waveforms_recording (yn);
312         }
313 }
314
315 void
316 AudioTimeAxisView::set_waveform_shape (WaveformShape shape)
317 {
318         AudioStreamView* asv = audio_view();
319
320         if (asv && !ignore_toggle) {
321                 asv->set_waveform_shape (shape);
322         }
323
324         map_frozen ();
325 }       
326
327 void
328 AudioTimeAxisView::set_waveform_scale (WaveformScale scale)
329 {
330         AudioStreamView* asv = audio_view();
331
332         if (asv && !ignore_toggle) {
333                 asv->set_waveform_scale (scale);
334         }
335
336         map_frozen ();
337 }       
338
339 void
340 AudioTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
341 {
342         if (param.type() == GainAutomation) {
343
344                 boost::shared_ptr<AutomationControl> c = _route->gain_control();
345                 if (!c) {
346                         error << "Route has no gain automation, unable to add automation track view." << endmsg;
347                         return;
348                 }
349
350                 boost::shared_ptr<AutomationTimeAxisView> gain_track(new AutomationTimeAxisView (_session,
351                                 _route, _route, c,
352                                 _editor,
353                                 *this,
354                                 false,
355                                 parent_canvas,
356                                 _route->describe_parameter(param)));
357
358                 add_automation_child(Evoral::Parameter(GainAutomation), gain_track, show);
359
360         } else if (param.type() == PanAutomation) {
361
362                 ensure_xml_node ();
363                 ensure_pan_views (show);
364
365         } else {
366                 error << "AudioTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
367         }
368 }
369
370 /** Ensure that we have the appropriate AutomationTimeAxisViews for the
371  *  panners that we have.
372  *
373  *  @param show true to show any new views that we create, otherwise false.
374  */
375 void
376 AudioTimeAxisView::ensure_pan_views (bool show)
377 {
378         if (!_route->panner()) {
379                 return;
380         }
381
382         const set<Evoral::Parameter>& params = _route->panner()->what_can_be_automated();
383         set<Evoral::Parameter>::iterator p;
384
385         for (p = params.begin(); p != params.end(); ++p) {
386                 boost::shared_ptr<ARDOUR::AutomationControl> pan_control
387                         = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
388                                 _route->panner()->data().control(*p));
389                 
390                 if (pan_control->parameter().type() == NullAutomation) {
391                         error << "Pan control has NULL automation type!" << endmsg;
392                         continue;
393                 }
394
395                 if (automation_child (pan_control->parameter ()).get () == 0) {
396
397                         /* we don't already have an AutomationTimeAxisView for this parameter */
398
399                         std::string const name = _route->describe_parameter (pan_control->parameter ());
400
401                         boost::shared_ptr<AutomationTimeAxisView> pan_track (
402                                 new AutomationTimeAxisView (_session,
403                                                             _route, _route, pan_control, 
404                                                             _editor,
405                                                             *this,
406                                                             false,
407                                                             parent_canvas,
408                                                             name));
409                         
410                         add_automation_child (*p, pan_track, show);
411                 }
412         }
413 }
414 #if 0
415 void
416 AudioTimeAxisView::toggle_gain_track ()
417 {
418         bool showit = gain_automation_item->get_active();
419
420         if (showit != gain_track->marked_for_display()) {
421                 if (showit) {
422                         gain_track->set_marked_for_display (true);
423                         gain_track->canvas_display->show();
424                         gain_track->canvas_background->show();
425                         gain_track->get_state_node()->add_property ("shown", X_("yes"));
426                 } else {
427                         gain_track->set_marked_for_display (false);
428                         gain_track->hide ();
429                         gain_track->get_state_node()->add_property ("shown", X_("no"));
430                 }
431
432                 /* now trigger a redisplay */
433                 
434                 if (!no_redraw) {
435                          _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
436                 }
437         }
438 }
439
440 void
441 AudioTimeAxisView::gain_hidden ()
442 {
443         gain_track->get_state_node()->add_property (X_("shown"), X_("no"));
444
445         if (gain_automation_item && !_hidden) {
446                 gain_automation_item->set_active (false);
447         }
448
449          _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
450 }
451
452 void
453 AudioTimeAxisView::toggle_pan_track ()
454 {
455         bool showit = pan_automation_item->get_active();
456
457         if (showit != pan_track->marked_for_display()) {
458                 if (showit) {
459                         pan_track->set_marked_for_display (true);
460                         pan_track->canvas_display->show();
461                         pan_track->canvas_background->show();
462                         pan_track->get_state_node()->add_property ("shown", X_("yes"));
463                 } else {
464                         pan_track->set_marked_for_display (false);
465                         pan_track->hide ();
466                         pan_track->get_state_node()->add_property ("shown", X_("no"));
467                 }
468
469                 /* now trigger a redisplay */
470         }
471 }
472 #endif
473                 
474 void
475 AudioTimeAxisView::show_all_automation ()
476 {
477         no_redraw = true;
478
479         RouteTimeAxisView::show_all_automation ();
480
481         no_redraw = false;
482
483          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
484 }
485
486 void
487 AudioTimeAxisView::show_existing_automation ()
488 {
489         no_redraw = true;
490
491         RouteTimeAxisView::show_existing_automation ();
492
493         no_redraw = false;
494
495          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
496 }
497
498 void
499 AudioTimeAxisView::hide_all_automation ()
500 {
501         no_redraw = true;
502
503         RouteTimeAxisView::hide_all_automation();
504
505         no_redraw = false;
506          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
507 }
508
509 void
510 AudioTimeAxisView::show_all_xfades ()
511 {
512         AudioStreamView* asv = audio_view();
513
514         if (asv) {
515                 asv->show_all_xfades ();
516         }
517 }
518
519 void
520 AudioTimeAxisView::hide_all_xfades ()
521 {
522         AudioStreamView* asv = audio_view();
523         
524         if (asv) {
525                 asv->hide_all_xfades ();
526         }
527 }
528
529 void
530 AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi)
531 {
532         AudioStreamView* asv = audio_view();
533         AudioRegionView* rv;
534
535         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
536                 asv->hide_xfades_involving (*rv);
537         }
538 }
539
540 void
541 AudioTimeAxisView::reveal_dependent_views (TimeAxisViewItem& tavi)
542 {
543         AudioStreamView* asv = audio_view();
544         AudioRegionView* rv;
545
546         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
547                 asv->reveal_xfades_involving (*rv);
548         }
549 }
550
551 void
552 AudioTimeAxisView::route_active_changed ()
553 {
554         RouteTimeAxisView::route_active_changed ();
555         update_control_names ();
556 }
557
558
559 /**
560  *    Set up the names of the controls so that they are coloured
561  *    correctly depending on whether this route is inactive or
562  *    selected.
563  */
564
565 void
566 AudioTimeAxisView::update_control_names ()
567 {
568         if (is_audio_track()) {
569                 if (_route->active()) {
570                         controls_base_selected_name = "AudioTrackControlsBaseSelected";
571                         controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
572                 } else {
573                         controls_base_selected_name = "AudioTrackControlsBaseInactiveSelected";
574                         controls_base_unselected_name = "AudioTrackControlsBaseInactiveUnselected";
575                 }
576         } else {
577                 if (_route->active()) {
578                         controls_base_selected_name = "BusControlsBaseSelected";
579                         controls_base_unselected_name = "BusControlsBaseUnselected";
580                 } else {
581                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
582                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
583                 }
584         }
585
586         if (get_selected()) {
587                 controls_ebox.set_name (controls_base_selected_name);
588         } else {
589                 controls_ebox.set_name (controls_base_unselected_name);
590         }
591 }
592