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;
61 using namespace ARDOUR_UI_UTILS;
63 using namespace Editing;
64 using namespace ArdourCanvas;
65 using Gtkmm2ext::Keyboard;
67 const double trim_handle_size = 6.0; /* pixels */
68 uint32_t TimeAxisView::button_height = 0;
69 uint32_t TimeAxisView::extra_height = 0;
70 int const TimeAxisView::_max_order = 512;
71 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
73 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
75 , controls_table (2, 8)
76 , _name_editing (false)
83 , in_destructor (false)
91 , _effective_height (0)
92 , _resize_drag_start (-1)
93 , _preresize_cursor (0)
94 , _have_preresize_cursor (false)
95 , _ebox_release_can_act (true)
97 if (extra_height == 0) {
101 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
102 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
103 _canvas_display->hide(); // reveal as needed
105 selection_group = new ArdourCanvas::Container (_canvas_display);
106 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
107 selection_group->set_data (X_("timeselection"), (void *) 1);
108 selection_group->hide();
110 _ghost_group = new ArdourCanvas::Container (_canvas_display);
111 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
112 _ghost_group->lower_to_bottom();
113 _ghost_group->show();
115 name_label.set_name ("TrackLabel");
116 name_label.set_alignment (0.0, 0.5);
117 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
119 Gtk::Entry* an_entry = new Gtk::Entry;
120 Gtk::Requisition req;
121 an_entry->size_request (req);
122 name_label.set_size_request (-1, req.height);
125 name_hbox.pack_start (name_label, true, true);
129 controls_table.set_size_request (200);
130 controls_table.set_row_spacings (2);
131 controls_table.set_col_spacings (2);
132 controls_table.set_border_width (2);
133 controls_table.set_homogeneous (true);
135 controls_table.attach (name_hbox, 0, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
136 controls_table.show_all ();
137 controls_table.set_no_show_all ();
139 HSeparator* separator = manage (new HSeparator());
140 separator->set_name("TrackSeparator");
141 separator->set_size_request(-1, 1);
144 controls_vbox.pack_start (controls_table, false, false);
145 controls_vbox.show ();
147 controls_hbox.pack_start (controls_vbox, true, true);
148 controls_hbox.show ();
150 //controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
151 controls_ebox.add (controls_hbox);
152 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
153 Gdk::BUTTON_RELEASE_MASK|
154 Gdk::POINTER_MOTION_MASK|
155 Gdk::ENTER_NOTIFY_MASK|
156 Gdk::LEAVE_NOTIFY_MASK|
158 controls_ebox.set_flags (CAN_FOCUS);
160 /* note that this handler connects *before* the default handler */
161 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
162 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
163 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
164 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
165 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
166 controls_ebox.show ();
168 time_axis_vbox.pack_start (controls_ebox, true, true, 0);
169 time_axis_vbox.pack_end (*separator, false, false);
170 time_axis_vbox.show();
172 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
174 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
177 TimeAxisView::~TimeAxisView()
179 in_destructor = true;
181 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
185 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
187 delete (*i)->start_trim;
188 delete (*i)->end_trim;
192 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
194 delete (*i)->start_trim;
195 delete (*i)->end_trim;
198 delete selection_group;
201 delete _canvas_display;
211 TimeAxisView::hide ()
217 _canvas_display->hide ();
219 if (control_parent) {
220 control_parent->remove (time_axis_vbox);
227 /* now hide children */
229 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
233 /* if its hidden, it cannot be selected */
234 _editor.get_selection().remove (this);
235 /* and neither can its regions */
236 _editor.get_selection().remove_regions (this);
241 /** Display this TimeAxisView as the nth component of the parent box, at y.
243 * @param y y position.
244 * @param nth index for this TimeAxisView, increased if this view has children.
245 * @param parent parent component.
246 * @return height of this TimeAxisView.
249 TimeAxisView::show_at (double y, int& nth, VBox *parent)
251 if (control_parent) {
252 control_parent->reorder_child (time_axis_vbox, nth);
254 control_parent = parent;
255 parent->pack_start (time_axis_vbox, false, false);
256 parent->reorder_child (time_axis_vbox, nth);
261 if (_y_position != y) {
262 _canvas_display->set_y_position (y);
267 _canvas_display->raise_to_top ();
268 _canvas_display->show ();
272 _effective_height = current_height ();
274 /* now show relevant children */
276 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
277 if ((*i)->marked_for_display()) {
279 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
285 return _effective_height;
289 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
291 switch (ev->direction) {
293 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
294 /* See Editor::_stepping_axis_view for notes on this hack */
295 Editor& e = dynamic_cast<Editor&> (_editor);
296 if (!e.stepping_axis_view ()) {
297 e.set_stepping_axis_view (this);
299 e.stepping_axis_view()->step_height (false);
304 case GDK_SCROLL_DOWN:
305 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
306 /* See Editor::_stepping_axis_view for notes on this hack */
307 Editor& e = dynamic_cast<Editor&> (_editor);
308 if (!e.stepping_axis_view ()) {
309 e.set_stepping_axis_view (this);
311 e.stepping_axis_view()->step_height (true);
317 /* no handling for left/right, yet */
321 /* Just forward to the normal canvas scroll method. The coordinate
322 systems are different but since the canvas is always larger than the
323 track headers, and aligned with the trackview area, this will work.
325 In the not too distant future this layout is going away anyway and
326 headers will be on the canvas.
328 return _editor.canvas_scroll_event (ev, false);
332 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
334 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
335 /* see if it is inside the name label */
336 if (name_label.is_ancestor (controls_ebox)) {
339 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
340 Gtk::Allocation a = name_label.get_allocation ();
341 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
343 _ebox_release_can_act = false;
350 _ebox_release_can_act = true;
352 if (maybe_set_cursor (event->y) > 0) {
353 _resize_drag_start = event->y_root;
360 TimeAxisView::idle_resize (uint32_t h)
367 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
369 if (_resize_drag_start >= 0) {
371 /* (ab)use the DragManager to do autoscrolling - basically we
372 * are pretending that the drag is taking place over the canvas
373 * (which perhaps in the glorious future, when track headers
374 * and the canvas are unified, will actually be true.)
377 _editor.maybe_autoscroll (false, true, true);
379 /* now schedule the actual TAV resize */
380 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
381 _editor.add_to_idle_resize (this, delta);
382 _resize_drag_start = ev->y_root;
384 /* not dragging but ... */
385 maybe_set_cursor (ev->y);
388 gdk_event_request_motions(ev);
393 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
395 if (_have_preresize_cursor) {
396 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
397 _have_preresize_cursor = false;
403 TimeAxisView::maybe_set_cursor (int y)
405 /* XXX no Gtkmm Gdk::Window::get_cursor() */
406 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
408 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
410 /* y-coordinate in lower 25% */
412 if (!_have_preresize_cursor) {
413 _preresize_cursor = gdk_window_get_cursor (win->gobj());
414 _have_preresize_cursor = true;
415 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
420 } else if (_have_preresize_cursor) {
421 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
422 _have_preresize_cursor = false;
431 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
433 if (_resize_drag_start >= 0) {
434 if (_have_preresize_cursor) {
435 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
436 _preresize_cursor = 0;
437 _have_preresize_cursor = false;
439 _editor.stop_canvas_autoscroll ();
440 _resize_drag_start = -1;
443 if (!_ebox_release_can_act) {
447 switch (ev->button) {
449 selection_click (ev);
453 popup_display_menu (ev->time);
461 TimeAxisView::selection_click (GdkEventButton* ev)
463 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
464 _editor.set_selected_track (*this, op, false);
468 /** Steps through the defined heights for this TrackView.
469 * @param coarser true if stepping should decrease in size, otherwise false.
472 TimeAxisView::step_height (bool coarser)
474 static const uint32_t step = 25;
478 if (height <= preset_height (HeightSmall)) {
480 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
481 set_height_enum (HeightSmall);
483 set_height (height - step);
488 if (height <= preset_height(HeightSmall)) {
489 set_height_enum (HeightNormal);
491 set_height (height + step);
498 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
500 if (apply_to_selection) {
501 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
503 set_height (preset_height (h));
508 TimeAxisView::set_height (uint32_t h)
510 if (h < preset_height (HeightSmall)) {
511 h = preset_height (HeightSmall);
514 time_axis_vbox.property_height_request () = h;
518 snprintf (buf, sizeof (buf), "%u", height);
519 set_gui_property ("height", buf);
521 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
525 if (selection_group->visible ()) {
526 /* resize the selection rect */
527 show_selection (_editor.get_selection().time);
530 _editor.override_visible_track_count ();
534 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
536 /* steal escape, tabs from GTK */
538 switch (ev->keyval) {
540 case GDK_ISO_Left_Tab:
548 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
550 TrackViewList::iterator i;
552 switch (ev->keyval) {
554 end_name_edit (RESPONSE_CANCEL);
557 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
558 * generates a different ev->keyval, rather than setting
561 case GDK_ISO_Left_Tab:
562 end_name_edit (RESPONSE_APPLY);
566 end_name_edit (RESPONSE_ACCEPT);
576 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
578 end_name_edit (RESPONSE_OK);
583 TimeAxisView::begin_name_edit ()
589 if (can_edit_name()) {
591 name_entry = manage (new Gtkmm2ext::FocusEntry);
593 name_entry->set_name ("EditorTrackNameDisplay");
594 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
595 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
596 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
597 name_entry->set_text (name_label.get_text());
598 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
600 if (name_label.is_ancestor (name_hbox)) {
601 name_hbox.remove (name_label);
604 name_hbox.pack_start (*name_entry);
607 name_entry->select_region (0, -1);
608 name_entry->set_state (STATE_SELECTED);
609 name_entry->grab_focus ();
610 name_entry->start_editing (0);
615 TimeAxisView::end_name_edit (int response)
621 bool edit_next = false;
622 bool edit_prev = false;
625 case RESPONSE_CANCEL:
628 name_entry_changed ();
630 case RESPONSE_ACCEPT:
631 name_entry_changed ();
634 name_entry_changed ();
638 /* this will delete the name_entry. but it will also drop focus, which
639 * will cause another callback to this function, so set name_entry = 0
640 * first to ensure we don't double-remove etc. etc.
643 Gtk::Entry* tmp = name_entry;
645 name_hbox.remove (*tmp);
647 /* put the name label back */
649 name_hbox.pack_start (name_label);
654 TrackViewList const & allviews = _editor.get_track_views ();
655 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
657 if (i != allviews.end()) {
660 if (++i == allviews.end()) {
664 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
666 if (rtav && rtav->route()->record_enabled()) {
670 if (!(*i)->hidden()) {
677 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
678 _editor.ensure_time_axis_view_is_visible (**i, false);
679 (*i)->begin_name_edit ();
682 } else if (edit_prev) {
684 TrackViewList const & allviews = _editor.get_track_views ();
685 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
687 if (i != allviews.begin()) {
689 if (i == allviews.begin()) {
695 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
697 if (rtav && rtav->route()->record_enabled()) {
701 if (!(*i)->hidden()) {
708 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
709 _editor.ensure_time_axis_view_is_visible (**i, false);
710 (*i)->begin_name_edit ();
716 TimeAxisView::name_entry_changed ()
721 TimeAxisView::can_edit_name () const
727 TimeAxisView::conditionally_add_to_selection ()
729 Selection& s (_editor.get_selection ());
731 if (!s.selected (this)) {
732 _editor.set_selected_track (*this, Selection::Set);
737 TimeAxisView::popup_display_menu (guint32 when)
739 conditionally_add_to_selection ();
741 build_display_menu ();
742 display_menu->popup (1, when);
746 TimeAxisView::set_selected (bool yn)
748 if (yn == _selected) {
752 Selectable::set_selected (yn);
755 controls_ebox.set_name (controls_base_selected_name);
756 time_axis_vbox.set_name (controls_base_selected_name);
757 controls_vbox.set_name (controls_base_selected_name);
759 controls_ebox.set_name (controls_base_unselected_name);
760 time_axis_vbox.set_name (controls_base_unselected_name);
761 controls_vbox.set_name (controls_base_unselected_name);
764 /* children will be set for the yn=true case. but when deselecting
765 the editor only has a list of top-level trackviews, so we
766 have to do this here.
769 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
770 (*i)->set_selected (false);
776 TimeAxisView::build_display_menu ()
778 using namespace Menu_Helpers;
782 display_menu = new Menu;
783 display_menu->set_name ("ArdourContextMenu");
785 // Just let implementing classes define what goes into the manu
789 TimeAxisView::set_samples_per_pixel (double fpp)
791 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
792 (*i)->set_samples_per_pixel (fpp);
797 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
799 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
800 (*i)->show_timestretch (start, end, layers, layer);
805 TimeAxisView::hide_timestretch ()
807 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
808 (*i)->hide_timestretch ();
813 TimeAxisView::show_selection (TimeSelection& ts)
820 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
821 (*i)->show_selection (ts);
824 if (selection_group->visible ()) {
825 while (!used_selection_rects.empty()) {
826 free_selection_rects.push_front (used_selection_rects.front());
827 used_selection_rects.pop_front();
828 free_selection_rects.front()->rect->hide();
829 free_selection_rects.front()->start_trim->hide();
830 free_selection_rects.front()->end_trim->hide();
832 selection_group->hide();
835 selection_group->show();
836 selection_group->raise_to_top();
838 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
839 framepos_t start, end;
844 cnt = end - start + 1;
846 rect = get_selection_rect ((*i).id);
848 x1 = _editor.sample_to_pixel (start);
849 x2 = _editor.sample_to_pixel (start + cnt - 1);
850 y2 = current_height() - 1;
852 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
854 // trim boxes are at the top for selections
857 rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
858 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
860 rect->start_trim->show();
861 rect->end_trim->show();
863 rect->start_trim->hide();
864 rect->end_trim->hide();
868 used_selection_rects.push_back (rect);
873 TimeAxisView::reshow_selection (TimeSelection& ts)
877 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
878 (*i)->show_selection (ts);
883 TimeAxisView::hide_selection ()
885 if (selection_group->visible ()) {
886 while (!used_selection_rects.empty()) {
887 free_selection_rects.push_front (used_selection_rects.front());
888 used_selection_rects.pop_front();
889 free_selection_rects.front()->rect->hide();
890 free_selection_rects.front()->start_trim->hide();
891 free_selection_rects.front()->end_trim->hide();
893 selection_group->hide();
896 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
897 (*i)->hide_selection ();
902 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
904 /* find the selection rect this is for. we have the item corresponding to one
908 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
909 if ((*i)->start_trim == item || (*i)->end_trim == item) {
911 /* make one trim handle be "above" the other so that if they overlap,
912 the top one is the one last used.
915 (*i)->rect->raise_to_top ();
916 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
917 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
925 TimeAxisView::get_selection_rect (uint32_t id)
929 /* check to see if we already have a visible rect for this particular selection ID */
931 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
932 if ((*i)->id == id) {
937 /* ditto for the free rect list */
939 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
940 if ((*i)->id == id) {
941 SelectionRect* ret = (*i);
942 free_selection_rects.erase (i);
947 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
949 if (free_selection_rects.empty()) {
951 rect = new SelectionRect;
953 rect->rect = new ArdourCanvas::Rectangle (selection_group);
954 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
955 rect->rect->set_outline (false);
956 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
958 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
959 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
960 rect->start_trim->set_outline (false);
961 rect->start_trim->set_fill (false);
963 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
964 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
965 rect->end_trim->set_outline (false);
966 rect->end_trim->set_fill (false);
968 free_selection_rects.push_front (rect);
970 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
971 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
972 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
975 rect = free_selection_rects.front();
977 free_selection_rects.pop_front();
981 struct null_deleter { void operator()(void const *) const {} };
984 TimeAxisView::is_child (TimeAxisView* tav)
986 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
990 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
992 children.push_back (child);
996 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
998 Children::iterator i;
1000 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1005 /** Get selectable things within a given range.
1006 * @param start Start time in session frames.
1007 * @param end End time in session frames.
1008 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1009 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1010 * @param result Filled in with selectable things.
1013 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1019 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1025 TimeAxisView::add_ghost (RegionView* rv)
1027 GhostRegion* gr = rv->add_ghost (*this);
1030 ghosts.push_back(gr);
1035 TimeAxisView::remove_ghost (RegionView* rv)
1037 rv->remove_ghost_in (*this);
1041 TimeAxisView::erase_ghost (GhostRegion* gr)
1043 if (in_destructor) {
1047 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1056 TimeAxisView::touched (double top, double bot)
1058 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1059 y_position is the "origin" or "top" of the track.
1062 double mybot = _y_position + current_height();
1064 return ((_y_position <= bot && _y_position >= top) ||
1065 ((mybot <= bot) && (top < mybot)) ||
1066 (mybot >= bot && _y_position < top));
1070 TimeAxisView::set_parent (TimeAxisView& p)
1076 TimeAxisView::reset_height ()
1078 set_height (height);
1080 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1081 (*i)->set_height ((*i)->height);
1086 TimeAxisView::compute_heights ()
1088 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1089 Gtk::Table two_row_table (2, 8);
1090 Gtk::Table one_row_table (1, 8);
1092 const int border_width = 2;
1094 const int separator_height = 2;
1095 extra_height = (2 * border_width) + separator_height;
1097 window.add (one_row_table);
1099 one_row_table.set_border_width (border_width);
1100 one_row_table.set_row_spacings (0);
1101 one_row_table.set_col_spacings (0);
1102 one_row_table.set_homogeneous (true);
1104 two_row_table.set_border_width (border_width);
1105 two_row_table.set_row_spacings (0);
1106 two_row_table.set_col_spacings (0);
1107 two_row_table.set_homogeneous (true);
1109 for (int i = 0; i < 5; ++i) {
1110 buttons[i] = manage (new Button (X_("f")));
1111 buttons[i]->set_name ("TrackMuteButton");
1114 one_row_table.attach (*buttons[0], 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
1116 one_row_table.show_all ();
1117 Gtk::Requisition req(one_row_table.size_request ());
1119 // height required to show 1 row of buttons
1120 button_height = req.height;
1124 TimeAxisView::color_handler ()
1126 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1130 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1132 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1133 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1135 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1136 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1138 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1139 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1142 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1144 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1145 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1147 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1148 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1150 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1151 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1155 /** @return Pair: TimeAxisView, layer index.
1156 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1157 * does. @param y is an offset from the top of the trackview area.
1159 * If the covering object is a child axis, then the child is returned.
1160 * TimeAxisView is 0 otherwise.
1162 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1163 * and is in stacked or expanded * region display mode, otherwise 0.
1165 std::pair<TimeAxisView*, double>
1166 TimeAxisView::covers_y_position (double y) const
1169 return std::make_pair ((TimeAxisView *) 0, 0);
1172 if (_y_position <= y && y < (_y_position + height)) {
1174 /* work out the layer index if appropriate */
1176 switch (layer_display ()) {
1182 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1183 /* clamp to max layers to be on the safe side; sometimes the above calculation
1184 returns a too-high value */
1185 if (l >= view()->layers ()) {
1186 l = view()->layers() - 1;
1192 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1194 if (l >= (view()->layers() - 0.5)) {
1195 l = view()->layers() - 0.5;
1201 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1204 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1206 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1212 return std::make_pair ((TimeAxisView *) 0, 0);
1216 TimeAxisView::covered_by_y_range (double y0, double y1) const
1222 /* if either the top or bottom of the axisview is in the vertical
1223 * range, we cover it.
1226 if ((y0 < _y_position && y1 < _y_position) ||
1227 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1231 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1232 if ((*i)->covered_by_y_range (y0, y1)) {
1241 TimeAxisView::preset_height (Height h)
1245 return (button_height * 2) + extra_height + 260;
1247 return (button_height * 2) + extra_height + 160;
1249 return (button_height * 2) + extra_height + 60;
1251 return (button_height * 2) + extra_height + 10;
1253 return button_height + extra_height;
1260 /** @return Child time axis views that are not hidden */
1261 TimeAxisView::Children
1262 TimeAxisView::get_child_list ()
1266 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1267 if (!(*i)->hidden()) {
1276 TimeAxisView::build_size_menu ()
1278 if (_size_menu && _size_menu->gobj ()) {
1284 using namespace Menu_Helpers;
1286 _size_menu = new Menu;
1287 _size_menu->set_name ("ArdourContextMenu");
1288 MenuList& items = _size_menu->items();
1290 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1291 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1292 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1293 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1294 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1298 TimeAxisView::reset_visual_state ()
1300 /* this method is not required to trigger a global redraw */
1302 string str = gui_property ("height");
1305 set_height (atoi (str));
1307 set_height (preset_height (HeightNormal));
1312 TrackViewList::filter_to_unique_playlists ()
1314 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1317 for (iterator i = begin(); i != end(); ++i) {
1318 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1320 /* not a route: include it anyway */
1323 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1325 if (playlists.insert (t->playlist()).second) {
1326 /* playlist not seen yet */
1330 /* not a track: include it anyway */