clean up GUI scaling
[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 #include "canvas/utils.h"
39 #include "canvas/colors.h"
40
41 #include "ardour/profile.h"
42
43 #include "ardour_ui.h"
44 #include "ardour_dialog.h"
45 #include "global_signals.h"
46 #include "gui_thread.h"
47 #include "public_editor.h"
48 #include "time_axis_view.h"
49 #include "region_view.h"
50 #include "ghostregion.h"
51 #include "selection.h"
52 #include "keyboard.h"
53 #include "rgb_macros.h"
54 #include "utils.h"
55 #include "streamview.h"
56 #include "editor_drag.h"
57 #include "editor.h"
58
59 #include "i18n.h"
60
61 using namespace std;
62 using namespace Gtk;
63 using namespace Gdk;
64 using namespace ARDOUR;
65 using namespace ARDOUR_UI_UTILS;
66 using namespace PBD;
67 using namespace Editing;
68 using namespace ArdourCanvas;
69 using Gtkmm2ext::Keyboard;
70
71 #define TOP_LEVEL_WIDGET controls_ebox
72
73 const double trim_handle_size = 6.0; /* pixels */
74 uint32_t TimeAxisView::button_height = 0;
75 uint32_t TimeAxisView::extra_height = 0;
76 int const TimeAxisView::_max_order = 512;
77 unsigned int TimeAxisView::name_width_px = 100;
78 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
79 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = Glib::RefPtr<Gtk::SizeGroup>();
80 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::midi_scroomer_size_group = Glib::RefPtr<Gtk::SizeGroup>();
81
82 void
83 TimeAxisView::setup_sizes()
84 {
85         name_width_px = ceilf (100.f * ARDOUR_UI::ui_scale);
86 }
87
88 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
89         : AxisView (sess)
90         , controls_table (3, 3)
91         , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
92         , _name_editing (false)
93         , height (0)
94         , display_menu (0)
95         , parent (rent)
96         , selection_group (0)
97         , _ghost_group (0)
98         , _hidden (false)
99         , in_destructor (false)
100         , _size_menu (0)
101         , _canvas_display (0)
102         , _y_position (0)
103         , _editor (ed)
104         , name_entry (0)
105         , control_parent (0)
106         , _order (0)
107         , _effective_height (0)
108         , _resize_drag_start (-1)
109         , _did_resize (false)
110         , _preresize_cursor (0)
111         , _have_preresize_cursor (false)
112         , _ebox_release_can_act (true)
113 {
114         if (!controls_meters_size_group) {
115                 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
116         }
117         if (!midi_scroomer_size_group) {
118                 midi_scroomer_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
119         }
120         if (extra_height == 0) {
121                 compute_heights ();
122         }
123
124         _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group ());
125         CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
126         _canvas_display->hide(); // reveal as needed
127
128         _canvas_separator = new ArdourCanvas::Line(_canvas_display);
129         CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
130         _canvas_separator->set (ArdourCanvas::Duple(0.0, 0.0), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, 0.0));
131         _canvas_separator->set_outline_color(ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
132         _canvas_separator->set_outline_width(1.0);
133         _canvas_separator->hide();
134
135         selection_group = new ArdourCanvas::Container (_canvas_display);
136         CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
137         selection_group->set_data (X_("timeselection"), (void *) 1);
138         selection_group->hide();
139         
140         _ghost_group = new ArdourCanvas::Container (_canvas_display);
141         CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
142         _ghost_group->lower_to_bottom();
143         _ghost_group->show();
144
145         name_label.set_name ("TrackLabel");
146         name_label.set_alignment (0.0, 0.5);
147         name_label.set_width_chars (12);
148         ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
149
150         Gtk::Entry* an_entry = new Gtkmm2ext::FocusEntry;
151         an_entry->set_name ("EditorTrackNameDisplay");
152         Gtk::Requisition req;
153         an_entry->size_request (req);
154         name_label.set_size_request (-1, req.height);
155         name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
156         delete an_entry;
157
158         name_hbox.pack_end (name_label, true, true);
159
160         // set min. track-header width if fader is not visible
161         name_hbox.set_size_request(name_width_px, -1);
162
163         name_hbox.show ();
164         name_label.show ();
165
166         controls_table.set_row_spacings (2);
167         controls_table.set_col_spacings (2);
168         controls_table.set_border_width (2);
169
170         if (ARDOUR::Profile->get_mixbus() ) {
171                 controls_table.attach (name_hbox, 4, 5, 0, 2,  Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
172         } else {
173                 controls_table.attach (name_hbox, 1, 2, 0, 2,  Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
174         }
175         controls_table.show_all ();
176         controls_table.set_no_show_all ();
177
178         controls_vbox.pack_start (controls_table, false, false);
179         controls_vbox.show ();
180
181         top_hbox.pack_start (controls_vbox, true, true);
182         top_hbox.show ();
183
184         controls_ebox.add (time_axis_hbox);
185         controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
186                                   Gdk::BUTTON_RELEASE_MASK|
187                                   Gdk::POINTER_MOTION_MASK|
188                                   Gdk::ENTER_NOTIFY_MASK|
189                                   Gdk::LEAVE_NOTIFY_MASK|
190                                   Gdk::SCROLL_MASK);
191         controls_ebox.set_flags (CAN_FOCUS);
192
193         /* note that this handler connects *before* the default handler */
194         controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
195         controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
196         controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
197         controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
198         controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
199         controls_ebox.show ();
200
201         time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
202         time_axis_frame.add(top_hbox);
203         time_axis_frame.show();
204
205         HSeparator* separator = manage (new HSeparator());
206         separator->set_name("TrackSeparator");
207         separator->set_size_request(-1, 1);
208         separator->show();
209
210         scroomer_placeholder.set_size_request (-1, -1);
211         scroomer_placeholder.show();
212         midi_scroomer_size_group->add_widget (scroomer_placeholder);
213
214         time_axis_vbox.pack_start (*separator, false, false);
215         time_axis_vbox.pack_start (time_axis_frame, true, true);
216         time_axis_vbox.show();
217         time_axis_hbox.pack_start (time_axis_vbox, true, true);
218         time_axis_hbox.show();
219         top_hbox.pack_start (scroomer_placeholder, false, false); // OR pack_end to move after meters ?
220
221         ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
222
223         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
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         bool edit_next = false;
701         bool edit_prev = false;
702
703         switch (response) {
704         case RESPONSE_CANCEL:
705                 break;
706         case RESPONSE_OK:
707                 name_entry_changed ();
708                 break;
709         case RESPONSE_ACCEPT:
710                 name_entry_changed ();
711                 edit_next = true;
712         case RESPONSE_APPLY:
713                 name_entry_changed ();
714                 edit_prev = true;
715         }
716
717         /* this will delete the name_entry. but it will also drop focus, which
718          * will cause another callback to this function, so set name_entry = 0
719          * first to ensure we don't double-remove etc. etc.
720          */
721
722         Gtk::Entry* tmp = name_entry;
723         name_entry = 0;
724         name_hbox.remove (*tmp);
725
726         /* put the name label back */
727
728         name_hbox.pack_end (name_label);
729         name_label.show ();
730
731         if (edit_next) {
732
733                 TrackViewList const & allviews = _editor.get_track_views ();
734                 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
735                 
736                 if (i != allviews.end()) {
737                         
738                         do {
739                                 if (++i == allviews.end()) {
740                                         return;
741                                 }
742                                 
743                                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
744                         
745                                 if (rtav && rtav->route()->record_enabled()) {
746                                         continue;
747                                 }
748                                 
749                                 if (!(*i)->hidden()) {
750                                         break;
751                                 }
752                                 
753                         } while (true);
754                 }
755
756                 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
757                         _editor.ensure_time_axis_view_is_visible (**i, false);
758                         (*i)->begin_name_edit ();
759                 } 
760
761         } else if (edit_prev) {
762
763                 TrackViewList const & allviews = _editor.get_track_views ();
764                 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
765                 
766                 if (i != allviews.begin()) {
767                         do {
768                                 if (i == allviews.begin()) {
769                                         return;
770                                 }
771                                 
772                                 --i;
773                                 
774                                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
775                                 
776                                 if (rtav && rtav->route()->record_enabled()) {
777                                         continue;
778                                 }
779                                 
780                                 if (!(*i)->hidden()) {
781                                         break;
782                                 }
783                                 
784                         } while (true);
785                 }
786                 
787                 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
788                         _editor.ensure_time_axis_view_is_visible (**i, false);
789                         (*i)->begin_name_edit ();
790                 } 
791         }
792 }
793
794 void
795 TimeAxisView::name_entry_changed ()
796 {
797 }
798
799 bool
800 TimeAxisView::can_edit_name () const
801 {
802         return true;
803 }
804
805 void
806 TimeAxisView::conditionally_add_to_selection ()
807 {
808         Selection& s (_editor.get_selection ());
809
810         if (!s.selected (this)) {
811                 _editor.set_selected_track (*this, Selection::Set);
812         }
813 }
814
815 void
816 TimeAxisView::popup_display_menu (guint32 when)
817 {
818         conditionally_add_to_selection ();
819
820         build_display_menu ();
821         display_menu->popup (1, when);
822 }
823
824 void
825 TimeAxisView::set_selected (bool yn)
826 {
827         if (can_edit_name() && name_entry && name_entry->get_visible()) {
828                 end_name_edit (RESPONSE_CANCEL);
829         }
830
831         if (yn == _selected) {
832                 return;
833         }
834
835         Selectable::set_selected (yn);
836
837         if (_selected) {
838                 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
839                 time_axis_frame.set_name ("MixerStripSelectedFrame");
840                 controls_ebox.set_name (controls_base_selected_name);
841                 controls_vbox.set_name (controls_base_selected_name);
842                 time_axis_vbox.set_name (controls_base_selected_name);
843         } else {
844                 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
845                 time_axis_frame.set_name (controls_base_unselected_name);
846                 controls_ebox.set_name (controls_base_unselected_name);
847                 controls_vbox.set_name (controls_base_unselected_name);
848                 time_axis_vbox.set_name (controls_base_unselected_name);
849
850                 hide_selection ();
851
852                 /* children will be set for the yn=true case. but when deselecting
853                    the editor only has a list of top-level trackviews, so we
854                    have to do this here.
855                 */
856
857                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
858                         (*i)->set_selected (false);
859                 }
860         }
861
862         time_axis_frame.show();
863
864 }
865
866 void
867 TimeAxisView::build_display_menu ()
868 {
869         using namespace Menu_Helpers;
870
871         delete display_menu;
872
873         display_menu = new Menu;
874         display_menu->set_name ("ArdourContextMenu");
875
876         // Just let implementing classes define what goes into the manu
877 }
878
879 void
880 TimeAxisView::set_samples_per_pixel (double fpp)
881 {
882         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
883                 (*i)->set_samples_per_pixel (fpp);
884         }
885 }
886
887 void
888 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
889 {
890         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
891                 (*i)->show_timestretch (start, end, layers, layer);
892         }
893 }
894
895 void
896 TimeAxisView::hide_timestretch ()
897 {
898         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
899                 (*i)->hide_timestretch ();
900         }
901 }
902
903 void
904 TimeAxisView::show_selection (TimeSelection& ts)
905 {
906         double x1;
907         double x2;
908         double y2;
909         SelectionRect *rect;    time_axis_frame.show();
910
911
912         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
913                 (*i)->show_selection (ts);
914         }
915
916         if (selection_group->visible ()) {
917                 while (!used_selection_rects.empty()) {
918                         free_selection_rects.push_front (used_selection_rects.front());
919                         used_selection_rects.pop_front();
920                         free_selection_rects.front()->rect->hide();
921                         free_selection_rects.front()->start_trim->hide();
922                         free_selection_rects.front()->end_trim->hide();
923                 }
924                 selection_group->hide();
925         }
926
927         selection_group->show();
928         selection_group->raise_to_top();
929
930         for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
931                 framepos_t start, end;
932                 framecnt_t cnt;
933
934                 start = (*i).start;
935                 end = (*i).end;
936                 cnt = end - start + 1;
937
938                 rect = get_selection_rect ((*i).id);
939
940                 x1 = _editor.sample_to_pixel (start);
941                 x2 = _editor.sample_to_pixel (start + cnt - 1);
942                 y2 = current_height() - 1;
943
944                 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
945
946                 // trim boxes are at the top for selections
947
948                 if (x2 > x1) {
949                         rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
950                         rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
951
952                         rect->start_trim->show();
953                         rect->end_trim->show();
954                 } else {
955                         rect->start_trim->hide();
956                         rect->end_trim->hide();
957                 }
958
959                 rect->rect->show ();
960                 used_selection_rects.push_back (rect);
961         }
962 }
963
964 void
965 TimeAxisView::reshow_selection (TimeSelection& ts)
966 {
967         show_selection (ts);
968
969         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
970                 (*i)->show_selection (ts);
971         }
972 }
973
974 void
975 TimeAxisView::hide_selection ()
976 {
977         if (selection_group->visible ()) {
978                 while (!used_selection_rects.empty()) {
979                         free_selection_rects.push_front (used_selection_rects.front());
980                         used_selection_rects.pop_front();
981                         free_selection_rects.front()->rect->hide();
982                         free_selection_rects.front()->start_trim->hide();
983                         free_selection_rects.front()->end_trim->hide();
984                 }
985                 selection_group->hide();
986         }
987
988         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
989                 (*i)->hide_selection ();
990         }
991 }
992
993 void
994 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
995 {
996         /* find the selection rect this is for. we have the item corresponding to one
997            of the trim handles.
998          */
999
1000         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1001                 if ((*i)->start_trim == item || (*i)->end_trim == item) {
1002
1003                         /* make one trim handle be "above" the other so that if they overlap,
1004                            the top one is the one last used.
1005                         */
1006
1007                         (*i)->rect->raise_to_top ();
1008                         (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
1009                         (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
1010
1011                         break;
1012                 }
1013         }
1014 }
1015
1016 SelectionRect *
1017 TimeAxisView::get_selection_rect (uint32_t id)
1018 {
1019         SelectionRect *rect;
1020
1021         /* check to see if we already have a visible rect for this particular selection ID */
1022
1023         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1024                 if ((*i)->id == id) {
1025                         return (*i);
1026                 }
1027         }
1028
1029         /* ditto for the free rect list */
1030
1031         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1032                 if ((*i)->id == id) {
1033                         SelectionRect* ret = (*i);
1034                         free_selection_rects.erase (i);
1035                         return ret;
1036                 }
1037         }
1038
1039         /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
1040
1041         if (free_selection_rects.empty()) {
1042
1043                 rect = new SelectionRect;
1044
1045                 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1046                 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1047                 rect->rect->set_outline (false);
1048                 rect->rect->set_fill_color (ARDOUR_UI::config()->color_mod ("selection rect", "selection rect"));
1049
1050                 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1051                 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1052                 rect->start_trim->set_outline (false);
1053                 rect->start_trim->set_fill (false);
1054
1055                 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1056                 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1057                 rect->end_trim->set_outline (false);
1058                 rect->end_trim->set_fill (false);
1059
1060                 free_selection_rects.push_front (rect);
1061
1062                 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1063                 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1064                 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1065         }
1066
1067         rect = free_selection_rects.front();
1068         rect->id = id;
1069         free_selection_rects.pop_front();
1070         return rect;
1071 }
1072
1073 struct null_deleter { void operator()(void const *) const {} };
1074
1075 bool
1076 TimeAxisView::is_child (TimeAxisView* tav)
1077 {
1078         return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1079 }
1080
1081 void
1082 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1083 {
1084         children.push_back (child);
1085 }
1086
1087 void
1088 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1089 {
1090         Children::iterator i;
1091
1092         if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1093                 children.erase (i);
1094         }
1095 }
1096
1097 /** Get selectable things within a given range.
1098  *  @param start Start time in session frames.
1099  *  @param end End time in session frames.
1100  *  @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1101  *  @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1102  *  @param result Filled in with selectable things.
1103  */
1104 void
1105 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/, bool /*within*/)
1106 {
1107         return;
1108 }
1109
1110 void
1111 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1112 {
1113         return;
1114 }
1115
1116 void
1117 TimeAxisView::add_ghost (RegionView* rv)
1118 {
1119         GhostRegion* gr = rv->add_ghost (*this);
1120
1121         if (gr) {
1122                 ghosts.push_back(gr);
1123         }
1124 }
1125
1126 void
1127 TimeAxisView::remove_ghost (RegionView* rv)
1128 {
1129         rv->remove_ghost_in (*this);
1130 }
1131
1132 void
1133 TimeAxisView::erase_ghost (GhostRegion* gr)
1134 {
1135         if (in_destructor) {
1136                 return;
1137         }
1138
1139         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1140                 if ((*i) == gr) {
1141                         ghosts.erase (i);
1142                         break;
1143                 }
1144         }
1145 }
1146
1147 bool
1148 TimeAxisView::touched (double top, double bot)
1149 {
1150         /* remember: this is X Window - coordinate space starts in upper left and moves down.
1151           y_position is the "origin" or "top" of the track.
1152         */
1153
1154         double mybot = _y_position + current_height();
1155
1156         return ((_y_position <= bot && _y_position >= top) ||
1157                 ((mybot <= bot) && (top < mybot)) ||
1158                 (mybot >= bot && _y_position < top));
1159 }
1160
1161 void
1162 TimeAxisView::set_parent (TimeAxisView& p)
1163 {
1164         parent = &p;
1165 }
1166
1167 void
1168 TimeAxisView::reset_height ()
1169 {
1170         set_height (height);
1171
1172         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1173                 (*i)->set_height ((*i)->height);
1174         }
1175 }
1176
1177 void
1178 TimeAxisView::compute_heights ()
1179 {
1180         // TODO this function should be re-evaluated when font-scaling changes (!)
1181         Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1182         Gtk::Table one_row_table (1, 1);
1183         ArdourButton* test_button = manage (new ArdourButton);
1184         const int border_width = 2;
1185         const int frame_height = 2;
1186         extra_height = (2 * border_width) + frame_height;
1187
1188         window.add (one_row_table);
1189         test_button->set_name ("mute button");
1190         test_button->set_text (S_("Mute|M"));
1191         test_button->set_tweaks (ArdourButton::TrackHeader);
1192
1193         one_row_table.set_border_width (border_width);
1194         one_row_table.set_row_spacings (2);
1195         one_row_table.set_col_spacings (2);
1196
1197         one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1198         one_row_table.show_all ();
1199
1200         Gtk::Requisition req(one_row_table.size_request ());
1201         button_height = req.height;
1202 }
1203
1204 void
1205 TimeAxisView::color_handler ()
1206 {
1207         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1208                 (*i)->set_colors();
1209         }
1210
1211         for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1212
1213                 (*i)->rect->set_fill_color (ARDOUR_UI::config()->color_mod ("selection rect", "selection rect"));
1214                 (*i)->rect->set_outline_color (ARDOUR_UI::config()->color ("selection"));
1215
1216                 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->color ("selection"));
1217                 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->color ("selection"));
1218                 
1219                 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->color ("selection"));
1220                 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->color ("selection"));
1221         }
1222         
1223         for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1224                 
1225                 (*i)->rect->set_fill_color (ARDOUR_UI::config()->color_mod ("selection rect", "selection rect"));
1226                 (*i)->rect->set_outline_color (ARDOUR_UI::config()->color ("selection"));
1227                 
1228                 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->color ("selection"));
1229                 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->color ("selection"));
1230                 
1231                 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->color ("selection"));
1232                 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->color ("selection"));
1233         }
1234 }
1235
1236 /** @return Pair: TimeAxisView, layer index.
1237  * TimeAxisView is non-0 if this object covers @param y, or one of its children
1238  * does. @param y is an offset from the top of the trackview area.
1239  *
1240  * If the covering object is a child axis, then the child is returned.
1241  * TimeAxisView is 0 otherwise.
1242  *
1243  * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1244  * and is in stacked or expanded * region display mode, otherwise 0.
1245  */
1246 std::pair<TimeAxisView*, double>
1247 TimeAxisView::covers_y_position (double y) const
1248 {
1249         if (hidden()) {
1250                 return std::make_pair ((TimeAxisView *) 0, 0);
1251         }
1252
1253         if (_y_position <= y && y < (_y_position + height)) {
1254
1255                 /* work out the layer index if appropriate */
1256                 double l = 0;
1257                 switch (layer_display ()) {
1258                 case Overlaid:
1259                         break;
1260                 case Stacked:
1261                         if (view ()) {
1262                                 /* compute layer */
1263                                 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1264                                 /* clamp to max layers to be on the safe side; sometimes the above calculation
1265                                    returns a too-high value */
1266                                 if (l >= view()->layers ()) {
1267                                         l = view()->layers() - 1;
1268                                 }
1269                         }
1270                         break;
1271                 case Expanded:
1272                         if (view ()) {
1273                                 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1274                                 l = n * 0.5 - 0.5;
1275                                 if (l >= (view()->layers() - 0.5)) {
1276                                         l = view()->layers() - 0.5;
1277                                 }
1278                         }
1279                         break;
1280                 }
1281
1282                 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1283         }
1284
1285         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1286
1287                 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1288                 if (r.first) {
1289                         return r;
1290                 }
1291         }
1292
1293         return std::make_pair ((TimeAxisView *) 0, 0);
1294 }
1295
1296 bool
1297 TimeAxisView::covered_by_y_range (double y0, double y1) const
1298 {
1299         if (hidden()) {
1300                 return false;
1301         }
1302
1303         /* if either the top or bottom of the axisview is in the vertical
1304          * range, we cover it.
1305          */
1306         
1307         if ((y0 < _y_position && y1 < _y_position) ||
1308             (y0 >= _y_position + height && y1 >= _y_position + height)) {
1309                 return false;
1310         }
1311
1312         for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1313                 if ((*i)->covered_by_y_range (y0, y1)) {
1314                         return true;
1315                 }
1316         }
1317
1318         return true;
1319 }
1320
1321 uint32_t
1322 TimeAxisView::preset_height (Height h)
1323 {
1324         switch (h) {
1325         case HeightLargest:
1326                 return (button_height * 2) + extra_height + 260;
1327         case HeightLarger:
1328                 return (button_height * 2) + extra_height + 160;
1329         case HeightLarge:
1330                 return (button_height * 2) + extra_height + 60;
1331         case HeightNormal:
1332                 return (button_height * 2) + extra_height + 10;
1333         case HeightSmall:
1334                 return button_height + extra_height;
1335         }
1336
1337         abort(); /* NOTREACHED */
1338         return 0;
1339 }
1340
1341 /** @return Child time axis views that are not hidden */
1342 TimeAxisView::Children
1343 TimeAxisView::get_child_list ()
1344 {
1345         Children c;
1346
1347         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1348                 if (!(*i)->hidden()) {
1349                         c.push_back(*i);
1350                 }
1351         }
1352
1353         return c;
1354 }
1355
1356 void
1357 TimeAxisView::build_size_menu ()
1358 {
1359         if (_size_menu && _size_menu->gobj ()) {
1360                 return;
1361         }
1362
1363         delete _size_menu;
1364
1365         using namespace Menu_Helpers;
1366
1367         _size_menu = new Menu;
1368         _size_menu->set_name ("ArdourContextMenu");
1369         MenuList& items = _size_menu->items();
1370
1371         items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1372         items.push_back (MenuElem (_("Larger"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1373         items.push_back (MenuElem (_("Large"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1374         items.push_back (MenuElem (_("Normal"),  sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1375         items.push_back (MenuElem (_("Small"),   sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1376 }
1377
1378 void
1379 TimeAxisView::reset_visual_state ()
1380 {
1381         /* this method is not required to trigger a global redraw */
1382
1383         string str = gui_property ("height");
1384         
1385         if (!str.empty()) {
1386                 set_height (atoi (str));
1387         } else {
1388                 set_height (preset_height (HeightNormal));
1389         }
1390 }
1391
1392 TrackViewList
1393 TrackViewList::filter_to_unique_playlists ()
1394 {
1395         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1396         TrackViewList ts;
1397
1398         for (iterator i = begin(); i != end(); ++i) {
1399                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1400                 if (!rtav) {
1401                         /* not a route: include it anyway */
1402                         ts.push_back (*i);
1403                 } else {
1404                         boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1405                         if (t) {
1406                                 if (playlists.insert (t->playlist()).second) {
1407                                         /* playlist not seen yet */
1408                                         ts.push_back (*i);
1409                                 }
1410                         } else {
1411                                 /* not a track: include it anyway */
1412                                 ts.push_back (*i);
1413                         }
1414                 }
1415         }
1416         return ts;
1417 }