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