2 Copyright (C) 2000 Paul Davis
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.
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.
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.
27 #include "pbd/error.h"
28 #include "pbd/convert.h"
29 #include "pbd/stacktrace.h"
30 #include "pbd/unwind.h"
32 #include <gtkmm2ext/doi.h>
33 #include <gtkmm2ext/utils.h>
34 #include <gtkmm2ext/selector.h>
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"
42 #include "ardour/profile.h"
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"
53 #include "rgb_macros.h"
55 #include "streamview.h"
56 #include "editor_drag.h"
59 #include "ui_config.h"
66 using namespace ARDOUR;
67 using namespace ARDOUR_UI_UTILS;
69 using namespace Editing;
70 using namespace ArdourCanvas;
71 using Gtkmm2ext::Keyboard;
73 #define TOP_LEVEL_WIDGET controls_ebox
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>();
85 TimeAxisView::setup_sizes()
87 name_width_px = ceilf (100.f * UIConfiguration::instance().get_ui_scale());
90 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
91 : controls_table (5, 4)
92 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
93 , _name_editing (false)
100 , in_destructor (false)
102 , _canvas_display (0)
107 , _effective_height (0)
108 , _resize_drag_start (-1)
109 , _did_resize (false)
110 , _preresize_cursor (0)
111 , _have_preresize_cursor (false)
112 , _ebox_release_can_act (true)
114 if (!controls_meters_size_group) {
115 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
117 if (!midi_scroomer_size_group) {
118 midi_scroomer_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
120 if (extra_height == 0) {
124 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group ());
125 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
126 _canvas_display->hide(); // reveal as needed
128 _canvas_separator = new ArdourCanvas::Line(_canvas_display);
129 CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
130 _canvas_separator->set (ArdourCanvas::Duple(0.0, 0.0), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, 0.0));
131 _canvas_separator->set_outline_color(ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
132 _canvas_separator->set_outline_width(1.0);
133 _canvas_separator->hide();
135 selection_group = new ArdourCanvas::Container (_canvas_display);
136 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
137 selection_group->set_data (X_("timeselection"), (void *) 1);
138 selection_group->hide();
140 _ghost_group = new ArdourCanvas::Container (_canvas_display);
141 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
142 _ghost_group->lower_to_bottom();
143 _ghost_group->show();
145 name_label.set_name (X_("TrackNameEditor"));
146 name_label.set_alignment (0.0, 0.5);
147 name_label.set_width_chars (12);
148 set_tooltip (name_label, _("Track/Bus name (double click to edit)"));
151 std::auto_ptr<Gtk::Entry> an_entry (new Gtkmm2ext::FocusEntry);
152 an_entry->set_name (X_("TrackNameEditor"));
153 Gtk::Requisition req;
154 an_entry->size_request (req);
156 name_label.set_size_request (-1, req.height);
157 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
160 // set min. track-header width if fader is not visible
161 name_label.set_size_request(name_width_px, -1);
165 controls_table.set_row_spacings (2);
166 controls_table.set_col_spacings (2);
167 controls_table.set_border_width (2);
169 if (ARDOUR::Profile->get_mixbus() ) {
170 controls_table.attach (name_label, 4, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
172 controls_table.attach (name_label, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
175 controls_table.show_all ();
176 controls_table.set_no_show_all ();
178 controls_vbox.pack_start (controls_table, false, false);
179 controls_vbox.show ();
181 top_hbox.pack_start (controls_vbox, true, true);
184 controls_ebox.add (time_axis_hbox);
185 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
186 Gdk::BUTTON_RELEASE_MASK|
187 Gdk::POINTER_MOTION_MASK|
188 Gdk::ENTER_NOTIFY_MASK|
189 Gdk::LEAVE_NOTIFY_MASK|
191 controls_ebox.set_flags (CAN_FOCUS);
193 /* note that this handler connects *before* the default handler */
194 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
195 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
196 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
197 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
198 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
199 controls_ebox.show ();
201 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
202 time_axis_frame.add(top_hbox);
203 time_axis_frame.show();
205 HSeparator* separator = manage (new HSeparator());
206 separator->set_name("TrackSeparator");
207 separator->set_size_request(-1, 1);
210 scroomer_placeholder.set_size_request (-1, -1);
211 scroomer_placeholder.show();
212 midi_scroomer_size_group->add_widget (scroomer_placeholder);
214 time_axis_vbox.pack_start (*separator, false, false);
215 time_axis_vbox.pack_start (time_axis_frame, true, true);
216 time_axis_vbox.show();
217 time_axis_hbox.pack_start (time_axis_vbox, true, true);
218 time_axis_hbox.show();
219 top_hbox.pack_start (scroomer_placeholder, false, false); // OR pack_end to move after meters ?
221 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
224 TimeAxisView::~TimeAxisView()
226 CatchDeletion (this);
228 in_destructor = true;
230 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
234 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
235 delete (*i)->rect; (*i)->rect=0;
236 delete (*i)->start_trim; (*i)->start_trim = 0;
237 delete (*i)->end_trim; (*i)->end_trim = 0;
241 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
242 delete (*i)->rect; (*i)->rect = 0;
243 delete (*i)->start_trim; (*i)->start_trim = 0;
244 delete (*i)->end_trim; (*i)->end_trim = 0;
247 delete selection_group;
250 delete _canvas_display;
260 TimeAxisView::hide ()
266 _canvas_display->hide ();
267 _canvas_separator->hide ();
269 if (control_parent) {
270 control_parent->remove (TOP_LEVEL_WIDGET);
277 /* now hide children */
279 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
283 /* if its hidden, it cannot be selected */
284 _editor.get_selection().remove (this);
285 /* and neither can its regions */
286 _editor.get_selection().remove_regions (this);
291 /** Display this TimeAxisView as the nth component of the parent box, at y.
293 * @param y y position.
294 * @param nth index for this TimeAxisView, increased if this view has children.
295 * @param parent parent component.
297 * @return height of this TimeAxisView.
300 TimeAxisView::show_at (double y, int& nth, VBox *parent)
302 if (control_parent) {
303 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
305 control_parent = parent;
306 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
307 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
312 if (_y_position != y) {
313 _canvas_display->set_y_position (y);
317 _canvas_display->raise_to_top ();
318 _canvas_display->show ();
322 _effective_height = current_height ();
324 /* now show relevant children */
326 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
327 if ((*i)->marked_for_display()) {
329 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
335 /* put separator at the bottom of this time axis view */
337 _canvas_separator->set (ArdourCanvas::Duple(0, height), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, height));
338 _canvas_separator->lower_to_bottom ();
339 _canvas_separator->show ();
341 return _effective_height;
345 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
347 switch (ev->direction) {
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);
355 e.stepping_axis_view()->step_height (false);
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);
367 e.stepping_axis_view()->step_height (true);
373 /* no handling for left/right, yet */
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.
381 In the not too distant future this layout is going away anyway and
382 headers will be on the canvas.
384 return _editor.canvas_scroll_event (ev, false);
388 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
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)) {
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()) {
399 _ebox_release_can_act = false;
406 _ebox_release_can_act = true;
408 if (maybe_set_cursor (event->y) > 0) {
409 _resize_drag_start = event->y_root;
416 TimeAxisView::idle_resize (int32_t h)
418 set_height (std::max(0, h));
423 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
425 if (_resize_drag_start >= 0) {
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.)
433 _editor.maybe_autoscroll (false, true, true);
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;
441 /* not dragging but ... */
442 maybe_set_cursor (ev->y);
445 gdk_event_request_motions(ev);
450 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
452 if (_have_preresize_cursor) {
453 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
454 _have_preresize_cursor = false;
460 TimeAxisView::maybe_set_cursor (int y)
462 /* XXX no Gtkmm Gdk::Window::get_cursor() */
463 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
465 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
467 /* y-coordinate in lower 25% */
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));
477 } else if (_have_preresize_cursor) {
478 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
479 _have_preresize_cursor = false;
488 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
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;
496 _editor.stop_canvas_autoscroll ();
497 _resize_drag_start = -1;
500 // don't change selection
505 if (!_ebox_release_can_act) {
509 switch (ev->button) {
512 selection_click (ev);
517 popup_display_menu (ev->time);
525 TimeAxisView::selection_click (GdkEventButton* ev)
527 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
528 _editor.set_selected_track (*this, op, false);
532 /** Steps through the defined heights for this TrackView.
533 * @param coarser true if stepping should decrease in size, otherwise false.
536 TimeAxisView::step_height (bool coarser)
538 static const uint32_t step = 25;
542 if (height <= preset_height (HeightSmall)) {
544 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
545 set_height_enum (HeightSmall);
547 set_height (height - step);
552 if (height <= preset_height(HeightSmall)) {
553 set_height_enum (HeightNormal);
555 set_height (height + step);
562 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
564 if (apply_to_selection) {
565 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
567 set_height (preset_height (h));
572 TimeAxisView::set_height (uint32_t h, TrackHeightMode m)
575 if (m == TotalHeight) {
576 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
577 if ( !(*i)->hidden()) ++lanes;
582 if (h < preset_height (HeightSmall)) {
583 h = preset_height (HeightSmall);
586 TOP_LEVEL_WIDGET.property_height_request () = h;
590 snprintf (buf, sizeof (buf), "%u", height);
591 set_gui_property ("height", buf);
593 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
597 if (selection_group->visible ()) {
598 /* resize the selection rect */
599 show_selection (_editor.get_selection().time);
603 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
604 (*i)->set_height(h, OnlySelf);
608 _editor.override_visible_track_count ();
612 TimeAxisView::begin_name_edit ()
614 if (!can_edit_name()) {
618 Gtk::Window* toplevel = (Gtk::Window*) control_parent->get_toplevel();
619 FloatingTextEntry* fte = new FloatingTextEntry (toplevel, name_label.get_text ());
621 fte->set_name ("TrackNameEditor");
622 fte->use_text.connect (sigc::mem_fun (*this, &TimeAxisView::end_name_edit));
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.
632 name_label.translate_coordinates (*toplevel, 0, 0, x, y);
633 toplevel->get_window()->get_origin (wx, wy);
635 fte->move (wx + x, wy + y);
640 TimeAxisView::end_name_edit (std::string str, int next_dir)
642 if (!name_entry_changed (str)) {
648 TrackViewList const & allviews = _editor.get_track_views ();
649 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
651 if (i != allviews.end()) {
654 if (++i == allviews.end()) {
658 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
660 if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) {
664 if (!(*i)->hidden()) {
671 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
672 _editor.ensure_time_axis_view_is_visible (**i, false);
673 (*i)->begin_name_edit ();
676 } else if (next_dir < 0) {
678 TrackViewList const & allviews = _editor.get_track_views ();
679 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
681 if (i != allviews.begin()) {
683 if (i == allviews.begin()) {
689 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
691 if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) {
695 if (!(*i)->hidden()) {
702 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
703 _editor.ensure_time_axis_view_is_visible (**i, false);
704 (*i)->begin_name_edit ();
710 TimeAxisView::name_entry_changed (string const&)
716 TimeAxisView::can_edit_name () const
722 TimeAxisView::conditionally_add_to_selection ()
728 Selection& s (_editor.get_selection ());
730 if (!s.selected (this)) {
731 _editor.set_selected_track (*this, Selection::Set);
736 TimeAxisView::popup_display_menu (guint32 when)
738 conditionally_add_to_selection ();
740 build_display_menu ();
742 if (!display_menu->items().empty()) {
743 display_menu->popup (1, when);
748 TimeAxisView::set_selected (bool yn)
750 if (yn == selected()) {
754 AxisView::set_selected (yn);
757 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
758 time_axis_frame.set_name ("MixerStripSelectedFrame");
759 controls_ebox.set_name (controls_base_selected_name);
760 controls_vbox.set_name (controls_base_selected_name);
761 time_axis_vbox.set_name (controls_base_selected_name);
763 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
764 time_axis_frame.set_name (controls_base_unselected_name);
765 controls_ebox.set_name (controls_base_unselected_name);
766 controls_vbox.set_name (controls_base_unselected_name);
767 time_axis_vbox.set_name (controls_base_unselected_name);
771 /* children will be set for the yn=true case. but when deselecting
772 the editor only has a list of top-level trackviews, so we
773 have to do this here.
776 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
777 (*i)->set_selected (false);
781 time_axis_frame.show();
786 TimeAxisView::build_display_menu ()
788 using namespace Menu_Helpers;
792 display_menu = new Menu;
793 display_menu->set_name ("ArdourContextMenu");
795 // Just let implementing classes define what goes into the manu
799 TimeAxisView::set_samples_per_pixel (double fpp)
801 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
802 (*i)->set_samples_per_pixel (fpp);
807 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
809 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
810 (*i)->show_timestretch (start, end, layers, layer);
815 TimeAxisView::hide_timestretch ()
817 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
818 (*i)->hide_timestretch ();
823 TimeAxisView::show_selection (TimeSelection& ts)
828 SelectionRect *rect; time_axis_frame.show();
831 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
832 (*i)->show_selection (ts);
835 if (selection_group->visible ()) {
836 while (!used_selection_rects.empty()) {
837 free_selection_rects.push_front (used_selection_rects.front());
838 used_selection_rects.pop_front();
839 free_selection_rects.front()->rect->hide();
840 free_selection_rects.front()->start_trim->hide();
841 free_selection_rects.front()->end_trim->hide();
843 selection_group->hide();
846 selection_group->show();
847 selection_group->raise_to_top();
849 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
850 framepos_t start, end;
855 cnt = end - start + 1;
857 rect = get_selection_rect ((*i).id);
859 x1 = _editor.sample_to_pixel (start);
860 x2 = _editor.sample_to_pixel (start + cnt - 1);
861 y2 = current_height() - 1;
863 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
865 // trim boxes are at the top for selections
868 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
869 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
871 rect->start_trim->show();
872 rect->end_trim->show();
874 rect->start_trim->hide();
875 rect->end_trim->hide();
879 used_selection_rects.push_back (rect);
884 TimeAxisView::reshow_selection (TimeSelection& ts)
888 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
889 (*i)->show_selection (ts);
894 TimeAxisView::hide_selection ()
896 if (selection_group->visible ()) {
897 while (!used_selection_rects.empty()) {
898 free_selection_rects.push_front (used_selection_rects.front());
899 used_selection_rects.pop_front();
900 free_selection_rects.front()->rect->hide();
901 free_selection_rects.front()->start_trim->hide();
902 free_selection_rects.front()->end_trim->hide();
904 selection_group->hide();
907 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
908 (*i)->hide_selection ();
913 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
915 /* find the selection rect this is for. we have the item corresponding to one
919 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
920 if ((*i)->start_trim == item || (*i)->end_trim == item) {
922 /* make one trim handle be "above" the other so that if they overlap,
923 the top one is the one last used.
926 (*i)->rect->raise_to_top ();
927 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
928 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
935 // retuned rect is pushed back into the used_selection_rects list
936 // in TimeAxisView::show_selection() which is the only caller.
938 TimeAxisView::get_selection_rect (uint32_t id)
942 /* check to see if we already have a visible rect for this particular selection ID */
944 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
945 if ((*i)->id == id) {
946 SelectionRect* ret = (*i);
947 used_selection_rects.erase (i);
952 /* ditto for the free rect list */
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);
962 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
964 if (free_selection_rects.empty()) {
966 rect = new SelectionRect;
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"));
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);
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);
983 free_selection_rects.push_front (rect);
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));
990 rect = free_selection_rects.front();
992 free_selection_rects.pop_front();
996 struct null_deleter { void operator()(void const *) const {} };
999 TimeAxisView::is_child (TimeAxisView* tav)
1001 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1005 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1007 children.push_back (child);
1011 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1013 Children::iterator i;
1015 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
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.
1028 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/, bool /*within*/)
1034 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1040 TimeAxisView::add_ghost (RegionView* rv)
1042 GhostRegion* gr = rv->add_ghost (*this);
1045 ghosts.push_back(gr);
1050 TimeAxisView::remove_ghost (RegionView* rv)
1052 rv->remove_ghost_in (*this);
1056 TimeAxisView::erase_ghost (GhostRegion* gr)
1058 if (in_destructor) {
1062 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1071 TimeAxisView::touched (double top, double bot)
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.
1077 double mybot = _y_position + current_height();
1079 return ((_y_position <= bot && _y_position >= top) ||
1080 ((mybot <= bot) && (top < mybot)) ||
1081 (mybot >= bot && _y_position < top));
1085 TimeAxisView::set_parent (TimeAxisView& p)
1091 TimeAxisView::reset_height ()
1093 set_height (height);
1095 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1096 (*i)->set_height ((*i)->height);
1101 TimeAxisView::compute_heights ()
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;
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);
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);
1120 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1121 one_row_table.show_all ();
1123 Gtk::Requisition req(one_row_table.size_request ());
1124 button_height = req.height;
1128 TimeAxisView::color_handler ()
1130 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1134 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1136 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1137 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1139 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1140 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1142 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1143 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1146 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1148 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1149 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1151 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1152 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1154 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1155 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
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.
1163 * If the covering object is a child axis, then the child is returned.
1164 * TimeAxisView is 0 otherwise.
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.
1169 std::pair<TimeAxisView*, double>
1170 TimeAxisView::covers_y_position (double y) const
1173 return std::make_pair ((TimeAxisView *) 0, 0);
1176 if (_y_position <= y && y < (_y_position + height)) {
1178 /* work out the layer index if appropriate */
1180 switch (layer_display ()) {
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;
1196 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1198 if (l >= (view()->layers() - 0.5)) {
1199 l = view()->layers() - 0.5;
1205 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1208 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1210 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1216 return std::make_pair ((TimeAxisView *) 0, 0);
1220 TimeAxisView::covered_by_y_range (double y0, double y1) const
1226 /* if either the top or bottom of the axisview is in the vertical
1227 * range, we cover it.
1230 if ((y0 < _y_position && y1 < _y_position) ||
1231 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1235 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1236 if ((*i)->covered_by_y_range (y0, y1)) {
1245 TimeAxisView::preset_height (Height h)
1249 return (button_height * 2) + extra_height + 260;
1251 return (button_height * 2) + extra_height + 160;
1253 return (button_height * 2) + extra_height + 60;
1255 return (button_height * 2) + extra_height + 10;
1257 return button_height + extra_height;
1260 abort(); /* NOTREACHED */
1264 /** @return Child time axis views that are not hidden */
1265 TimeAxisView::Children
1266 TimeAxisView::get_child_list ()
1270 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1271 if (!(*i)->hidden()) {
1280 TimeAxisView::build_size_menu ()
1282 if (_size_menu && _size_menu->gobj ()) {
1288 using namespace Menu_Helpers;
1290 _size_menu = new Menu;
1291 _size_menu->set_name ("ArdourContextMenu");
1292 MenuList& items = _size_menu->items();
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)));
1302 TimeAxisView::reset_visual_state ()
1304 /* this method is not required to trigger a global redraw */
1306 string str = gui_property ("height");
1309 set_height (atoi (str));
1311 set_height (preset_height (HeightNormal));
1316 TrackViewList::filter_to_unique_playlists ()
1318 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1321 for (iterator i = begin(); i != end(); ++i) {
1322 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1324 /* not a route: include it anyway */
1327 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1329 if (playlists.insert (t->playlist()).second) {
1330 /* playlist not seen yet */
1334 /* not a track: include it anyway */