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