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