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_ui.h"
40 #include "ardour_dialog.h"
41 #include "global_signals.h"
42 #include "gui_thread.h"
43 #include "public_editor.h"
44 #include "time_axis_view.h"
45 #include "region_view.h"
46 #include "ghostregion.h"
47 #include "selection.h"
49 #include "rgb_macros.h"
51 #include "streamview.h"
52 #include "editor_drag.h"
60 using namespace ARDOUR;
62 using namespace Editing;
63 using namespace ArdourCanvas;
64 using Gtkmm2ext::Keyboard;
66 const double trim_handle_size = 6.0; /* pixels */
67 uint32_t TimeAxisView::button_height = 0;
68 uint32_t TimeAxisView::extra_height = 0;
69 int const TimeAxisView::_max_order = 512;
70 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
72 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
74 , controls_table (2, 8)
75 , _name_editing (false)
82 , in_destructor (false)
90 , _effective_height (0)
91 , _resize_drag_start (-1)
92 , _preresize_cursor (0)
93 , _have_preresize_cursor (false)
94 , _ebox_release_can_act (true)
96 if (extra_height == 0) {
100 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
101 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
102 _canvas_display->hide(); // reveal as needed
104 selection_group = new ArdourCanvas::Container (_canvas_display);
105 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
106 selection_group->set_data (X_("timeselection"), (void *) 1);
107 selection_group->hide();
109 _ghost_group = new ArdourCanvas::Container (_canvas_display);
110 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
111 _ghost_group->lower_to_bottom();
112 _ghost_group->show();
114 name_label.set_name ("TrackLabel");
115 name_label.set_alignment (0.0, 0.5);
116 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
118 Gtk::Entry* an_entry = new Gtk::Entry;
119 Gtk::Requisition req;
120 an_entry->size_request (req);
121 name_label.set_size_request (-1, req.height);
124 name_hbox.pack_start (name_label, true, true);
128 controls_table.set_size_request (200);
129 controls_table.set_row_spacings (2);
130 controls_table.set_col_spacings (2);
131 controls_table.set_border_width (2);
132 controls_table.set_homogeneous (true);
134 controls_table.attach (name_hbox, 0, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
135 controls_table.show_all ();
136 controls_table.set_no_show_all ();
138 HSeparator* separator = manage (new HSeparator());
139 separator->set_name("TrackSeparator");
140 separator->set_size_request(-1, 1);
143 controls_vbox.pack_start (controls_table, false, false);
144 controls_vbox.show ();
146 controls_hbox.pack_start (controls_vbox, true, true);
147 controls_hbox.show ();
149 //controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
150 controls_ebox.add (controls_hbox);
151 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
152 Gdk::BUTTON_RELEASE_MASK|
153 Gdk::POINTER_MOTION_MASK|
154 Gdk::ENTER_NOTIFY_MASK|
155 Gdk::LEAVE_NOTIFY_MASK|
157 controls_ebox.set_flags (CAN_FOCUS);
159 /* note that this handler connects *before* the default handler */
160 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
161 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
162 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
163 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
164 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
165 controls_ebox.show ();
167 time_axis_vbox.pack_start (controls_ebox, true, true, 0);
168 time_axis_vbox.pack_end (*separator, false, false);
169 time_axis_vbox.show();
171 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
173 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
176 TimeAxisView::~TimeAxisView()
178 in_destructor = true;
180 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
184 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
186 delete (*i)->start_trim;
187 delete (*i)->end_trim;
191 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
193 delete (*i)->start_trim;
194 delete (*i)->end_trim;
197 delete selection_group;
200 delete _canvas_display;
210 TimeAxisView::hide ()
216 _canvas_display->hide ();
218 if (control_parent) {
219 control_parent->remove (time_axis_vbox);
226 /* now hide children */
228 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
232 /* if its hidden, it cannot be selected */
233 _editor.get_selection().remove (this);
234 /* and neither can its regions */
235 _editor.get_selection().remove_regions (this);
240 /** Display this TimeAxisView as the nth component of the parent box, at y.
242 * @param y y position.
243 * @param nth index for this TimeAxisView, increased if this view has children.
244 * @param parent parent component.
245 * @return height of this TimeAxisView.
248 TimeAxisView::show_at (double y, int& nth, VBox *parent)
250 if (control_parent) {
251 control_parent->reorder_child (time_axis_vbox, nth);
253 control_parent = parent;
254 parent->pack_start (time_axis_vbox, false, false);
255 parent->reorder_child (time_axis_vbox, nth);
260 if (_y_position != y) {
261 _canvas_display->set_y_position (y);
266 _canvas_display->raise_to_top ();
267 _canvas_display->show ();
271 _effective_height = current_height ();
273 /* now show relevant children */
275 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
276 if ((*i)->marked_for_display()) {
278 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
284 return _effective_height;
288 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
290 switch (ev->direction) {
292 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
293 /* See Editor::_stepping_axis_view for notes on this hack */
294 Editor& e = dynamic_cast<Editor&> (_editor);
295 if (!e.stepping_axis_view ()) {
296 e.set_stepping_axis_view (this);
298 e.stepping_axis_view()->step_height (false);
303 case GDK_SCROLL_DOWN:
304 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
305 /* See Editor::_stepping_axis_view for notes on this hack */
306 Editor& e = dynamic_cast<Editor&> (_editor);
307 if (!e.stepping_axis_view ()) {
308 e.set_stepping_axis_view (this);
310 e.stepping_axis_view()->step_height (true);
316 /* no handling for left/right, yet */
320 /* Just forward to the normal canvas scroll method. The coordinate
321 systems are different but since the canvas is always larger than the
322 track headers, and aligned with the trackview area, this will work.
324 In the not too distant future this layout is going away anyway and
325 headers will be on the canvas.
327 return _editor.canvas_scroll_event (ev, false);
331 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
333 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
334 /* see if it is inside the name label */
335 if (name_label.is_ancestor (controls_ebox)) {
338 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
339 Gtk::Allocation a = name_label.get_allocation ();
340 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
342 _ebox_release_can_act = false;
349 _ebox_release_can_act = true;
351 if (maybe_set_cursor (event->y) > 0) {
352 _resize_drag_start = event->y_root;
359 TimeAxisView::idle_resize (uint32_t h)
366 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
368 if (_resize_drag_start >= 0) {
370 /* (ab)use the DragManager to do autoscrolling - basically we
371 * are pretending that the drag is taking place over the canvas
372 * (which perhaps in the glorious future, when track headers
373 * and the canvas are unified, will actually be true.)
376 _editor.maybe_autoscroll (false, true, true);
378 /* now schedule the actual TAV resize */
379 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
380 _editor.add_to_idle_resize (this, delta);
381 _resize_drag_start = ev->y_root;
383 /* not dragging but ... */
384 maybe_set_cursor (ev->y);
387 gdk_event_request_motions(ev);
392 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
394 if (_have_preresize_cursor) {
395 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
396 _have_preresize_cursor = false;
402 TimeAxisView::maybe_set_cursor (int y)
404 /* XXX no Gtkmm Gdk::Window::get_cursor() */
405 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
407 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
409 /* y-coordinate in lower 25% */
411 if (!_have_preresize_cursor) {
412 _preresize_cursor = gdk_window_get_cursor (win->gobj());
413 _have_preresize_cursor = true;
414 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
419 } else if (_have_preresize_cursor) {
420 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
421 _have_preresize_cursor = false;
430 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
432 if (_resize_drag_start >= 0) {
433 if (_have_preresize_cursor) {
434 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
435 _preresize_cursor = 0;
436 _have_preresize_cursor = false;
438 _editor.stop_canvas_autoscroll ();
439 _resize_drag_start = -1;
442 if (!_ebox_release_can_act) {
446 switch (ev->button) {
448 selection_click (ev);
452 popup_display_menu (ev->time);
460 TimeAxisView::selection_click (GdkEventButton* ev)
462 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
463 _editor.set_selected_track (*this, op, false);
467 /** Steps through the defined heights for this TrackView.
468 * @param coarser true if stepping should decrease in size, otherwise false.
471 TimeAxisView::step_height (bool coarser)
473 static const uint32_t step = 25;
477 if (height <= preset_height (HeightSmall)) {
479 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
480 set_height_enum (HeightSmall);
482 set_height (height - step);
487 if (height <= preset_height(HeightSmall)) {
488 set_height_enum (HeightNormal);
490 set_height (height + step);
497 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
499 if (apply_to_selection) {
500 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
502 set_height (preset_height (h));
507 TimeAxisView::set_height (uint32_t h)
509 if (h < preset_height (HeightSmall)) {
510 h = preset_height (HeightSmall);
513 time_axis_vbox.property_height_request () = h;
517 snprintf (buf, sizeof (buf), "%u", height);
518 set_gui_property ("height", buf);
520 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
524 if (selection_group->visible ()) {
525 /* resize the selection rect */
526 show_selection (_editor.get_selection().time);
529 _editor.override_visible_track_count ();
533 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
535 /* steal escape, tabs from GTK */
537 switch (ev->keyval) {
539 case GDK_ISO_Left_Tab:
547 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
549 TrackViewList::iterator i;
551 switch (ev->keyval) {
553 end_name_edit (RESPONSE_CANCEL);
556 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
557 * generates a different ev->keyval, rather than setting
560 case GDK_ISO_Left_Tab:
561 end_name_edit (RESPONSE_APPLY);
565 end_name_edit (RESPONSE_ACCEPT);
575 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
577 end_name_edit (RESPONSE_OK);
582 TimeAxisView::begin_name_edit ()
588 if (can_edit_name()) {
590 name_entry = manage (new Gtkmm2ext::FocusEntry);
592 name_entry->set_name ("EditorTrackNameDisplay");
593 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
594 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
595 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
596 name_entry->set_text (name_label.get_text());
597 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
599 if (name_label.is_ancestor (name_hbox)) {
600 name_hbox.remove (name_label);
603 name_hbox.pack_start (*name_entry);
606 name_entry->select_region (0, -1);
607 name_entry->set_state (STATE_SELECTED);
608 name_entry->grab_focus ();
609 name_entry->start_editing (0);
614 TimeAxisView::end_name_edit (int response)
620 bool edit_next = false;
621 bool edit_prev = false;
624 case RESPONSE_CANCEL:
627 name_entry_changed ();
629 case RESPONSE_ACCEPT:
630 name_entry_changed ();
633 name_entry_changed ();
637 /* this will delete the name_entry. but it will also drop focus, which
638 * will cause another callback to this function, so set name_entry = 0
639 * first to ensure we don't double-remove etc. etc.
642 Gtk::Entry* tmp = name_entry;
644 name_hbox.remove (*tmp);
646 /* put the name label back */
648 name_hbox.pack_start (name_label);
653 TrackViewList const & allviews = _editor.get_track_views ();
654 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
656 if (i != allviews.end()) {
659 if (++i == allviews.end()) {
663 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
665 if (rtav && rtav->route()->record_enabled()) {
669 if (!(*i)->hidden()) {
676 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
677 _editor.ensure_time_axis_view_is_visible (**i);
678 (*i)->begin_name_edit ();
681 } else if (edit_prev) {
683 TrackViewList const & allviews = _editor.get_track_views ();
684 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
686 if (i != allviews.begin()) {
688 if (i == allviews.begin()) {
694 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
696 if (rtav && rtav->route()->record_enabled()) {
700 if (!(*i)->hidden()) {
707 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
708 _editor.ensure_time_axis_view_is_visible (**i);
709 (*i)->begin_name_edit ();
715 TimeAxisView::name_entry_changed ()
720 TimeAxisView::can_edit_name () const
726 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 ();
741 display_menu->popup (1, when);
745 TimeAxisView::set_selected (bool yn)
747 if (yn == _selected) {
751 Selectable::set_selected (yn);
754 controls_ebox.set_name (controls_base_selected_name);
755 time_axis_vbox.set_name (controls_base_selected_name);
756 controls_vbox.set_name (controls_base_selected_name);
758 controls_ebox.set_name (controls_base_unselected_name);
759 time_axis_vbox.set_name (controls_base_unselected_name);
760 controls_vbox.set_name (controls_base_unselected_name);
763 /* children will be set for the yn=true case. but when deselecting
764 the editor only has a list of top-level trackviews, so we
765 have to do this here.
768 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
769 (*i)->set_selected (false);
775 TimeAxisView::build_display_menu ()
777 using namespace Menu_Helpers;
781 display_menu = new Menu;
782 display_menu->set_name ("ArdourContextMenu");
784 // Just let implementing classes define what goes into the manu
788 TimeAxisView::set_samples_per_pixel (double fpp)
790 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
791 (*i)->set_samples_per_pixel (fpp);
796 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
798 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
799 (*i)->show_timestretch (start, end, layers, layer);
804 TimeAxisView::hide_timestretch ()
806 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
807 (*i)->hide_timestretch ();
812 TimeAxisView::show_selection (TimeSelection& ts)
819 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
820 (*i)->show_selection (ts);
823 if (selection_group->visible ()) {
824 while (!used_selection_rects.empty()) {
825 free_selection_rects.push_front (used_selection_rects.front());
826 used_selection_rects.pop_front();
827 free_selection_rects.front()->rect->hide();
828 free_selection_rects.front()->start_trim->hide();
829 free_selection_rects.front()->end_trim->hide();
831 selection_group->hide();
834 selection_group->show();
835 selection_group->raise_to_top();
837 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
838 framepos_t start, end;
843 cnt = end - start + 1;
845 rect = get_selection_rect ((*i).id);
847 x1 = _editor.sample_to_pixel (start);
848 x2 = _editor.sample_to_pixel (start + cnt - 1);
849 y2 = current_height();
851 rect->rect->set (ArdourCanvas::Rect (x1, 1, x2, y2));
853 // trim boxes are at the top for selections
856 rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
857 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
859 rect->start_trim->show();
860 rect->end_trim->show();
862 rect->start_trim->hide();
863 rect->end_trim->hide();
867 used_selection_rects.push_back (rect);
872 TimeAxisView::reshow_selection (TimeSelection& ts)
876 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
877 (*i)->show_selection (ts);
882 TimeAxisView::hide_selection ()
884 if (selection_group->visible ()) {
885 while (!used_selection_rects.empty()) {
886 free_selection_rects.push_front (used_selection_rects.front());
887 used_selection_rects.pop_front();
888 free_selection_rects.front()->rect->hide();
889 free_selection_rects.front()->start_trim->hide();
890 free_selection_rects.front()->end_trim->hide();
892 selection_group->hide();
895 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
896 (*i)->hide_selection ();
901 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
903 /* find the selection rect this is for. we have the item corresponding to one
907 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
908 if ((*i)->start_trim == item || (*i)->end_trim == item) {
910 /* make one trim handle be "above" the other so that if they overlap,
911 the top one is the one last used.
914 (*i)->rect->raise_to_top ();
915 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
916 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
924 TimeAxisView::get_selection_rect (uint32_t id)
928 /* check to see if we already have a visible rect for this particular selection ID */
930 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
931 if ((*i)->id == id) {
936 /* ditto for the free rect list */
938 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
939 if ((*i)->id == id) {
940 SelectionRect* ret = (*i);
941 free_selection_rects.erase (i);
946 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
948 if (free_selection_rects.empty()) {
950 rect = new SelectionRect;
952 rect->rect = new ArdourCanvas::Rectangle (selection_group);
953 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
954 rect->rect->set_outline (false);
955 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
957 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
958 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
959 rect->start_trim->set_outline (false);
960 rect->start_trim->set_fill (false);
962 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
963 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
964 rect->end_trim->set_outline (false);
965 rect->end_trim->set_fill (false);
967 free_selection_rects.push_front (rect);
969 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
970 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
971 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
974 rect = free_selection_rects.front();
976 free_selection_rects.pop_front();
980 struct null_deleter { void operator()(void const *) const {} };
983 TimeAxisView::is_child (TimeAxisView* tav)
985 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
989 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
991 children.push_back (child);
995 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
997 Children::iterator i;
999 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1004 /** Get selectable things within a given range.
1005 * @param start Start time in session frames.
1006 * @param end End time in session frames.
1007 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1008 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1009 * @param result Filled in with selectable things.
1012 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1018 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1024 TimeAxisView::add_ghost (RegionView* rv)
1026 GhostRegion* gr = rv->add_ghost (*this);
1029 ghosts.push_back(gr);
1034 TimeAxisView::remove_ghost (RegionView* rv)
1036 rv->remove_ghost_in (*this);
1040 TimeAxisView::erase_ghost (GhostRegion* gr)
1042 if (in_destructor) {
1046 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1055 TimeAxisView::touched (double top, double bot)
1057 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1058 y_position is the "origin" or "top" of the track.
1061 double mybot = _y_position + current_height();
1063 return ((_y_position <= bot && _y_position >= top) ||
1064 ((mybot <= bot) && (top < mybot)) ||
1065 (mybot >= bot && _y_position < top));
1069 TimeAxisView::set_parent (TimeAxisView& p)
1075 TimeAxisView::reset_height ()
1077 set_height (height);
1079 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1080 (*i)->set_height ((*i)->height);
1085 TimeAxisView::compute_heights ()
1087 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1088 Gtk::Table two_row_table (2, 8);
1089 Gtk::Table one_row_table (1, 8);
1091 const int border_width = 2;
1093 const int separator_height = 2;
1094 extra_height = (2 * border_width) + separator_height;
1096 window.add (one_row_table);
1098 one_row_table.set_border_width (border_width);
1099 one_row_table.set_row_spacings (0);
1100 one_row_table.set_col_spacings (0);
1101 one_row_table.set_homogeneous (true);
1103 two_row_table.set_border_width (border_width);
1104 two_row_table.set_row_spacings (0);
1105 two_row_table.set_col_spacings (0);
1106 two_row_table.set_homogeneous (true);
1108 for (int i = 0; i < 5; ++i) {
1109 buttons[i] = manage (new Button (X_("f")));
1110 buttons[i]->set_name ("TrackMuteButton");
1113 one_row_table.attach (*buttons[0], 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
1115 one_row_table.show_all ();
1116 Gtk::Requisition req(one_row_table.size_request ());
1118 // height required to show 1 row of buttons
1119 button_height = req.height;
1123 TimeAxisView::color_handler ()
1125 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1129 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1131 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1132 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1134 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1135 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1137 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1138 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1141 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1143 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1144 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1146 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1147 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1149 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1150 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1154 /** @return Pair: TimeAxisView, layer index.
1155 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1156 * does. @param y is an offset from the top of the trackview area.
1158 * If the covering object is a child axis, then the child is returned.
1159 * TimeAxisView is 0 otherwise.
1161 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1162 * and is in stacked or expanded * region display mode, otherwise 0.
1164 std::pair<TimeAxisView*, double>
1165 TimeAxisView::covers_y_position (double y) const
1168 return std::make_pair ((TimeAxisView *) 0, 0);
1171 if (_y_position <= y && y < (_y_position + height)) {
1173 /* work out the layer index if appropriate */
1175 switch (layer_display ()) {
1181 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1182 /* clamp to max layers to be on the safe side; sometimes the above calculation
1183 returns a too-high value */
1184 if (l >= view()->layers ()) {
1185 l = view()->layers() - 1;
1191 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1193 if (l >= (view()->layers() - 0.5)) {
1194 l = view()->layers() - 0.5;
1200 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1203 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1205 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1211 return std::make_pair ((TimeAxisView *) 0, 0);
1215 TimeAxisView::covered_by_y_range (double y0, double y1) const
1221 /* if either the top or bottom of the axisview is in the vertical
1222 * range, we cover it.
1225 if ((y0 < _y_position && y1 < _y_position) ||
1226 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1230 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1231 if ((*i)->covered_by_y_range (y0, y1)) {
1240 TimeAxisView::preset_height (Height h)
1244 return (button_height * 2) + extra_height + 260;
1246 return (button_height * 2) + extra_height + 160;
1248 return (button_height * 2) + extra_height + 60;
1250 return (button_height * 2) + extra_height + 10;
1252 return button_height + extra_height;
1259 /** @return Child time axis views that are not hidden */
1260 TimeAxisView::Children
1261 TimeAxisView::get_child_list ()
1265 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1266 if (!(*i)->hidden()) {
1275 TimeAxisView::build_size_menu ()
1277 if (_size_menu && _size_menu->gobj ()) {
1283 using namespace Menu_Helpers;
1285 _size_menu = new Menu;
1286 _size_menu->set_name ("ArdourContextMenu");
1287 MenuList& items = _size_menu->items();
1289 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1290 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1291 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1292 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1293 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1297 TimeAxisView::reset_visual_state ()
1299 /* this method is not required to trigger a global redraw */
1301 string str = gui_property ("height");
1304 set_height (atoi (str));
1306 set_height (preset_height (HeightNormal));
1311 TrackViewList::filter_to_unique_playlists ()
1313 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1316 for (iterator i = begin(); i != end(); ++i) {
1317 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1319 /* not a route: include it anyway */
1322 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1324 if (playlists.insert (t->playlist()).second) {
1325 /* playlist not seen yet */
1329 /* not a track: include it anyway */