Heavy-duty abstraction work to split type-specific classes into
[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/whitespace.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 "plugin_selector.h"
63 #include "plugin_ui.h"
64 #include "prompter.h"
65 #include "public_editor.h"
66 #include "audio_region_view.h"
67 #include "simplerect.h"
68 #include "audio_streamview.h"
69 #include "utils.h"
70
71 #include <ardour/audio_track.h>
72
73 #include "i18n.h"
74
75 using namespace ARDOUR;
76 using namespace PBD;
77 using namespace Gtk;
78 using namespace Editing;
79
80
81 AudioTimeAxisView::AudioTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
82         : AxisView(sess)
83         , RouteTimeAxisView(ed, sess, rt, canvas)
84 {
85         // Make sure things are sane...
86         assert(!is_track() || is_audio_track());
87
88         subplugin_menu.set_name ("ArdourContextMenu");
89         gain_track = 0;
90         pan_track = 0;
91         waveform_item = 0;
92         pan_automation_item = 0;
93         gain_automation_item = 0;
94
95         _view = new AudioStreamView (*this);
96
97         add_gain_automation_child ();
98         add_pan_automation_child ();
99
100         ignore_toggle = false;
101
102         mute_button->set_active (false);
103         solo_button->set_active (false);
104         
105         if (is_audio_track())
106                 controls_ebox.set_name ("AudioTimeAxisViewControlsBaseUnselected");
107         else // bus
108                 controls_ebox.set_name ("AudioBusControlsBaseUnselected");
109
110         /* map current state of the route */
111
112         redirects_changed (0);
113         reset_redirect_automation_curves ();
114
115         ensure_xml_node ();
116
117         set_state (*xml_node);
118         
119         _route->panner().Changed.connect (mem_fun(*this, &AudioTimeAxisView::update_pans));
120
121         if (is_audio_track()) {
122
123                 controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
124                 controls_base_selected_name = "AudioTrackControlsBaseSelected";
125                 controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
126
127                 /* ask for notifications of any new RegionViews */
128                 _view->RegionViewAdded.connect (mem_fun(*this, &AudioTimeAxisView::region_view_added));
129                 _view->attach ();
130
131         } else { /* bus */
132
133                 controls_ebox.set_name ("AudioBusControlsBaseUnselected");
134                 controls_base_selected_name = "AudioBusControlsBaseSelected";
135                 controls_base_unselected_name = "AudioBusControlsBaseUnselected";
136         }
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         RadioMenuItem::Group group;
265
266         waveform_items.push_back (RadioMenuElem (group, _("Traditional"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Traditional)));
267         traditional_item = static_cast<RadioMenuItem *> (&waveform_items.back());
268
269         waveform_items.push_back (RadioMenuElem (group, _("Rectified"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Rectified)));
270         rectified_item = static_cast<RadioMenuItem *> (&waveform_items.back());
271
272         items.push_back (MenuElem (_("Waveform"), *waveform_menu));
273 }
274
275 void
276 AudioTimeAxisView::toggle_waveforms ()
277 {
278         AudioStreamView* asv = audio_view();
279         assert(asv);
280
281         if (asv && waveform_item && !ignore_toggle) {
282                 asv->set_show_waveforms (waveform_item->get_active());
283         }
284 }
285
286 void
287 AudioTimeAxisView::set_show_waveforms (bool yn)
288 {
289         AudioStreamView* asv = audio_view();
290         assert(asv);
291
292         if (waveform_item) {
293                 waveform_item->set_active (yn);
294         } else {
295                 asv->set_show_waveforms (yn);
296         }
297 }
298
299 void
300 AudioTimeAxisView::set_show_waveforms_recording (bool yn)
301 {
302         AudioStreamView* asv = audio_view();
303
304         if (asv) {
305                 asv->set_show_waveforms_recording (yn);
306         }
307 }
308
309 void
310 AudioTimeAxisView::set_waveform_shape (WaveformShape shape)
311 {
312         AudioStreamView* asv = audio_view();
313
314         if (asv) {
315                 asv->set_waveform_shape (shape);
316         }
317
318         map_frozen ();
319 }       
320
321 void
322 AudioTimeAxisView::add_gain_automation_child ()
323 {
324         XMLProperty* prop;
325         AutomationLine* line;
326
327         gain_track = new GainAutomationTimeAxisView (_session,
328                                                      _route,
329                                                      editor,
330                                                      *this,
331                                                      parent_canvas,
332                                                      _("gain"),
333                                                      _route->gain_automation_curve());
334         
335         line = new AutomationGainLine ("automation gain",
336                                        _session,
337                                        *gain_track,
338                                        *gain_track->canvas_display,
339                                        _route->gain_automation_curve());
340
341         line->set_line_color (color_map[cAutomationLine]);
342         
343
344         gain_track->add_line (*line);
345
346         add_child (gain_track);
347
348         gain_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::gain_hidden));
349
350         bool hideit = true;
351         
352         XMLNode* node;
353
354         if ((node = gain_track->get_state_node()) != 0) {
355                 if  ((prop = node->property ("shown")) != 0) {
356                         if (prop->value() == "yes") {
357                                 hideit = false;
358                         }
359                 } 
360         }
361
362         if (hideit) {
363                 gain_track->hide ();
364         }
365 }
366
367 void
368 AudioTimeAxisView::add_pan_automation_child ()
369 {
370         XMLProperty* prop;
371
372         pan_track = new PanAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, _("pan"));
373
374         update_pans ();
375         
376         add_child (pan_track);
377
378         pan_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::pan_hidden));
379
380         ensure_xml_node ();
381         bool hideit = true;
382         
383         XMLNode* node;
384
385         if ((node = pan_track->get_state_node()) != 0) {
386                 if ((prop = node->property ("shown")) != 0) {
387                         if (prop->value() == "yes") {
388                                 hideit = false;
389                         }
390                 } 
391         }
392
393         if (hideit) {
394                 pan_track->hide ();
395         }
396 }
397
398 void
399 AudioTimeAxisView::update_pans ()
400 {
401         Panner::iterator p;
402         
403         pan_track->clear_lines ();
404         
405         /* we don't draw lines for "greater than stereo" panning.
406          */
407
408         if (_route->n_outputs() > 2) {
409                 return;
410         }
411
412         for (p = _route->panner().begin(); p != _route->panner().end(); ++p) {
413
414                 AutomationLine* line;
415
416                 line = new AutomationPanLine ("automation pan", _session, *pan_track,
417                                               *pan_track->canvas_display, 
418                                               (*p)->automation());
419
420                 if (p == _route->panner().begin()) {
421                         /* first line is a nice orange */
422                         line->set_line_color (color_map[cLeftPanAutomationLine]);
423                 } else {
424                         /* second line is a nice blue */
425                         line->set_line_color (color_map[cRightPanAutomationLine]);
426                 }
427
428                 pan_track->add_line (*line);
429         }
430 }
431                 
432 void
433 AudioTimeAxisView::toggle_gain_track ()
434 {
435
436         bool showit = gain_automation_item->get_active();
437
438         if (showit != gain_track->marked_for_display()) {
439                 if (showit) {
440                         gain_track->set_marked_for_display (true);
441                         gain_track->canvas_display->show();
442                         gain_track->get_state_node()->add_property ("shown", X_("yes"));
443                 } else {
444                         gain_track->set_marked_for_display (false);
445                         gain_track->hide ();
446                         gain_track->get_state_node()->add_property ("shown", X_("no"));
447                 }
448
449                 /* now trigger a redisplay */
450                 
451                 if (!no_redraw) {
452                          _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
453                 }
454         }
455 }
456
457 void
458 AudioTimeAxisView::gain_hidden ()
459 {
460         gain_track->get_state_node()->add_property (X_("shown"), X_("no"));
461
462         if (gain_automation_item && !_hidden) {
463                 gain_automation_item->set_active (false);
464         }
465
466          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
467 }
468
469 void
470 AudioTimeAxisView::toggle_pan_track ()
471 {
472         bool showit = pan_automation_item->get_active();
473
474         if (showit != pan_track->marked_for_display()) {
475                 if (showit) {
476                         pan_track->set_marked_for_display (true);
477                         pan_track->canvas_display->show();
478                         pan_track->get_state_node()->add_property ("shown", X_("yes"));
479                 } else {
480                         pan_track->set_marked_for_display (false);
481                         pan_track->hide ();
482                         pan_track->get_state_node()->add_property ("shown", X_("no"));
483                 }
484
485                 /* now trigger a redisplay */
486                 
487                 if (!no_redraw) {
488                          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
489                 }
490         }
491 }
492
493 void
494 AudioTimeAxisView::pan_hidden ()
495 {
496         pan_track->get_state_node()->add_property ("shown", "no");
497
498         if (pan_automation_item && !_hidden) {
499                 pan_automation_item->set_active (false);
500         }
501
502          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
503 }
504
505 void
506 AudioTimeAxisView::show_all_automation ()
507 {
508         no_redraw = true;
509
510         pan_automation_item->set_active (true);
511         gain_automation_item->set_active (true);
512         
513         RouteTimeAxisView::show_all_automation ();
514
515         no_redraw = false;
516
517          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
518 }
519
520 void
521 AudioTimeAxisView::show_existing_automation ()
522 {
523         no_redraw = true;
524
525         pan_automation_item->set_active (true);
526         gain_automation_item->set_active (true);
527
528         RouteTimeAxisView::show_existing_automation ();
529
530         no_redraw = false;
531
532          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
533 }
534
535 void
536 AudioTimeAxisView::hide_all_automation ()
537 {
538         no_redraw = true;
539
540         pan_automation_item->set_active (false);
541         gain_automation_item->set_active (false);
542
543         RouteTimeAxisView::hide_all_automation();
544
545         no_redraw = false;
546          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
547 }
548
549 void
550 AudioTimeAxisView::show_all_xfades ()
551 {
552         AudioStreamView* asv = audio_view();
553
554         if (asv) {
555                 asv->show_all_xfades ();
556         }
557 }
558
559 void
560 AudioTimeAxisView::hide_all_xfades ()
561 {
562         AudioStreamView* asv = audio_view();
563         
564         if (asv) {
565                 asv->hide_all_xfades ();
566         }
567 }
568
569 void
570 AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi)
571 {
572         AudioStreamView* asv = audio_view();
573         AudioRegionView* rv;
574
575         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
576                 asv->hide_xfades_involving (*rv);
577         }
578 }
579
580 void
581 AudioTimeAxisView::reveal_dependent_views (TimeAxisViewItem& tavi)
582 {
583         AudioStreamView* asv = audio_view();
584         AudioRegionView* rv;
585
586         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
587                 asv->reveal_xfades_involving (*rv);
588         }
589 }
590
591 void
592 AudioTimeAxisView::route_active_changed ()
593 {
594         RouteTimeAxisView::route_active_changed ();
595
596         if (is_audio_track()) {
597                 if (_route->active()) {
598                         controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
599                         controls_base_selected_name = "AudioTrackControlsBaseSelected";
600                         controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
601                 } else {
602                         controls_ebox.set_name ("AudioTrackControlsBaseInactiveUnselected");
603                         controls_base_selected_name = "AudioTrackControlsBaseInactiveSelected";
604                         controls_base_unselected_name = "AudioTrackControlsBaseInactiveUnselected";
605                 }
606         } else {
607                 if (_route->active()) {
608                         controls_ebox.set_name ("BusControlsBaseUnselected");
609                         controls_base_selected_name = "BusControlsBaseSelected";
610                         controls_base_unselected_name = "BusControlsBaseUnselected";
611                 } else {
612                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
613                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
614                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
615                 }
616         }
617 }
618
619 XMLNode* 
620 AudioTimeAxisView::get_child_xml_node (const string & childname)
621 {
622         return RouteUI::get_child_xml_node (childname);
623 }
624