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