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