fix an issue with selection rects not being redrawn correctly
[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 must be visible for the change in its bounding box to
868                    cause a redraw.
869                 */
870
871                 rect->rect->show ();
872                 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
873
874                 // trim boxes are at the top for selections
875
876                 if (x2 > x1) {
877                         rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
878                         rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
879
880                         rect->start_trim->show();
881                         rect->end_trim->show();
882                 } else {
883                         rect->start_trim->hide();
884                         rect->end_trim->hide();
885                 }
886
887                 used_selection_rects.push_back (rect);
888         }
889 }
890
891 void
892 TimeAxisView::reshow_selection (TimeSelection& ts)
893 {
894         show_selection (ts);
895
896         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
897                 (*i)->show_selection (ts);
898         }
899 }
900
901 void
902 TimeAxisView::hide_selection ()
903 {
904         if (selection_group->visible ()) {
905                 while (!used_selection_rects.empty()) {
906                         free_selection_rects.push_front (used_selection_rects.front());
907                         used_selection_rects.pop_front();
908                         free_selection_rects.front()->rect->hide();
909                         free_selection_rects.front()->start_trim->hide();
910                         free_selection_rects.front()->end_trim->hide();
911                 }
912                 selection_group->hide();
913         }
914
915         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
916                 (*i)->hide_selection ();
917         }
918 }
919
920 void
921 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
922 {
923         /* find the selection rect this is for. we have the item corresponding to one
924            of the trim handles.
925          */
926
927         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
928                 if ((*i)->start_trim == item || (*i)->end_trim == item) {
929
930                         /* make one trim handle be "above" the other so that if they overlap,
931                            the top one is the one last used.
932                         */
933
934                         (*i)->rect->raise_to_top ();
935                         (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
936                         (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
937
938                         break;
939                 }
940         }
941 }
942
943 // retuned rect is pushed back into the used_selection_rects list
944 // in TimeAxisView::show_selection() which is the only caller.
945 SelectionRect *
946 TimeAxisView::get_selection_rect (uint32_t id)
947 {
948         SelectionRect *rect;
949
950         /* check to see if we already have a visible rect for this particular selection ID */
951
952         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
953                 if ((*i)->id == id) {
954                         SelectionRect* ret = (*i);
955                         used_selection_rects.erase (i);
956                         return ret;
957                 }
958         }
959
960         /* ditto for the free rect list */
961
962         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
963                 if ((*i)->id == id) {
964                         SelectionRect* ret = (*i);
965                         free_selection_rects.erase (i);
966                         return ret;
967                 }
968         }
969
970         /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
971
972         if (free_selection_rects.empty()) {
973
974                 rect = new SelectionRect;
975
976                 rect->rect = new ArdourCanvas::Rectangle (selection_group);
977                 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
978                 rect->rect->set_outline (false);
979                 rect->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
980
981                 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
982                 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
983                 rect->start_trim->set_outline (false);
984                 rect->start_trim->set_fill (false);
985
986                 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
987                 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
988                 rect->end_trim->set_outline (false);
989                 rect->end_trim->set_fill (false);
990
991                 free_selection_rects.push_front (rect);
992
993                 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
994                 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
995                 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
996         }
997
998         rect = free_selection_rects.front();
999         rect->id = id;
1000         free_selection_rects.pop_front();
1001         return rect;
1002 }
1003
1004 struct null_deleter { void operator()(void const *) const {} };
1005
1006 bool
1007 TimeAxisView::is_child (TimeAxisView* tav)
1008 {
1009         return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1010 }
1011
1012 void
1013 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1014 {
1015         children.push_back (child);
1016 }
1017
1018 void
1019 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1020 {
1021         Children::iterator i;
1022
1023         if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1024                 children.erase (i);
1025         }
1026 }
1027
1028 /** Get selectable things within a given range.
1029  *  @param start Start time in session frames.
1030  *  @param end End time in session frames.
1031  *  @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1032  *  @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1033  *  @param result Filled in with selectable things.
1034  */
1035 void
1036 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/, bool /*within*/)
1037 {
1038         return;
1039 }
1040
1041 void
1042 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1043 {
1044         return;
1045 }
1046
1047 void
1048 TimeAxisView::add_ghost (RegionView* rv)
1049 {
1050         GhostRegion* gr = rv->add_ghost (*this);
1051
1052         if (gr) {
1053                 ghosts.push_back(gr);
1054         }
1055 }
1056
1057 void
1058 TimeAxisView::remove_ghost (RegionView* rv)
1059 {
1060         rv->remove_ghost_in (*this);
1061 }
1062
1063 void
1064 TimeAxisView::erase_ghost (GhostRegion* gr)
1065 {
1066         if (in_destructor) {
1067                 return;
1068         }
1069
1070         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1071                 if ((*i) == gr) {
1072                         ghosts.erase (i);
1073                         break;
1074                 }
1075         }
1076 }
1077
1078 bool
1079 TimeAxisView::touched (double top, double bot)
1080 {
1081         /* remember: this is X Window - coordinate space starts in upper left and moves down.
1082           y_position is the "origin" or "top" of the track.
1083         */
1084
1085         double mybot = _y_position + current_height();
1086
1087         return ((_y_position <= bot && _y_position >= top) ||
1088                 ((mybot <= bot) && (top < mybot)) ||
1089                 (mybot >= bot && _y_position < top));
1090 }
1091
1092 void
1093 TimeAxisView::set_parent (TimeAxisView& p)
1094 {
1095         parent = &p;
1096 }
1097
1098 void
1099 TimeAxisView::reset_height ()
1100 {
1101         set_height (height);
1102
1103         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1104                 (*i)->set_height ((*i)->height);
1105         }
1106 }
1107
1108 void
1109 TimeAxisView::compute_heights ()
1110 {
1111         // TODO this function should be re-evaluated when font-scaling changes (!)
1112         Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1113         Gtk::Table one_row_table (1, 1);
1114         ArdourButton* test_button = manage (new ArdourButton);
1115         const int border_width = 2;
1116         const int frame_height = 2;
1117         extra_height = (2 * border_width) + frame_height;
1118
1119         window.add (one_row_table);
1120         test_button->set_name ("mute button");
1121         test_button->set_text (S_("Mute|M"));
1122         test_button->set_tweaks (ArdourButton::TrackHeader);
1123
1124         one_row_table.set_border_width (border_width);
1125         one_row_table.set_row_spacings (2);
1126         one_row_table.set_col_spacings (2);
1127
1128         one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1129         one_row_table.show_all ();
1130
1131         Gtk::Requisition req(one_row_table.size_request ());
1132         button_height = req.height;
1133 }
1134
1135 void
1136 TimeAxisView::color_handler ()
1137 {
1138         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1139                 (*i)->set_colors();
1140         }
1141
1142         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1143
1144                 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1145                 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1146
1147                 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1148                 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1149
1150                 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1151                 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1152         }
1153
1154         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1155
1156                 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1157                 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1158
1159                 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1160                 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1161
1162                 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1163                 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1164         }
1165 }
1166
1167 /** @return Pair: TimeAxisView, layer index.
1168  * TimeAxisView is non-0 if this object covers @param y, or one of its children
1169  * does. @param y is an offset from the top of the trackview area.
1170  *
1171  * If the covering object is a child axis, then the child is returned.
1172  * TimeAxisView is 0 otherwise.
1173  *
1174  * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1175  * and is in stacked or expanded * region display mode, otherwise 0.
1176  */
1177 std::pair<TimeAxisView*, double>
1178 TimeAxisView::covers_y_position (double y) const
1179 {
1180         if (hidden()) {
1181                 return std::make_pair ((TimeAxisView *) 0, 0);
1182         }
1183
1184         if (_y_position <= y && y < (_y_position + height)) {
1185
1186                 /* work out the layer index if appropriate */
1187                 double l = 0;
1188                 switch (layer_display ()) {
1189                 case Overlaid:
1190                         break;
1191                 case Stacked:
1192                         if (view ()) {
1193                                 /* compute layer */
1194                                 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1195                                 /* clamp to max layers to be on the safe side; sometimes the above calculation
1196                                    returns a too-high value */
1197                                 if (l >= view()->layers ()) {
1198                                         l = view()->layers() - 1;
1199                                 }
1200                         }
1201                         break;
1202                 case Expanded:
1203                         if (view ()) {
1204                                 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1205                                 l = n * 0.5 - 0.5;
1206                                 if (l >= (view()->layers() - 0.5)) {
1207                                         l = view()->layers() - 0.5;
1208                                 }
1209                         }
1210                         break;
1211                 }
1212
1213                 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1214         }
1215
1216         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1217
1218                 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1219                 if (r.first) {
1220                         return r;
1221                 }
1222         }
1223
1224         return std::make_pair ((TimeAxisView *) 0, 0);
1225 }
1226
1227 bool
1228 TimeAxisView::covered_by_y_range (double y0, double y1) const
1229 {
1230         if (hidden()) {
1231                 return false;
1232         }
1233
1234         /* if either the top or bottom of the axisview is in the vertical
1235          * range, we cover it.
1236          */
1237
1238         if ((y0 < _y_position && y1 < _y_position) ||
1239             (y0 >= _y_position + height && y1 >= _y_position + height)) {
1240                 return false;
1241         }
1242
1243         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1244                 if ((*i)->covered_by_y_range (y0, y1)) {
1245                         return true;
1246                 }
1247         }
1248
1249         return true;
1250 }
1251
1252 uint32_t
1253 TimeAxisView::preset_height (Height h)
1254 {
1255         switch (h) {
1256         case HeightLargest:
1257                 return (button_height * 2) + extra_height + 260;
1258         case HeightLarger:
1259                 return (button_height * 2) + extra_height + 160;
1260         case HeightLarge:
1261                 return (button_height * 2) + extra_height + 60;
1262         case HeightNormal:
1263                 return (button_height * 2) + extra_height + 10;
1264         case HeightSmall:
1265                 return button_height + extra_height;
1266         }
1267
1268         abort(); /* NOTREACHED */
1269         return 0;
1270 }
1271
1272 /** @return Child time axis views that are not hidden */
1273 TimeAxisView::Children
1274 TimeAxisView::get_child_list ()
1275 {
1276         Children c;
1277
1278         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1279                 if (!(*i)->hidden()) {
1280                         c.push_back(*i);
1281                 }
1282         }
1283
1284         return c;
1285 }
1286
1287 void
1288 TimeAxisView::build_size_menu ()
1289 {
1290         if (_size_menu && _size_menu->gobj ()) {
1291                 return;
1292         }
1293
1294         delete _size_menu;
1295
1296         using namespace Menu_Helpers;
1297
1298         _size_menu = new Menu;
1299         _size_menu->set_name ("ArdourContextMenu");
1300         MenuList& items = _size_menu->items();
1301
1302         items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1303         items.push_back (MenuElem (_("Larger"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1304         items.push_back (MenuElem (_("Large"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1305         items.push_back (MenuElem (_("Normal"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1306         items.push_back (MenuElem (_("Small"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1307 }
1308
1309 void
1310 TimeAxisView::reset_visual_state ()
1311 {
1312         /* this method is not required to trigger a global redraw */
1313
1314         string str = gui_property ("height");
1315
1316         if (!str.empty()) {
1317                 set_height (atoi (str));
1318         } else {
1319                 set_height (preset_height (HeightNormal));
1320         }
1321 }
1322
1323 TrackViewList
1324 TrackViewList::filter_to_unique_playlists ()
1325 {
1326         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1327         TrackViewList ts;
1328
1329         for (iterator i = begin(); i != end(); ++i) {
1330                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1331                 if (!rtav) {
1332                         /* not a route: include it anyway */
1333                         ts.push_back (*i);
1334                 } else {
1335                         boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1336                         if (t) {
1337                                 if (playlists.insert (t->playlist()).second) {
1338                                         /* playlist not seen yet */
1339                                         ts.push_back (*i);
1340                                 }
1341                         } else {
1342                                 /* not a track: include it anyway */
1343                                 ts.push_back (*i);
1344                         }
1345                 }
1346         }
1347         return ts;
1348 }