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