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 Group (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 Group (_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 Group (_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 /* Just forward to the normal canvas scroll method. The coordinate
291 systems are different but since the canvas is always larger than the
292 track headers, and aligned with the trackview area, this will work.
294 In the not too distant future this layout is going away anyway and
295 headers will be on the canvas.
297 return _editor.canvas_scroll_event (ev);
301 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
303 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
304 /* see if it is inside the name label */
305 if (name_label.is_ancestor (controls_ebox)) {
308 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
309 Gtk::Allocation a = name_label.get_allocation ();
310 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
312 _ebox_release_can_act = false;
319 _ebox_release_can_act = true;
321 if (maybe_set_cursor (event->y) > 0) {
322 _resize_drag_start = event->y_root;
329 TimeAxisView::idle_resize (uint32_t h)
336 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
338 if (_resize_drag_start >= 0) {
340 /* (ab)use the DragManager to do autoscrolling - basically we
341 * are pretending that the drag is taking place over the canvas
342 * (which perhaps in the glorious future, when track headers
343 * and the canvas are unified, will actually be true.)
346 _editor.maybe_autoscroll (false, true, true);
348 /* now schedule the actual TAV resize */
349 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
350 _editor.add_to_idle_resize (this, delta);
351 _resize_drag_start = ev->y_root;
353 /* not dragging but ... */
354 maybe_set_cursor (ev->y);
357 gdk_event_request_motions(ev);
362 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
364 if (_have_preresize_cursor) {
365 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
366 _have_preresize_cursor = false;
372 TimeAxisView::maybe_set_cursor (int y)
374 /* XXX no Gtkmm Gdk::Window::get_cursor() */
375 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
377 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
379 /* y-coordinate in lower 25% */
381 if (!_have_preresize_cursor) {
382 _preresize_cursor = gdk_window_get_cursor (win->gobj());
383 _have_preresize_cursor = true;
384 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
389 } else if (_have_preresize_cursor) {
390 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
391 _have_preresize_cursor = false;
400 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
402 if (_resize_drag_start >= 0) {
403 if (_have_preresize_cursor) {
404 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
405 _preresize_cursor = 0;
406 _have_preresize_cursor = false;
408 _editor.stop_canvas_autoscroll ();
409 _resize_drag_start = -1;
412 if (!_ebox_release_can_act) {
416 switch (ev->button) {
418 selection_click (ev);
422 popup_display_menu (ev->time);
430 TimeAxisView::selection_click (GdkEventButton* ev)
432 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
433 _editor.set_selected_track (*this, op, false);
437 /** Steps through the defined heights for this TrackView.
438 * @param coarser true if stepping should decrease in size, otherwise false.
441 TimeAxisView::step_height (bool coarser)
443 static const uint32_t step = 25;
447 if (height <= preset_height (HeightSmall)) {
449 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
450 set_height_enum (HeightSmall);
452 set_height (height - step);
457 if (height <= preset_height(HeightSmall)) {
458 set_height_enum (HeightNormal);
460 set_height (height + step);
467 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
469 if (apply_to_selection) {
470 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
472 set_height (preset_height (h));
477 TimeAxisView::set_height (uint32_t h)
479 if (h < preset_height (HeightSmall)) {
480 h = preset_height (HeightSmall);
483 time_axis_vbox.property_height_request () = h;
487 snprintf (buf, sizeof (buf), "%u", height);
488 set_gui_property ("height", buf);
490 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
494 if (selection_group->visible ()) {
495 /* resize the selection rect */
496 show_selection (_editor.get_selection().time);
499 _editor.override_visible_track_count ();
503 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
505 /* steal escape, tabs from GTK */
507 switch (ev->keyval) {
509 case GDK_ISO_Left_Tab:
517 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
519 TrackViewList::iterator i;
521 switch (ev->keyval) {
523 end_name_edit (RESPONSE_CANCEL);
526 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
527 * generates a different ev->keyval, rather than setting
530 case GDK_ISO_Left_Tab:
531 end_name_edit (RESPONSE_APPLY);
535 end_name_edit (RESPONSE_ACCEPT);
545 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
547 end_name_edit (RESPONSE_OK);
552 TimeAxisView::begin_name_edit ()
558 if (can_edit_name()) {
560 name_entry = manage (new Gtkmm2ext::FocusEntry);
562 name_entry->set_name ("EditorTrackNameDisplay");
563 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
564 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
565 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
566 name_entry->set_text (name_label.get_text());
567 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
569 if (name_label.is_ancestor (name_hbox)) {
570 name_hbox.remove (name_label);
573 name_hbox.pack_start (*name_entry);
576 name_entry->select_region (0, -1);
577 name_entry->set_state (STATE_SELECTED);
578 name_entry->grab_focus ();
579 name_entry->start_editing (0);
584 TimeAxisView::end_name_edit (int response)
590 bool edit_next = false;
591 bool edit_prev = false;
594 case RESPONSE_CANCEL:
597 name_entry_changed ();
599 case RESPONSE_ACCEPT:
600 name_entry_changed ();
603 name_entry_changed ();
607 /* this will delete the name_entry. but it will also drop focus, which
608 * will cause another callback to this function, so set name_entry = 0
609 * first to ensure we don't double-remove etc. etc.
612 Gtk::Entry* tmp = name_entry;
614 name_hbox.remove (*tmp);
616 /* put the name label back */
618 name_hbox.pack_start (name_label);
623 TrackViewList const & allviews = _editor.get_track_views ();
624 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
626 if (i != allviews.end()) {
629 if (++i == allviews.end()) {
633 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
635 if (rtav && rtav->route()->record_enabled()) {
639 if (!(*i)->hidden()) {
646 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
647 _editor.ensure_time_axis_view_is_visible (**i);
648 (*i)->begin_name_edit ();
651 } else if (edit_prev) {
653 TrackViewList const & allviews = _editor.get_track_views ();
654 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
656 if (i != allviews.begin()) {
658 if (i == allviews.begin()) {
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);
679 (*i)->begin_name_edit ();
685 TimeAxisView::name_entry_changed ()
690 TimeAxisView::can_edit_name () const
696 TimeAxisView::conditionally_add_to_selection ()
698 Selection& s (_editor.get_selection ());
700 if (!s.selected (this)) {
701 _editor.set_selected_track (*this, Selection::Set);
706 TimeAxisView::popup_display_menu (guint32 when)
708 conditionally_add_to_selection ();
710 build_display_menu ();
711 display_menu->popup (1, when);
715 TimeAxisView::set_selected (bool yn)
717 if (yn == _selected) {
721 Selectable::set_selected (yn);
724 controls_ebox.set_name (controls_base_selected_name);
725 time_axis_vbox.set_name (controls_base_selected_name);
726 controls_vbox.set_name (controls_base_selected_name);
728 controls_ebox.set_name (controls_base_unselected_name);
729 time_axis_vbox.set_name (controls_base_unselected_name);
730 controls_vbox.set_name (controls_base_unselected_name);
733 /* children will be set for the yn=true case. but when deselecting
734 the editor only has a list of top-level trackviews, so we
735 have to do this here.
738 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
739 (*i)->set_selected (false);
745 TimeAxisView::build_display_menu ()
747 using namespace Menu_Helpers;
751 display_menu = new Menu;
752 display_menu->set_name ("ArdourContextMenu");
754 // Just let implementing classes define what goes into the manu
758 TimeAxisView::set_samples_per_pixel (double fpp)
760 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
761 (*i)->set_samples_per_pixel (fpp);
766 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
768 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
769 (*i)->show_timestretch (start, end, layers, layer);
774 TimeAxisView::hide_timestretch ()
776 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
777 (*i)->hide_timestretch ();
782 TimeAxisView::show_selection (TimeSelection& ts)
789 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
790 (*i)->show_selection (ts);
793 if (selection_group->visible ()) {
794 while (!used_selection_rects.empty()) {
795 free_selection_rects.push_front (used_selection_rects.front());
796 used_selection_rects.pop_front();
797 free_selection_rects.front()->rect->hide();
798 free_selection_rects.front()->start_trim->hide();
799 free_selection_rects.front()->end_trim->hide();
801 selection_group->hide();
804 selection_group->show();
805 selection_group->raise_to_top();
807 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
808 framepos_t start, end;
813 cnt = end - start + 1;
815 rect = get_selection_rect ((*i).id);
817 x1 = _editor.sample_to_pixel (start);
818 x2 = _editor.sample_to_pixel (start + cnt - 1);
819 y2 = current_height();
821 rect->rect->set (ArdourCanvas::Rect (x1, 1, x2, y2));
823 // trim boxes are at the top for selections
826 rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
827 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
829 rect->start_trim->show();
830 rect->end_trim->show();
832 rect->start_trim->hide();
833 rect->end_trim->hide();
837 used_selection_rects.push_back (rect);
842 TimeAxisView::reshow_selection (TimeSelection& ts)
846 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
847 (*i)->show_selection (ts);
852 TimeAxisView::hide_selection ()
854 if (selection_group->visible ()) {
855 while (!used_selection_rects.empty()) {
856 free_selection_rects.push_front (used_selection_rects.front());
857 used_selection_rects.pop_front();
858 free_selection_rects.front()->rect->hide();
859 free_selection_rects.front()->start_trim->hide();
860 free_selection_rects.front()->end_trim->hide();
862 selection_group->hide();
865 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
866 (*i)->hide_selection ();
871 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
873 /* find the selection rect this is for. we have the item corresponding to one
877 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
878 if ((*i)->start_trim == item || (*i)->end_trim == item) {
880 /* make one trim handle be "above" the other so that if they overlap,
881 the top one is the one last used.
884 (*i)->rect->raise_to_top ();
885 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
886 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
894 TimeAxisView::get_selection_rect (uint32_t id)
898 /* check to see if we already have a visible rect for this particular selection ID */
900 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
901 if ((*i)->id == id) {
906 /* ditto for the free rect list */
908 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
909 if ((*i)->id == id) {
910 SelectionRect* ret = (*i);
911 free_selection_rects.erase (i);
916 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
918 if (free_selection_rects.empty()) {
920 rect = new SelectionRect;
922 rect->rect = new ArdourCanvas::Rectangle (selection_group);
923 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
924 rect->rect->set_outline (false);
925 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
927 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
928 CANVAS_DEBUG_NAME (rect->rect, "selection rect start trim");
929 rect->start_trim->set_outline (false);
930 rect->start_trim->set_fill (false);
932 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
933 CANVAS_DEBUG_NAME (rect->rect, "selection rect end trim");
934 rect->end_trim->set_outline (false);
935 rect->end_trim->set_fill (false);
937 free_selection_rects.push_front (rect);
939 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
940 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
941 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
944 rect = free_selection_rects.front();
946 free_selection_rects.pop_front();
950 struct null_deleter { void operator()(void const *) const {} };
953 TimeAxisView::is_child (TimeAxisView* tav)
955 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
959 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
961 children.push_back (child);
965 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
967 Children::iterator i;
969 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
974 /** Get selectable things within a given range.
975 * @param start Start time in session frames.
976 * @param end End time in session frames.
977 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
978 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
979 * @param result Filled in with selectable things.
982 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
988 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
994 TimeAxisView::add_ghost (RegionView* rv)
996 GhostRegion* gr = rv->add_ghost (*this);
999 ghosts.push_back(gr);
1004 TimeAxisView::remove_ghost (RegionView* rv)
1006 rv->remove_ghost_in (*this);
1010 TimeAxisView::erase_ghost (GhostRegion* gr)
1012 if (in_destructor) {
1016 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1025 TimeAxisView::touched (double top, double bot)
1027 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1028 y_position is the "origin" or "top" of the track.
1031 double mybot = _y_position + current_height();
1033 return ((_y_position <= bot && _y_position >= top) ||
1034 ((mybot <= bot) && (top < mybot)) ||
1035 (mybot >= bot && _y_position < top));
1039 TimeAxisView::set_parent (TimeAxisView& p)
1045 TimeAxisView::reset_height ()
1047 set_height (height);
1049 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1050 (*i)->set_height ((*i)->height);
1055 TimeAxisView::compute_heights ()
1057 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1058 Gtk::Table two_row_table (2, 8);
1059 Gtk::Table one_row_table (1, 8);
1061 const int border_width = 2;
1063 const int separator_height = 2;
1064 extra_height = (2 * border_width) + separator_height;
1066 window.add (one_row_table);
1068 one_row_table.set_border_width (border_width);
1069 one_row_table.set_row_spacings (0);
1070 one_row_table.set_col_spacings (0);
1071 one_row_table.set_homogeneous (true);
1073 two_row_table.set_border_width (border_width);
1074 two_row_table.set_row_spacings (0);
1075 two_row_table.set_col_spacings (0);
1076 two_row_table.set_homogeneous (true);
1078 for (int i = 0; i < 5; ++i) {
1079 buttons[i] = manage (new Button (X_("f")));
1080 buttons[i]->set_name ("TrackMuteButton");
1083 one_row_table.attach (*buttons[0], 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
1085 one_row_table.show_all ();
1086 Gtk::Requisition req(one_row_table.size_request ());
1088 // height required to show 1 row of buttons
1089 button_height = req.height;
1093 TimeAxisView::color_handler ()
1095 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1099 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1101 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1102 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1104 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1105 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1107 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1108 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1111 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1113 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1114 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1116 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1117 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1119 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1120 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1124 /** @return Pair: TimeAxisView, layer index.
1125 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1126 * does. @param y is an offset from the top of the trackview area.
1128 * If the covering object is a child axis, then the child is returned.
1129 * TimeAxisView is 0 otherwise.
1131 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1132 * and is in stacked or expanded * region display mode, otherwise 0.
1134 std::pair<TimeAxisView*, double>
1135 TimeAxisView::covers_y_position (double y) const
1138 return std::make_pair ((TimeAxisView *) 0, 0);
1141 if (_y_position <= y && y < (_y_position + height)) {
1143 /* work out the layer index if appropriate */
1145 switch (layer_display ()) {
1151 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1152 /* clamp to max layers to be on the safe side; sometimes the above calculation
1153 returns a too-high value */
1154 if (l >= view()->layers ()) {
1155 l = view()->layers() - 1;
1161 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1163 if (l >= (view()->layers() - 0.5)) {
1164 l = view()->layers() - 0.5;
1170 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1173 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1175 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1181 return std::make_pair ((TimeAxisView *) 0, 0);
1185 TimeAxisView::covered_by_y_range (double y0, double y1) const
1191 /* if either the top or bottom of the axisview is in the vertical
1192 * range, we cover it.
1195 if ((y0 < _y_position && y1 < _y_position) ||
1196 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1200 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1201 if ((*i)->covered_by_y_range (y0, y1)) {
1210 TimeAxisView::preset_height (Height h)
1214 return (button_height * 2) + extra_height + 260;
1216 return (button_height * 2) + extra_height + 160;
1218 return (button_height * 2) + extra_height + 60;
1220 return (button_height * 2) + extra_height + 10;
1222 return button_height + extra_height;
1229 /** @return Child time axis views that are not hidden */
1230 TimeAxisView::Children
1231 TimeAxisView::get_child_list ()
1235 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1236 if (!(*i)->hidden()) {
1245 TimeAxisView::build_size_menu ()
1247 if (_size_menu && _size_menu->gobj ()) {
1253 using namespace Menu_Helpers;
1255 _size_menu = new Menu;
1256 _size_menu->set_name ("ArdourContextMenu");
1257 MenuList& items = _size_menu->items();
1259 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1260 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1261 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1262 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1263 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1267 TimeAxisView::reset_visual_state ()
1269 /* this method is not required to trigger a global redraw */
1271 string str = gui_property ("height");
1274 set_height (atoi (str));
1276 set_height (preset_height (HeightNormal));
1281 TrackViewList::filter_to_unique_playlists ()
1283 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1286 for (iterator i = begin(); i != end(); ++i) {
1287 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1289 /* not a route: include it anyway */
1292 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1294 if (playlists.insert (t->playlist()).second) {
1295 /* playlist not seen yet */
1299 /* not a track: include it anyway */