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