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