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