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