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