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