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