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