OSC: no op, white space cleanup
[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         /* put separator at the bottom of this time axis view */
336
337         _canvas_separator->set (ArdourCanvas::Duple(0, height), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, height));
338         _canvas_separator->lower_to_bottom ();
339         _canvas_separator->show ();
340
341         return _effective_height;
342 }
343
344 bool
345 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
346 {
347         switch (ev->direction) {
348         case GDK_SCROLL_UP:
349                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
350                         /* See Editor::_stepping_axis_view for notes on this hack */
351                         Editor& e = dynamic_cast<Editor&> (_editor);
352                         if (!e.stepping_axis_view ()) {
353                                 e.set_stepping_axis_view (this);
354                         }
355                         e.stepping_axis_view()->step_height (false);
356                         return true;
357                 }
358                 break;
359
360         case GDK_SCROLL_DOWN:
361                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
362                         /* See Editor::_stepping_axis_view for notes on this hack */
363                         Editor& e = dynamic_cast<Editor&> (_editor);
364                         if (!e.stepping_axis_view ()) {
365                                 e.set_stepping_axis_view (this);
366                         }
367                         e.stepping_axis_view()->step_height (true);
368                         return true;
369                 }
370                 break;
371
372         default:
373                 /* no handling for left/right, yet */
374                 break;
375         }
376
377         /* Just forward to the normal canvas scroll method. The coordinate
378            systems are different but since the canvas is always larger than the
379            track headers, and aligned with the trackview area, this will work.
380
381            In the not too distant future this layout is going away anyway and
382            headers will be on the canvas.
383         */
384         return _editor.canvas_scroll_event (ev, false);
385 }
386
387 bool
388 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
389 {
390         if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
391                 /* see if it is inside the name label */
392                 if (name_label.is_ancestor (controls_ebox)) {
393                         int nlx;
394                         int nly;
395                         controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
396                         Gtk::Allocation a = name_label.get_allocation ();
397                         if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
398                                 begin_name_edit ();
399                                 _ebox_release_can_act = false;
400                                 return true;
401                         }
402                 }
403
404         }
405
406         _ebox_release_can_act = true;
407
408         if (maybe_set_cursor (event->y) > 0) {
409                 _resize_drag_start = event->y_root;
410         }
411
412         return true;
413 }
414
415 void
416 TimeAxisView::idle_resize (int32_t h)
417 {
418         set_height (std::max(0, h));
419 }
420
421
422 bool
423 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
424 {
425         if (_resize_drag_start >= 0) {
426
427                 /* (ab)use the DragManager to do autoscrolling - basically we
428                  * are pretending that the drag is taking place over the canvas
429                  * (which perhaps in the glorious future, when track headers
430                  * and the canvas are unified, will actually be true.)
431                  */
432
433                 _editor.maybe_autoscroll (false, true, true);
434
435                 /* now schedule the actual TAV resize */
436                 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
437                 _editor.add_to_idle_resize (this, delta);
438                 _resize_drag_start = ev->y_root;
439                 _did_resize = true;
440         } else {
441                 /* not dragging but ... */
442                 maybe_set_cursor (ev->y);
443         }
444
445         gdk_event_request_motions(ev);
446         return true;
447 }
448
449 bool
450 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
451 {
452         if (_have_preresize_cursor) {
453                 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
454                 _have_preresize_cursor = false;
455         }
456         return true;
457 }
458
459 bool
460 TimeAxisView::maybe_set_cursor (int y)
461 {
462         /* XXX no Gtkmm Gdk::Window::get_cursor() */
463         Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
464
465         if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
466
467                 /* y-coordinate in lower 25% */
468
469                 if (!_have_preresize_cursor) {
470                         _preresize_cursor = gdk_window_get_cursor (win->gobj());
471                         _have_preresize_cursor = true;
472                         win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
473                 }
474
475                 return 1;
476
477         } else if (_have_preresize_cursor) {
478                 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
479                 _have_preresize_cursor = false;
480
481                 return -1;
482         }
483
484         return 0;
485 }
486
487 bool
488 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
489 {
490         if (_resize_drag_start >= 0) {
491                 if (_have_preresize_cursor) {
492                         gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
493                         _preresize_cursor = 0;
494                         _have_preresize_cursor = false;
495                 }
496                 _editor.stop_canvas_autoscroll ();
497                 _resize_drag_start = -1;
498                 if (_did_resize) {
499                         _did_resize = false;
500                         // don't change selection
501                         return true;
502                 }
503         }
504
505         if (!_ebox_release_can_act) {
506                 return true;
507         }
508
509         switch (ev->button) {
510         case 1:
511                 if (selectable()) {
512                         selection_click (ev);
513                 }
514                 break;
515
516         case 3:
517                 popup_display_menu (ev->time);
518                 break;
519         }
520
521         return true;
522 }
523
524 void
525 TimeAxisView::selection_click (GdkEventButton* ev)
526 {
527         Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
528         _editor.set_selected_track (*this, op, false);
529 }
530
531
532 /** Steps through the defined heights for this TrackView.
533  *  @param coarser true if stepping should decrease in size, otherwise false.
534  */
535 void
536 TimeAxisView::step_height (bool coarser)
537 {
538         static const uint32_t step = 25;
539
540         if (coarser) {
541
542                 if (height <= preset_height (HeightSmall)) {
543                         return;
544                 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
545                         set_height_enum (HeightSmall);
546                 } else {
547                         set_height (height - step);
548                 }
549
550         } else {
551
552                 if (height <= preset_height(HeightSmall)) {
553                         set_height_enum (HeightNormal);
554                 } else {
555                         set_height (height + step);
556                 }
557
558         }
559 }
560
561 void
562 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
563 {
564         if (apply_to_selection) {
565                 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
566         } else {
567                 set_height (preset_height (h));
568         }
569 }
570
571 void
572 TimeAxisView::set_height (uint32_t h, TrackHeightMode m)
573 {
574         uint32_t lanes = 0;
575         if (m == TotalHeight) {
576                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
577                         if ( !(*i)->hidden()) ++lanes;
578                 }
579         }
580         h /= (lanes + 1);
581
582         if (h < preset_height (HeightSmall)) {
583                 h = preset_height (HeightSmall);
584         }
585
586         TOP_LEVEL_WIDGET.property_height_request () = h;
587         height = h;
588
589         char buf[32];
590         snprintf (buf, sizeof (buf), "%u", height);
591         set_gui_property ("height", buf);
592
593         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
594                 (*i)->set_height ();
595         }
596
597         if (selection_group->visible ()) {
598                 /* resize the selection rect */
599                 show_selection (_editor.get_selection().time);
600         }
601
602         if (m != OnlySelf) {
603                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
604                         (*i)->set_height(h, OnlySelf);
605                 }
606         }
607
608         _editor.override_visible_track_count ();
609 }
610
611 void
612 TimeAxisView::begin_name_edit ()
613 {
614         if (!can_edit_name()) {
615                 return;
616         }
617
618         Gtk::Window* toplevel = (Gtk::Window*) control_parent->get_toplevel();
619         FloatingTextEntry* fte = new FloatingTextEntry (toplevel, name_label.get_text ());
620
621         fte->set_name ("TrackNameEditor");
622         fte->use_text.connect (sigc::mem_fun (*this, &TimeAxisView::end_name_edit));
623
624         /* We want to new toplevel window to overlay the name label, so
625          * translate the coordinates of the upper left corner of the name label
626          * into the coordinate space of the top level window.
627          */
628
629         int x, y;
630         int wx, wy;
631
632         name_label.translate_coordinates (*toplevel, 0, 0, x, y);
633         toplevel->get_window()->get_origin (wx, wy);
634
635         fte->move (wx + x, wy + y);
636         fte->present ();
637 }
638
639 void
640 TimeAxisView::end_name_edit (std::string str, int next_dir)
641 {
642         if (!name_entry_changed (str)) {
643                 next_dir = 0;
644         }
645
646         if (next_dir > 0) {
647
648                 TrackViewList const & allviews = _editor.get_track_views ();
649                 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
650
651                 if (i != allviews.end()) {
652
653                         do {
654                                 if (++i == allviews.end()) {
655                                         return;
656                                 }
657
658                                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
659
660                                 if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) {
661                                         continue;
662                                 }
663
664                                 if (!(*i)->hidden()) {
665                                         break;
666                                 }
667
668                         } while (true);
669                 }
670
671                 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
672                         _editor.ensure_time_axis_view_is_visible (**i, false);
673                         (*i)->begin_name_edit ();
674                 }
675
676         } else if (next_dir < 0) {
677
678                 TrackViewList const & allviews = _editor.get_track_views ();
679                 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
680
681                 if (i != allviews.begin()) {
682                         do {
683                                 if (i == allviews.begin()) {
684                                         return;
685                                 }
686
687                                 --i;
688
689                                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
690
691                                 if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) {
692                                         continue;
693                                 }
694
695                                 if (!(*i)->hidden()) {
696                                         break;
697                                 }
698
699                         } while (true);
700                 }
701
702                 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
703                         _editor.ensure_time_axis_view_is_visible (**i, false);
704                         (*i)->begin_name_edit ();
705                 }
706         }
707 }
708
709 bool
710 TimeAxisView::name_entry_changed (string const&)
711 {
712         return true;
713 }
714
715 bool
716 TimeAxisView::can_edit_name () const
717 {
718         return true;
719 }
720
721 void
722 TimeAxisView::conditionally_add_to_selection ()
723 {
724         if (!selectable()) {
725                 return;
726         }
727
728         Selection& s (_editor.get_selection ());
729
730         if (!s.selected (this)) {
731                 _editor.set_selected_track (*this, Selection::Set);
732         }
733 }
734
735 void
736 TimeAxisView::popup_display_menu (guint32 when)
737 {
738         conditionally_add_to_selection ();
739
740         build_display_menu ();
741
742         if (!display_menu->items().empty()) {
743                 display_menu->popup (1, when);
744         }
745 }
746
747 void
748 TimeAxisView::set_selected (bool yn)
749 {
750         if (yn == selected()) {
751                 return;
752         }
753
754         AxisView::set_selected (yn);
755
756         if (_selected) {
757                 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
758                 time_axis_frame.set_name ("MixerStripSelectedFrame");
759                 controls_ebox.set_name (controls_base_selected_name);
760                 controls_vbox.set_name (controls_base_selected_name);
761                 time_axis_vbox.set_name (controls_base_selected_name);
762         } else {
763                 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
764                 time_axis_frame.set_name (controls_base_unselected_name);
765                 controls_ebox.set_name (controls_base_unselected_name);
766                 controls_vbox.set_name (controls_base_unselected_name);
767                 time_axis_vbox.set_name (controls_base_unselected_name);
768
769                 hide_selection ();
770
771                 /* children will be set for the yn=true case. but when deselecting
772                    the editor only has a list of top-level trackviews, so we
773                    have to do this here.
774                 */
775
776                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
777                         (*i)->set_selected (false);
778                 }
779         }
780
781         time_axis_frame.show();
782
783 }
784
785 void
786 TimeAxisView::build_display_menu ()
787 {
788         using namespace Menu_Helpers;
789
790         delete display_menu;
791
792         display_menu = new Menu;
793         display_menu->set_name ("ArdourContextMenu");
794
795         // Just let implementing classes define what goes into the manu
796 }
797
798 void
799 TimeAxisView::set_samples_per_pixel (double fpp)
800 {
801         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
802                 (*i)->set_samples_per_pixel (fpp);
803         }
804 }
805
806 void
807 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
808 {
809         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
810                 (*i)->show_timestretch (start, end, layers, layer);
811         }
812 }
813
814 void
815 TimeAxisView::hide_timestretch ()
816 {
817         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
818                 (*i)->hide_timestretch ();
819         }
820 }
821
822 void
823 TimeAxisView::show_selection (TimeSelection& ts)
824 {
825         double x1;
826         double x2;
827         double y2;
828         SelectionRect *rect;    time_axis_frame.show();
829
830
831         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
832                 (*i)->show_selection (ts);
833         }
834
835         if (selection_group->visible ()) {
836                 while (!used_selection_rects.empty()) {
837                         free_selection_rects.push_front (used_selection_rects.front());
838                         used_selection_rects.pop_front();
839                         free_selection_rects.front()->rect->hide();
840                         free_selection_rects.front()->start_trim->hide();
841                         free_selection_rects.front()->end_trim->hide();
842                 }
843                 selection_group->hide();
844         }
845
846         selection_group->show();
847         selection_group->raise_to_top();
848
849         for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
850                 framepos_t start, end;
851                 framecnt_t cnt;
852
853                 start = (*i).start;
854                 end = (*i).end;
855                 cnt = end - start + 1;
856
857                 rect = get_selection_rect ((*i).id);
858
859                 x1 = _editor.sample_to_pixel (start);
860                 x2 = _editor.sample_to_pixel (start + cnt - 1);
861                 y2 = current_height() - 1;
862
863                 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
864
865                 // trim boxes are at the top for selections
866
867                 if (x2 > x1) {
868                         rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
869                         rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
870
871                         rect->start_trim->show();
872                         rect->end_trim->show();
873                 } else {
874                         rect->start_trim->hide();
875                         rect->end_trim->hide();
876                 }
877
878                 rect->rect->show ();
879                 used_selection_rects.push_back (rect);
880         }
881 }
882
883 void
884 TimeAxisView::reshow_selection (TimeSelection& ts)
885 {
886         show_selection (ts);
887
888         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
889                 (*i)->show_selection (ts);
890         }
891 }
892
893 void
894 TimeAxisView::hide_selection ()
895 {
896         if (selection_group->visible ()) {
897                 while (!used_selection_rects.empty()) {
898                         free_selection_rects.push_front (used_selection_rects.front());
899                         used_selection_rects.pop_front();
900                         free_selection_rects.front()->rect->hide();
901                         free_selection_rects.front()->start_trim->hide();
902                         free_selection_rects.front()->end_trim->hide();
903                 }
904                 selection_group->hide();
905         }
906
907         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
908                 (*i)->hide_selection ();
909         }
910 }
911
912 void
913 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
914 {
915         /* find the selection rect this is for. we have the item corresponding to one
916            of the trim handles.
917          */
918
919         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
920                 if ((*i)->start_trim == item || (*i)->end_trim == item) {
921
922                         /* make one trim handle be "above" the other so that if they overlap,
923                            the top one is the one last used.
924                         */
925
926                         (*i)->rect->raise_to_top ();
927                         (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
928                         (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
929
930                         break;
931                 }
932         }
933 }
934
935 // retuned rect is pushed back into the used_selection_rects list
936 // in TimeAxisView::show_selection() which is the only caller.
937 SelectionRect *
938 TimeAxisView::get_selection_rect (uint32_t id)
939 {
940         SelectionRect *rect;
941
942         /* check to see if we already have a visible rect for this particular selection ID */
943
944         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
945                 if ((*i)->id == id) {
946                         SelectionRect* ret = (*i);
947                         used_selection_rects.erase (i);
948                         return ret;
949                 }
950         }
951
952         /* ditto for the free rect list */
953
954         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
955                 if ((*i)->id == id) {
956                         SelectionRect* ret = (*i);
957                         free_selection_rects.erase (i);
958                         return ret;
959                 }
960         }
961
962         /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
963
964         if (free_selection_rects.empty()) {
965
966                 rect = new SelectionRect;
967
968                 rect->rect = new ArdourCanvas::Rectangle (selection_group);
969                 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
970                 rect->rect->set_outline (false);
971                 rect->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
972
973                 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
974                 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
975                 rect->start_trim->set_outline (false);
976                 rect->start_trim->set_fill (false);
977
978                 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
979                 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
980                 rect->end_trim->set_outline (false);
981                 rect->end_trim->set_fill (false);
982
983                 free_selection_rects.push_front (rect);
984
985                 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
986                 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
987                 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
988         }
989
990         rect = free_selection_rects.front();
991         rect->id = id;
992         free_selection_rects.pop_front();
993         return rect;
994 }
995
996 struct null_deleter { void operator()(void const *) const {} };
997
998 bool
999 TimeAxisView::is_child (TimeAxisView* tav)
1000 {
1001         return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1002 }
1003
1004 void
1005 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1006 {
1007         children.push_back (child);
1008 }
1009
1010 void
1011 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1012 {
1013         Children::iterator i;
1014
1015         if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1016                 children.erase (i);
1017         }
1018 }
1019
1020 /** Get selectable things within a given range.
1021  *  @param start Start time in session frames.
1022  *  @param end End time in session frames.
1023  *  @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1024  *  @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1025  *  @param result Filled in with selectable things.
1026  */
1027 void
1028 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/, bool /*within*/)
1029 {
1030         return;
1031 }
1032
1033 void
1034 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1035 {
1036         return;
1037 }
1038
1039 void
1040 TimeAxisView::add_ghost (RegionView* rv)
1041 {
1042         GhostRegion* gr = rv->add_ghost (*this);
1043
1044         if (gr) {
1045                 ghosts.push_back(gr);
1046         }
1047 }
1048
1049 void
1050 TimeAxisView::remove_ghost (RegionView* rv)
1051 {
1052         rv->remove_ghost_in (*this);
1053 }
1054
1055 void
1056 TimeAxisView::erase_ghost (GhostRegion* gr)
1057 {
1058         if (in_destructor) {
1059                 return;
1060         }
1061
1062         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1063                 if ((*i) == gr) {
1064                         ghosts.erase (i);
1065                         break;
1066                 }
1067         }
1068 }
1069
1070 bool
1071 TimeAxisView::touched (double top, double bot)
1072 {
1073         /* remember: this is X Window - coordinate space starts in upper left and moves down.
1074           y_position is the "origin" or "top" of the track.
1075         */
1076
1077         double mybot = _y_position + current_height();
1078
1079         return ((_y_position <= bot && _y_position >= top) ||
1080                 ((mybot <= bot) && (top < mybot)) ||
1081                 (mybot >= bot && _y_position < top));
1082 }
1083
1084 void
1085 TimeAxisView::set_parent (TimeAxisView& p)
1086 {
1087         parent = &p;
1088 }
1089
1090 void
1091 TimeAxisView::reset_height ()
1092 {
1093         set_height (height);
1094
1095         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1096                 (*i)->set_height ((*i)->height);
1097         }
1098 }
1099
1100 void
1101 TimeAxisView::compute_heights ()
1102 {
1103         // TODO this function should be re-evaluated when font-scaling changes (!)
1104         Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1105         Gtk::Table one_row_table (1, 1);
1106         ArdourButton* test_button = manage (new ArdourButton);
1107         const int border_width = 2;
1108         const int frame_height = 2;
1109         extra_height = (2 * border_width) + frame_height;
1110
1111         window.add (one_row_table);
1112         test_button->set_name ("mute button");
1113         test_button->set_text (S_("Mute|M"));
1114         test_button->set_tweaks (ArdourButton::TrackHeader);
1115
1116         one_row_table.set_border_width (border_width);
1117         one_row_table.set_row_spacings (2);
1118         one_row_table.set_col_spacings (2);
1119
1120         one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1121         one_row_table.show_all ();
1122
1123         Gtk::Requisition req(one_row_table.size_request ());
1124         button_height = req.height;
1125 }
1126
1127 void
1128 TimeAxisView::color_handler ()
1129 {
1130         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1131                 (*i)->set_colors();
1132         }
1133
1134         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1135
1136                 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1137                 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1138
1139                 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1140                 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1141
1142                 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1143                 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1144         }
1145
1146         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1147
1148                 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1149                 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1150
1151                 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1152                 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1153
1154                 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1155                 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1156         }
1157 }
1158
1159 /** @return Pair: TimeAxisView, layer index.
1160  * TimeAxisView is non-0 if this object covers @param y, or one of its children
1161  * does. @param y is an offset from the top of the trackview area.
1162  *
1163  * If the covering object is a child axis, then the child is returned.
1164  * TimeAxisView is 0 otherwise.
1165  *
1166  * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1167  * and is in stacked or expanded * region display mode, otherwise 0.
1168  */
1169 std::pair<TimeAxisView*, double>
1170 TimeAxisView::covers_y_position (double y) const
1171 {
1172         if (hidden()) {
1173                 return std::make_pair ((TimeAxisView *) 0, 0);
1174         }
1175
1176         if (_y_position <= y && y < (_y_position + height)) {
1177
1178                 /* work out the layer index if appropriate */
1179                 double l = 0;
1180                 switch (layer_display ()) {
1181                 case Overlaid:
1182                         break;
1183                 case Stacked:
1184                         if (view ()) {
1185                                 /* compute layer */
1186                                 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1187                                 /* clamp to max layers to be on the safe side; sometimes the above calculation
1188                                    returns a too-high value */
1189                                 if (l >= view()->layers ()) {
1190                                         l = view()->layers() - 1;
1191                                 }
1192                         }
1193                         break;
1194                 case Expanded:
1195                         if (view ()) {
1196                                 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1197                                 l = n * 0.5 - 0.5;
1198                                 if (l >= (view()->layers() - 0.5)) {
1199                                         l = view()->layers() - 0.5;
1200                                 }
1201                         }
1202                         break;
1203                 }
1204
1205                 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1206         }
1207
1208         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1209
1210                 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1211                 if (r.first) {
1212                         return r;
1213                 }
1214         }
1215
1216         return std::make_pair ((TimeAxisView *) 0, 0);
1217 }
1218
1219 bool
1220 TimeAxisView::covered_by_y_range (double y0, double y1) const
1221 {
1222         if (hidden()) {
1223                 return false;
1224         }
1225
1226         /* if either the top or bottom of the axisview is in the vertical
1227          * range, we cover it.
1228          */
1229
1230         if ((y0 < _y_position && y1 < _y_position) ||
1231             (y0 >= _y_position + height && y1 >= _y_position + height)) {
1232                 return false;
1233         }
1234
1235         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1236                 if ((*i)->covered_by_y_range (y0, y1)) {
1237                         return true;
1238                 }
1239         }
1240
1241         return true;
1242 }
1243
1244 uint32_t
1245 TimeAxisView::preset_height (Height h)
1246 {
1247         switch (h) {
1248         case HeightLargest:
1249                 return (button_height * 2) + extra_height + 260;
1250         case HeightLarger:
1251                 return (button_height * 2) + extra_height + 160;
1252         case HeightLarge:
1253                 return (button_height * 2) + extra_height + 60;
1254         case HeightNormal:
1255                 return (button_height * 2) + extra_height + 10;
1256         case HeightSmall:
1257                 return button_height + extra_height;
1258         }
1259
1260         abort(); /* NOTREACHED */
1261         return 0;
1262 }
1263
1264 /** @return Child time axis views that are not hidden */
1265 TimeAxisView::Children
1266 TimeAxisView::get_child_list ()
1267 {
1268         Children c;
1269
1270         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1271                 if (!(*i)->hidden()) {
1272                         c.push_back(*i);
1273                 }
1274         }
1275
1276         return c;
1277 }
1278
1279 void
1280 TimeAxisView::build_size_menu ()
1281 {
1282         if (_size_menu && _size_menu->gobj ()) {
1283                 return;
1284         }
1285
1286         delete _size_menu;
1287
1288         using namespace Menu_Helpers;
1289
1290         _size_menu = new Menu;
1291         _size_menu->set_name ("ArdourContextMenu");
1292         MenuList& items = _size_menu->items();
1293
1294         items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1295         items.push_back (MenuElem (_("Larger"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1296         items.push_back (MenuElem (_("Large"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1297         items.push_back (MenuElem (_("Normal"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1298         items.push_back (MenuElem (_("Small"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1299 }
1300
1301 void
1302 TimeAxisView::reset_visual_state ()
1303 {
1304         /* this method is not required to trigger a global redraw */
1305
1306         string str = gui_property ("height");
1307
1308         if (!str.empty()) {
1309                 set_height (atoi (str));
1310         } else {
1311                 set_height (preset_height (HeightNormal));
1312         }
1313 }
1314
1315 TrackViewList
1316 TrackViewList::filter_to_unique_playlists ()
1317 {
1318         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1319         TrackViewList ts;
1320
1321         for (iterator i = begin(); i != end(); ++i) {
1322                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1323                 if (!rtav) {
1324                         /* not a route: include it anyway */
1325                         ts.push_back (*i);
1326                 } else {
1327                         boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1328                         if (t) {
1329                                 if (playlists.insert (t->playlist()).second) {
1330                                         /* playlist not seen yet */
1331                                         ts.push_back (*i);
1332                                 }
1333                         } else {
1334                                 /* not a track: include it anyway */
1335                                 ts.push_back (*i);
1336                         }
1337                 }
1338         }
1339         return ts;
1340 }