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