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