1c0b920c13be0bfeb9550bbc9a079dc405c495ff
[ardour.git] / gtk2_ardour / audio_time_axis.cc
1 /*
2     Copyright (C) 2000 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
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/whitespace.h>
33
34 #include <gtkmm2ext/bindable_button.h>
35 #include <gtkmm2ext/gtk_ui.h>
36 #include <gtkmm2ext/selector.h>
37 #include <gtkmm2ext/stop_signal.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/ladspa_plugin.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 "automation_time_axis.h"
56 #include "canvas_impl.h"
57 #include "crossfade_view.h"
58 #include "enums.h"
59 #include "gain_automation_time_axis.h"
60 #include "gui_thread.h"
61 #include "keyboard.h"
62 #include "pan_automation_time_axis.h"
63 #include "playlist_selector.h"
64 #include "plugin_selector.h"
65 #include "plugin_ui.h"
66 #include "point_selection.h"
67 #include "prompter.h"
68 #include "public_editor.h"
69 #include "redirect_automation_line.h"
70 #include "redirect_automation_time_axis.h"
71 #include "regionview.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "simplerect.h"
75 #include "streamview.h"
76 #include "utils.h"
77
78 #include <ardour/audio_track.h>
79
80 #include "i18n.h"
81
82 using namespace ARDOUR;
83 using namespace PBD;
84 using namespace LADSPA;
85 using namespace Gtk;
86 using namespace Editing;
87
88
89 AudioTimeAxisView::AudioTimeAxisView (PublicEditor& ed, Session& sess, Route& rt, Canvas& canvas)
90         : AxisView(sess),
91           RouteUI(rt, sess, _("m"), _("s"), _("r")), // mute, solo, and record
92           TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas),
93           parent_canvas (canvas),
94           button_table (3, 3),
95           edit_group_button (_("g")), // group
96           playlist_button (_("p")), 
97           size_button (_("h")), // height
98           automation_button (_("a")),
99           visual_button (_("v"))
100
101 {
102         _has_state = true;
103         subplugin_menu.set_name ("ArdourContextMenu");
104         playlist_menu = 0;
105         playlist_action_menu = 0;
106         automation_action_menu = 0;
107         gain_track = 0;
108         pan_track = 0;
109         view = 0;
110         timestretch_rect = 0;
111         waveform_item = 0;
112         pan_automation_item = 0;
113         gain_automation_item = 0;
114         no_redraw = false;
115
116         view = new StreamView (*this);
117
118         add_gain_automation_child ();
119         add_pan_automation_child ();
120
121         ignore_toggle = false;
122
123         rec_enable_button->set_active (false);
124         mute_button->set_active (false);
125         solo_button->set_active (false);
126         
127         rec_enable_button->set_name ("TrackRecordEnableButton");
128         mute_button->set_name ("TrackMuteButton");
129         solo_button->set_name ("SoloButton");
130         edit_group_button.set_name ("TrackGroupButton");
131         playlist_button.set_name ("TrackPlaylistButton");
132         automation_button.set_name ("TrackAutomationButton");
133         size_button.set_name ("TrackSizeButton");
134         visual_button.set_name ("TrackVisualButton");
135         hide_button.set_name ("TrackRemoveButton");
136
137         hide_button.add (*(manage (new Image (get_xpm("small_x.xpm")))));
138
139         solo_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
140         mute_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
141         rec_enable_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
142         playlist_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
143         automation_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
144         size_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
145         visual_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
146         hide_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
147
148         solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false);
149         solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false);
150         mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
151         mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
152         rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press));
153         edit_group_button.signal_button_release_event().connect (mem_fun(*this, &AudioTimeAxisView::edit_click), false);
154         playlist_button.signal_clicked().connect (mem_fun(*this, &AudioTimeAxisView::playlist_click));
155         automation_button.signal_clicked().connect (mem_fun(*this, &AudioTimeAxisView::automation_click));
156         size_button.signal_button_release_event().connect (mem_fun(*this, &AudioTimeAxisView::size_click), false);
157         visual_button.signal_clicked().connect (mem_fun(*this, &AudioTimeAxisView::visual_click));
158         hide_button.signal_clicked().connect (mem_fun(*this, &AudioTimeAxisView::hide_click));
159
160         // FIXME: hack (pretty colours)
161         if (is_audio_track())
162                 controls_ebox.set_name ("AudioTimeAxisViewControlsBaseUnselected");
163         else
164                 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
165
166         if (is_audio_track()) {
167                 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
168         }
169         controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
170         controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::FILL|Gtk::EXPAND, 0, 0);
171
172         controls_table.attach (edit_group_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
173
174         ARDOUR_UI::instance()->tooltips().set_tip(*rec_enable_button, _("Record"));
175         ARDOUR_UI::instance()->tooltips().set_tip(*solo_button,_("Solo"));
176         ARDOUR_UI::instance()->tooltips().set_tip(*mute_button,_("Mute"));
177         ARDOUR_UI::instance()->tooltips().set_tip(edit_group_button,_("Edit Group"));
178         ARDOUR_UI::instance()->tooltips().set_tip(size_button,_("Display Height"));
179         ARDOUR_UI::instance()->tooltips().set_tip(playlist_button,_("Playlist"));
180         ARDOUR_UI::instance()->tooltips().set_tip(automation_button, _("Automation"));
181         ARDOUR_UI::instance()->tooltips().set_tip(visual_button, _("Visual options"));
182         ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("Hide this track"));
183         
184         label_view ();
185
186         controls_table.attach (hide_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
187         controls_table.attach (visual_button, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
188         controls_table.attach (size_button, 2, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
189         controls_table.attach (automation_button, 3, 4, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
190
191         if (is_audio_track() && audio_track()->mode() == ARDOUR::Normal) {
192                 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
193         }
194
195         /* remove focus from the buttons */
196         
197         automation_button.unset_flags (Gtk::CAN_FOCUS);
198         solo_button->unset_flags (Gtk::CAN_FOCUS);
199         mute_button->unset_flags (Gtk::CAN_FOCUS);
200         edit_group_button.unset_flags (Gtk::CAN_FOCUS);
201         size_button.unset_flags (Gtk::CAN_FOCUS);
202         playlist_button.unset_flags (Gtk::CAN_FOCUS);
203         hide_button.unset_flags (Gtk::CAN_FOCUS);
204         visual_button.unset_flags (Gtk::CAN_FOCUS);
205
206         /* map current state of the route */
207
208         update_diskstream_display ();
209         solo_changed(0);
210         mute_changed(0);
211         redirects_changed (0);
212         reset_redirect_automation_curves ();
213         y_position = -1;
214
215         ensure_xml_node ();
216
217         set_state (*xml_node);
218         
219         _route.mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
220         _route.solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
221         _route.redirects_changed.connect (mem_fun(*this, &AudioTimeAxisView::redirects_changed));
222         _route.name_changed.connect (mem_fun(*this, &AudioTimeAxisView::route_name_changed));
223         _route.solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
224         _route.panner().Changed.connect (mem_fun(*this, &AudioTimeAxisView::update_pans));
225
226         if (is_audio_track()) {
227
228                 /* track */
229
230                 audio_track()->FreezeChange.connect (mem_fun(*this, &AudioTimeAxisView::map_frozen));
231
232                 audio_track()->diskstream_changed.connect (mem_fun(*this, &AudioTimeAxisView::diskstream_changed));
233                 get_diskstream()->speed_changed.connect (mem_fun(*this, &AudioTimeAxisView::speed_changed));
234
235                 controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
236                 controls_base_selected_name = "AudioTrackControlsBaseSelected";
237                 controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
238
239                 /* ask for notifications of any new RegionViews */
240
241                 view->AudioRegionViewAdded.connect (mem_fun(*this, &AudioTimeAxisView::region_view_added));
242
243                 view->attach ();
244
245                 /* pick up the correct freeze state */
246
247                 map_frozen ();
248
249         } else {
250                 /* bus */
251
252                 controls_ebox.set_name ("BusControlsBaseUnselected");
253                 controls_base_selected_name = "BusControlsBaseSelected";
254                 controls_base_unselected_name = "BusControlsBaseUnselected";
255         }
256
257         editor.ZoomChanged.connect (mem_fun(*this, &AudioTimeAxisView::reset_samples_per_unit));
258         ColorChanged.connect (mem_fun (*this, &AudioTimeAxisView::color_handler));
259 }
260
261 AudioTimeAxisView::~AudioTimeAxisView ()
262 {
263         GoingAway (); /* EMIT_SIGNAL */
264
265         if (playlist_menu) {
266                 delete playlist_menu;
267                 playlist_menu = 0;
268         }
269   
270         if (playlist_action_menu) {
271                 delete playlist_action_menu;
272                 playlist_action_menu = 0;
273         }
274
275         vector_delete (&redirect_automation_curves);
276
277         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
278                 delete *i;
279         }
280
281         if (view) {
282                 delete view;
283                 view = 0;
284         }
285 }
286
287 guint32
288 AudioTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
289 {
290         ensure_xml_node ();
291         xml_node->add_property ("shown_editor", "yes");
292                 
293         return TimeAxisView::show_at (y, nth, parent);
294 }
295
296 void
297 AudioTimeAxisView::hide ()
298 {
299         ensure_xml_node ();
300         xml_node->add_property ("shown_editor", "no");
301
302         TimeAxisView::hide ();
303 }
304
305 void
306 AudioTimeAxisView::set_playlist (AudioPlaylist *newplaylist)
307 {
308         AudioPlaylist *pl;
309
310         modified_connection.disconnect ();
311         state_changed_connection.disconnect ();
312         
313         if ((pl = dynamic_cast<AudioPlaylist*> (playlist())) != 0) {
314                 state_changed_connection = pl->StateChanged.connect (mem_fun(*this, &AudioTimeAxisView::playlist_state_changed));
315                 modified_connection = pl->Modified.connect (mem_fun(*this, &AudioTimeAxisView::playlist_modified));
316         }
317 }
318
319 void
320 AudioTimeAxisView::playlist_modified ()
321 {
322 }
323
324 gint
325 AudioTimeAxisView::edit_click (GdkEventButton *ev)
326 {
327         if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
328                 _route.set_edit_group (0, this);
329                 return FALSE;
330         } 
331
332         using namespace Menu_Helpers;
333
334         MenuList& items = edit_group_menu.items ();
335         RadioMenuItem::Group group;
336
337         items.clear ();
338         items.push_back (RadioMenuElem (group, _("No group"), 
339                                         bind (mem_fun(*this, &AudioTimeAxisView::set_edit_group_from_menu), (RouteGroup *) 0)));
340         
341         if (_route.edit_group() == 0) {
342                 static_cast<RadioMenuItem*>(&items.back())->set_active ();
343         }
344         
345         _session.foreach_edit_group (bind (mem_fun (*this, &AudioTimeAxisView::add_edit_group_menu_item), &group));
346         edit_group_menu.popup (ev->button, ev->time);
347
348         return FALSE;
349 }
350
351 void
352 AudioTimeAxisView::add_edit_group_menu_item (RouteGroup *eg, RadioMenuItem::Group* group)
353 {
354         using namespace Menu_Helpers;
355
356         MenuList &items = edit_group_menu.items();
357
358         cerr << "adding edit group called " << eg->name() << endl;
359
360         items.push_back (RadioMenuElem (*group, eg->name(), bind (mem_fun(*this, &AudioTimeAxisView::set_edit_group_from_menu), eg)));
361         if (_route.edit_group() == eg) {
362                 static_cast<RadioMenuItem*>(&items.back())->set_active ();
363         }
364 }
365
366 void
367 AudioTimeAxisView::set_edit_group_from_menu (RouteGroup *eg)
368
369 {
370         _route.set_edit_group (eg, this);
371 }
372
373 void
374 AudioTimeAxisView::playlist_state_changed (Change ignored)
375 {
376         // ENSURE_GUI_THREAD (bind (mem_fun(*this, &AudioTimeAxisView::playlist_state_changed), ignored));
377         // why are we here ?
378 }
379
380 void
381 AudioTimeAxisView::playlist_changed ()
382
383 {
384         label_view ();
385
386         if (is_audio_track()) {
387                 set_playlist (dynamic_cast<AudioPlaylist*>(get_diskstream()->playlist()));
388         }
389 }
390
391 void
392 AudioTimeAxisView::label_view ()
393 {
394         string x = _route.name();
395
396         if (x != name_entry.get_text()) {
397                 name_entry.set_text (x);
398         }
399
400         ARDOUR_UI::instance()->tooltips().set_tip (name_entry, x);
401 }
402
403 void
404 AudioTimeAxisView::route_name_changed (void *src)
405 {
406         editor.route_name_changed (this);
407         label_view ();
408 }
409
410 void
411 AudioTimeAxisView::take_name_changed (void *src)
412
413 {
414         if (src != this) {
415                 label_view ();
416         }
417 }
418
419 void
420 AudioTimeAxisView::playlist_click ()
421 {
422         // always build a new action menu
423         
424         if (playlist_action_menu == 0) {
425                 playlist_action_menu = new Menu;
426                 playlist_action_menu->set_name ("ArdourContextMenu");
427         }
428         
429         build_playlist_menu(playlist_action_menu);
430
431         playlist_action_menu->popup (1, 0);
432 }
433
434 void
435 AudioTimeAxisView::automation_click ()
436 {
437         if (automation_action_menu == 0) {
438                 /* this seems odd, but the automation action
439                    menu is built as part of the display menu.
440                 */
441                 build_display_menu ();
442         }
443         automation_action_menu->popup (1, 0);
444 }
445
446 void
447 AudioTimeAxisView::show_timestretch (jack_nframes_t start, jack_nframes_t end)
448 {
449         double x1;
450         double x2;
451         double y2;
452         
453         TimeAxisView::show_timestretch (start, end);
454
455         hide_timestretch ();
456
457 #if 0   
458         if (ts.empty()) {
459                 return;
460         }
461
462
463         /* check that the time selection was made in our route, or our edit group.
464            remember that edit_group() == 0 implies the route is *not* in a edit group.
465         */
466
467         if (!(ts.track == this || (ts.group != 0 && ts.group == _route.edit_group()))) {
468                 /* this doesn't apply to us */
469                 return;
470         }
471
472         /* ignore it if our edit group is not active */
473         
474         if ((ts.track != this) && _route.edit_group() && !_route.edit_group()->is_active()) {
475                 return;
476         }
477 #endif
478
479         if (timestretch_rect == 0) {
480                 timestretch_rect = new SimpleRect (*canvas_display);
481                 timestretch_rect->property_x1() =  0.0;
482                 timestretch_rect->property_y1() =  0.0;
483                 timestretch_rect->property_x2() =  0.0;
484                 timestretch_rect->property_y2() =  0.0;
485                 timestretch_rect->property_fill_color_rgba() =  color_map[cTimeStretchFill];
486                 timestretch_rect->property_outline_color_rgba() = color_map[cTimeStretchOutline];
487         }
488
489         timestretch_rect->show ();
490         timestretch_rect->raise_to_top ();
491
492         x1 = start / editor.get_current_zoom();
493         x2 = (end - 1) / editor.get_current_zoom();
494         y2 = height - 2;
495         
496         timestretch_rect->property_x1() = x1;
497         timestretch_rect->property_y1() = 1.0;
498         timestretch_rect->property_x2() = x2;
499         timestretch_rect->property_y2() = y2;
500 }
501
502 void
503 AudioTimeAxisView::hide_timestretch ()
504 {
505         TimeAxisView::hide_timestretch ();
506
507         if (timestretch_rect) {
508                 timestretch_rect->hide ();
509         }
510 }
511
512 void
513 AudioTimeAxisView::show_selection (TimeSelection& ts)
514 {
515
516 #if 0
517         /* ignore it if our edit group is not active or if the selection was started
518            in some other track or edit group (remember that edit_group() == 0 means
519            that the track is not in an edit group).
520         */
521
522         if (((ts.track != this && !is_child (ts.track)) && _route.edit_group() && !_route.edit_group()->is_active()) ||
523             (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route.edit_group())))) {
524                 hide_selection ();
525                 return;
526         }
527 #endif
528
529         TimeAxisView::show_selection (ts);
530 }
531
532 void
533 AudioTimeAxisView::set_state (const XMLNode& node)
534 {
535         const XMLProperty *prop;
536         
537         TimeAxisView::set_state (node);
538         
539         if ((prop = node.property ("shown_editor")) != 0) {
540                 if (prop->value() == "no") {
541                         _marked_for_display = false;
542                 } else {
543                         _marked_for_display = true;
544                 }
545         } else {
546                 _marked_for_display = true;
547         }
548         
549         XMLNodeList nlist = node.children();
550         XMLNodeConstIterator niter;
551         XMLNode *child_node;
552         
553         
554         show_gain_automation = false;
555         show_pan_automation  = false;
556         
557         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
558                 child_node = *niter;
559
560                 if (child_node->name() == "gain") {
561                         XMLProperty *prop=child_node->property ("shown");
562                         
563                         if (prop != 0) {
564                                 if (prop->value() == "yes") {
565                                         show_gain_automation = true;
566                                 }
567                         }
568                         continue;
569                 }
570                 
571                 if (child_node->name() == "pan") {
572                         XMLProperty *prop=child_node->property ("shown");
573                         
574                         if (prop != 0) {
575                                 if (prop->value() == "yes") {
576                                         show_pan_automation = true;
577                                 }                       
578                         }
579                         continue;
580                 }
581         }
582 }
583
584 void
585 AudioTimeAxisView::set_height (TrackHeight h)
586 {
587         bool height_changed = (height == 0) || (h != height_style);
588
589         TimeAxisView::set_height (h);
590
591         ensure_xml_node ();
592
593         view->set_height ((double) height);
594
595         switch (height_style) {
596         case Largest:
597                 xml_node->add_property ("track_height", "largest");
598                 show_name_entry ();
599                 hide_name_label ();
600                 controls_table.show_all();
601                 break;
602         case Large:
603                 xml_node->add_property ("track_height", "large");
604                 show_name_entry ();
605                 hide_name_label ();
606                 controls_table.show_all();
607                 break;
608         case Larger:
609                 xml_node->add_property ("track_height", "larger");
610                 show_name_entry ();
611                 hide_name_label ();
612                 controls_table.show_all();
613                 break;
614         case Normal:
615                 xml_node->add_property ("track_height", "normal");
616                 show_name_entry ();
617                 hide_name_label ();
618                 controls_table.show_all();
619                 break;
620         case Smaller:
621                 xml_node->add_property ("track_height", "smaller");
622                 controls_table.show_all ();
623                 show_name_entry ();
624                 hide_name_label ();
625                 edit_group_button.hide ();
626                 hide_button.hide ();
627                 visual_button.hide ();
628                 size_button.hide ();
629                 automation_button.hide ();
630                 playlist_button.hide ();
631                 break;
632         case Small:
633                 xml_node->add_property ("track_height", "small");
634                 controls_table.hide_all ();
635                 controls_table.show ();
636                 hide_name_entry ();
637                 show_name_label ();
638                 name_label.set_text (_route.name());
639                 break;
640         }
641
642         if (height_changed) {
643                 /* only emit the signal if the height really changed */
644                  _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
645         }
646 }
647
648 void
649 AudioTimeAxisView::select_track_color ()
650 {
651         if (RouteUI::choose_color ()) {
652
653                 if (view) {
654                         view->apply_color (_color, StreamView::RegionColor);
655                 }
656         }
657 }
658
659 void
660 AudioTimeAxisView::reset_redirect_automation_curves ()
661 {
662         for (vector<RedirectAutomationLine*>::iterator i = redirect_automation_curves.begin(); i != redirect_automation_curves.end(); ++i) {
663                 (*i)->reset();
664         }
665 }
666
667 void
668 AudioTimeAxisView::reset_samples_per_unit ()
669 {
670         set_samples_per_unit (editor.get_current_zoom());
671 }
672
673 void
674 AudioTimeAxisView::set_samples_per_unit (double spu)
675 {
676         double speed = 1.0;
677
678         if (get_diskstream() != 0) {
679                 speed = get_diskstream()->speed();
680         }
681         
682         if (view) {
683                 view->set_samples_per_unit (spu * speed);
684         }
685
686         TimeAxisView::set_samples_per_unit (spu * speed);
687 }
688
689 void
690 AudioTimeAxisView::build_display_menu ()
691 {
692         using namespace Menu_Helpers;
693
694         /* get the size menu ready */
695
696         build_size_menu ();
697
698         /* prepare it */
699
700         TimeAxisView::build_display_menu ();
701
702         /* now fill it with our stuff */
703
704         MenuList& items = display_menu->items();
705         display_menu->set_name ("ArdourContextMenu");
706         
707         items.push_back (MenuElem (_("Height"), *size_menu));
708         items.push_back (MenuElem (_("Color"), mem_fun(*this, &AudioTimeAxisView::select_track_color)));
709
710
711         items.push_back (SeparatorElem());
712         items.push_back (MenuElem (_("Hide all crossfades"), mem_fun(*this, &AudioTimeAxisView::hide_all_xfades)));
713         items.push_back (MenuElem (_("Show all crossfades"), mem_fun(*this, &AudioTimeAxisView::show_all_xfades)));
714         items.push_back (SeparatorElem());
715
716         build_remote_control_menu ();
717         items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu));
718
719         automation_action_menu = manage (new Menu);
720         MenuList& automation_items = automation_action_menu->items();
721         automation_action_menu->set_name ("ArdourContextMenu");
722         
723         automation_items.push_back (MenuElem (_("Show all automation"),
724                                               mem_fun(*this, &AudioTimeAxisView::show_all_automation)));
725
726         automation_items.push_back (MenuElem (_("Show existing automation"),
727                                               mem_fun(*this, &AudioTimeAxisView::show_existing_automation)));
728
729         automation_items.push_back (MenuElem (_("Hide all automation"),
730                                               mem_fun(*this, &AudioTimeAxisView::hide_all_automation)));
731
732         automation_items.push_back (SeparatorElem());
733
734         automation_items.push_back (CheckMenuElem (_("Fader"), 
735                                                    mem_fun(*this, &AudioTimeAxisView::toggle_gain_track)));
736         gain_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
737         gain_automation_item->set_active(show_gain_automation);
738
739         automation_items.push_back (CheckMenuElem (_("Pan"),
740                                                    mem_fun(*this, &AudioTimeAxisView::toggle_pan_track)));
741         pan_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
742         pan_automation_item->set_active(show_pan_automation);
743
744         automation_items.push_back (MenuElem (_("Plugins"), subplugin_menu));
745
746         items.push_back (MenuElem (_("Automation"), *automation_action_menu));
747
748         Menu *waveform_menu = manage(new Menu);
749         MenuList& waveform_items = waveform_menu->items();
750         waveform_menu->set_name ("ArdourContextMenu");
751         
752         waveform_items.push_back (CheckMenuElem (_("Show waveforms"), mem_fun(*this, &AudioTimeAxisView::toggle_waveforms)));
753         waveform_item = static_cast<CheckMenuItem *> (&waveform_items.back());
754         ignore_toggle = true;
755         waveform_item->set_active (editor.show_waveforms());
756         ignore_toggle = false;
757
758         RadioMenuItem::Group group;
759
760         waveform_items.push_back (RadioMenuElem (group, _("Traditional"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Traditional)));
761         traditional_item = static_cast<RadioMenuItem *> (&waveform_items.back());
762
763         waveform_items.push_back (RadioMenuElem (group, _("Rectified"), bind (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Rectified)));
764         rectified_item = static_cast<RadioMenuItem *> (&waveform_items.back());
765
766         items.push_back (MenuElem (_("Waveform"), *waveform_menu));
767
768         if (is_audio_track()) {
769
770                 Menu* alignment_menu = manage (new Menu);
771                 MenuList& alignment_items = alignment_menu->items();
772                 alignment_menu->set_name ("ArdourContextMenu");
773
774                 RadioMenuItem::Group align_group;
775                 
776                 alignment_items.push_back (RadioMenuElem (align_group, _("Align with existing material"), bind (mem_fun(*this, &AudioTimeAxisView::set_align_style), ExistingMaterial)));
777                 align_existing_item = dynamic_cast<RadioMenuItem*>(&alignment_items.back());
778                 if (get_diskstream()->alignment_style() == ExistingMaterial) {
779                         align_existing_item->set_active();
780                 }
781                 alignment_items.push_back (RadioMenuElem (align_group, _("Align with capture time"), bind (mem_fun(*this, &AudioTimeAxisView::set_align_style), CaptureTime)));
782                 align_capture_item = dynamic_cast<RadioMenuItem*>(&alignment_items.back());
783                 if (get_diskstream()->alignment_style() == CaptureTime) {
784                         align_capture_item->set_active();
785                 }
786                 
787                 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
788
789                 get_diskstream()->AlignmentStyleChanged.connect (mem_fun(*this, &AudioTimeAxisView::align_style_changed));
790         }
791
792         items.push_back (SeparatorElem());
793         items.push_back (CheckMenuElem (_("Active"), mem_fun(*this, &RouteUI::toggle_route_active)));
794         route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
795         route_active_menu_item->set_active (_route.active());
796
797         items.push_back (SeparatorElem());
798         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route)));
799
800 }
801
802 void
803 AudioTimeAxisView::align_style_changed ()
804 {
805         switch (get_diskstream()->alignment_style()) {
806         case ExistingMaterial:
807                 if (!align_existing_item->get_active()) {
808                         align_existing_item->set_active();
809                 }
810                 break;
811         case CaptureTime:
812                 if (!align_capture_item->get_active()) {
813                         align_capture_item->set_active();
814                 }
815                 break;
816         }
817 }
818
819 void
820 AudioTimeAxisView::set_align_style (AlignStyle style)
821 {
822         get_diskstream()->set_align_style (style);
823 }
824
825 void
826 AudioTimeAxisView::rename_current_playlist ()
827 {
828         ArdourPrompter prompter (true);
829         string name;
830
831         AudioPlaylist *pl;
832         AudioDiskstream *ds;
833
834         if (((ds = dynamic_cast<AudioDiskstream*>(get_diskstream())) == 0) || ds->destructive()
835                         || ((pl = dynamic_cast<AudioPlaylist*>(ds->playlist())) == 0)) {
836                 return;
837         }
838
839         prompter.set_prompt (_("Name for playlist"));
840         prompter.set_initial_text (pl->name());
841         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
842         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
843
844         switch (prompter.run ()) {
845         case Gtk::RESPONSE_ACCEPT:
846                 prompter.get_result (name);
847                 if (name.length()) {
848                         pl->set_name (name);
849                 }
850                 break;
851
852         default:
853                 break;
854         }
855 }
856
857 void
858 AudioTimeAxisView::use_copy_playlist (bool prompt)
859 {
860         AudioPlaylist *pl;
861         AudioDiskstream *ds;
862         string name;
863
864         if (((ds = dynamic_cast<AudioDiskstream*>(get_diskstream())) == 0) || ds->destructive()
865                         || ((pl = dynamic_cast<AudioPlaylist*>(ds->playlist())) == 0)) {
866                 return;
867         }
868         
869         name = Playlist::bump_name (pl->name(), _session);
870
871         if (prompt) {
872
873                 ArdourPrompter prompter (true);
874                 
875                 prompter.set_prompt (_("Name for Playlist"));
876                 prompter.set_initial_text (name);
877                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
878                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
879                 prompter.show_all ();
880                 
881                 switch (prompter.run ()) {
882                 case Gtk::RESPONSE_ACCEPT:
883                         prompter.get_result (name);
884                         break;
885                         
886                 default:
887                         return;
888                 }
889         }
890
891         if (name.length()) {
892                 ds->use_copy_playlist ();
893                 pl = dynamic_cast<AudioPlaylist*>(ds->playlist());
894                 pl->set_name (name);
895         }
896 }
897
898 void
899 AudioTimeAxisView::use_new_playlist (bool prompt)
900 {
901         AudioPlaylist *pl;
902         AudioDiskstream *ds;
903         string name;
904
905         if (((ds = dynamic_cast<AudioDiskstream*>(get_diskstream())) == 0) || ds->destructive()
906                         || ((pl = dynamic_cast<AudioPlaylist*>(ds->playlist())) == 0)) {
907                 return;
908         }
909         
910         name = Playlist::bump_name (pl->name(), _session);
911
912         if (prompt) {
913                 
914                 ArdourPrompter prompter (true);
915                 
916                 prompter.set_prompt (_("Name for Playlist"));
917                 prompter.set_initial_text (name);
918                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
919                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
920                 
921                 switch (prompter.run ()) {
922                 case Gtk::RESPONSE_ACCEPT:
923                         prompter.get_result (name);
924                         break;
925                         
926                 default:
927                         return;
928                 }
929         }
930
931         if (name.length()) {
932                 ds->use_new_playlist ();
933                 pl = dynamic_cast<AudioPlaylist*>(ds->playlist());
934                 pl->set_name (name);
935         }
936 }
937
938 void
939 AudioTimeAxisView::clear_playlist ()
940 {
941         AudioPlaylist *pl;
942         AudioDiskstream *ds;
943         
944         if ((ds = dynamic_cast<AudioDiskstream*>(get_diskstream())) != 0) {
945                 if ((pl = dynamic_cast<AudioPlaylist*>(ds->playlist())) != 0) {
946                         editor.clear_playlist (*pl);
947                 }
948         }
949 }
950
951 void
952 AudioTimeAxisView::toggle_waveforms ()
953 {
954         if (view && waveform_item && !ignore_toggle) {
955                 view->set_show_waveforms (waveform_item->get_active());
956         }
957 }
958
959 void
960 AudioTimeAxisView::set_show_waveforms (bool yn)
961 {
962         if (waveform_item) {
963                 waveform_item->set_active (yn);
964         } else {
965                 view->set_show_waveforms (yn);
966         }
967 }
968
969 void
970 AudioTimeAxisView::set_show_waveforms_recording (bool yn)
971 {
972         if (view) {
973                 view->set_show_waveforms_recording (yn);
974         }
975 }
976
977 void
978 AudioTimeAxisView::set_waveform_shape (WaveformShape shape)
979 {
980         if (view) {
981                 view->set_waveform_shape (shape);
982         }
983 }
984
985 void
986 AudioTimeAxisView::speed_changed ()
987 {
988         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AudioTimeAxisView::reset_samples_per_unit));
989 }
990
991 void
992 AudioTimeAxisView::diskstream_changed (void *src)
993 {
994         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AudioTimeAxisView::update_diskstream_display));
995 }       
996
997 void
998 AudioTimeAxisView::update_diskstream_display ()
999 {
1000         AudioDiskstream *ds;
1001
1002         if ((ds = dynamic_cast<AudioDiskstream*>(get_diskstream())) != 0) {
1003                 set_playlist (dynamic_cast<AudioPlaylist*> (ds->playlist ()));
1004         }
1005
1006         map_frozen ();
1007 }       
1008
1009 void
1010 AudioTimeAxisView::selection_click (GdkEventButton* ev)
1011 {
1012         PublicEditor::TrackViewList* tracks = editor.get_valid_views (this, _route.edit_group());
1013
1014         switch (Keyboard::selection_type (ev->state)) {
1015         case Selection::Toggle:
1016                 /* XXX this is not right */
1017                 editor.get_selection().add (*tracks);
1018                 break;
1019                 
1020         case Selection::Set:
1021                 editor.get_selection().set (*tracks);
1022                 break;
1023
1024         case Selection::Extend:
1025                 /* not defined yet */
1026                 break;
1027         }
1028
1029         delete tracks;
1030 }
1031
1032 void
1033 AudioTimeAxisView::set_selected_regionviews (AudioRegionSelection& regions)
1034 {
1035         if (view) {
1036                 view->set_selected_regionviews (regions);
1037         }
1038 }
1039
1040 void
1041 AudioTimeAxisView::set_selected_points (PointSelection& points)
1042 {
1043         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
1044                 (*i)->set_selected_points (points);
1045         }
1046 }
1047
1048 void
1049 AudioTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
1050 {
1051         double speed = 1.0;
1052         
1053         if (get_diskstream() != 0) {
1054                 speed = get_diskstream()->speed();
1055         }
1056         
1057         jack_nframes_t start_adjusted = session_frame_to_track_frame(start, speed);
1058         jack_nframes_t end_adjusted   = session_frame_to_track_frame(end, speed);
1059
1060         if (view && ((top < 0.0 && bot < 0.0)) || touched (top, bot)) {
1061                 view->get_selectables (start_adjusted, end_adjusted, results);
1062         }
1063
1064         /* pick up visible automation tracks */
1065         
1066         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
1067                 if (!(*i)->hidden()) {
1068                         (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1069                 }
1070         }
1071 }
1072
1073 void
1074 AudioTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1075 {
1076         if (view) {
1077                 view->get_inverted_selectables (sel, results);
1078         }
1079
1080         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
1081                 if (!(*i)->hidden()) {
1082                         (*i)->get_inverted_selectables (sel, results);
1083                 }
1084         }
1085
1086         return;
1087 }
1088
1089 RouteGroup*
1090 AudioTimeAxisView::edit_group() const
1091 {
1092         return _route.edit_group();
1093 }
1094
1095 string
1096 AudioTimeAxisView::name() const
1097 {
1098         return _route.name();
1099 }
1100
1101 Playlist *
1102 AudioTimeAxisView::playlist () const 
1103 {
1104         Diskstream *ds;
1105
1106         if ((ds = get_diskstream()) != 0) {
1107                 return ds->playlist(); 
1108         } else {
1109                 return 0; 
1110         }
1111 }
1112
1113 void
1114 AudioTimeAxisView::name_entry_changed ()
1115 {
1116         string x;
1117
1118         x = name_entry.get_text ();
1119         
1120         if (x == _route.name()) {
1121                 return;
1122         }
1123
1124         if (x.length() == 0) {
1125                 name_entry.set_text (_route.name());
1126                 return;
1127         }
1128
1129         strip_whitespace_edges(x);
1130
1131         if (_session.route_name_unique (x)) {
1132                 _route.set_name (x, this);
1133         } else {
1134                 ARDOUR_UI::instance()->popup_error (_("a track already exists with that name"));
1135                 name_entry.set_text (_route.name());
1136         }
1137 }
1138
1139 void
1140 AudioTimeAxisView::visual_click ()
1141 {
1142         popup_display_menu (0);
1143 }
1144
1145 void
1146 AudioTimeAxisView::hide_click ()
1147 {
1148         editor.hide_track_in_display (*this);
1149 }
1150
1151 ARDOUR::AudioDiskstream*
1152 AudioTimeAxisView::get_diskstream() const
1153 {
1154         return dynamic_cast<ARDOUR::AudioDiskstream*>(RouteUI::get_diskstream());
1155 }
1156
1157 Region*
1158 AudioTimeAxisView::find_next_region (jack_nframes_t pos, RegionPoint point, int32_t dir)
1159 {
1160         Diskstream *stream;
1161         Playlist *playlist;
1162
1163         if ((stream = get_diskstream()) != 0 && (playlist = stream->playlist()) != 0) {
1164                 return playlist->find_next_region (pos, point, dir);
1165         }
1166
1167         return 0;
1168 }
1169
1170 void
1171 AudioTimeAxisView::add_gain_automation_child ()
1172 {
1173         XMLProperty* prop;
1174         AutomationLine* line;
1175
1176         gain_track = new GainAutomationTimeAxisView (_session,
1177                                                      _route,
1178                                                      editor,
1179                                                      *this,
1180                                                      parent_canvas,
1181                                                      _("gain"),
1182                                                      _route.gain_automation_curve());
1183         
1184         line = new AutomationGainLine ("automation gain",
1185                                        _session,
1186                                        *gain_track,
1187                                        *gain_track->canvas_display,
1188                                        _route.gain_automation_curve());
1189
1190         line->set_line_color (color_map[cAutomationLine]);
1191         
1192
1193         gain_track->add_line (*line);
1194
1195         add_child (gain_track);
1196
1197         gain_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::gain_hidden));
1198
1199         bool hideit = true;
1200         
1201         XMLNode* node;
1202
1203         if ((node = gain_track->get_state_node()) != 0) {
1204                 if  ((prop = node->property ("shown")) != 0) {
1205                         if (prop->value() == "yes") {
1206                                 hideit = false;
1207                         }
1208                 } 
1209         }
1210
1211         if (hideit) {
1212                 gain_track->hide ();
1213         }
1214 }
1215
1216 void
1217 AudioTimeAxisView::add_pan_automation_child ()
1218 {
1219         XMLProperty* prop;
1220
1221         pan_track = new PanAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, _("pan"));
1222
1223         update_pans ();
1224         
1225         add_child (pan_track);
1226
1227         pan_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::pan_hidden));
1228
1229         ensure_xml_node ();
1230         bool hideit = true;
1231         
1232         XMLNode* node;
1233
1234         if ((node = pan_track->get_state_node()) != 0) {
1235                 if ((prop = node->property ("shown")) != 0) {
1236                         if (prop->value() == "yes") {
1237                                 hideit = false;
1238                         }
1239                 } 
1240         }
1241
1242         if (hideit) {
1243                 pan_track->hide ();
1244         }
1245 }
1246
1247 void
1248 AudioTimeAxisView::update_pans ()
1249 {
1250         Panner::iterator p;
1251         
1252         pan_track->clear_lines ();
1253         
1254         /* we don't draw lines for "greater than stereo" panning.
1255          */
1256
1257         if (_route.n_outputs() > 2) {
1258                 return;
1259         }
1260
1261         for (p = _route.panner().begin(); p != _route.panner().end(); ++p) {
1262
1263                 AutomationLine* line;
1264
1265                 line = new AutomationPanLine ("automation pan", _session, *pan_track,
1266                                               *pan_track->canvas_display, 
1267                                               (*p)->automation());
1268
1269                 if (p == _route.panner().begin()) {
1270                         /* first line is a nice orange */
1271                         line->set_line_color (color_map[cLeftPanAutomationLine]);
1272                 } else {
1273                         /* second line is a nice blue */
1274                         line->set_line_color (color_map[cRightPanAutomationLine]);
1275                 }
1276
1277                 pan_track->add_line (*line);
1278         }
1279 }
1280                 
1281 void
1282 AudioTimeAxisView::toggle_gain_track ()
1283 {
1284
1285         bool showit = gain_automation_item->get_active();
1286
1287         if (showit != gain_track->marked_for_display()) {
1288                 if (showit) {
1289                         gain_track->set_marked_for_display (true);
1290                         gain_track->canvas_display->show();
1291                         gain_track->get_state_node()->add_property ("shown", X_("yes"));
1292                 } else {
1293                         gain_track->set_marked_for_display (false);
1294                         gain_track->hide ();
1295                         gain_track->get_state_node()->add_property ("shown", X_("no"));
1296                 }
1297
1298                 /* now trigger a redisplay */
1299                 
1300                 if (!no_redraw) {
1301                          _route.gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1302                 }
1303         }
1304 }
1305
1306 void
1307 AudioTimeAxisView::gain_hidden ()
1308 {
1309         gain_track->get_state_node()->add_property (X_("shown"), X_("no"));
1310
1311         if (gain_automation_item && !_hidden) {
1312                 gain_automation_item->set_active (false);
1313         }
1314
1315          _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1316 }
1317
1318 void
1319 AudioTimeAxisView::toggle_pan_track ()
1320 {
1321         bool showit = pan_automation_item->get_active();
1322
1323         if (showit != pan_track->marked_for_display()) {
1324                 if (showit) {
1325                         pan_track->set_marked_for_display (true);
1326                         pan_track->canvas_display->show();
1327                         pan_track->get_state_node()->add_property ("shown", X_("yes"));
1328                 } else {
1329                         pan_track->set_marked_for_display (false);
1330                         pan_track->hide ();
1331                         pan_track->get_state_node()->add_property ("shown", X_("no"));
1332                 }
1333
1334                 /* now trigger a redisplay */
1335                 
1336                 if (!no_redraw) {
1337                          _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1338                 }
1339         }
1340 }
1341
1342 void
1343 AudioTimeAxisView::pan_hidden ()
1344 {
1345         pan_track->get_state_node()->add_property ("shown", "no");
1346
1347         if (pan_automation_item && !_hidden) {
1348                 pan_automation_item->set_active (false);
1349         }
1350
1351          _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1352 }
1353
1354 AudioTimeAxisView::RedirectAutomationInfo::~RedirectAutomationInfo ()
1355 {
1356         for (vector<RedirectAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1357                 delete *i;
1358         }
1359 }
1360
1361
1362 AudioTimeAxisView::RedirectAutomationNode::~RedirectAutomationNode ()
1363 {
1364         parent.remove_ran (this);
1365
1366         if (view) {
1367                 delete view;
1368         }
1369 }
1370
1371 void
1372 AudioTimeAxisView::remove_ran (RedirectAutomationNode* ran)
1373 {
1374         if (ran->view) {
1375                 remove_child (ran->view);
1376         }
1377 }
1378
1379 AudioTimeAxisView::RedirectAutomationNode*
1380 AudioTimeAxisView::find_redirect_automation_node (Redirect *redirect, uint32_t what)
1381 {
1382         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1383
1384                 if ((*i)->redirect == redirect) {
1385
1386                         for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1387                                 if ((*ii)->what == what) {
1388                                         return *ii;
1389                                 }
1390                         }
1391                 }
1392         }
1393
1394         return 0;
1395 }
1396
1397 static string 
1398 legalize_for_xml_node (string str)
1399 {
1400         string::size_type pos;
1401         string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=:";
1402         string legal;
1403
1404         legal = str;
1405         pos = 0;
1406
1407         while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
1408                 legal.replace (pos, 1, "_");
1409                 pos += 1;
1410         }
1411
1412         return legal;
1413 }
1414
1415
1416 void
1417 AudioTimeAxisView::add_redirect_automation_curve (Redirect *redirect, uint32_t what)
1418 {
1419         RedirectAutomationLine* ral;
1420         string name;
1421         RedirectAutomationNode* ran;
1422
1423         if ((ran = find_redirect_automation_node (redirect, what)) == 0) {
1424                 fatal << _("programming error: ")
1425                       << string_compose (X_("redirect automation curve for %1:%2 not registered with audio track!"),
1426                                   redirect->name(), what)
1427                       << endmsg;
1428                 /*NOTREACHED*/
1429                 return;
1430         }
1431
1432         if (ran->view) {
1433                 return;
1434         }
1435
1436         name = redirect->describe_parameter (what);
1437
1438         /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1439
1440         char state_name[256];
1441         snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (redirect->name()).c_str(), what);
1442
1443         ran->view = new RedirectAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, name, what, *redirect, state_name);
1444
1445         ral = new RedirectAutomationLine (name, 
1446                                           *redirect, what, _session, *ran->view,
1447                                           *ran->view->canvas_display, redirect->automation_list (what));
1448         
1449         ral->set_line_color (color_map[cRedirectAutomationLine]);
1450         ral->queue_reset ();
1451
1452         ran->view->add_line (*ral);
1453
1454         ran->view->Hiding.connect (bind (mem_fun(*this, &AudioTimeAxisView::redirect_automation_track_hidden), ran, redirect));
1455
1456         if (!ran->view->marked_for_display()) {
1457                 ran->view->hide ();
1458         } else {
1459                 ran->menu_item->set_active (true);
1460         }
1461
1462         add_child (ran->view);
1463
1464         view->foreach_regionview (bind (mem_fun(*this, &AudioTimeAxisView::add_ghost_to_redirect), ran->view));
1465
1466         redirect->mark_automation_visible (what, true);
1467 }
1468
1469 void
1470 AudioTimeAxisView::redirect_automation_track_hidden (AudioTimeAxisView::RedirectAutomationNode* ran, Redirect* r)
1471 {
1472         if (!_hidden) {
1473                 ran->menu_item->set_active (false);
1474         }
1475
1476         r->mark_automation_visible (ran->what, false);
1477
1478          _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1479 }
1480
1481 void
1482 AudioTimeAxisView::add_existing_redirect_automation_curves (Redirect *redirect)
1483 {
1484         set<uint32_t> s;
1485         RedirectAutomationLine *ral;
1486
1487         redirect->what_has_visible_automation (s);
1488
1489         for (set<uint32_t>::iterator i = s.begin(); i != s.end(); ++i) {
1490                 
1491                 if ((ral = find_redirect_automation_curve (redirect, *i)) != 0) {
1492                         ral->queue_reset ();
1493                 } else {
1494                         add_redirect_automation_curve (redirect, (*i));
1495                 }
1496         }
1497 }
1498
1499 void
1500 AudioTimeAxisView::add_redirect_to_subplugin_menu (Redirect* r)
1501 {
1502         using namespace Menu_Helpers;
1503         RedirectAutomationInfo *rai;
1504         list<RedirectAutomationInfo*>::iterator x;
1505         
1506         const std::set<uint32_t>& automatable = r->what_can_be_automated ();
1507         std::set<uint32_t> has_visible_automation;
1508
1509         r->what_has_visible_automation(has_visible_automation);
1510
1511         if (automatable.empty()) {
1512                 return;
1513         }
1514
1515         for (x = redirect_automation.begin(); x != redirect_automation.end(); ++x) {
1516                 if ((*x)->redirect == r) {
1517                         break;
1518                 }
1519         }
1520
1521         if (x == redirect_automation.end()) {
1522
1523                 rai = new RedirectAutomationInfo (r);
1524                 redirect_automation.push_back (rai);
1525
1526         } else {
1527
1528                 rai = *x;
1529
1530         }
1531
1532         /* any older menu was deleted at the top of redirects_changed()
1533            when we cleared the subplugin menu.
1534         */
1535
1536         rai->menu = manage (new Menu);
1537         MenuList& items = rai->menu->items();
1538         rai->menu->set_name ("ArdourContextMenu");
1539
1540         items.clear ();
1541
1542         for (std::set<uint32_t>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
1543
1544                 RedirectAutomationNode* ran;
1545                 CheckMenuItem* mitem;
1546                 
1547                 string name = r->describe_parameter (*i);
1548                 
1549                 items.push_back (CheckMenuElem (name));
1550                 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
1551
1552                 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
1553                         mitem->set_active(true);
1554                 }
1555
1556                 if ((ran = find_redirect_automation_node (r, *i)) == 0) {
1557
1558                         /* new item */
1559                         
1560                         ran = new RedirectAutomationNode (*i, mitem, *this);
1561                         
1562                         rai->lines.push_back (ran);
1563
1564                 } else {
1565
1566                         ran->menu_item = mitem;
1567
1568                 }
1569
1570                 mitem->signal_toggled().connect (bind (mem_fun(*this, &AudioTimeAxisView::redirect_menu_item_toggled), rai, ran));
1571         }
1572
1573         /* add the menu for this redirect, because the subplugin
1574            menu is always cleared at the top of redirects_changed().
1575            this is the result of some poor design in gtkmm and/or
1576            GTK+.
1577         */
1578
1579         subplugin_menu.items().push_back (MenuElem (r->name(), *rai->menu));
1580         rai->valid = true;
1581 }
1582
1583 void
1584 AudioTimeAxisView::redirect_menu_item_toggled (AudioTimeAxisView::RedirectAutomationInfo* rai,
1585                                                AudioTimeAxisView::RedirectAutomationNode* ran)
1586 {
1587         bool showit = ran->menu_item->get_active();
1588         bool redraw = false;
1589
1590         if (ran->view == 0 && showit) {
1591                 add_redirect_automation_curve (rai->redirect, ran->what);
1592                 redraw = true;
1593         }
1594
1595         if (showit != ran->view->marked_for_display()) {
1596
1597                 if (showit) {
1598                         ran->view->set_marked_for_display (true);
1599                         ran->view->canvas_display->show();
1600                 } else {
1601                         rai->redirect->mark_automation_visible (ran->what, true);
1602                         ran->view->set_marked_for_display (false);
1603                         ran->view->hide ();
1604                 }
1605
1606                 redraw = true;
1607
1608         }
1609
1610         if (redraw && !no_redraw) {
1611
1612                 /* now trigger a redisplay */
1613                 
1614                  _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1615
1616         }
1617 }
1618
1619 void
1620 AudioTimeAxisView::redirects_changed (void *src)
1621 {
1622         using namespace Menu_Helpers;
1623
1624         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1625                 (*i)->valid = false;
1626         }
1627
1628         subplugin_menu.items().clear ();
1629
1630         _route.foreach_redirect (this, &AudioTimeAxisView::add_redirect_to_subplugin_menu);
1631         _route.foreach_redirect (this, &AudioTimeAxisView::add_existing_redirect_automation_curves);
1632
1633         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ) {
1634
1635                 list<RedirectAutomationInfo*>::iterator tmp;
1636
1637                 tmp = i;
1638                 ++tmp;
1639
1640                 if (!(*i)->valid) {
1641
1642                         delete *i;
1643                         redirect_automation.erase (i);
1644
1645                 } 
1646
1647                 i = tmp;
1648         }
1649
1650         /* change in visibility was possible */
1651
1652         _route.gui_changed ("track_height", this);
1653 }
1654
1655 RedirectAutomationLine *
1656 AudioTimeAxisView::find_redirect_automation_curve (Redirect *redirect, uint32_t what)
1657 {
1658         RedirectAutomationNode* ran;
1659
1660         if ((ran = find_redirect_automation_node (redirect, what)) != 0) {
1661                 if (ran->view) {
1662                         return dynamic_cast<RedirectAutomationLine*> (ran->view->lines.front());
1663                 } 
1664         }
1665
1666         return 0;
1667 }
1668
1669 void
1670 AudioTimeAxisView::show_all_automation ()
1671 {
1672         no_redraw = true;
1673
1674         pan_automation_item->set_active (true);
1675         gain_automation_item->set_active (true);
1676         
1677         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1678                 for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1679                         if ((*ii)->view == 0) {
1680                                 add_redirect_automation_curve ((*i)->redirect, (*ii)->what);
1681                         } 
1682
1683                         (*ii)->menu_item->set_active (true);
1684                 }
1685         }
1686
1687         no_redraw = false;
1688
1689          _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1690 }
1691
1692 void
1693 AudioTimeAxisView::show_existing_automation ()
1694 {
1695         no_redraw = true;
1696
1697         pan_automation_item->set_active (true);
1698         gain_automation_item->set_active (true);
1699
1700         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1701                 for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1702                         if ((*ii)->view != 0) {
1703                                 (*ii)->menu_item->set_active (true);
1704                         }
1705                 }
1706         }
1707
1708         no_redraw = false;
1709
1710          _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1711 }
1712
1713 void
1714 AudioTimeAxisView::hide_all_automation ()
1715 {
1716         no_redraw = true;
1717
1718         pan_automation_item->set_active (false);
1719         gain_automation_item->set_active (false);
1720
1721         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1722                 for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1723                         (*ii)->menu_item->set_active (false);
1724                 }
1725         }
1726
1727         no_redraw = false;
1728          _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1729 }
1730
1731 bool
1732 AudioTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1733 {
1734         Playlist* what_we_got;
1735         AudioDiskstream* ds = dynamic_cast<AudioDiskstream*>(get_diskstream());
1736         Playlist* playlist;
1737         bool ret = false;
1738
1739         if (ds == 0) {
1740                 /* route is a bus, not a track */
1741                 return false;
1742         }
1743
1744         playlist = ds->playlist();
1745
1746
1747         TimeSelection time (selection.time);
1748         float speed = ds->speed();
1749         if (speed != 1.0f) {
1750                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1751                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1752                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1753                 }
1754         }
1755         
1756         switch (op) {
1757         case Cut:
1758                 _session.add_undo (playlist->get_memento());
1759                 if ((what_we_got = playlist->cut (time)) != 0) {
1760                         editor.get_cut_buffer().add (what_we_got);
1761                         _session.add_redo_no_execute (playlist->get_memento());
1762                         ret = true;
1763                 }
1764                 break;
1765         case Copy:
1766                 if ((what_we_got = playlist->copy (time)) != 0) {
1767                         editor.get_cut_buffer().add (what_we_got);
1768                 }
1769                 break;
1770
1771         case Clear:
1772                 _session.add_undo (playlist->get_memento());
1773                 if ((what_we_got = playlist->cut (time)) != 0) {
1774                         _session.add_redo_no_execute (playlist->get_memento());
1775                         what_we_got->unref ();
1776                         ret = true;
1777                 }
1778                 break;
1779         }
1780
1781         return ret;
1782 }
1783
1784 bool
1785 AudioTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
1786 {
1787         if (!is_audio_track()) {
1788                 return false;
1789         }
1790
1791         Playlist* playlist = get_diskstream()->playlist();
1792         PlaylistSelection::iterator p;
1793         
1794         for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth);
1795
1796         if (p == selection.playlists.end()) {
1797                 return false;
1798         }
1799
1800         if (get_diskstream()->speed() != 1.0f)
1801                 pos = session_frame_to_track_frame(pos, get_diskstream()->speed() );
1802         
1803         _session.add_undo (playlist->get_memento());
1804         playlist->paste (**p, pos, times);
1805         _session.add_redo_no_execute (playlist->get_memento());
1806
1807         return true;
1808 }
1809
1810 void
1811 AudioTimeAxisView::region_view_added (AudioRegionView* arv)
1812 {
1813         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
1814                 AutomationTimeAxisView* atv;
1815
1816                 if ((atv = dynamic_cast<AutomationTimeAxisView*> (*i)) != 0) {
1817                         arv->add_ghost (*atv);
1818                 }
1819         }
1820 }
1821
1822 void
1823 AudioTimeAxisView::add_ghost_to_redirect (AudioRegionView* arv, AutomationTimeAxisView* atv)
1824 {
1825         arv->add_ghost (*atv);
1826 }
1827
1828 list<TimeAxisView*>
1829 AudioTimeAxisView::get_child_list()
1830 {
1831   
1832         list<TimeAxisView*>redirect_children;
1833         
1834         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
1835                 if (!(*i)->hidden()) {
1836                         redirect_children.push_back(*i);
1837                 }
1838         }
1839         return redirect_children;
1840 }
1841
1842
1843 void
1844 AudioTimeAxisView::build_playlist_menu (Gtk::Menu * menu)
1845 {
1846         using namespace Menu_Helpers;
1847
1848         if (!menu || !is_audio_track()) {
1849                 return;
1850         }
1851
1852         MenuList& playlist_items = menu->items();
1853         menu->set_name ("ArdourContextMenu");
1854         playlist_items.clear();
1855
1856         if (playlist_menu) {
1857                 delete playlist_menu;
1858         }
1859         playlist_menu = new Menu;
1860         playlist_menu->set_name ("ArdourContextMenu");
1861
1862         playlist_items.push_back (MenuElem (string_compose (_("Current: %1"), get_diskstream()->playlist()->name())));
1863         playlist_items.push_back (SeparatorElem());
1864         
1865         playlist_items.push_back (MenuElem (_("Rename"), mem_fun(*this, &AudioTimeAxisView::rename_current_playlist)));
1866         playlist_items.push_back (SeparatorElem());
1867
1868         playlist_items.push_back (MenuElem (_("New"), mem_fun(editor, &PublicEditor::new_playlists)));
1869         playlist_items.push_back (MenuElem (_("New Copy"), mem_fun(editor, &PublicEditor::copy_playlists)));
1870         playlist_items.push_back (SeparatorElem());
1871         playlist_items.push_back (MenuElem (_("Clear Current"), mem_fun(editor, &PublicEditor::clear_playlists)));
1872         playlist_items.push_back (SeparatorElem());
1873         playlist_items.push_back (MenuElem(_("Select"), mem_fun(*this, &AudioTimeAxisView::show_playlist_selector)));
1874
1875 }
1876
1877 void
1878 AudioTimeAxisView::show_playlist_selector ()
1879 {
1880         editor.playlist_selector().show_for (this);
1881 }
1882
1883
1884 void
1885 AudioTimeAxisView::map_frozen ()
1886 {
1887         if (!is_audio_track()) {
1888                 return;
1889         }
1890
1891         ENSURE_GUI_THREAD (mem_fun(*this, &AudioTimeAxisView::map_frozen));
1892
1893
1894         switch (audio_track()->freeze_state()) {
1895         case AudioTrack::Frozen:
1896                 playlist_button.set_sensitive (false);
1897                 rec_enable_button->set_sensitive (false);
1898                 break;
1899         default:
1900                 playlist_button.set_sensitive (true);
1901                 rec_enable_button->set_sensitive (true);
1902                 break;
1903         }
1904 }
1905
1906 void
1907 AudioTimeAxisView::show_all_xfades ()
1908 {
1909         if (view) {
1910                 view->show_all_xfades ();
1911         }
1912 }
1913
1914 void
1915 AudioTimeAxisView::hide_all_xfades ()
1916 {
1917         if (view) {
1918                 view->hide_all_xfades ();
1919         }
1920 }
1921
1922 void
1923 AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi)
1924 {
1925         AudioRegionView* rv;
1926
1927         if (view && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
1928                 view->hide_xfades_involving (*rv);
1929         }
1930 }
1931
1932 void
1933 AudioTimeAxisView::reveal_dependent_views (TimeAxisViewItem& tavi)
1934 {
1935         AudioRegionView* rv;
1936
1937         if (view && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
1938                 view->reveal_xfades_involving (*rv);
1939         }
1940 }
1941
1942 void
1943 AudioTimeAxisView::route_active_changed ()
1944 {
1945         RouteUI::route_active_changed ();
1946
1947         if (is_audio_track()) {
1948                 if (_route.active()) {
1949                         controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
1950                         controls_base_selected_name = "AudioTrackControlsBaseSelected";
1951                         controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
1952                 } else {
1953                         controls_ebox.set_name ("AudioTrackControlsBaseInactiveUnselected");
1954                         controls_base_selected_name = "AudioTrackControlsBaseInactiveSelected";
1955                         controls_base_unselected_name = "AudioTrackControlsBaseInactiveUnselected";
1956                 }
1957         } else {
1958                 if (_route.active()) {
1959                         controls_ebox.set_name ("BusControlsBaseUnselected");
1960                         controls_base_selected_name = "BusControlsBaseSelected";
1961                         controls_base_unselected_name = "BusControlsBaseUnselected";
1962                 } else {
1963                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1964                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
1965                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1966                 }
1967         }
1968 }
1969
1970 XMLNode* 
1971 AudioTimeAxisView::get_child_xml_node (const string & childname)
1972 {
1973         return RouteUI::get_child_xml_node (childname);
1974 }
1975
1976 void
1977 AudioTimeAxisView::color_handler (ColorID id, uint32_t val)
1978 {
1979         switch (id) {
1980         case cTimeStretchOutline:
1981                 timestretch_rect->property_outline_color_rgba() = val;
1982                 break;
1983         case cTimeStretchFill:
1984                 timestretch_rect->property_fill_color_rgba() = val;
1985                 break;
1986         default:
1987                 break;
1988         }
1989 }
1990
1991 bool
1992 AudioTimeAxisView::select_me (GdkEventButton* ev)
1993 {
1994         editor.get_selection().add (this);
1995         return false;
1996 }