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