Vkeybd: add a mod-wheel
[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         in_destructor = true;
238
239         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
240                 delete *i;
241         }
242
243         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_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
250         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
251                 delete (*i)->rect; (*i)->rect = 0;
252                 delete (*i)->start_trim; (*i)->start_trim = 0;
253                 delete (*i)->end_trim; (*i)->end_trim = 0;
254         }
255
256         delete selection_group;
257         selection_group = 0;
258
259         delete _canvas_display;
260         _canvas_display = 0;
261
262         delete display_menu;
263         display_menu = 0;
264
265         delete _size_menu;
266 }
267
268 void
269 TimeAxisView::hide ()
270 {
271         if (_hidden) {
272                 return;
273         }
274
275         _canvas_display->hide ();
276         _canvas_separator->hide ();
277
278         if (control_parent) {
279                 control_parent->remove (TOP_LEVEL_WIDGET);
280                 control_parent = 0;
281         }
282
283         _y_position = -1;
284         _hidden = true;
285
286         /* now hide children */
287
288         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
289                 (*i)->hide ();
290         }
291
292         /* if its hidden, it cannot be selected */
293         _editor.get_selection().remove (this);
294         /* and neither can its regions */
295         _editor.get_selection().remove_regions (this);
296
297         Hiding ();
298 }
299
300 /** Display this TimeAxisView as the nth component of the parent box, at y.
301 *
302 * @param y y position.
303 * @param nth index for this TimeAxisView, increased if this view has children.
304 * @param parent parent component.
305 *
306 * @return height of this TimeAxisView.
307 */
308 guint32
309 TimeAxisView::show_at (double y, int& nth, VBox *parent)
310 {
311         if (control_parent) {
312                 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
313         } else {
314                 control_parent = parent;
315                 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
316                 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
317         }
318
319         _order = nth;
320
321         if (_y_position != y) {
322                 _canvas_display->set_y_position (y);
323                 _y_position = y;
324         }
325
326         _canvas_display->raise_to_top ();
327         _canvas_display->show ();
328
329         _hidden = false;
330
331         _effective_height = current_height ();
332
333         /* now show relevant children */
334
335         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
336                 if ((*i)->marked_for_display()) {
337                         ++nth;
338                         _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
339                 } else {
340                         (*i)->hide ();
341                 }
342         }
343
344         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
345                 (*i)->set_height ();
346         }
347
348         /* put separator at the bottom of this time axis view */
349
350         _canvas_separator->set (ArdourCanvas::Duple(0, height), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, height));
351         _canvas_separator->lower_to_bottom ();
352         _canvas_separator->show ();
353
354         return _effective_height;
355 }
356
357 bool
358 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
359 {
360         switch (ev->direction) {
361         case GDK_SCROLL_UP:
362                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
363                         /* See Editor::_stepping_axis_view for notes on this hack */
364                         Editor& e = dynamic_cast<Editor&> (_editor);
365                         if (!e.stepping_axis_view ()) {
366                                 e.set_stepping_axis_view (this);
367                         }
368                         e.stepping_axis_view()->step_height (false);
369                         return true;
370                 }
371                 break;
372
373         case GDK_SCROLL_DOWN:
374                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
375                         /* See Editor::_stepping_axis_view for notes on this hack */
376                         Editor& e = dynamic_cast<Editor&> (_editor);
377                         if (!e.stepping_axis_view ()) {
378                                 e.set_stepping_axis_view (this);
379                         }
380                         e.stepping_axis_view()->step_height (true);
381                         return true;
382                 }
383                 break;
384
385         default:
386                 /* no handling for left/right, yet */
387                 break;
388         }
389
390         /* Just forward to the normal canvas scroll method. The coordinate
391            systems are different but since the canvas is always larger than the
392            track headers, and aligned with the trackview area, this will work.
393
394            In the not too distant future this layout is going away anyway and
395            headers will be on the canvas.
396         */
397         return _editor.canvas_scroll_event (ev, false);
398 }
399
400 bool
401 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
402 {
403         if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
404                 /* see if it is inside the name label */
405                 if (name_label.is_ancestor (controls_ebox)) {
406                         int nlx;
407                         int nly;
408                         controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
409                         Gtk::Allocation a = name_label.get_allocation ();
410                         if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
411                                 begin_name_edit ();
412                                 _ebox_release_can_act = false;
413                                 return true;
414                         }
415                 }
416
417         }
418
419         _ebox_release_can_act = true;
420
421         if (maybe_set_cursor (event->y) > 0) {
422                 _resize_drag_start = event->y_root;
423         }
424
425         return true;
426 }
427
428 void
429 TimeAxisView::idle_resize (int32_t h)
430 {
431         set_height (std::max(0, h));
432 }
433
434
435 bool
436 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
437 {
438         if (_resize_drag_start >= 0) {
439
440                 /* (ab)use the DragManager to do autoscrolling - basically we
441                  * are pretending that the drag is taking place over the canvas
442                  * (which perhaps in the glorious future, when track headers
443                  * and the canvas are unified, will actually be true.)
444                  */
445
446                 _editor.maybe_autoscroll (false, true, true);
447
448                 /* now schedule the actual TAV resize */
449                 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
450                 _editor.add_to_idle_resize (this, delta);
451                 _resize_drag_start = ev->y_root;
452                 _did_resize = true;
453         } else {
454                 /* not dragging but ... */
455                 maybe_set_cursor (ev->y);
456         }
457
458         gdk_event_request_motions(ev);
459         return true;
460 }
461
462 bool
463 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
464 {
465         if (_have_preresize_cursor) {
466                 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
467                 _have_preresize_cursor = false;
468         }
469         return true;
470 }
471
472 bool
473 TimeAxisView::maybe_set_cursor (int y)
474 {
475         /* XXX no Gtkmm Gdk::Window::get_cursor() */
476         Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
477
478         if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
479
480                 /* y-coordinate in lower 25% */
481
482                 if (!_have_preresize_cursor) {
483                         _preresize_cursor = gdk_window_get_cursor (win->gobj());
484                         _have_preresize_cursor = true;
485                         win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
486                 }
487
488                 return 1;
489
490         } else if (_have_preresize_cursor) {
491                 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
492                 _have_preresize_cursor = false;
493
494                 return -1;
495         }
496
497         return 0;
498 }
499
500 bool
501 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
502 {
503         if (_resize_drag_start >= 0) {
504                 if (_have_preresize_cursor) {
505                         gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
506                         _preresize_cursor = 0;
507                         _have_preresize_cursor = false;
508                 }
509                 _editor.stop_canvas_autoscroll ();
510                 _resize_drag_start = -1;
511                 if (_did_resize) {
512                         _did_resize = false;
513                         // don't change selection
514                         return true;
515                 }
516         }
517
518         if (!_ebox_release_can_act) {
519                 return true;
520         }
521
522         switch (ev->button) {
523         case 1:
524                 if (selectable()) {
525                         selection_click (ev);
526                 }
527                 break;
528
529         case 3:
530                 popup_display_menu (ev->time);
531                 break;
532         }
533
534         return true;
535 }
536
537 void
538 TimeAxisView::selection_click (GdkEventButton* ev)
539 {
540         Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
541         _editor.set_selected_track (*this, op, false);
542 }
543
544
545 /** Steps through the defined heights for this TrackView.
546  *  @param coarser true if stepping should decrease in size, otherwise false.
547  */
548 void
549 TimeAxisView::step_height (bool coarser)
550 {
551         static const uint32_t step = 25;
552
553         if (coarser) {
554
555                 if (height <= preset_height (HeightSmall)) {
556                         return;
557                 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
558                         set_height_enum (HeightSmall);
559                 } else {
560                         set_height (height - step);
561                 }
562
563         } else {
564
565                 if (height <= preset_height(HeightSmall)) {
566                         set_height_enum (HeightNormal);
567                 } else {
568                         set_height (height + step);
569                 }
570
571         }
572 }
573
574 void
575 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
576 {
577         if (apply_to_selection) {
578                 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
579         } else {
580                 set_height (preset_height (h));
581         }
582 }
583
584 void
585 TimeAxisView::set_height (uint32_t h, TrackHeightMode m)
586 {
587         uint32_t lanes = 0;
588         if (m == TotalHeight) {
589                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
590                         if (!(*i)->hidden()) {
591                                 ++lanes;
592                         }
593                 }
594         }
595         h /= (lanes + 1);
596
597         if (h < preset_height (HeightSmall)) {
598                 h = preset_height (HeightSmall);
599         }
600
601         TOP_LEVEL_WIDGET.property_height_request () = h;
602         height = h;
603
604         set_gui_property ("height", height);
605
606         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
607                 (*i)->set_height ();
608         }
609
610         if (selection_group->visible ()) {
611                 /* resize the selection rect */
612                 show_selection (_editor.get_selection().time);
613         }
614
615         if (m != OnlySelf) {
616                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
617                         (*i)->set_height(h, OnlySelf);
618                 }
619         }
620
621         _editor.override_visible_track_count ();
622 }
623
624 void
625 TimeAxisView::begin_name_edit ()
626 {
627         if (!can_edit_name()) {
628                 return;
629         }
630
631         Gtk::Window* toplevel = (Gtk::Window*) control_parent->get_toplevel();
632         FloatingTextEntry* fte = new FloatingTextEntry (toplevel, name ());
633
634         fte->set_name ("TrackNameEditor");
635         fte->use_text.connect (sigc::mem_fun (*this, &TimeAxisView::end_name_edit));
636
637         /* We want to new toplevel window to overlay the name label, so
638          * translate the coordinates of the upper left corner of the name label
639          * into the coordinate space of the top level window.
640          */
641
642         int x, y;
643         int wx, wy;
644
645         name_label.translate_coordinates (*toplevel, 0, 0, x, y);
646         toplevel->get_window()->get_origin (wx, wy);
647
648         fte->move (wx + x, wy + y);
649         fte->present ();
650 }
651
652 void
653 TimeAxisView::end_name_edit (std::string str, int next_dir)
654 {
655         if (!name_entry_changed (str)) {
656                 next_dir = 0;
657         }
658
659         if (next_dir > 0) {
660
661                 TrackViewList const & allviews = _editor.get_track_views ();
662                 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
663
664                 if (i != allviews.end()) {
665
666                         do {
667                                 if (++i == allviews.end()) {
668                                         return;
669                                 }
670
671                                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
672
673                                 if (rtav && rtav->is_track() && rtav->track()->rec_enable_control()->get_value()) {
674                                         continue;
675                                 }
676
677                                 if (!(*i)->hidden()) {
678                                         break;
679                                 }
680
681                         } while (true);
682                 }
683
684                 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
685                         _editor.ensure_time_axis_view_is_visible (**i, false);
686                         (*i)->begin_name_edit ();
687                 }
688
689         } else if (next_dir < 0) {
690
691                 TrackViewList const & allviews = _editor.get_track_views ();
692                 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
693
694                 if (i != allviews.begin()) {
695                         do {
696                                 if (i == allviews.begin()) {
697                                         return;
698                                 }
699
700                                 --i;
701
702                                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
703
704                                 if (rtav && rtav->is_track() && rtav->track()->rec_enable_control()->get_value()) {
705                                         continue;
706                                 }
707
708                                 if (!(*i)->hidden()) {
709                                         break;
710                                 }
711
712                         } while (true);
713                 }
714
715                 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
716                         _editor.ensure_time_axis_view_is_visible (**i, false);
717                         (*i)->begin_name_edit ();
718                 }
719         }
720 }
721
722 bool
723 TimeAxisView::name_entry_changed (string const&)
724 {
725         return true;
726 }
727
728 bool
729 TimeAxisView::can_edit_name () const
730 {
731         return true;
732 }
733
734 void
735 TimeAxisView::conditionally_add_to_selection ()
736 {
737         if (!selectable()) {
738                 return;
739         }
740
741         Selection& s (_editor.get_selection ());
742
743         if (!s.selected (this)) {
744                 _editor.set_selected_track (*this, Selection::Set);
745         }
746 }
747
748 void
749 TimeAxisView::popup_display_menu (guint32 when)
750 {
751         conditionally_add_to_selection ();
752
753         build_display_menu ();
754
755         if (!display_menu->items().empty()) {
756                 display_menu->popup (1, when);
757         }
758 }
759
760 void
761 TimeAxisView::set_selected (bool yn)
762 {
763         if (yn == selected()) {
764                 return;
765         }
766
767         AxisView::set_selected (yn);
768
769         if (_selected) {
770                 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
771                 time_axis_frame.set_name ("MixerStripSelectedFrame");
772                 controls_ebox.set_name (controls_base_selected_name);
773                 controls_vbox.set_name (controls_base_selected_name);
774                 time_axis_vbox.set_name (controls_base_selected_name);
775         } else {
776                 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
777                 time_axis_frame.set_name (controls_base_unselected_name);
778                 controls_ebox.set_name (controls_base_unselected_name);
779                 controls_vbox.set_name (controls_base_unselected_name);
780                 time_axis_vbox.set_name (controls_base_unselected_name);
781
782                 hide_selection ();
783         }
784
785         time_axis_frame.show();
786 }
787
788 void
789 TimeAxisView::build_display_menu ()
790 {
791         using namespace Menu_Helpers;
792
793         delete display_menu;
794
795         display_menu = new Menu;
796         display_menu->set_name ("ArdourContextMenu");
797
798         // Just let implementing classes define what goes into the manu
799 }
800
801 void
802 TimeAxisView::set_samples_per_pixel (double fpp)
803 {
804         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
805                 (*i)->set_samples_per_pixel (fpp);
806         }
807 }
808
809 void
810 TimeAxisView::show_timestretch (samplepos_t start, samplepos_t end, int layers, int layer)
811 {
812         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
813                 (*i)->show_timestretch (start, end, layers, layer);
814         }
815 }
816
817 void
818 TimeAxisView::hide_timestretch ()
819 {
820         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
821                 (*i)->hide_timestretch ();
822         }
823 }
824
825 void
826 TimeAxisView::show_selection (TimeSelection& ts)
827 {
828         double x1;
829         double x2;
830         double y2;
831         SelectionRect *rect;
832
833         time_axis_frame.show();
834
835         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
836                 if (!(*i)->selected () && !(*i)->propagate_time_selection ()) {
837                         continue;
838                 }
839                 (*i)->show_selection (ts);
840         }
841
842         if (selection_group->visible ()) {
843                 while (!used_selection_rects.empty()) {
844                         free_selection_rects.push_front (used_selection_rects.front());
845                         used_selection_rects.pop_front();
846                         free_selection_rects.front()->rect->hide();
847                         free_selection_rects.front()->start_trim->hide();
848                         free_selection_rects.front()->end_trim->hide();
849                 }
850                 selection_group->hide();
851         }
852
853         selection_group->show();
854         selection_group->raise_to_top();
855
856         uint32_t gap = UIConfiguration::instance().get_vertical_region_gap ();
857         float ui_scale = UIConfiguration::instance().get_ui_scale ();
858         if (gap > 0 && ui_scale > 0) {
859                 gap = ceil (gap * ui_scale);
860         }
861
862         for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
863                 samplepos_t start, end;
864                 samplecnt_t cnt;
865
866                 start = (*i).start;
867                 end = (*i).end;
868                 cnt = end - start + 1;
869
870                 rect = get_selection_rect ((*i).id);
871
872                 x1 = _editor.sample_to_pixel (start);
873                 x2 = _editor.sample_to_pixel (start + cnt - 1);
874                 y2 = current_height() - 1;
875
876                 if (dynamic_cast<AudioTimeAxisView*>(this)) {
877                         if (y2 > gap) {
878                                 y2 -= gap;
879                         } else {
880                                 y2 = 1;
881                         }
882                 }
883
884                 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
885
886                 // trim boxes are at the top for selections
887
888                 if (x2 > x1) {
889                         rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
890                         rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
891
892                         rect->start_trim->show();
893                         rect->end_trim->show();
894                 } else {
895                         rect->start_trim->hide();
896                         rect->end_trim->hide();
897                 }
898
899                 rect->rect->show ();
900                 used_selection_rects.push_back (rect);
901         }
902 }
903
904 void
905 TimeAxisView::reshow_selection (TimeSelection& ts)
906 {
907         show_selection (ts);
908
909         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
910                 if (!(*i)->selected () && !(*i)->propagate_time_selection ()) {
911                         continue;
912                 }
913                 (*i)->show_selection (ts);
914         }
915 }
916
917 void
918 TimeAxisView::hide_selection ()
919 {
920         if (selection_group->visible ()) {
921                 while (!used_selection_rects.empty()) {
922                         free_selection_rects.push_front (used_selection_rects.front());
923                         used_selection_rects.pop_front();
924                         free_selection_rects.front()->rect->hide();
925                         free_selection_rects.front()->start_trim->hide();
926                         free_selection_rects.front()->end_trim->hide();
927                 }
928                 selection_group->hide();
929         }
930
931         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
932                 (*i)->hide_selection ();
933         }
934 }
935
936 void
937 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
938 {
939         /* find the selection rect this is for. we have the item corresponding to one
940            of the trim handles.
941          */
942
943         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
944                 if ((*i)->start_trim == item || (*i)->end_trim == item) {
945
946                         /* make one trim handle be "above" the other so that if they overlap,
947                            the top one is the one last used.
948                         */
949
950                         (*i)->rect->raise_to_top ();
951                         (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
952                         (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
953
954                         break;
955                 }
956         }
957 }
958
959 // retuned rect is pushed back into the used_selection_rects list
960 // in TimeAxisView::show_selection() which is the only caller.
961 SelectionRect *
962 TimeAxisView::get_selection_rect (uint32_t id)
963 {
964         SelectionRect *rect;
965
966         /* check to see if we already have a visible rect for this particular selection ID */
967
968         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
969                 if ((*i)->id == id) {
970                         SelectionRect* ret = (*i);
971                         used_selection_rects.erase (i);
972                         return ret;
973                 }
974         }
975
976         /* ditto for the free rect list */
977
978         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
979                 if ((*i)->id == id) {
980                         SelectionRect* ret = (*i);
981                         free_selection_rects.erase (i);
982                         return ret;
983                 }
984         }
985
986         /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
987
988         if (free_selection_rects.empty()) {
989
990                 rect = new SelectionRect;
991
992                 rect->rect = new ArdourCanvas::Rectangle (selection_group);
993                 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
994                 rect->rect->set_outline (true);
995                 rect->rect->set_outline_width (1.0);
996                 rect->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
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 }