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