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