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