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