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 (4, 4)
76 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
77 , _name_editing (false)
84 , in_destructor (false)
92 , _effective_height (0)
93 , _resize_drag_start (-1)
94 , _preresize_cursor (0)
95 , _have_preresize_cursor (false)
96 , _ebox_release_can_act (true)
98 if (extra_height == 0) {
102 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
103 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
104 _canvas_display->hide(); // reveal as needed
106 selection_group = new ArdourCanvas::Container (_canvas_display);
107 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
108 selection_group->set_data (X_("timeselection"), (void *) 1);
109 selection_group->hide();
111 _ghost_group = new ArdourCanvas::Container (_canvas_display);
112 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
113 _ghost_group->lower_to_bottom();
114 _ghost_group->show();
116 name_label.set_name ("TrackLabel");
117 name_label.set_alignment (0.0, 0.5);
118 name_label.set_width_chars (12);
119 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
121 Gtk::Entry* an_entry = new Gtk::Entry;
122 Gtk::Requisition req;
123 an_entry->size_request (req);
124 name_label.set_size_request (-1, req.height);
125 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
128 name_hbox.pack_end (name_label, true, true);
129 name_hbox.set_size_request(100, 0); // XXX min header width (if fader is not visible)
133 controls_table.set_row_spacings (2);
134 controls_table.set_col_spacings (2);
135 controls_table.set_border_width (2);
137 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
138 controls_table.show_all ();
139 controls_table.set_no_show_all ();
141 controls_vbox.pack_start (controls_table, false, false);
142 controls_vbox.show ();
144 top_hbox.pack_start (controls_vbox, true, true);
147 //controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
148 controls_ebox.add (top_hbox);
149 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
150 Gdk::BUTTON_RELEASE_MASK|
151 Gdk::POINTER_MOTION_MASK|
152 Gdk::ENTER_NOTIFY_MASK|
153 Gdk::LEAVE_NOTIFY_MASK|
155 controls_ebox.set_flags (CAN_FOCUS);
157 /* note that this handler connects *before* the default handler */
158 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
159 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
160 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
161 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
162 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
163 controls_ebox.show ();
165 time_axis_frame.add(controls_ebox);
166 time_axis_frame.show();
168 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
170 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
173 TimeAxisView::~TimeAxisView()
175 in_destructor = true;
177 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
181 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
183 delete (*i)->start_trim;
184 delete (*i)->end_trim;
188 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
190 delete (*i)->start_trim;
191 delete (*i)->end_trim;
194 delete selection_group;
197 delete _canvas_display;
207 TimeAxisView::hide ()
213 _canvas_display->hide ();
215 if (control_parent) {
216 control_parent->remove (time_axis_frame);
223 /* now hide children */
225 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
229 /* if its hidden, it cannot be selected */
230 _editor.get_selection().remove (this);
231 /* and neither can its regions */
232 _editor.get_selection().remove_regions (this);
237 /** Display this TimeAxisView as the nth component of the parent box, at y.
239 * @param y y position.
240 * @param nth index for this TimeAxisView, increased if this view has children.
241 * @param parent parent component.
242 * @return height of this TimeAxisView.
245 TimeAxisView::show_at (double y, int& nth, VBox *parent)
247 if (control_parent) {
248 control_parent->reorder_child (time_axis_frame, nth);
250 control_parent = parent;
251 parent->pack_start (time_axis_frame, false, false);
252 parent->reorder_child (time_axis_frame, nth);
257 if (_y_position != y) {
258 _canvas_display->set_y_position (y);
263 _canvas_display->raise_to_top ();
264 _canvas_display->show ();
268 _effective_height = current_height ();
270 /* now show relevant children */
272 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
273 if ((*i)->marked_for_display()) {
275 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
281 return _effective_height;
285 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
287 switch (ev->direction) {
289 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
290 /* See Editor::_stepping_axis_view for notes on this hack */
291 Editor& e = dynamic_cast<Editor&> (_editor);
292 if (!e.stepping_axis_view ()) {
293 e.set_stepping_axis_view (this);
295 e.stepping_axis_view()->step_height (false);
300 case GDK_SCROLL_DOWN:
301 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
302 /* See Editor::_stepping_axis_view for notes on this hack */
303 Editor& e = dynamic_cast<Editor&> (_editor);
304 if (!e.stepping_axis_view ()) {
305 e.set_stepping_axis_view (this);
307 e.stepping_axis_view()->step_height (true);
313 /* no handling for left/right, yet */
317 /* Just forward to the normal canvas scroll method. The coordinate
318 systems are different but since the canvas is always larger than the
319 track headers, and aligned with the trackview area, this will work.
321 In the not too distant future this layout is going away anyway and
322 headers will be on the canvas.
324 return _editor.canvas_scroll_event (ev, false);
328 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
330 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
331 /* see if it is inside the name label */
332 if (name_label.is_ancestor (controls_ebox)) {
335 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
336 Gtk::Allocation a = name_label.get_allocation ();
337 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
339 _ebox_release_can_act = false;
346 _ebox_release_can_act = true;
348 if (maybe_set_cursor (event->y) > 0) {
349 _resize_drag_start = event->y_root;
356 TimeAxisView::idle_resize (uint32_t h)
363 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
365 if (_resize_drag_start >= 0) {
367 /* (ab)use the DragManager to do autoscrolling - basically we
368 * are pretending that the drag is taking place over the canvas
369 * (which perhaps in the glorious future, when track headers
370 * and the canvas are unified, will actually be true.)
373 _editor.maybe_autoscroll (false, true, true);
375 /* now schedule the actual TAV resize */
376 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
377 _editor.add_to_idle_resize (this, delta);
378 _resize_drag_start = ev->y_root;
380 /* not dragging but ... */
381 maybe_set_cursor (ev->y);
384 gdk_event_request_motions(ev);
389 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
391 if (_have_preresize_cursor) {
392 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
393 _have_preresize_cursor = false;
399 TimeAxisView::maybe_set_cursor (int y)
401 /* XXX no Gtkmm Gdk::Window::get_cursor() */
402 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
404 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
406 /* y-coordinate in lower 25% */
408 if (!_have_preresize_cursor) {
409 _preresize_cursor = gdk_window_get_cursor (win->gobj());
410 _have_preresize_cursor = true;
411 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
416 } else if (_have_preresize_cursor) {
417 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
418 _have_preresize_cursor = false;
427 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
429 if (_resize_drag_start >= 0) {
430 if (_have_preresize_cursor) {
431 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
432 _preresize_cursor = 0;
433 _have_preresize_cursor = false;
435 _editor.stop_canvas_autoscroll ();
436 _resize_drag_start = -1;
439 if (!_ebox_release_can_act) {
443 switch (ev->button) {
445 selection_click (ev);
449 popup_display_menu (ev->time);
457 TimeAxisView::selection_click (GdkEventButton* ev)
459 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
460 _editor.set_selected_track (*this, op, false);
464 /** Steps through the defined heights for this TrackView.
465 * @param coarser true if stepping should decrease in size, otherwise false.
468 TimeAxisView::step_height (bool coarser)
470 static const uint32_t step = 25;
474 if (height <= preset_height (HeightSmall)) {
476 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
477 set_height_enum (HeightSmall);
479 set_height (height - step);
484 if (height <= preset_height(HeightSmall)) {
485 set_height_enum (HeightNormal);
487 set_height (height + step);
494 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
496 if (apply_to_selection) {
497 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
499 set_height (preset_height (h));
504 TimeAxisView::set_height (uint32_t h)
506 if (h < preset_height (HeightSmall)) {
507 h = preset_height (HeightSmall);
510 time_axis_frame.property_height_request () = h;
514 snprintf (buf, sizeof (buf), "%u", height);
515 set_gui_property ("height", buf);
517 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
521 if (selection_group->visible ()) {
522 /* resize the selection rect */
523 show_selection (_editor.get_selection().time);
526 _editor.override_visible_track_count ();
530 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
532 /* steal escape, tabs from GTK */
534 switch (ev->keyval) {
536 case GDK_ISO_Left_Tab:
544 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
546 TrackViewList::iterator i;
548 switch (ev->keyval) {
550 end_name_edit (RESPONSE_CANCEL);
553 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
554 * generates a different ev->keyval, rather than setting
557 case GDK_ISO_Left_Tab:
558 end_name_edit (RESPONSE_APPLY);
562 end_name_edit (RESPONSE_ACCEPT);
572 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
574 end_name_edit (RESPONSE_OK);
579 TimeAxisView::begin_name_edit ()
585 if (can_edit_name()) {
587 name_entry = manage (new Gtkmm2ext::FocusEntry);
589 name_entry->set_width_chars(8); // min width, entry expands
591 name_entry->set_name ("EditorTrackNameDisplay");
592 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
593 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
594 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
595 name_entry->set_text (name_label.get_text());
596 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
598 if (name_label.is_ancestor (name_hbox)) {
599 name_hbox.remove (name_label);
602 name_hbox.pack_end (*name_entry, true, true);
605 name_entry->select_region (0, -1);
606 name_entry->set_state (STATE_SELECTED);
607 name_entry->grab_focus ();
608 name_entry->start_editing (0);
613 TimeAxisView::end_name_edit (int response)
619 bool edit_next = false;
620 bool edit_prev = false;
623 case RESPONSE_CANCEL:
626 name_entry_changed ();
628 case RESPONSE_ACCEPT:
629 name_entry_changed ();
632 name_entry_changed ();
636 /* this will delete the name_entry. but it will also drop focus, which
637 * will cause another callback to this function, so set name_entry = 0
638 * first to ensure we don't double-remove etc. etc.
641 Gtk::Entry* tmp = name_entry;
643 name_hbox.remove (*tmp);
645 /* put the name label back */
647 name_hbox.pack_end (name_label);
652 TrackViewList const & allviews = _editor.get_track_views ();
653 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
655 if (i != allviews.end()) {
658 if (++i == allviews.end()) {
662 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
664 if (rtav && rtav->route()->record_enabled()) {
668 if (!(*i)->hidden()) {
675 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
676 _editor.ensure_time_axis_view_is_visible (**i, false);
677 (*i)->begin_name_edit ();
680 } else if (edit_prev) {
682 TrackViewList const & allviews = _editor.get_track_views ();
683 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
685 if (i != allviews.begin()) {
687 if (i == allviews.begin()) {
693 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
695 if (rtav && rtav->route()->record_enabled()) {
699 if (!(*i)->hidden()) {
706 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
707 _editor.ensure_time_axis_view_is_visible (**i, false);
708 (*i)->begin_name_edit ();
714 TimeAxisView::name_entry_changed ()
719 TimeAxisView::can_edit_name () const
725 TimeAxisView::conditionally_add_to_selection ()
727 Selection& s (_editor.get_selection ());
729 if (!s.selected (this)) {
730 _editor.set_selected_track (*this, Selection::Set);
735 TimeAxisView::popup_display_menu (guint32 when)
737 conditionally_add_to_selection ();
739 build_display_menu ();
740 display_menu->popup (1, when);
744 TimeAxisView::set_selected (bool yn)
746 if (yn == _selected) {
750 Selectable::set_selected (yn);
753 time_axis_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
754 time_axis_frame.set_name ("MixerStripSelectedFrame");
756 //time_axis_frame.set_name (controls_base_selected_name);
757 controls_ebox.set_name (controls_base_selected_name);
758 controls_vbox.set_name (controls_base_selected_name);
760 time_axis_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
761 time_axis_frame.set_name (controls_base_unselected_name);
763 //time_axis_frame.set_name (controls_base_unselected_name);
764 controls_ebox.set_name (controls_base_unselected_name);
765 controls_vbox.set_name (controls_base_unselected_name);
769 /* children will be set for the yn=true case. but when deselecting
770 the editor only has a list of top-level trackviews, so we
771 have to do this here.
774 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
775 (*i)->set_selected (false);
779 time_axis_frame.show();
784 TimeAxisView::build_display_menu ()
786 using namespace Menu_Helpers;
790 display_menu = new Menu;
791 display_menu->set_name ("ArdourContextMenu");
793 // Just let implementing classes define what goes into the manu
797 TimeAxisView::set_samples_per_pixel (double fpp)
799 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
800 (*i)->set_samples_per_pixel (fpp);
805 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
807 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
808 (*i)->show_timestretch (start, end, layers, layer);
813 TimeAxisView::hide_timestretch ()
815 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
816 (*i)->hide_timestretch ();
821 TimeAxisView::show_selection (TimeSelection& ts)
826 SelectionRect *rect; time_axis_frame.show();
829 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
830 (*i)->show_selection (ts);
833 if (selection_group->visible ()) {
834 while (!used_selection_rects.empty()) {
835 free_selection_rects.push_front (used_selection_rects.front());
836 used_selection_rects.pop_front();
837 free_selection_rects.front()->rect->hide();
838 free_selection_rects.front()->start_trim->hide();
839 free_selection_rects.front()->end_trim->hide();
841 selection_group->hide();
844 selection_group->show();
845 selection_group->raise_to_top();
847 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
848 framepos_t start, end;
853 cnt = end - start + 1;
855 rect = get_selection_rect ((*i).id);
857 x1 = _editor.sample_to_pixel (start);
858 x2 = _editor.sample_to_pixel (start + cnt - 1);
859 y2 = current_height() - 1;
861 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
863 // trim boxes are at the top for selections
866 rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
867 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
869 rect->start_trim->show();
870 rect->end_trim->show();
872 rect->start_trim->hide();
873 rect->end_trim->hide();
877 used_selection_rects.push_back (rect);
882 TimeAxisView::reshow_selection (TimeSelection& ts)
886 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
887 (*i)->show_selection (ts);
892 TimeAxisView::hide_selection ()
894 if (selection_group->visible ()) {
895 while (!used_selection_rects.empty()) {
896 free_selection_rects.push_front (used_selection_rects.front());
897 used_selection_rects.pop_front();
898 free_selection_rects.front()->rect->hide();
899 free_selection_rects.front()->start_trim->hide();
900 free_selection_rects.front()->end_trim->hide();
902 selection_group->hide();
905 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
906 (*i)->hide_selection ();
911 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
913 /* find the selection rect this is for. we have the item corresponding to one
917 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
918 if ((*i)->start_trim == item || (*i)->end_trim == item) {
920 /* make one trim handle be "above" the other so that if they overlap,
921 the top one is the one last used.
924 (*i)->rect->raise_to_top ();
925 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
926 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
934 TimeAxisView::get_selection_rect (uint32_t id)
938 /* check to see if we already have a visible rect for this particular selection ID */
940 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
941 if ((*i)->id == id) {
946 /* ditto for the free rect list */
948 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
949 if ((*i)->id == id) {
950 SelectionRect* ret = (*i);
951 free_selection_rects.erase (i);
956 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
958 if (free_selection_rects.empty()) {
960 rect = new SelectionRect;
962 rect->rect = new ArdourCanvas::Rectangle (selection_group);
963 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
964 rect->rect->set_outline (false);
965 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
967 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
968 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
969 rect->start_trim->set_outline (false);
970 rect->start_trim->set_fill (false);
972 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
973 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
974 rect->end_trim->set_outline (false);
975 rect->end_trim->set_fill (false);
977 free_selection_rects.push_front (rect);
979 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
980 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
981 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
984 rect = free_selection_rects.front();
986 free_selection_rects.pop_front();
990 struct null_deleter { void operator()(void const *) const {} };
993 TimeAxisView::is_child (TimeAxisView* tav)
995 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
999 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1001 children.push_back (child);
1005 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1007 Children::iterator i;
1009 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1014 /** Get selectable things within a given range.
1015 * @param start Start time in session frames.
1016 * @param end End time in session frames.
1017 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1018 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1019 * @param result Filled in with selectable things.
1022 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1028 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1034 TimeAxisView::add_ghost (RegionView* rv)
1036 GhostRegion* gr = rv->add_ghost (*this);
1039 ghosts.push_back(gr);
1044 TimeAxisView::remove_ghost (RegionView* rv)
1046 rv->remove_ghost_in (*this);
1050 TimeAxisView::erase_ghost (GhostRegion* gr)
1052 if (in_destructor) {
1056 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1065 TimeAxisView::touched (double top, double bot)
1067 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1068 y_position is the "origin" or "top" of the track.
1071 double mybot = _y_position + current_height();
1073 return ((_y_position <= bot && _y_position >= top) ||
1074 ((mybot <= bot) && (top < mybot)) ||
1075 (mybot >= bot && _y_position < top));
1079 TimeAxisView::set_parent (TimeAxisView& p)
1085 TimeAxisView::reset_height ()
1087 set_height (height);
1089 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1090 (*i)->set_height ((*i)->height);
1095 TimeAxisView::compute_heights ()
1097 // TODO this function should be re-evaluated when font-scaling changes (!)
1098 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1099 Gtk::Table one_row_table (1, 8);
1100 ArdourButton* test_button = manage (new ArdourButton);
1101 const int border_width = 2;
1102 const int frame_height = 2;
1103 extra_height = (2 * border_width) + frame_height;
1105 window.add (one_row_table);
1106 test_button->set_name ("mute button");
1107 test_button->set_text (_("M"));
1109 one_row_table.set_border_width (border_width);
1110 one_row_table.set_row_spacings (2);
1111 one_row_table.set_col_spacings (2);
1113 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1114 one_row_table.show_all ();
1116 Gtk::Requisition req(one_row_table.size_request ());
1117 button_height = req.height;
1121 TimeAxisView::color_handler ()
1123 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1127 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1129 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1130 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1132 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1133 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1135 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1136 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1139 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1141 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1142 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1144 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1145 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1147 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1148 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1152 /** @return Pair: TimeAxisView, layer index.
1153 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1154 * does. @param y is an offset from the top of the trackview area.
1156 * If the covering object is a child axis, then the child is returned.
1157 * TimeAxisView is 0 otherwise.
1159 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1160 * and is in stacked or expanded * region display mode, otherwise 0.
1162 std::pair<TimeAxisView*, double>
1163 TimeAxisView::covers_y_position (double y) const
1166 return std::make_pair ((TimeAxisView *) 0, 0);
1169 if (_y_position <= y && y < (_y_position + height)) {
1171 /* work out the layer index if appropriate */
1173 switch (layer_display ()) {
1179 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1180 /* clamp to max layers to be on the safe side; sometimes the above calculation
1181 returns a too-high value */
1182 if (l >= view()->layers ()) {
1183 l = view()->layers() - 1;
1189 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1191 if (l >= (view()->layers() - 0.5)) {
1192 l = view()->layers() - 0.5;
1198 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1201 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1203 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1209 return std::make_pair ((TimeAxisView *) 0, 0);
1213 TimeAxisView::covered_by_y_range (double y0, double y1) const
1219 /* if either the top or bottom of the axisview is in the vertical
1220 * range, we cover it.
1223 if ((y0 < _y_position && y1 < _y_position) ||
1224 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1228 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1229 if ((*i)->covered_by_y_range (y0, y1)) {
1238 TimeAxisView::preset_height (Height h)
1242 return (button_height * 2) + extra_height + 260;
1244 return (button_height * 2) + extra_height + 160;
1246 return (button_height * 2) + extra_height + 60;
1248 return (button_height * 2) + extra_height + 10;
1250 return button_height + extra_height;
1257 /** @return Child time axis views that are not hidden */
1258 TimeAxisView::Children
1259 TimeAxisView::get_child_list ()
1263 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1264 if (!(*i)->hidden()) {
1273 TimeAxisView::build_size_menu ()
1275 if (_size_menu && _size_menu->gobj ()) {
1281 using namespace Menu_Helpers;
1283 _size_menu = new Menu;
1284 _size_menu->set_name ("ArdourContextMenu");
1285 MenuList& items = _size_menu->items();
1287 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1288 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1289 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1290 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1291 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1295 TimeAxisView::reset_visual_state ()
1297 /* this method is not required to trigger a global redraw */
1299 string str = gui_property ("height");
1302 set_height (atoi (str));
1304 set_height (preset_height (HeightNormal));
1309 TrackViewList::filter_to_unique_playlists ()
1311 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1314 for (iterator i = begin(); i != end(); ++i) {
1315 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1317 /* not a route: include it anyway */
1320 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1322 if (playlists.insert (t->playlist()).second) {
1323 /* playlist not seen yet */
1327 /* not a track: include it anyway */