shuffle packing order to accomodate midi controls
[ardour.git] / gtk2_ardour / time_axis_view.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 */
19
20 #include <cstdlib>
21 #include <cmath>
22 #include <algorithm>
23 #include <string>
24 #include <list>
25
26
27 #include "pbd/error.h"
28 #include "pbd/convert.h"
29 #include "pbd/stacktrace.h"
30
31 #include <gtkmm2ext/doi.h>
32 #include <gtkmm2ext/utils.h>
33 #include <gtkmm2ext/selector.h>
34
35 #include "canvas/canvas.h"
36 #include "canvas/rectangle.h"
37 #include "canvas/debug.h"
38
39 #include "ardour_ui.h"
40 #include "ardour_dialog.h"
41 #include "global_signals.h"
42 #include "gui_thread.h"
43 #include "public_editor.h"
44 #include "time_axis_view.h"
45 #include "region_view.h"
46 #include "ghostregion.h"
47 #include "selection.h"
48 #include "keyboard.h"
49 #include "rgb_macros.h"
50 #include "utils.h"
51 #include "streamview.h"
52 #include "editor_drag.h"
53 #include "editor.h"
54
55 #include "i18n.h"
56
57 using namespace std;
58 using namespace Gtk;
59 using namespace Gdk;
60 using namespace ARDOUR;
61 using namespace ARDOUR_UI_UTILS;
62 using namespace PBD;
63 using namespace Editing;
64 using namespace ArdourCanvas;
65 using Gtkmm2ext::Keyboard;
66
67 const double trim_handle_size = 6.0; /* pixels */
68 uint32_t TimeAxisView::button_height = 0;
69 uint32_t TimeAxisView::extra_height = 0;
70 int const TimeAxisView::_max_order = 512;
71 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
72
73 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
74         : AxisView (sess)
75         , controls_table (2, 3)
76         , name_table (2, 1)
77         , _name_editing (false)
78         , height (0)
79         , display_menu (0)
80         , parent (rent)
81         , selection_group (0)
82         , _ghost_group (0)
83         , _hidden (false)
84         , in_destructor (false)
85         , _size_menu (0)
86         , _canvas_display (0)
87         , _y_position (0)
88         , _editor (ed)
89         , name_entry (0)
90         , control_parent (0)
91         , _order (0)
92         , _effective_height (0)
93         , _resize_drag_start (-1)
94         , _preresize_cursor (0)
95         , _have_preresize_cursor (false)
96         , _ebox_release_can_act (true)
97 {
98         if (extra_height == 0) {
99                 compute_heights ();
100         }
101
102         _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
103         CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
104         _canvas_display->hide(); // reveal as needed
105
106         selection_group = new ArdourCanvas::Container (_canvas_display);
107         CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
108         selection_group->set_data (X_("timeselection"), (void *) 1);
109         selection_group->hide();
110         
111         _ghost_group = new ArdourCanvas::Container (_canvas_display);
112         CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
113         _ghost_group->lower_to_bottom();
114         _ghost_group->show();
115
116         name_label.set_name ("TrackLabel");
117         name_label.set_alignment (0.0, 0.5);
118         name_label.set_width_chars (12);
119         ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
120
121         Gtk::Entry* an_entry = new Gtk::Entry;
122         Gtk::Requisition req;
123         an_entry->size_request (req);
124         name_label.set_size_request (-1, req.height);
125         name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
126         delete an_entry;
127
128         name_hbox.pack_start (name_label, true, true);
129         name_hbox.show ();
130         name_label.show ();
131
132         controls_table.set_row_spacings (2);
133         controls_table.set_col_spacings (2);
134         controls_table.set_border_width (2);
135         controls_table.set_homogeneous (true);
136
137         controls_table.show_all ();
138         controls_table.set_no_show_all ();
139
140         name_table.attach (name_hbox, 0, 5, 0, 1,  Gtk::FILL|Gtk::EXPAND,  Gtk::FILL|Gtk::EXPAND, 3, 0);
141         name_table.show_all ();
142         name_table.set_no_show_all ();
143
144         HSeparator* separator = manage (new HSeparator());
145         separator->set_name("TrackSeparator");
146         separator->set_size_request(-1, 1);
147         separator->show();
148
149         name_vbox.pack_start (name_table, false, false);
150         name_vbox.show ();
151
152         controls_hbox.pack_start (controls_table, false, false);
153         controls_hbox.show ();
154
155         controls_hbox.pack_start (name_vbox, true, true);
156         controls_hbox.show ();
157
158         controls_vbox.pack_start (controls_hbox, false, false);
159         controls_vbox.show ();
160
161         top_hbox.pack_start (controls_vbox, true, true);
162         top_hbox.show ();
163
164         //controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
165         controls_ebox.add (top_hbox);
166         controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
167                                   Gdk::BUTTON_RELEASE_MASK|
168                                   Gdk::POINTER_MOTION_MASK|
169                                   Gdk::ENTER_NOTIFY_MASK|
170                                   Gdk::LEAVE_NOTIFY_MASK|
171                                   Gdk::SCROLL_MASK);
172         controls_ebox.set_flags (CAN_FOCUS);
173
174         /* note that this handler connects *before* the default handler */
175         controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
176         controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
177         controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
178         controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
179         controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
180         controls_ebox.show ();
181
182         time_axis_vbox.pack_start (controls_ebox, true, true, 0);
183         time_axis_vbox.pack_end (*separator, false, false);
184         time_axis_vbox.show();
185
186         ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
187
188         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
189 }
190
191 TimeAxisView::~TimeAxisView()
192 {
193         in_destructor = true;
194
195         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
196                 delete *i;
197         }
198
199         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
200                 delete (*i)->rect;
201                 delete (*i)->start_trim;
202                 delete (*i)->end_trim;
203
204         }
205
206         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
207                 delete (*i)->rect;
208                 delete (*i)->start_trim;
209                 delete (*i)->end_trim;
210         }
211
212         delete selection_group;
213         selection_group = 0;
214
215         delete _canvas_display;
216         _canvas_display = 0;
217
218         delete display_menu;
219         display_menu = 0;
220
221         delete _size_menu;
222 }
223
224 void
225 TimeAxisView::hide ()
226 {
227         if (_hidden) {
228                 return;
229         }
230
231         _canvas_display->hide ();
232
233         if (control_parent) {
234                 control_parent->remove (time_axis_vbox);
235                 control_parent = 0;
236         }
237
238         _y_position = -1;
239         _hidden = true;
240
241         /* now hide children */
242
243         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
244                 (*i)->hide ();
245         }
246
247         /* if its hidden, it cannot be selected */
248         _editor.get_selection().remove (this);
249         /* and neither can its regions */
250         _editor.get_selection().remove_regions (this);
251
252         Hiding ();
253 }
254
255 /** Display this TimeAxisView as the nth component of the parent box, at y.
256 *
257 * @param y y position.
258 * @param nth index for this TimeAxisView, increased if this view has children.
259 * @param parent parent component.
260 * @return height of this TimeAxisView.
261 */
262 guint32
263 TimeAxisView::show_at (double y, int& nth, VBox *parent)
264 {
265         if (control_parent) {
266                 control_parent->reorder_child (time_axis_vbox, nth);
267         } else {
268                 control_parent = parent;
269                 parent->pack_start (time_axis_vbox, false, false);
270                 parent->reorder_child (time_axis_vbox, nth);
271         }
272
273         _order = nth;
274
275         if (_y_position != y) {
276                 _canvas_display->set_y_position (y);
277                 _y_position = y;
278
279         }
280
281         _canvas_display->raise_to_top ();
282         _canvas_display->show ();
283
284         _hidden = false;
285
286         _effective_height = current_height ();
287
288         /* now show relevant children */
289
290         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
291                 if ((*i)->marked_for_display()) {
292                         ++nth;
293                         _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
294                 } else {
295                         (*i)->hide ();
296                 }
297         }
298
299         return _effective_height;
300 }
301
302 bool
303 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
304 {
305         switch (ev->direction) {
306         case GDK_SCROLL_UP:
307                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
308                         /* See Editor::_stepping_axis_view for notes on this hack */
309                         Editor& e = dynamic_cast<Editor&> (_editor);
310                         if (!e.stepping_axis_view ()) {
311                                 e.set_stepping_axis_view (this);
312                         }
313                         e.stepping_axis_view()->step_height (false);
314                         return true;
315                 } 
316                 break;
317
318         case GDK_SCROLL_DOWN:
319                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
320                         /* See Editor::_stepping_axis_view for notes on this hack */
321                         Editor& e = dynamic_cast<Editor&> (_editor);
322                         if (!e.stepping_axis_view ()) {
323                                 e.set_stepping_axis_view (this);
324                         }
325                         e.stepping_axis_view()->step_height (true);
326                         return true;
327                 } 
328                 break;
329
330         default:
331                 /* no handling for left/right, yet */
332                 break;
333         }
334
335         /* Just forward to the normal canvas scroll method. The coordinate
336            systems are different but since the canvas is always larger than the
337            track headers, and aligned with the trackview area, this will work.
338
339            In the not too distant future this layout is going away anyway and
340            headers will be on the canvas.
341         */
342         return _editor.canvas_scroll_event (ev, false);
343 }
344
345 bool
346 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
347 {
348         if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
349                 /* see if it is inside the name label */
350                 if (name_label.is_ancestor (controls_ebox)) {
351                         int nlx;
352                         int nly;
353                         controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
354                         Gtk::Allocation a = name_label.get_allocation ();
355                         if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
356                                 begin_name_edit ();
357                                 _ebox_release_can_act = false;
358                                 return true;
359                         }
360                 }
361
362         }
363
364         _ebox_release_can_act = true;
365                         
366         if (maybe_set_cursor (event->y) > 0) {
367                 _resize_drag_start = event->y_root;
368         }
369
370         return true;
371 }
372
373 void
374 TimeAxisView::idle_resize (uint32_t h)
375 {
376         set_height (h);
377 }
378
379
380 bool
381 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
382 {
383         if (_resize_drag_start >= 0) {
384
385                 /* (ab)use the DragManager to do autoscrolling - basically we
386                  * are pretending that the drag is taking place over the canvas
387                  * (which perhaps in the glorious future, when track headers
388                  * and the canvas are unified, will actually be true.)
389                 */
390
391                 _editor.maybe_autoscroll (false, true, true);
392
393                 /* now schedule the actual TAV resize */
394                 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
395                 _editor.add_to_idle_resize (this, delta);
396                 _resize_drag_start = ev->y_root;
397         } else {
398                 /* not dragging but ... */
399                 maybe_set_cursor (ev->y);
400         }
401
402         gdk_event_request_motions(ev);
403         return true;
404 }
405
406 bool
407 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
408 {
409         if (_have_preresize_cursor) {
410                 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
411                 _have_preresize_cursor = false;
412         }
413         return true;
414 }
415
416 bool
417 TimeAxisView::maybe_set_cursor (int y)
418 {
419         /* XXX no Gtkmm Gdk::Window::get_cursor() */
420         Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
421
422         if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
423
424                 /* y-coordinate in lower 25% */
425
426                 if (!_have_preresize_cursor) {
427                         _preresize_cursor = gdk_window_get_cursor (win->gobj());
428                         _have_preresize_cursor = true;
429                         win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
430                 }
431
432                 return 1;
433
434         } else if (_have_preresize_cursor) {
435                 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
436                 _have_preresize_cursor = false;
437
438                 return -1;
439         }
440
441         return 0;
442 }
443
444 bool
445 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
446 {
447         if (_resize_drag_start >= 0) {
448                 if (_have_preresize_cursor) {
449                         gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
450                         _preresize_cursor = 0;
451                         _have_preresize_cursor = false;
452                 }
453                 _editor.stop_canvas_autoscroll ();
454                 _resize_drag_start = -1;
455         }
456
457         if (!_ebox_release_can_act) {
458                 return true;
459         }
460
461         switch (ev->button) {
462         case 1:
463                 selection_click (ev);
464                 break;
465
466         case 3:
467                 popup_display_menu (ev->time);
468                 break;
469         }
470
471         return true;
472 }
473
474 void
475 TimeAxisView::selection_click (GdkEventButton* ev)
476 {
477         Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
478         _editor.set_selected_track (*this, op, false);
479 }
480
481
482 /** Steps through the defined heights for this TrackView.
483  *  @param coarser true if stepping should decrease in size, otherwise false.
484  */
485 void
486 TimeAxisView::step_height (bool coarser)
487 {
488         static const uint32_t step = 25;
489
490         if (coarser) {
491
492                 if (height <= preset_height (HeightSmall)) {
493                         return;
494                 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
495                         set_height_enum (HeightSmall);
496                 } else {
497                         set_height (height - step);
498                 }
499
500         } else {
501
502                 if (height <= preset_height(HeightSmall)) {
503                         set_height_enum (HeightNormal);
504                 } else {
505                         set_height (height + step);
506                 }
507
508         }
509 }
510
511 void
512 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
513 {
514         if (apply_to_selection) {
515                 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
516         } else {
517                 set_height (preset_height (h));
518         }
519 }
520
521 void
522 TimeAxisView::set_height (uint32_t h)
523 {
524         if (h < preset_height (HeightSmall)) {
525                 h = preset_height (HeightSmall);
526         }
527
528         time_axis_vbox.property_height_request () = h;
529         height = h;
530
531         char buf[32];
532         snprintf (buf, sizeof (buf), "%u", height);
533         set_gui_property ("height", buf);
534
535         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
536                 (*i)->set_height ();
537         }
538
539         if (selection_group->visible ()) {
540                 /* resize the selection rect */
541                 show_selection (_editor.get_selection().time);
542         }
543
544         _editor.override_visible_track_count ();
545 }
546
547 bool
548 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
549 {
550         /* steal escape, tabs from GTK */
551
552         switch (ev->keyval) {
553         case GDK_Escape:
554         case GDK_ISO_Left_Tab:
555         case GDK_Tab:
556                 return true;
557         }
558         return false;
559 }
560
561 bool
562 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
563 {
564         TrackViewList::iterator i;
565
566         switch (ev->keyval) {
567         case GDK_Escape:
568                 end_name_edit (RESPONSE_CANCEL);
569                 return true;
570
571         /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
572          * generates a different ev->keyval, rather than setting
573          * ev->state.
574          */
575         case GDK_ISO_Left_Tab:
576                 end_name_edit (RESPONSE_APPLY);
577                 return true;
578
579         case GDK_Tab:
580                 end_name_edit (RESPONSE_ACCEPT);
581                 return true;
582         default:
583                 break;
584         }
585
586         return false;
587 }
588
589 bool
590 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
591 {
592         end_name_edit (RESPONSE_OK);
593         return false;
594 }
595
596 void
597 TimeAxisView::begin_name_edit ()
598 {
599         if (name_entry) {
600                 return;
601         }
602
603         if (can_edit_name()) {
604
605                 name_entry = manage (new Gtkmm2ext::FocusEntry);
606                 
607                 name_entry->set_width_chars(15);
608
609                 name_entry->set_name ("EditorTrackNameDisplay");
610                 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
611                 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
612                 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
613                 name_entry->set_text (name_label.get_text());
614                 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
615
616                 if (name_label.is_ancestor (name_hbox)) {
617                         name_hbox.remove (name_label);
618                 }
619                 
620                 name_hbox.pack_start (*name_entry, false, false);
621                 name_entry->show ();
622
623                 name_entry->select_region (0, -1);
624                 name_entry->set_state (STATE_SELECTED);
625                 name_entry->grab_focus ();
626                 name_entry->start_editing (0);
627         }
628 }
629
630 void
631 TimeAxisView::end_name_edit (int response)
632 {
633         if (!name_entry) {
634                 return;
635         }
636         
637         bool edit_next = false;
638         bool edit_prev = false;
639
640         switch (response) {
641         case RESPONSE_CANCEL:
642                 break;
643         case RESPONSE_OK:
644                 name_entry_changed ();
645                 break;
646         case RESPONSE_ACCEPT:
647                 name_entry_changed ();
648                 edit_next = true;
649         case RESPONSE_APPLY:
650                 name_entry_changed ();
651                 edit_prev = true;
652         }
653
654         /* this will delete the name_entry. but it will also drop focus, which
655          * will cause another callback to this function, so set name_entry = 0
656          * first to ensure we don't double-remove etc. etc.
657          */
658
659         Gtk::Entry* tmp = name_entry;
660         name_entry = 0;
661         name_hbox.remove (*tmp);
662
663         /* put the name label back */
664
665         name_hbox.pack_start (name_label);
666         name_label.show ();
667
668         if (edit_next) {
669
670                 TrackViewList const & allviews = _editor.get_track_views ();
671                 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
672                 
673                 if (i != allviews.end()) {
674                         
675                         do {
676                                 if (++i == allviews.end()) {
677                                         return;
678                                 }
679                                 
680                                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
681                         
682                                 if (rtav && rtav->route()->record_enabled()) {
683                                         continue;
684                                 }
685                                 
686                                 if (!(*i)->hidden()) {
687                                         break;
688                                 }
689                                 
690                         } while (true);
691                 }
692
693                 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
694                         _editor.ensure_time_axis_view_is_visible (**i, false);
695                         (*i)->begin_name_edit ();
696                 } 
697
698         } else if (edit_prev) {
699
700                 TrackViewList const & allviews = _editor.get_track_views ();
701                 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
702                 
703                 if (i != allviews.begin()) {
704                         do {
705                                 if (i == allviews.begin()) {
706                                         return;
707                                 }
708                                 
709                                 --i;
710                                 
711                                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
712                                 
713                                 if (rtav && rtav->route()->record_enabled()) {
714                                         continue;
715                                 }
716                                 
717                                 if (!(*i)->hidden()) {
718                                         break;
719                                 }
720                                 
721                         } while (true);
722                 }
723                 
724                 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
725                         _editor.ensure_time_axis_view_is_visible (**i, false);
726                         (*i)->begin_name_edit ();
727                 } 
728         }
729 }
730
731 void
732 TimeAxisView::name_entry_changed ()
733 {
734 }
735
736 bool
737 TimeAxisView::can_edit_name () const
738 {
739         return true;
740 }
741
742 void
743 TimeAxisView::conditionally_add_to_selection ()
744 {
745         Selection& s (_editor.get_selection ());
746
747         if (!s.selected (this)) {
748                 _editor.set_selected_track (*this, Selection::Set);
749         }
750 }
751
752 void
753 TimeAxisView::popup_display_menu (guint32 when)
754 {
755         conditionally_add_to_selection ();
756
757         build_display_menu ();
758         display_menu->popup (1, when);
759 }
760
761 void
762 TimeAxisView::set_selected (bool yn)
763 {
764         if (yn == _selected) {
765                 return;
766         }
767
768         Selectable::set_selected (yn);
769
770         if (_selected) {
771                 controls_ebox.set_name (controls_base_selected_name);
772                 time_axis_vbox.set_name (controls_base_selected_name);
773                 controls_vbox.set_name (controls_base_selected_name);
774         } else {
775                 controls_ebox.set_name (controls_base_unselected_name);
776                 time_axis_vbox.set_name (controls_base_unselected_name);
777                 controls_vbox.set_name (controls_base_unselected_name);
778                 hide_selection ();
779
780                 /* children will be set for the yn=true case. but when deselecting
781                    the editor only has a list of top-level trackviews, so we
782                    have to do this here.
783                 */
784
785                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
786                         (*i)->set_selected (false);
787                 }
788         }
789 }
790
791 void
792 TimeAxisView::build_display_menu ()
793 {
794         using namespace Menu_Helpers;
795
796         delete display_menu;
797
798         display_menu = new Menu;
799         display_menu->set_name ("ArdourContextMenu");
800
801         // Just let implementing classes define what goes into the manu
802 }
803
804 void
805 TimeAxisView::set_samples_per_pixel (double fpp)
806 {
807         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
808                 (*i)->set_samples_per_pixel (fpp);
809         }
810 }
811
812 void
813 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
814 {
815         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
816                 (*i)->show_timestretch (start, end, layers, layer);
817         }
818 }
819
820 void
821 TimeAxisView::hide_timestretch ()
822 {
823         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
824                 (*i)->hide_timestretch ();
825         }
826 }
827
828 void
829 TimeAxisView::show_selection (TimeSelection& ts)
830 {
831         double x1;
832         double x2;
833         double y2;
834         SelectionRect *rect;
835
836         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
837                 (*i)->show_selection (ts);
838         }
839
840         if (selection_group->visible ()) {
841                 while (!used_selection_rects.empty()) {
842                         free_selection_rects.push_front (used_selection_rects.front());
843                         used_selection_rects.pop_front();
844                         free_selection_rects.front()->rect->hide();
845                         free_selection_rects.front()->start_trim->hide();
846                         free_selection_rects.front()->end_trim->hide();
847                 }
848                 selection_group->hide();
849         }
850
851         selection_group->show();
852         selection_group->raise_to_top();
853
854         for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
855                 framepos_t start, end;
856                 framecnt_t cnt;
857
858                 start = (*i).start;
859                 end = (*i).end;
860                 cnt = end - start + 1;
861
862                 rect = get_selection_rect ((*i).id);
863
864                 x1 = _editor.sample_to_pixel (start);
865                 x2 = _editor.sample_to_pixel (start + cnt - 1);
866                 y2 = current_height() - 1;
867
868                 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
869
870                 // trim boxes are at the top for selections
871
872                 if (x2 > x1) {
873                         rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
874                         rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
875
876                         rect->start_trim->show();
877                         rect->end_trim->show();
878                 } else {
879                         rect->start_trim->hide();
880                         rect->end_trim->hide();
881                 }
882
883                 rect->rect->show ();
884                 used_selection_rects.push_back (rect);
885         }
886 }
887
888 void
889 TimeAxisView::reshow_selection (TimeSelection& ts)
890 {
891         show_selection (ts);
892
893         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
894                 (*i)->show_selection (ts);
895         }
896 }
897
898 void
899 TimeAxisView::hide_selection ()
900 {
901         if (selection_group->visible ()) {
902                 while (!used_selection_rects.empty()) {
903                         free_selection_rects.push_front (used_selection_rects.front());
904                         used_selection_rects.pop_front();
905                         free_selection_rects.front()->rect->hide();
906                         free_selection_rects.front()->start_trim->hide();
907                         free_selection_rects.front()->end_trim->hide();
908                 }
909                 selection_group->hide();
910         }
911
912         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
913                 (*i)->hide_selection ();
914         }
915 }
916
917 void
918 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
919 {
920         /* find the selection rect this is for. we have the item corresponding to one
921            of the trim handles.
922          */
923
924         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
925                 if ((*i)->start_trim == item || (*i)->end_trim == item) {
926
927                         /* make one trim handle be "above" the other so that if they overlap,
928                            the top one is the one last used.
929                         */
930
931                         (*i)->rect->raise_to_top ();
932                         (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
933                         (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
934
935                         break;
936                 }
937         }
938 }
939
940 SelectionRect *
941 TimeAxisView::get_selection_rect (uint32_t id)
942 {
943         SelectionRect *rect;
944
945         /* check to see if we already have a visible rect for this particular selection ID */
946
947         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
948                 if ((*i)->id == id) {
949                         return (*i);
950                 }
951         }
952
953         /* ditto for the free rect list */
954
955         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
956                 if ((*i)->id == id) {
957                         SelectionRect* ret = (*i);
958                         free_selection_rects.erase (i);
959                         return ret;
960                 }
961         }
962
963         /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
964
965         if (free_selection_rects.empty()) {
966
967                 rect = new SelectionRect;
968
969                 rect->rect = new ArdourCanvas::Rectangle (selection_group);
970                 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
971                 rect->rect->set_outline (false);
972                 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
973
974                 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
975                 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
976                 rect->start_trim->set_outline (false);
977                 rect->start_trim->set_fill (false);
978
979                 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
980                 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
981                 rect->end_trim->set_outline (false);
982                 rect->end_trim->set_fill (false);
983
984                 free_selection_rects.push_front (rect);
985
986                 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
987                 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
988                 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
989         }
990
991         rect = free_selection_rects.front();
992         rect->id = id;
993         free_selection_rects.pop_front();
994         return rect;
995 }
996
997 struct null_deleter { void operator()(void const *) const {} };
998
999 bool
1000 TimeAxisView::is_child (TimeAxisView* tav)
1001 {
1002         return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1003 }
1004
1005 void
1006 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1007 {
1008         children.push_back (child);
1009 }
1010
1011 void
1012 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1013 {
1014         Children::iterator i;
1015
1016         if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1017                 children.erase (i);
1018         }
1019 }
1020
1021 /** Get selectable things within a given range.
1022  *  @param start Start time in session frames.
1023  *  @param end End time in session frames.
1024  *  @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1025  *  @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1026  *  @param result Filled in with selectable things.
1027  */
1028 void
1029 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1030 {
1031         return;
1032 }
1033
1034 void
1035 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1036 {
1037         return;
1038 }
1039
1040 void
1041 TimeAxisView::add_ghost (RegionView* rv)
1042 {
1043         GhostRegion* gr = rv->add_ghost (*this);
1044
1045         if (gr) {
1046                 ghosts.push_back(gr);
1047         }
1048 }
1049
1050 void
1051 TimeAxisView::remove_ghost (RegionView* rv)
1052 {
1053         rv->remove_ghost_in (*this);
1054 }
1055
1056 void
1057 TimeAxisView::erase_ghost (GhostRegion* gr)
1058 {
1059         if (in_destructor) {
1060                 return;
1061         }
1062
1063         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1064                 if ((*i) == gr) {
1065                         ghosts.erase (i);
1066                         break;
1067                 }
1068         }
1069 }
1070
1071 bool
1072 TimeAxisView::touched (double top, double bot)
1073 {
1074         /* remember: this is X Window - coordinate space starts in upper left and moves down.
1075           y_position is the "origin" or "top" of the track.
1076         */
1077
1078         double mybot = _y_position + current_height();
1079
1080         return ((_y_position <= bot && _y_position >= top) ||
1081                 ((mybot <= bot) && (top < mybot)) ||
1082                 (mybot >= bot && _y_position < top));
1083 }
1084
1085 void
1086 TimeAxisView::set_parent (TimeAxisView& p)
1087 {
1088         parent = &p;
1089 }
1090
1091 void
1092 TimeAxisView::reset_height ()
1093 {
1094         set_height (height);
1095
1096         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1097                 (*i)->set_height ((*i)->height);
1098         }
1099 }
1100
1101 void
1102 TimeAxisView::compute_heights ()
1103 {
1104         Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1105         Gtk::Table two_row_table (2, 8);
1106         Gtk::Table one_row_table (1, 8);
1107         Button* buttons[5];
1108         const int border_width = 2;
1109
1110         const int separator_height = 2;
1111         extra_height = (2 * border_width) + separator_height;
1112
1113         window.add (one_row_table);
1114
1115         one_row_table.set_border_width (border_width);
1116         one_row_table.set_row_spacings (0);
1117         one_row_table.set_col_spacings (0);
1118         one_row_table.set_homogeneous (true);
1119
1120         two_row_table.set_border_width (border_width);
1121         two_row_table.set_row_spacings (0);
1122         two_row_table.set_col_spacings (0);
1123         two_row_table.set_homogeneous (true);
1124
1125         for (int i = 0; i < 5; ++i) {
1126                 buttons[i] = manage (new Button (X_("f")));
1127                 buttons[i]->set_name ("TrackMuteButton");
1128         }
1129
1130         one_row_table.attach (*buttons[0], 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
1131
1132         one_row_table.show_all ();
1133         Gtk::Requisition req(one_row_table.size_request ());
1134
1135         // height required to show 1 row of buttons
1136         button_height = req.height;
1137 }
1138
1139 void
1140 TimeAxisView::color_handler ()
1141 {
1142         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1143                 (*i)->set_colors();
1144         }
1145
1146         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1147
1148                 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1149                 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1150
1151                 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1152                 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1153                 
1154                 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1155                 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1156         }
1157         
1158         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1159                 
1160                 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1161                 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1162                 
1163                 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1164                 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1165                 
1166                 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1167                 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1168         }
1169 }
1170
1171 /** @return Pair: TimeAxisView, layer index.
1172  * TimeAxisView is non-0 if this object covers @param y, or one of its children
1173  * does. @param y is an offset from the top of the trackview area.
1174  *
1175  * If the covering object is a child axis, then the child is returned.
1176  * TimeAxisView is 0 otherwise.
1177  *
1178  * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1179  * and is in stacked or expanded * region display mode, otherwise 0.
1180  */
1181 std::pair<TimeAxisView*, double>
1182 TimeAxisView::covers_y_position (double y) const
1183 {
1184         if (hidden()) {
1185                 return std::make_pair ((TimeAxisView *) 0, 0);
1186         }
1187
1188         if (_y_position <= y && y < (_y_position + height)) {
1189
1190                 /* work out the layer index if appropriate */
1191                 double l = 0;
1192                 switch (layer_display ()) {
1193                 case Overlaid:
1194                         break;
1195                 case Stacked:
1196                         if (view ()) {
1197                                 /* compute layer */
1198                                 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1199                                 /* clamp to max layers to be on the safe side; sometimes the above calculation
1200                                    returns a too-high value */
1201                                 if (l >= view()->layers ()) {
1202                                         l = view()->layers() - 1;
1203                                 }
1204                         }
1205                         break;
1206                 case Expanded:
1207                         if (view ()) {
1208                                 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1209                                 l = n * 0.5 - 0.5;
1210                                 if (l >= (view()->layers() - 0.5)) {
1211                                         l = view()->layers() - 0.5;
1212                                 }
1213                         }
1214                         break;
1215                 }
1216
1217                 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1218         }
1219
1220         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1221
1222                 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1223                 if (r.first) {
1224                         return r;
1225                 }
1226         }
1227
1228         return std::make_pair ((TimeAxisView *) 0, 0);
1229 }
1230
1231 bool
1232 TimeAxisView::covered_by_y_range (double y0, double y1) const
1233 {
1234         if (hidden()) {
1235                 return false;
1236         }
1237
1238         /* if either the top or bottom of the axisview is in the vertical
1239          * range, we cover it.
1240          */
1241         
1242         if ((y0 < _y_position && y1 < _y_position) ||
1243             (y0 >= _y_position + height && y1 >= _y_position + height)) {
1244                 return false;
1245         }
1246
1247         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1248                 if ((*i)->covered_by_y_range (y0, y1)) {
1249                         return true;
1250                 }
1251         }
1252
1253         return true;
1254 }
1255
1256 uint32_t
1257 TimeAxisView::preset_height (Height h)
1258 {
1259         switch (h) {
1260         case HeightLargest:
1261                 return (button_height * 2) + extra_height + 260;
1262         case HeightLarger:
1263                 return (button_height * 2) + extra_height + 160;
1264         case HeightLarge:
1265                 return (button_height * 2) + extra_height + 60;
1266         case HeightNormal:
1267                 return (button_height * 2) + extra_height + 10;
1268         case HeightSmall:
1269                 return button_height + extra_height;
1270         }
1271
1272         /* NOTREACHED */
1273         return 0;
1274 }
1275
1276 /** @return Child time axis views that are not hidden */
1277 TimeAxisView::Children
1278 TimeAxisView::get_child_list ()
1279 {
1280         Children c;
1281
1282         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1283                 if (!(*i)->hidden()) {
1284                         c.push_back(*i);
1285                 }
1286         }
1287
1288         return c;
1289 }
1290
1291 void
1292 TimeAxisView::build_size_menu ()
1293 {
1294         if (_size_menu && _size_menu->gobj ()) {
1295                 return;
1296         }
1297
1298         delete _size_menu;
1299
1300         using namespace Menu_Helpers;
1301
1302         _size_menu = new Menu;
1303         _size_menu->set_name ("ArdourContextMenu");
1304         MenuList& items = _size_menu->items();
1305
1306         items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1307         items.push_back (MenuElem (_("Larger"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1308         items.push_back (MenuElem (_("Large"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1309         items.push_back (MenuElem (_("Normal"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1310         items.push_back (MenuElem (_("Small"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1311 }
1312
1313 void
1314 TimeAxisView::reset_visual_state ()
1315 {
1316         /* this method is not required to trigger a global redraw */
1317
1318         string str = gui_property ("height");
1319         
1320         if (!str.empty()) {
1321                 set_height (atoi (str));
1322         } else {
1323                 set_height (preset_height (HeightNormal));
1324         }
1325 }
1326
1327 TrackViewList
1328 TrackViewList::filter_to_unique_playlists ()
1329 {
1330         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1331         TrackViewList ts;
1332
1333         for (iterator i = begin(); i != end(); ++i) {
1334                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1335                 if (!rtav) {
1336                         /* not a route: include it anyway */
1337                         ts.push_back (*i);
1338                 } else {
1339                         boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1340                         if (t) {
1341                                 if (playlists.insert (t->playlist()).second) {
1342                                         /* playlist not seen yet */
1343                                         ts.push_back (*i);
1344                                 }
1345                         } else {
1346                                 /* not a track: include it anyway */
1347                                 ts.push_back (*i);
1348                         }
1349                 }
1350         }
1351         return ts;
1352 }