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