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