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