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