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