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