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