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