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