last minute cleanups before beta6
[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 #include <pbd/memento_command.h>
35
36 #include <gtkmm2ext/gtk_ui.h>
37 #include <gtkmm2ext/selector.h>
38 #include <gtkmm2ext/stop_signal.h>
39 #include <gtkmm2ext/bindable_button.h>
40 #include <gtkmm2ext/utils.h>
41
42 #include <ardour/audioplaylist.h>
43 #include <ardour/audio_diskstream.h>
44 #include <ardour/insert.h>
45 #include <ardour/location.h>
46 #include <ardour/panner.h>
47 #include <ardour/playlist.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_gain_line.h"
55 #include "automation_pan_line.h"
56 #include "canvas_impl.h"
57 #include "crossfade_view.h"
58 #include "enums.h"
59 #include "gain_automation_time_axis.h"
60 #include "keyboard.h"
61 #include "pan_automation_time_axis.h"
62 #include "playlist_selector.h"
63 #include "prompter.h"
64 #include "public_editor.h"
65 #include "audio_region_view.h"
66 #include "simplerect.h"
67 #include "audio_streamview.h"
68 #include "utils.h"
69
70 #include <ardour/audio_track.h>
71
72 #include "i18n.h"
73
74 using namespace ARDOUR;
75 using namespace PBD;
76 using namespace Gtk;
77 using namespace Editing;
78
79
80 AudioTimeAxisView::AudioTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
81         : AxisView(sess)
82         , RouteTimeAxisView(ed, sess, rt, canvas)
83 {
84         // Make sure things are sane...
85         assert(!is_track() || is_audio_track());
86
87         subplugin_menu.set_name ("ArdourContextMenu");
88         gain_track = 0;
89         pan_track = 0;
90         waveform_item = 0;
91         pan_automation_item = 0;
92         gain_automation_item = 0;
93
94         _view = new AudioStreamView (*this);
95
96         add_gain_automation_child ();
97         add_pan_automation_child ();
98
99         ignore_toggle = false;
100
101         if (is_audio_track())
102                 controls_ebox.set_name ("AudioTimeAxisViewControlsBaseUnselected");
103         else // bus
104                 controls_ebox.set_name ("AudioBusControlsBaseUnselected");
105
106         ensure_xml_node ();
107
108         set_state (*xml_node);
109         
110         _route->panner().Changed.connect (mem_fun(*this, &AudioTimeAxisView::update_pans));
111
112         if (is_audio_track()) {
113
114                 controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
115                 controls_base_selected_name = "AudioTrackControlsBaseSelected";
116                 controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
117
118                 /* ask for notifications of any new RegionViews */
119                 _view->RegionViewAdded.connect (mem_fun(*this, &AudioTimeAxisView::region_view_added));
120                 _view->attach ();
121
122         } else { /* bus */
123
124                 controls_ebox.set_name ("AudioBusControlsBaseUnselected");
125                 controls_base_selected_name = "AudioBusControlsBaseSelected";
126                 controls_base_unselected_name = "AudioBusControlsBaseUnselected";
127         }
128 }
129
130 AudioTimeAxisView::~AudioTimeAxisView ()
131 {
132 }
133
134 AudioStreamView*
135 AudioTimeAxisView::audio_view()
136 {
137         return dynamic_cast<AudioStreamView*>(_view);
138 }
139
140 guint32
141 AudioTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
142 {
143         ensure_xml_node ();
144         xml_node->add_property ("shown_editor", "yes");
145                 
146         return TimeAxisView::show_at (y, nth, parent);
147 }
148
149 void
150 AudioTimeAxisView::hide ()
151 {
152         ensure_xml_node ();
153         xml_node->add_property ("shown_editor", "no");
154
155         TimeAxisView::hide ();
156 }
157
158 void
159 AudioTimeAxisView::set_state (const XMLNode& node)
160 {
161         const XMLProperty *prop;
162         
163         TimeAxisView::set_state (node);
164         
165         if ((prop = node.property ("shown_editor")) != 0) {
166                 if (prop->value() == "no") {
167                         _marked_for_display = false;
168                 } else {
169                         _marked_for_display = true;
170                 }
171         } else {
172                 _marked_for_display = true;
173         }
174         
175         XMLNodeList nlist = node.children();
176         XMLNodeConstIterator niter;
177         XMLNode *child_node;
178         
179         
180         show_gain_automation = false;
181         show_pan_automation  = false;
182         
183         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
184                 child_node = *niter;
185
186                 if (child_node->name() == "gain") {
187                         XMLProperty *prop=child_node->property ("shown");
188                         
189                         if (prop != 0) {
190                                 if (prop->value() == "yes") {
191                                         show_gain_automation = true;
192                                 }
193                         }
194                         continue;
195                 }
196                 
197                 if (child_node->name() == "pan") {
198                         XMLProperty *prop=child_node->property ("shown");
199                         
200                         if (prop != 0) {
201                                 if (prop->value() == "yes") {
202                                         show_pan_automation = true;
203                                 }                       
204                         }
205                         continue;
206                 }
207         }
208 }
209
210 void
211 AudioTimeAxisView::build_automation_action_menu ()
212 {
213         using namespace Menu_Helpers;
214
215         RouteTimeAxisView::build_automation_action_menu ();
216
217         MenuList& automation_items = automation_action_menu->items();
218         
219         automation_items.push_back (SeparatorElem());
220
221         automation_items.push_back (CheckMenuElem (_("Fader"), 
222                                                    mem_fun(*this, &AudioTimeAxisView::toggle_gain_track)));
223         gain_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
224         gain_automation_item->set_active(show_gain_automation);
225
226         automation_items.push_back (CheckMenuElem (_("Pan"),
227                                                    mem_fun(*this, &AudioTimeAxisView::toggle_pan_track)));
228         pan_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
229         pan_automation_item->set_active(show_pan_automation);
230         
231 }
232
233 void
234 AudioTimeAxisView::append_extra_display_menu_items ()
235 {
236         using namespace Menu_Helpers;
237
238         MenuList& items = display_menu->items();
239
240         // crossfade stuff
241         items.push_back (MenuElem (_("Hide all crossfades"), mem_fun(*this, &AudioTimeAxisView::hide_all_xfades)));
242         items.push_back (MenuElem (_("Show all crossfades"), mem_fun(*this, &AudioTimeAxisView::show_all_xfades)));
243
244         // waveform menu
245         Menu *waveform_menu = manage(new Menu);
246         MenuList& waveform_items = waveform_menu->items();
247         waveform_menu->set_name ("ArdourContextMenu");
248         
249         waveform_items.push_back (CheckMenuElem (_("Show waveforms"), mem_fun(*this, &AudioTimeAxisView::toggle_waveforms)));
250         waveform_item = static_cast<CheckMenuItem *> (&waveform_items.back());
251         ignore_toggle = true;
252         waveform_item->set_active (editor.show_waveforms());
253         ignore_toggle = false;
254
255         RadioMenuItem::Group group;
256
257         waveform_items.push_back (RadioMenuElem (group, _("Traditional"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Traditional)));
258         traditional_item = static_cast<RadioMenuItem *> (&waveform_items.back());
259
260         waveform_items.push_back (RadioMenuElem (group, _("Rectified"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Rectified)));
261         rectified_item = static_cast<RadioMenuItem *> (&waveform_items.back());
262
263         items.push_back (MenuElem (_("Waveform"), *waveform_menu));
264 }
265
266 void
267 AudioTimeAxisView::toggle_waveforms ()
268 {
269         AudioStreamView* asv = audio_view();
270         assert(asv);
271
272         if (asv && waveform_item && !ignore_toggle) {
273                 asv->set_show_waveforms (waveform_item->get_active());
274         }
275 }
276
277 void
278 AudioTimeAxisView::set_show_waveforms (bool yn)
279 {
280         AudioStreamView* asv = audio_view();
281         assert(asv);
282
283         if (waveform_item) {
284                 waveform_item->set_active (yn);
285         } else {
286                 asv->set_show_waveforms (yn);
287         }
288 }
289
290 void
291 AudioTimeAxisView::set_show_waveforms_recording (bool yn)
292 {
293         AudioStreamView* asv = audio_view();
294
295         if (asv) {
296                 asv->set_show_waveforms_recording (yn);
297         }
298 }
299
300 void
301 AudioTimeAxisView::set_waveform_shape (WaveformShape shape)
302 {
303         AudioStreamView* asv = audio_view();
304
305         if (asv) {
306                 asv->set_waveform_shape (shape);
307         }
308
309         map_frozen ();
310 }       
311
312 void
313 AudioTimeAxisView::add_gain_automation_child ()
314 {
315         XMLProperty* prop;
316         AutomationLine* line;
317
318         gain_track = new GainAutomationTimeAxisView (_session,
319                                                      _route,
320                                                      editor,
321                                                      *this,
322                                                      parent_canvas,
323                                                      _("gain"),
324                                                      _route->gain_automation_curve());
325         
326         line = new AutomationGainLine ("automation gain",
327                                        _session,
328                                        *gain_track,
329                                        *gain_track->canvas_display,
330                                        _route->gain_automation_curve());
331
332         line->set_line_color (color_map[cAutomationLine]);
333         
334
335         gain_track->add_line (*line);
336
337         add_child (gain_track);
338
339         gain_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::gain_hidden));
340
341         bool hideit = true;
342         
343         XMLNode* node;
344
345         if ((node = gain_track->get_state_node()) != 0) {
346                 if  ((prop = node->property ("shown")) != 0) {
347                         if (prop->value() == "yes") {
348                                 hideit = false;
349                         }
350                 } 
351         }
352
353         if (hideit) {
354                 gain_track->hide ();
355         }
356 }
357
358 void
359 AudioTimeAxisView::add_pan_automation_child ()
360 {
361         XMLProperty* prop;
362
363         pan_track = new PanAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, _("pan"));
364
365         update_pans ();
366         
367         add_child (pan_track);
368
369         pan_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::pan_hidden));
370
371         ensure_xml_node ();
372         bool hideit = true;
373         
374         XMLNode* node;
375
376         if ((node = pan_track->get_state_node()) != 0) {
377                 if ((prop = node->property ("shown")) != 0) {
378                         if (prop->value() == "yes") {
379                                 hideit = false;
380                         }
381                 } 
382         }
383
384         if (hideit) {
385                 pan_track->hide ();
386         }
387 }
388
389 void
390 AudioTimeAxisView::update_pans ()
391 {
392         Panner::iterator p;
393         
394         pan_track->clear_lines ();
395         
396         /* we don't draw lines for "greater than stereo" panning.
397          */
398
399         if (_route->n_outputs() > 2) {
400                 return;
401         }
402
403         for (p = _route->panner().begin(); p != _route->panner().end(); ++p) {
404
405                 AutomationLine* line;
406
407                 line = new AutomationPanLine ("automation pan", _session, *pan_track,
408                                               *pan_track->canvas_display, 
409                                               (*p)->automation());
410
411                 if (p == _route->panner().begin()) {
412                         /* first line is a nice orange */
413                         line->set_line_color (color_map[cLeftPanAutomationLine]);
414                 } else {
415                         /* second line is a nice blue */
416                         line->set_line_color (color_map[cRightPanAutomationLine]);
417                 }
418
419                 pan_track->add_line (*line);
420         }
421 }
422                 
423 void
424 AudioTimeAxisView::toggle_gain_track ()
425 {
426
427         bool showit = gain_automation_item->get_active();
428
429         if (showit != gain_track->marked_for_display()) {
430                 if (showit) {
431                         gain_track->set_marked_for_display (true);
432                         gain_track->canvas_display->show();
433                         gain_track->get_state_node()->add_property ("shown", X_("yes"));
434                 } else {
435                         gain_track->set_marked_for_display (false);
436                         gain_track->hide ();
437                         gain_track->get_state_node()->add_property ("shown", X_("no"));
438                 }
439
440                 /* now trigger a redisplay */
441                 
442                 if (!no_redraw) {
443                          _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
444                 }
445         }
446 }
447
448 void
449 AudioTimeAxisView::gain_hidden ()
450 {
451         gain_track->get_state_node()->add_property (X_("shown"), X_("no"));
452
453         if (gain_automation_item && !_hidden) {
454                 gain_automation_item->set_active (false);
455         }
456
457          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
458 }
459
460 void
461 AudioTimeAxisView::toggle_pan_track ()
462 {
463         bool showit = pan_automation_item->get_active();
464
465         if (showit != pan_track->marked_for_display()) {
466                 if (showit) {
467                         pan_track->set_marked_for_display (true);
468                         pan_track->canvas_display->show();
469                         pan_track->get_state_node()->add_property ("shown", X_("yes"));
470                 } else {
471                         pan_track->set_marked_for_display (false);
472                         pan_track->hide ();
473                         pan_track->get_state_node()->add_property ("shown", X_("no"));
474                 }
475
476                 /* now trigger a redisplay */
477                 
478                 if (!no_redraw) {
479                          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
480                 }
481         }
482 }
483
484 void
485 AudioTimeAxisView::pan_hidden ()
486 {
487         pan_track->get_state_node()->add_property ("shown", "no");
488
489         if (pan_automation_item && !_hidden) {
490                 pan_automation_item->set_active (false);
491         }
492
493          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
494 }
495
496 void
497 AudioTimeAxisView::show_all_automation ()
498 {
499         no_redraw = true;
500
501         pan_automation_item->set_active (true);
502         gain_automation_item->set_active (true);
503         
504         RouteTimeAxisView::show_all_automation ();
505
506         no_redraw = false;
507
508          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
509 }
510
511 void
512 AudioTimeAxisView::show_existing_automation ()
513 {
514         no_redraw = true;
515
516         pan_automation_item->set_active (true);
517         gain_automation_item->set_active (true);
518
519         RouteTimeAxisView::show_existing_automation ();
520
521         no_redraw = false;
522
523          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
524 }
525
526 void
527 AudioTimeAxisView::hide_all_automation ()
528 {
529         no_redraw = true;
530
531         pan_automation_item->set_active (false);
532         gain_automation_item->set_active (false);
533
534         RouteTimeAxisView::hide_all_automation();
535
536         no_redraw = false;
537          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
538 }
539
540 void
541 AudioTimeAxisView::show_all_xfades ()
542 {
543         AudioStreamView* asv = audio_view();
544
545         if (asv) {
546                 asv->show_all_xfades ();
547         }
548 }
549
550 void
551 AudioTimeAxisView::hide_all_xfades ()
552 {
553         AudioStreamView* asv = audio_view();
554         
555         if (asv) {
556                 asv->hide_all_xfades ();
557         }
558 }
559
560 void
561 AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi)
562 {
563         AudioStreamView* asv = audio_view();
564         AudioRegionView* rv;
565
566         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
567                 asv->hide_xfades_involving (*rv);
568         }
569 }
570
571 void
572 AudioTimeAxisView::reveal_dependent_views (TimeAxisViewItem& tavi)
573 {
574         AudioStreamView* asv = audio_view();
575         AudioRegionView* rv;
576
577         if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
578                 asv->reveal_xfades_involving (*rv);
579         }
580 }
581
582 void
583 AudioTimeAxisView::route_active_changed ()
584 {
585         RouteTimeAxisView::route_active_changed ();
586
587         if (is_audio_track()) {
588                 if (_route->active()) {
589                         controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
590                         controls_base_selected_name = "AudioTrackControlsBaseSelected";
591                         controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
592                 } else {
593                         controls_ebox.set_name ("AudioTrackControlsBaseInactiveUnselected");
594                         controls_base_selected_name = "AudioTrackControlsBaseInactiveSelected";
595                         controls_base_unselected_name = "AudioTrackControlsBaseInactiveUnselected";
596                 }
597         } else {
598                 if (_route->active()) {
599                         controls_ebox.set_name ("BusControlsBaseUnselected");
600                         controls_base_selected_name = "BusControlsBaseSelected";
601                         controls_base_unselected_name = "BusControlsBaseUnselected";
602                 } else {
603                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
604                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
605                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
606                 }
607         }
608 }
609
610 XMLNode* 
611 AudioTimeAxisView::get_child_xml_node (const string & childname)
612 {
613         return RouteUI::get_child_xml_node (childname);
614 }
615