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"
31 #include <gtkmm2ext/doi.h>
32 #include <gtkmm2ext/utils.h>
33 #include <gtkmm2ext/selector.h>
35 #include "canvas/canvas.h"
36 #include "canvas/rectangle.h"
37 #include "canvas/debug.h"
39 #include "ardour/profile.h"
41 #include "ardour_ui.h"
42 #include "ardour_dialog.h"
43 #include "global_signals.h"
44 #include "gui_thread.h"
45 #include "public_editor.h"
46 #include "time_axis_view.h"
47 #include "region_view.h"
48 #include "ghostregion.h"
49 #include "selection.h"
51 #include "rgb_macros.h"
53 #include "streamview.h"
54 #include "editor_drag.h"
62 using namespace ARDOUR;
63 using namespace ARDOUR_UI_UTILS;
65 using namespace Editing;
66 using namespace ArdourCanvas;
67 using Gtkmm2ext::Keyboard;
69 #define TOP_LEVEL_WIDGET controls_ebox
71 const double trim_handle_size = 6.0; /* pixels */
72 uint32_t TimeAxisView::button_height = 0;
73 uint32_t TimeAxisView::extra_height = 0;
74 int const TimeAxisView::_max_order = 512;
75 unsigned int TimeAxisView::name_width_px = 100; // TODO adjust with font-scaling on style-change
76 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
77 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = Glib::RefPtr<Gtk::SizeGroup>();
78 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::midi_scroomer_size_group = Glib::RefPtr<Gtk::SizeGroup>();
80 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
82 , controls_table (3, 3)
83 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
84 , _name_editing (false)
91 , in_destructor (false)
99 , _effective_height (0)
100 , _resize_drag_start (-1)
101 , _did_resize (false)
102 , _preresize_cursor (0)
103 , _have_preresize_cursor (false)
104 , _ebox_release_can_act (true)
106 if (!controls_meters_size_group) {
107 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
109 if (!midi_scroomer_size_group) {
110 midi_scroomer_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
112 if (extra_height == 0) {
116 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (1.0, 0.0));
117 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
118 _canvas_display->hide(); // reveal as needed
120 _canvas_separator = new ArdourCanvas::Line(ed.get_trackview_group ());
121 CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
122 _canvas_separator->set_outline_color(RGBA_TO_UINT (0, 0, 0, 255));
123 _canvas_separator->set_outline_width(1.0);
124 _canvas_separator->hide();
126 selection_group = new ArdourCanvas::Container (_canvas_display);
127 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
128 selection_group->set_data (X_("timeselection"), (void *) 1);
129 selection_group->hide();
131 _ghost_group = new ArdourCanvas::Container (_canvas_display);
132 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
133 _ghost_group->lower_to_bottom();
134 _ghost_group->show();
136 name_label.set_name ("TrackLabel");
137 name_label.set_alignment (0.0, 0.5);
138 name_label.set_width_chars (12);
139 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
141 Gtk::Entry* an_entry = new Gtkmm2ext::FocusEntry;
142 an_entry->set_name ("EditorTrackNameDisplay");
143 Gtk::Requisition req;
144 an_entry->size_request (req);
145 name_label.set_size_request (-1, req.height);
146 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
149 name_hbox.pack_end (name_label, true, true);
151 // set min. track-header width if fader is not visible
152 name_hbox.set_size_request(name_width_px, -1);
157 controls_table.set_row_spacings (2);
158 controls_table.set_col_spacings (2);
159 controls_table.set_border_width (2);
161 if (ARDOUR::Profile->get_mixbus() ) {
162 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
164 controls_table.attach (name_hbox, 1, 2, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
166 controls_table.show_all ();
167 controls_table.set_no_show_all ();
169 controls_vbox.pack_start (controls_table, false, false);
170 controls_vbox.show ();
172 top_hbox.pack_start (controls_vbox, true, true);
175 controls_ebox.add (time_axis_hbox);
176 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
177 Gdk::BUTTON_RELEASE_MASK|
178 Gdk::POINTER_MOTION_MASK|
179 Gdk::ENTER_NOTIFY_MASK|
180 Gdk::LEAVE_NOTIFY_MASK|
182 controls_ebox.set_flags (CAN_FOCUS);
184 /* note that this handler connects *before* the default handler */
185 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
186 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
187 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
188 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
189 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
190 controls_ebox.show ();
192 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
193 time_axis_frame.add(top_hbox);
194 time_axis_frame.show();
196 HSeparator* separator = manage (new HSeparator());
197 separator->set_name("TrackSeparator");
198 separator->set_size_request(-1, 1);
201 scroomer_placeholder.set_size_request (-1, -1);
202 scroomer_placeholder.show();
203 midi_scroomer_size_group->add_widget (scroomer_placeholder);
205 time_axis_vbox.pack_start (*separator, false, false);
206 time_axis_vbox.pack_start (time_axis_frame, true, true);
207 time_axis_vbox.show();
208 time_axis_hbox.pack_start (time_axis_vbox, true, true);
209 time_axis_hbox.show();
210 top_hbox.pack_start (scroomer_placeholder, false, false); // OR pack_end to move after meters ?
212 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
214 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
217 TimeAxisView::~TimeAxisView()
219 in_destructor = true;
221 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
225 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
227 delete (*i)->start_trim;
228 delete (*i)->end_trim;
232 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
234 delete (*i)->start_trim;
235 delete (*i)->end_trim;
238 delete selection_group;
241 delete _canvas_display;
244 delete _canvas_separator;
245 _canvas_separator = 0;
254 TimeAxisView::hide ()
260 _canvas_display->hide ();
261 _canvas_separator->hide ();
263 if (control_parent) {
264 control_parent->remove (TOP_LEVEL_WIDGET);
271 /* now hide children */
273 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
277 /* if its hidden, it cannot be selected */
278 _editor.get_selection().remove (this);
279 /* and neither can its regions */
280 _editor.get_selection().remove_regions (this);
285 /** Display this TimeAxisView as the nth component of the parent box, at y.
287 * @param y y position.
288 * @param nth index for this TimeAxisView, increased if this view has children.
289 * @param parent parent component.
290 * @return height of this TimeAxisView.
293 TimeAxisView::show_at (double y, int& nth, VBox *parent)
295 if (control_parent) {
296 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
298 control_parent = parent;
299 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
300 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
305 if (_y_position != y) {
306 _canvas_separator->set (ArdourCanvas::Duple(0, y), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, y));
307 _canvas_display->set_y_position (y + 1);
311 _canvas_display->raise_to_top ();
312 _canvas_display->show ();
314 _canvas_separator->raise_to_top ();
315 _canvas_separator->show ();
319 _effective_height = current_height ();
321 /* now show relevant children */
323 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
324 if ((*i)->marked_for_display()) {
326 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
332 return _effective_height;
336 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
338 switch (ev->direction) {
340 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
341 /* See Editor::_stepping_axis_view for notes on this hack */
342 Editor& e = dynamic_cast<Editor&> (_editor);
343 if (!e.stepping_axis_view ()) {
344 e.set_stepping_axis_view (this);
346 e.stepping_axis_view()->step_height (false);
351 case GDK_SCROLL_DOWN:
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);
358 e.stepping_axis_view()->step_height (true);
364 /* no handling for left/right, yet */
368 /* Just forward to the normal canvas scroll method. The coordinate
369 systems are different but since the canvas is always larger than the
370 track headers, and aligned with the trackview area, this will work.
372 In the not too distant future this layout is going away anyway and
373 headers will be on the canvas.
375 return _editor.canvas_scroll_event (ev, false);
379 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
381 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
382 /* see if it is inside the name label */
383 if (name_label.is_ancestor (controls_ebox)) {
386 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
387 Gtk::Allocation a = name_label.get_allocation ();
388 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
390 _ebox_release_can_act = false;
397 _ebox_release_can_act = true;
399 if (maybe_set_cursor (event->y) > 0) {
400 _resize_drag_start = event->y_root;
407 TimeAxisView::idle_resize (uint32_t h)
414 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
416 if (_resize_drag_start >= 0) {
418 /* (ab)use the DragManager to do autoscrolling - basically we
419 * are pretending that the drag is taking place over the canvas
420 * (which perhaps in the glorious future, when track headers
421 * and the canvas are unified, will actually be true.)
424 _editor.maybe_autoscroll (false, true, true);
426 /* now schedule the actual TAV resize */
427 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
428 _editor.add_to_idle_resize (this, delta);
429 _resize_drag_start = ev->y_root;
432 /* not dragging but ... */
433 maybe_set_cursor (ev->y);
436 gdk_event_request_motions(ev);
441 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
443 if (_have_preresize_cursor) {
444 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
445 _have_preresize_cursor = false;
451 TimeAxisView::maybe_set_cursor (int y)
453 /* XXX no Gtkmm Gdk::Window::get_cursor() */
454 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
456 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
458 /* y-coordinate in lower 25% */
460 if (!_have_preresize_cursor) {
461 _preresize_cursor = gdk_window_get_cursor (win->gobj());
462 _have_preresize_cursor = true;
463 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
468 } else if (_have_preresize_cursor) {
469 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
470 _have_preresize_cursor = false;
479 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
481 if (_resize_drag_start >= 0) {
482 if (_have_preresize_cursor) {
483 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
484 _preresize_cursor = 0;
485 _have_preresize_cursor = false;
487 _editor.stop_canvas_autoscroll ();
488 _resize_drag_start = -1;
491 // don't change selection
496 if (!_ebox_release_can_act) {
500 switch (ev->button) {
502 selection_click (ev);
506 popup_display_menu (ev->time);
514 TimeAxisView::selection_click (GdkEventButton* ev)
516 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
517 _editor.set_selected_track (*this, op, false);
521 /** Steps through the defined heights for this TrackView.
522 * @param coarser true if stepping should decrease in size, otherwise false.
525 TimeAxisView::step_height (bool coarser)
527 static const uint32_t step = 25;
531 if (height <= preset_height (HeightSmall)) {
533 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
534 set_height_enum (HeightSmall);
536 set_height (height - step);
541 if (height <= preset_height(HeightSmall)) {
542 set_height_enum (HeightNormal);
544 set_height (height + step);
551 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
553 if (apply_to_selection) {
554 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
556 set_height (preset_height (h));
561 TimeAxisView::set_height (uint32_t h)
563 if (h < preset_height (HeightSmall)) {
564 h = preset_height (HeightSmall);
567 TOP_LEVEL_WIDGET.property_height_request () = h;
571 snprintf (buf, sizeof (buf), "%u", height);
572 set_gui_property ("height", buf);
574 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
578 if (selection_group->visible ()) {
579 /* resize the selection rect */
580 show_selection (_editor.get_selection().time);
583 _editor.override_visible_track_count ();
587 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
589 /* steal escape, tabs from GTK */
591 switch (ev->keyval) {
593 case GDK_ISO_Left_Tab:
601 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
603 TrackViewList::iterator i;
605 switch (ev->keyval) {
607 end_name_edit (RESPONSE_CANCEL);
610 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
611 * generates a different ev->keyval, rather than setting
614 case GDK_ISO_Left_Tab:
615 end_name_edit (RESPONSE_APPLY);
619 end_name_edit (RESPONSE_ACCEPT);
629 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
631 end_name_edit (RESPONSE_OK);
636 TimeAxisView::begin_name_edit ()
642 if (can_edit_name()) {
644 name_entry = manage (new Gtkmm2ext::FocusEntry);
646 name_entry->set_width_chars(8); // min width, entry expands
648 name_entry->set_name ("EditorTrackNameDisplay");
649 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
650 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
651 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
652 name_entry->set_text (name_label.get_text());
653 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
655 if (name_label.is_ancestor (name_hbox)) {
656 name_hbox.remove (name_label);
659 name_hbox.pack_end (*name_entry, true, true);
662 name_entry->select_region (0, -1);
663 name_entry->set_state (STATE_SELECTED);
664 name_entry->grab_focus ();
665 name_entry->start_editing (0);
670 TimeAxisView::end_name_edit (int response)
676 bool edit_next = false;
677 bool edit_prev = false;
680 case RESPONSE_CANCEL:
683 name_entry_changed ();
685 case RESPONSE_ACCEPT:
686 name_entry_changed ();
689 name_entry_changed ();
693 /* this will delete the name_entry. but it will also drop focus, which
694 * will cause another callback to this function, so set name_entry = 0
695 * first to ensure we don't double-remove etc. etc.
698 Gtk::Entry* tmp = name_entry;
700 name_hbox.remove (*tmp);
702 /* put the name label back */
704 name_hbox.pack_end (name_label);
709 TrackViewList const & allviews = _editor.get_track_views ();
710 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
712 if (i != allviews.end()) {
715 if (++i == allviews.end()) {
719 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
721 if (rtav && rtav->route()->record_enabled()) {
725 if (!(*i)->hidden()) {
732 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
733 _editor.ensure_time_axis_view_is_visible (**i, false);
734 (*i)->begin_name_edit ();
737 } else if (edit_prev) {
739 TrackViewList const & allviews = _editor.get_track_views ();
740 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
742 if (i != allviews.begin()) {
744 if (i == allviews.begin()) {
750 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
752 if (rtav && rtav->route()->record_enabled()) {
756 if (!(*i)->hidden()) {
763 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
764 _editor.ensure_time_axis_view_is_visible (**i, false);
765 (*i)->begin_name_edit ();
771 TimeAxisView::name_entry_changed ()
776 TimeAxisView::can_edit_name () const
782 TimeAxisView::conditionally_add_to_selection ()
784 Selection& s (_editor.get_selection ());
786 if (!s.selected (this)) {
787 _editor.set_selected_track (*this, Selection::Set);
792 TimeAxisView::popup_display_menu (guint32 when)
794 conditionally_add_to_selection ();
796 build_display_menu ();
797 display_menu->popup (1, when);
801 TimeAxisView::set_selected (bool yn)
803 if (can_edit_name() && name_entry && name_entry->get_visible()) {
804 end_name_edit (RESPONSE_CANCEL);
807 if (yn == _selected) {
811 Selectable::set_selected (yn);
814 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
815 time_axis_frame.set_name ("MixerStripSelectedFrame");
816 controls_ebox.set_name (controls_base_selected_name);
817 controls_vbox.set_name (controls_base_selected_name);
818 time_axis_vbox.set_name (controls_base_selected_name);
820 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
821 time_axis_frame.set_name (controls_base_unselected_name);
822 controls_ebox.set_name (controls_base_unselected_name);
823 controls_vbox.set_name (controls_base_unselected_name);
824 time_axis_vbox.set_name (controls_base_unselected_name);
828 /* children will be set for the yn=true case. but when deselecting
829 the editor only has a list of top-level trackviews, so we
830 have to do this here.
833 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
834 (*i)->set_selected (false);
838 time_axis_frame.show();
843 TimeAxisView::build_display_menu ()
845 using namespace Menu_Helpers;
849 display_menu = new Menu;
850 display_menu->set_name ("ArdourContextMenu");
852 // Just let implementing classes define what goes into the manu
856 TimeAxisView::set_samples_per_pixel (double fpp)
858 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
859 (*i)->set_samples_per_pixel (fpp);
864 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
866 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
867 (*i)->show_timestretch (start, end, layers, layer);
872 TimeAxisView::hide_timestretch ()
874 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
875 (*i)->hide_timestretch ();
880 TimeAxisView::show_selection (TimeSelection& ts)
885 SelectionRect *rect; time_axis_frame.show();
888 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
889 (*i)->show_selection (ts);
892 if (selection_group->visible ()) {
893 while (!used_selection_rects.empty()) {
894 free_selection_rects.push_front (used_selection_rects.front());
895 used_selection_rects.pop_front();
896 free_selection_rects.front()->rect->hide();
897 free_selection_rects.front()->start_trim->hide();
898 free_selection_rects.front()->end_trim->hide();
900 selection_group->hide();
903 selection_group->show();
904 selection_group->raise_to_top();
906 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
907 framepos_t start, end;
912 cnt = end - start + 1;
914 rect = get_selection_rect ((*i).id);
916 x1 = _editor.sample_to_pixel (start);
917 x2 = _editor.sample_to_pixel (start + cnt - 1);
918 y2 = current_height() - 1;
920 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
922 // trim boxes are at the top for selections
925 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
926 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
928 rect->start_trim->show();
929 rect->end_trim->show();
931 rect->start_trim->hide();
932 rect->end_trim->hide();
936 used_selection_rects.push_back (rect);
941 TimeAxisView::reshow_selection (TimeSelection& ts)
945 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
946 (*i)->show_selection (ts);
951 TimeAxisView::hide_selection ()
953 if (selection_group->visible ()) {
954 while (!used_selection_rects.empty()) {
955 free_selection_rects.push_front (used_selection_rects.front());
956 used_selection_rects.pop_front();
957 free_selection_rects.front()->rect->hide();
958 free_selection_rects.front()->start_trim->hide();
959 free_selection_rects.front()->end_trim->hide();
961 selection_group->hide();
964 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
965 (*i)->hide_selection ();
970 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
972 /* find the selection rect this is for. we have the item corresponding to one
976 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
977 if ((*i)->start_trim == item || (*i)->end_trim == item) {
979 /* make one trim handle be "above" the other so that if they overlap,
980 the top one is the one last used.
983 (*i)->rect->raise_to_top ();
984 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
985 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
993 TimeAxisView::get_selection_rect (uint32_t id)
997 /* check to see if we already have a visible rect for this particular selection ID */
999 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1000 if ((*i)->id == id) {
1005 /* ditto for the free rect list */
1007 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1008 if ((*i)->id == id) {
1009 SelectionRect* ret = (*i);
1010 free_selection_rects.erase (i);
1015 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
1017 if (free_selection_rects.empty()) {
1019 rect = new SelectionRect;
1021 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1022 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1023 rect->rect->set_outline (false);
1024 rect->rect->set_fill_color (ARDOUR_UI::config()->get_SelectionRect());
1026 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1027 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1028 rect->start_trim->set_outline (false);
1029 rect->start_trim->set_fill (false);
1031 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1032 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1033 rect->end_trim->set_outline (false);
1034 rect->end_trim->set_fill (false);
1036 free_selection_rects.push_front (rect);
1038 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1039 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1040 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1043 rect = free_selection_rects.front();
1045 free_selection_rects.pop_front();
1049 struct null_deleter { void operator()(void const *) const {} };
1052 TimeAxisView::is_child (TimeAxisView* tav)
1054 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1058 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1060 children.push_back (child);
1064 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1066 Children::iterator i;
1068 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1073 /** Get selectable things within a given range.
1074 * @param start Start time in session frames.
1075 * @param end End time in session frames.
1076 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1077 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1078 * @param result Filled in with selectable things.
1081 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1087 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1093 TimeAxisView::add_ghost (RegionView* rv)
1095 GhostRegion* gr = rv->add_ghost (*this);
1098 ghosts.push_back(gr);
1103 TimeAxisView::remove_ghost (RegionView* rv)
1105 rv->remove_ghost_in (*this);
1109 TimeAxisView::erase_ghost (GhostRegion* gr)
1111 if (in_destructor) {
1115 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1124 TimeAxisView::touched (double top, double bot)
1126 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1127 y_position is the "origin" or "top" of the track.
1130 double mybot = _y_position + current_height();
1132 return ((_y_position <= bot && _y_position >= top) ||
1133 ((mybot <= bot) && (top < mybot)) ||
1134 (mybot >= bot && _y_position < top));
1138 TimeAxisView::set_parent (TimeAxisView& p)
1144 TimeAxisView::reset_height ()
1146 set_height (height);
1148 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1149 (*i)->set_height ((*i)->height);
1154 TimeAxisView::compute_heights ()
1156 // TODO this function should be re-evaluated when font-scaling changes (!)
1157 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1158 Gtk::Table one_row_table (1, 1);
1159 ArdourButton* test_button = manage (new ArdourButton);
1160 const int border_width = 2;
1161 const int frame_height = 2;
1162 extra_height = (2 * border_width) + frame_height;
1164 window.add (one_row_table);
1165 test_button->set_name ("mute button");
1166 test_button->set_text (_("M"));
1167 test_button->set_tweaks (ArdourButton::TrackHeader);
1169 one_row_table.set_border_width (border_width);
1170 one_row_table.set_row_spacings (2);
1171 one_row_table.set_col_spacings (2);
1173 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1174 one_row_table.show_all ();
1176 Gtk::Requisition req(one_row_table.size_request ());
1177 button_height = req.height;
1181 TimeAxisView::color_handler ()
1183 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1187 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1189 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_SelectionRect());
1190 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_Selection());
1192 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_Selection());
1193 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_Selection());
1195 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_Selection());
1196 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_Selection());
1199 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1201 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_SelectionRect());
1202 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_Selection());
1204 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_Selection());
1205 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_Selection());
1207 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_Selection());
1208 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_Selection());
1212 /** @return Pair: TimeAxisView, layer index.
1213 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1214 * does. @param y is an offset from the top of the trackview area.
1216 * If the covering object is a child axis, then the child is returned.
1217 * TimeAxisView is 0 otherwise.
1219 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1220 * and is in stacked or expanded * region display mode, otherwise 0.
1222 std::pair<TimeAxisView*, double>
1223 TimeAxisView::covers_y_position (double y) const
1226 return std::make_pair ((TimeAxisView *) 0, 0);
1229 if (_y_position <= y && y < (_y_position + height)) {
1231 /* work out the layer index if appropriate */
1233 switch (layer_display ()) {
1239 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1240 /* clamp to max layers to be on the safe side; sometimes the above calculation
1241 returns a too-high value */
1242 if (l >= view()->layers ()) {
1243 l = view()->layers() - 1;
1249 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1251 if (l >= (view()->layers() - 0.5)) {
1252 l = view()->layers() - 0.5;
1258 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1261 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1263 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1269 return std::make_pair ((TimeAxisView *) 0, 0);
1273 TimeAxisView::covered_by_y_range (double y0, double y1) const
1279 /* if either the top or bottom of the axisview is in the vertical
1280 * range, we cover it.
1283 if ((y0 < _y_position && y1 < _y_position) ||
1284 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1288 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1289 if ((*i)->covered_by_y_range (y0, y1)) {
1298 TimeAxisView::preset_height (Height h)
1302 return (button_height * 2) + extra_height + 260;
1304 return (button_height * 2) + extra_height + 160;
1306 return (button_height * 2) + extra_height + 60;
1308 return (button_height * 2) + extra_height + 10;
1310 return button_height + extra_height;
1317 /** @return Child time axis views that are not hidden */
1318 TimeAxisView::Children
1319 TimeAxisView::get_child_list ()
1323 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1324 if (!(*i)->hidden()) {
1333 TimeAxisView::build_size_menu ()
1335 if (_size_menu && _size_menu->gobj ()) {
1341 using namespace Menu_Helpers;
1343 _size_menu = new Menu;
1344 _size_menu->set_name ("ArdourContextMenu");
1345 MenuList& items = _size_menu->items();
1347 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1348 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1349 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1350 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1351 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1355 TimeAxisView::reset_visual_state ()
1357 /* this method is not required to trigger a global redraw */
1359 string str = gui_property ("height");
1362 set_height (atoi (str));
1364 set_height (preset_height (HeightNormal));
1369 TrackViewList::filter_to_unique_playlists ()
1371 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1374 for (iterator i = begin(); i != end(); ++i) {
1375 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1377 /* not a route: include it anyway */
1380 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1382 if (playlists.insert (t->playlist()).second) {
1383 /* playlist not seen yet */
1387 /* not a track: include it anyway */