when track selection changes, cancel any and all name editing
[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 (can_edit_name() && name_entry.get_visible()) {
785                 end_name_edit (RESPONSE_CANCEL);
786         }
787
788         if (yn == _selected) {
789                 return;
790         }
791
792         Selectable::set_selected (yn);
793
794         if (_selected) {
795                 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
796                 time_axis_frame.set_name ("MixerStripSelectedFrame");
797                 controls_ebox.set_name (controls_base_selected_name);
798                 controls_vbox.set_name (controls_base_selected_name);
799                 time_axis_vbox.set_name (controls_base_selected_name);
800         } else {
801                 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
802                 time_axis_frame.set_name (controls_base_unselected_name);
803                 controls_ebox.set_name (controls_base_unselected_name);
804                 controls_vbox.set_name (controls_base_unselected_name);
805                 time_axis_vbox.set_name (controls_base_unselected_name);
806
807                 hide_selection ();
808
809                 /* children will be set for the yn=true case. but when deselecting
810                    the editor only has a list of top-level trackviews, so we
811                    have to do this here.
812                 */
813
814                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
815                         (*i)->set_selected (false);
816                 }
817         }
818
819         time_axis_frame.show();
820
821 }
822
823 void
824 TimeAxisView::build_display_menu ()
825 {
826         using namespace Menu_Helpers;
827
828         delete display_menu;
829
830         display_menu = new Menu;
831         display_menu->set_name ("ArdourContextMenu");
832
833         // Just let implementing classes define what goes into the manu
834 }
835
836 void
837 TimeAxisView::set_samples_per_pixel (double fpp)
838 {
839         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
840                 (*i)->set_samples_per_pixel (fpp);
841         }
842 }
843
844 void
845 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
846 {
847         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
848                 (*i)->show_timestretch (start, end, layers, layer);
849         }
850 }
851
852 void
853 TimeAxisView::hide_timestretch ()
854 {
855         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
856                 (*i)->hide_timestretch ();
857         }
858 }
859
860 void
861 TimeAxisView::show_selection (TimeSelection& ts)
862 {
863         double x1;
864         double x2;
865         double y2;
866         SelectionRect *rect;    time_axis_frame.show();
867
868
869         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
870                 (*i)->show_selection (ts);
871         }
872
873         if (selection_group->visible ()) {
874                 while (!used_selection_rects.empty()) {
875                         free_selection_rects.push_front (used_selection_rects.front());
876                         used_selection_rects.pop_front();
877                         free_selection_rects.front()->rect->hide();
878                         free_selection_rects.front()->start_trim->hide();
879                         free_selection_rects.front()->end_trim->hide();
880                 }
881                 selection_group->hide();
882         }
883
884         selection_group->show();
885         selection_group->raise_to_top();
886
887         for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
888                 framepos_t start, end;
889                 framecnt_t cnt;
890
891                 start = (*i).start;
892                 end = (*i).end;
893                 cnt = end - start + 1;
894
895                 rect = get_selection_rect ((*i).id);
896
897                 x1 = _editor.sample_to_pixel (start);
898                 x2 = _editor.sample_to_pixel (start + cnt - 1);
899                 y2 = current_height() - 1;
900
901                 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
902
903                 // trim boxes are at the top for selections
904
905                 if (x2 > x1) {
906                         rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
907                         rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
908
909                         rect->start_trim->show();
910                         rect->end_trim->show();
911                 } else {
912                         rect->start_trim->hide();
913                         rect->end_trim->hide();
914                 }
915
916                 rect->rect->show ();
917                 used_selection_rects.push_back (rect);
918         }
919 }
920
921 void
922 TimeAxisView::reshow_selection (TimeSelection& ts)
923 {
924         show_selection (ts);
925
926         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
927                 (*i)->show_selection (ts);
928         }
929 }
930
931 void
932 TimeAxisView::hide_selection ()
933 {
934         if (selection_group->visible ()) {
935                 while (!used_selection_rects.empty()) {
936                         free_selection_rects.push_front (used_selection_rects.front());
937                         used_selection_rects.pop_front();
938                         free_selection_rects.front()->rect->hide();
939                         free_selection_rects.front()->start_trim->hide();
940                         free_selection_rects.front()->end_trim->hide();
941                 }
942                 selection_group->hide();
943         }
944
945         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
946                 (*i)->hide_selection ();
947         }
948 }
949
950 void
951 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
952 {
953         /* find the selection rect this is for. we have the item corresponding to one
954            of the trim handles.
955          */
956
957         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
958                 if ((*i)->start_trim == item || (*i)->end_trim == item) {
959
960                         /* make one trim handle be "above" the other so that if they overlap,
961                            the top one is the one last used.
962                         */
963
964                         (*i)->rect->raise_to_top ();
965                         (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
966                         (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
967
968                         break;
969                 }
970         }
971 }
972
973 SelectionRect *
974 TimeAxisView::get_selection_rect (uint32_t id)
975 {
976         SelectionRect *rect;
977
978         /* check to see if we already have a visible rect for this particular selection ID */
979
980         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
981                 if ((*i)->id == id) {
982                         return (*i);
983                 }
984         }
985
986         /* ditto for the free rect list */
987
988         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
989                 if ((*i)->id == id) {
990                         SelectionRect* ret = (*i);
991                         free_selection_rects.erase (i);
992                         return ret;
993                 }
994         }
995
996         /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
997
998         if (free_selection_rects.empty()) {
999
1000                 rect = new SelectionRect;
1001
1002                 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1003                 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1004                 rect->rect->set_outline (false);
1005                 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1006
1007                 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1008                 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1009                 rect->start_trim->set_outline (false);
1010                 rect->start_trim->set_fill (false);
1011
1012                 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1013                 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1014                 rect->end_trim->set_outline (false);
1015                 rect->end_trim->set_fill (false);
1016
1017                 free_selection_rects.push_front (rect);
1018
1019                 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1020                 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1021                 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1022         }
1023
1024         rect = free_selection_rects.front();
1025         rect->id = id;
1026         free_selection_rects.pop_front();
1027         return rect;
1028 }
1029
1030 struct null_deleter { void operator()(void const *) const {} };
1031
1032 bool
1033 TimeAxisView::is_child (TimeAxisView* tav)
1034 {
1035         return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1036 }
1037
1038 void
1039 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1040 {
1041         children.push_back (child);
1042 }
1043
1044 void
1045 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1046 {
1047         Children::iterator i;
1048
1049         if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1050                 children.erase (i);
1051         }
1052 }
1053
1054 /** Get selectable things within a given range.
1055  *  @param start Start time in session frames.
1056  *  @param end End time in session frames.
1057  *  @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1058  *  @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1059  *  @param result Filled in with selectable things.
1060  */
1061 void
1062 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1063 {
1064         return;
1065 }
1066
1067 void
1068 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1069 {
1070         return;
1071 }
1072
1073 void
1074 TimeAxisView::add_ghost (RegionView* rv)
1075 {
1076         GhostRegion* gr = rv->add_ghost (*this);
1077
1078         if (gr) {
1079                 ghosts.push_back(gr);
1080         }
1081 }
1082
1083 void
1084 TimeAxisView::remove_ghost (RegionView* rv)
1085 {
1086         rv->remove_ghost_in (*this);
1087 }
1088
1089 void
1090 TimeAxisView::erase_ghost (GhostRegion* gr)
1091 {
1092         if (in_destructor) {
1093                 return;
1094         }
1095
1096         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1097                 if ((*i) == gr) {
1098                         ghosts.erase (i);
1099                         break;
1100                 }
1101         }
1102 }
1103
1104 bool
1105 TimeAxisView::touched (double top, double bot)
1106 {
1107         /* remember: this is X Window - coordinate space starts in upper left and moves down.
1108           y_position is the "origin" or "top" of the track.
1109         */
1110
1111         double mybot = _y_position + current_height();
1112
1113         return ((_y_position <= bot && _y_position >= top) ||
1114                 ((mybot <= bot) && (top < mybot)) ||
1115                 (mybot >= bot && _y_position < top));
1116 }
1117
1118 void
1119 TimeAxisView::set_parent (TimeAxisView& p)
1120 {
1121         parent = &p;
1122 }
1123
1124 void
1125 TimeAxisView::reset_height ()
1126 {
1127         set_height (height);
1128
1129         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1130                 (*i)->set_height ((*i)->height);
1131         }
1132 }
1133
1134 void
1135 TimeAxisView::compute_heights ()
1136 {
1137         // TODO this function should be re-evaluated when font-scaling changes (!)
1138         Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1139         Gtk::Table one_row_table (1, 1);
1140         ArdourButton* test_button = manage (new ArdourButton);
1141         const int border_width = 2;
1142         const int frame_height = 2;
1143         extra_height = (2 * border_width) + frame_height;
1144
1145         window.add (one_row_table);
1146         test_button->set_name ("mute button");
1147         test_button->set_text (_("M"));
1148
1149         one_row_table.set_border_width (border_width);
1150         one_row_table.set_row_spacings (2);
1151         one_row_table.set_col_spacings (2);
1152
1153         one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1154         one_row_table.show_all ();
1155
1156         Gtk::Requisition req(one_row_table.size_request ());
1157         button_height = req.height;
1158 }
1159
1160 void
1161 TimeAxisView::color_handler ()
1162 {
1163         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1164                 (*i)->set_colors();
1165         }
1166
1167         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1168
1169                 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1170                 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1171
1172                 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1173                 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1174                 
1175                 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1176                 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1177         }
1178         
1179         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1180                 
1181                 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1182                 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1183                 
1184                 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1185                 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1186                 
1187                 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1188                 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1189         }
1190 }
1191
1192 /** @return Pair: TimeAxisView, layer index.
1193  * TimeAxisView is non-0 if this object covers @param y, or one of its children
1194  * does. @param y is an offset from the top of the trackview area.
1195  *
1196  * If the covering object is a child axis, then the child is returned.
1197  * TimeAxisView is 0 otherwise.
1198  *
1199  * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1200  * and is in stacked or expanded * region display mode, otherwise 0.
1201  */
1202 std::pair<TimeAxisView*, double>
1203 TimeAxisView::covers_y_position (double y) const
1204 {
1205         if (hidden()) {
1206                 return std::make_pair ((TimeAxisView *) 0, 0);
1207         }
1208
1209         if (_y_position <= y && y < (_y_position + height)) {
1210
1211                 /* work out the layer index if appropriate */
1212                 double l = 0;
1213                 switch (layer_display ()) {
1214                 case Overlaid:
1215                         break;
1216                 case Stacked:
1217                         if (view ()) {
1218                                 /* compute layer */
1219                                 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1220                                 /* clamp to max layers to be on the safe side; sometimes the above calculation
1221                                    returns a too-high value */
1222                                 if (l >= view()->layers ()) {
1223                                         l = view()->layers() - 1;
1224                                 }
1225                         }
1226                         break;
1227                 case Expanded:
1228                         if (view ()) {
1229                                 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1230                                 l = n * 0.5 - 0.5;
1231                                 if (l >= (view()->layers() - 0.5)) {
1232                                         l = view()->layers() - 0.5;
1233                                 }
1234                         }
1235                         break;
1236                 }
1237
1238                 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1239         }
1240
1241         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1242
1243                 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1244                 if (r.first) {
1245                         return r;
1246                 }
1247         }
1248
1249         return std::make_pair ((TimeAxisView *) 0, 0);
1250 }
1251
1252 bool
1253 TimeAxisView::covered_by_y_range (double y0, double y1) const
1254 {
1255         if (hidden()) {
1256                 return false;
1257         }
1258
1259         /* if either the top or bottom of the axisview is in the vertical
1260          * range, we cover it.
1261          */
1262         
1263         if ((y0 < _y_position && y1 < _y_position) ||
1264             (y0 >= _y_position + height && y1 >= _y_position + height)) {
1265                 return false;
1266         }
1267
1268         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1269                 if ((*i)->covered_by_y_range (y0, y1)) {
1270                         return true;
1271                 }
1272         }
1273
1274         return true;
1275 }
1276
1277 uint32_t
1278 TimeAxisView::preset_height (Height h)
1279 {
1280         switch (h) {
1281         case HeightLargest:
1282                 return (button_height * 2) + extra_height + 260;
1283         case HeightLarger:
1284                 return (button_height * 2) + extra_height + 160;
1285         case HeightLarge:
1286                 return (button_height * 2) + extra_height + 60;
1287         case HeightNormal:
1288                 return (button_height * 2) + extra_height + 10;
1289         case HeightSmall:
1290                 return button_height + extra_height;
1291         }
1292
1293         /* NOTREACHED */
1294         return 0;
1295 }
1296
1297 /** @return Child time axis views that are not hidden */
1298 TimeAxisView::Children
1299 TimeAxisView::get_child_list ()
1300 {
1301         Children c;
1302
1303         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1304                 if (!(*i)->hidden()) {
1305                         c.push_back(*i);
1306                 }
1307         }
1308
1309         return c;
1310 }
1311
1312 void
1313 TimeAxisView::build_size_menu ()
1314 {
1315         if (_size_menu && _size_menu->gobj ()) {
1316                 return;
1317         }
1318
1319         delete _size_menu;
1320
1321         using namespace Menu_Helpers;
1322
1323         _size_menu = new Menu;
1324         _size_menu->set_name ("ArdourContextMenu");
1325         MenuList& items = _size_menu->items();
1326
1327         items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1328         items.push_back (MenuElem (_("Larger"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1329         items.push_back (MenuElem (_("Large"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1330         items.push_back (MenuElem (_("Normal"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1331         items.push_back (MenuElem (_("Small"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1332 }
1333
1334 void
1335 TimeAxisView::reset_visual_state ()
1336 {
1337         /* this method is not required to trigger a global redraw */
1338
1339         string str = gui_property ("height");
1340         
1341         if (!str.empty()) {
1342                 set_height (atoi (str));
1343         } else {
1344                 set_height (preset_height (HeightNormal));
1345         }
1346 }
1347
1348 TrackViewList
1349 TrackViewList::filter_to_unique_playlists ()
1350 {
1351         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1352         TrackViewList ts;
1353
1354         for (iterator i = begin(); i != end(); ++i) {
1355                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1356                 if (!rtav) {
1357                         /* not a route: include it anyway */
1358                         ts.push_back (*i);
1359                 } else {
1360                         boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1361                         if (t) {
1362                                 if (playlists.insert (t->playlist()).second) {
1363                                         /* playlist not seen yet */
1364                                         ts.push_back (*i);
1365                                 }
1366                         } else {
1367                                 /* not a track: include it anyway */
1368                                 ts.push_back (*i);
1369                         }
1370                 }
1371         }
1372         return ts;
1373 }