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 HSeparator* separator = manage (new HSeparator());
142 separator->set_name("TrackSeparator");
143 separator->set_size_request(-1, 1);
146 controls_vbox.pack_start (controls_table, false, false);
147 controls_vbox.show ();
149 top_hbox.pack_start (controls_vbox, true, true);
152 //controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
153 controls_ebox.add (top_hbox);
154 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
155 Gdk::BUTTON_RELEASE_MASK|
156 Gdk::POINTER_MOTION_MASK|
157 Gdk::ENTER_NOTIFY_MASK|
158 Gdk::LEAVE_NOTIFY_MASK|
160 controls_ebox.set_flags (CAN_FOCUS);
162 /* note that this handler connects *before* the default handler */
163 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
164 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
165 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
166 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
167 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
168 controls_ebox.show ();
170 time_axis_frame.add(controls_ebox);
171 time_axis_frame.show();
173 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
175 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
178 TimeAxisView::~TimeAxisView()
180 in_destructor = true;
182 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
186 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
188 delete (*i)->start_trim;
189 delete (*i)->end_trim;
193 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
195 delete (*i)->start_trim;
196 delete (*i)->end_trim;
199 delete selection_group;
202 delete _canvas_display;
212 TimeAxisView::hide ()
218 _canvas_display->hide ();
220 if (control_parent) {
221 control_parent->remove (time_axis_frame);
228 /* now hide children */
230 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
234 /* if its hidden, it cannot be selected */
235 _editor.get_selection().remove (this);
236 /* and neither can its regions */
237 _editor.get_selection().remove_regions (this);
242 /** Display this TimeAxisView as the nth component of the parent box, at y.
244 * @param y y position.
245 * @param nth index for this TimeAxisView, increased if this view has children.
246 * @param parent parent component.
247 * @return height of this TimeAxisView.
250 TimeAxisView::show_at (double y, int& nth, VBox *parent)
252 if (control_parent) {
253 control_parent->reorder_child (time_axis_frame, nth);
255 control_parent = parent;
256 parent->pack_start (time_axis_frame, false, false);
257 parent->reorder_child (time_axis_frame, nth);
262 if (_y_position != y) {
263 _canvas_display->set_y_position (y);
268 _canvas_display->raise_to_top ();
269 _canvas_display->show ();
273 _effective_height = current_height ();
275 /* now show relevant children */
277 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
278 if ((*i)->marked_for_display()) {
280 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
286 return _effective_height;
290 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
292 switch (ev->direction) {
294 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
295 /* See Editor::_stepping_axis_view for notes on this hack */
296 Editor& e = dynamic_cast<Editor&> (_editor);
297 if (!e.stepping_axis_view ()) {
298 e.set_stepping_axis_view (this);
300 e.stepping_axis_view()->step_height (false);
305 case GDK_SCROLL_DOWN:
306 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
307 /* See Editor::_stepping_axis_view for notes on this hack */
308 Editor& e = dynamic_cast<Editor&> (_editor);
309 if (!e.stepping_axis_view ()) {
310 e.set_stepping_axis_view (this);
312 e.stepping_axis_view()->step_height (true);
318 /* no handling for left/right, yet */
322 /* Just forward to the normal canvas scroll method. The coordinate
323 systems are different but since the canvas is always larger than the
324 track headers, and aligned with the trackview area, this will work.
326 In the not too distant future this layout is going away anyway and
327 headers will be on the canvas.
329 return _editor.canvas_scroll_event (ev, false);
333 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
335 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
336 /* see if it is inside the name label */
337 if (name_label.is_ancestor (controls_ebox)) {
340 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
341 Gtk::Allocation a = name_label.get_allocation ();
342 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
344 _ebox_release_can_act = false;
351 _ebox_release_can_act = true;
353 if (maybe_set_cursor (event->y) > 0) {
354 _resize_drag_start = event->y_root;
361 TimeAxisView::idle_resize (uint32_t h)
368 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
370 if (_resize_drag_start >= 0) {
372 /* (ab)use the DragManager to do autoscrolling - basically we
373 * are pretending that the drag is taking place over the canvas
374 * (which perhaps in the glorious future, when track headers
375 * and the canvas are unified, will actually be true.)
378 _editor.maybe_autoscroll (false, true, true);
380 /* now schedule the actual TAV resize */
381 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
382 _editor.add_to_idle_resize (this, delta);
383 _resize_drag_start = ev->y_root;
385 /* not dragging but ... */
386 maybe_set_cursor (ev->y);
389 gdk_event_request_motions(ev);
394 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
396 if (_have_preresize_cursor) {
397 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
398 _have_preresize_cursor = false;
404 TimeAxisView::maybe_set_cursor (int y)
406 /* XXX no Gtkmm Gdk::Window::get_cursor() */
407 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
409 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
411 /* y-coordinate in lower 25% */
413 if (!_have_preresize_cursor) {
414 _preresize_cursor = gdk_window_get_cursor (win->gobj());
415 _have_preresize_cursor = true;
416 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
421 } else if (_have_preresize_cursor) {
422 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
423 _have_preresize_cursor = false;
432 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
434 if (_resize_drag_start >= 0) {
435 if (_have_preresize_cursor) {
436 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
437 _preresize_cursor = 0;
438 _have_preresize_cursor = false;
440 _editor.stop_canvas_autoscroll ();
441 _resize_drag_start = -1;
444 if (!_ebox_release_can_act) {
448 switch (ev->button) {
450 selection_click (ev);
454 popup_display_menu (ev->time);
462 TimeAxisView::selection_click (GdkEventButton* ev)
464 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
465 _editor.set_selected_track (*this, op, false);
469 /** Steps through the defined heights for this TrackView.
470 * @param coarser true if stepping should decrease in size, otherwise false.
473 TimeAxisView::step_height (bool coarser)
475 static const uint32_t step = 25;
479 if (height <= preset_height (HeightSmall)) {
481 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
482 set_height_enum (HeightSmall);
484 set_height (height - step);
489 if (height <= preset_height(HeightSmall)) {
490 set_height_enum (HeightNormal);
492 set_height (height + step);
499 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
501 if (apply_to_selection) {
502 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
504 set_height (preset_height (h));
509 TimeAxisView::set_height (uint32_t h)
511 if (h < preset_height (HeightSmall)) {
512 h = preset_height (HeightSmall);
515 time_axis_frame.property_height_request () = h;
519 snprintf (buf, sizeof (buf), "%u", height);
520 set_gui_property ("height", buf);
522 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
526 if (selection_group->visible ()) {
527 /* resize the selection rect */
528 show_selection (_editor.get_selection().time);
531 _editor.override_visible_track_count ();
535 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
537 /* steal escape, tabs from GTK */
539 switch (ev->keyval) {
541 case GDK_ISO_Left_Tab:
549 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
551 TrackViewList::iterator i;
553 switch (ev->keyval) {
555 end_name_edit (RESPONSE_CANCEL);
558 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
559 * generates a different ev->keyval, rather than setting
562 case GDK_ISO_Left_Tab:
563 end_name_edit (RESPONSE_APPLY);
567 end_name_edit (RESPONSE_ACCEPT);
577 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
579 end_name_edit (RESPONSE_OK);
584 TimeAxisView::begin_name_edit ()
590 if (can_edit_name()) {
592 name_entry = manage (new Gtkmm2ext::FocusEntry);
594 name_entry->set_width_chars(8); // min width, entry expands
596 name_entry->set_name ("EditorTrackNameDisplay");
597 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
598 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
599 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
600 name_entry->set_text (name_label.get_text());
601 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
603 if (name_label.is_ancestor (name_hbox)) {
604 name_hbox.remove (name_label);
607 name_hbox.pack_end (*name_entry, true, true);
610 name_entry->select_region (0, -1);
611 name_entry->set_state (STATE_SELECTED);
612 name_entry->grab_focus ();
613 name_entry->start_editing (0);
618 TimeAxisView::end_name_edit (int response)
624 bool edit_next = false;
625 bool edit_prev = false;
628 case RESPONSE_CANCEL:
631 name_entry_changed ();
633 case RESPONSE_ACCEPT:
634 name_entry_changed ();
637 name_entry_changed ();
641 /* this will delete the name_entry. but it will also drop focus, which
642 * will cause another callback to this function, so set name_entry = 0
643 * first to ensure we don't double-remove etc. etc.
646 Gtk::Entry* tmp = name_entry;
648 name_hbox.remove (*tmp);
650 /* put the name label back */
652 name_hbox.pack_end (name_label);
657 TrackViewList const & allviews = _editor.get_track_views ();
658 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
660 if (i != allviews.end()) {
663 if (++i == allviews.end()) {
667 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
669 if (rtav && rtav->route()->record_enabled()) {
673 if (!(*i)->hidden()) {
680 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
681 _editor.ensure_time_axis_view_is_visible (**i, false);
682 (*i)->begin_name_edit ();
685 } else if (edit_prev) {
687 TrackViewList const & allviews = _editor.get_track_views ();
688 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
690 if (i != allviews.begin()) {
692 if (i == allviews.begin()) {
698 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
700 if (rtav && rtav->route()->record_enabled()) {
704 if (!(*i)->hidden()) {
711 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
712 _editor.ensure_time_axis_view_is_visible (**i, false);
713 (*i)->begin_name_edit ();
719 TimeAxisView::name_entry_changed ()
724 TimeAxisView::can_edit_name () const
730 TimeAxisView::conditionally_add_to_selection ()
732 Selection& s (_editor.get_selection ());
734 if (!s.selected (this)) {
735 _editor.set_selected_track (*this, Selection::Set);
740 TimeAxisView::popup_display_menu (guint32 when)
742 conditionally_add_to_selection ();
744 build_display_menu ();
745 display_menu->popup (1, when);
749 TimeAxisView::set_selected (bool yn)
751 if (yn == _selected) {
755 Selectable::set_selected (yn);
758 time_axis_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
759 time_axis_frame.set_name ("MixerStripSelectedFrame");
761 //time_axis_frame.set_name (controls_base_selected_name);
762 controls_ebox.set_name (controls_base_selected_name);
763 controls_vbox.set_name (controls_base_selected_name);
765 time_axis_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
766 time_axis_frame.set_name (controls_base_unselected_name);
768 //time_axis_frame.set_name (controls_base_unselected_name);
769 controls_ebox.set_name (controls_base_unselected_name);
770 controls_vbox.set_name (controls_base_unselected_name);
774 /* children will be set for the yn=true case. but when deselecting
775 the editor only has a list of top-level trackviews, so we
776 have to do this here.
779 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
780 (*i)->set_selected (false);
784 time_axis_frame.show();
789 TimeAxisView::build_display_menu ()
791 using namespace Menu_Helpers;
795 display_menu = new Menu;
796 display_menu->set_name ("ArdourContextMenu");
798 // Just let implementing classes define what goes into the manu
802 TimeAxisView::set_samples_per_pixel (double fpp)
804 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
805 (*i)->set_samples_per_pixel (fpp);
810 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
812 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
813 (*i)->show_timestretch (start, end, layers, layer);
818 TimeAxisView::hide_timestretch ()
820 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
821 (*i)->hide_timestretch ();
826 TimeAxisView::show_selection (TimeSelection& ts)
831 SelectionRect *rect; time_axis_frame.show();
834 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
835 (*i)->show_selection (ts);
838 if (selection_group->visible ()) {
839 while (!used_selection_rects.empty()) {
840 free_selection_rects.push_front (used_selection_rects.front());
841 used_selection_rects.pop_front();
842 free_selection_rects.front()->rect->hide();
843 free_selection_rects.front()->start_trim->hide();
844 free_selection_rects.front()->end_trim->hide();
846 selection_group->hide();
849 selection_group->show();
850 selection_group->raise_to_top();
852 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
853 framepos_t start, end;
858 cnt = end - start + 1;
860 rect = get_selection_rect ((*i).id);
862 x1 = _editor.sample_to_pixel (start);
863 x2 = _editor.sample_to_pixel (start + cnt - 1);
864 y2 = current_height() - 1;
866 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
868 // trim boxes are at the top for selections
871 rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
872 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
874 rect->start_trim->show();
875 rect->end_trim->show();
877 rect->start_trim->hide();
878 rect->end_trim->hide();
882 used_selection_rects.push_back (rect);
887 TimeAxisView::reshow_selection (TimeSelection& ts)
891 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
892 (*i)->show_selection (ts);
897 TimeAxisView::hide_selection ()
899 if (selection_group->visible ()) {
900 while (!used_selection_rects.empty()) {
901 free_selection_rects.push_front (used_selection_rects.front());
902 used_selection_rects.pop_front();
903 free_selection_rects.front()->rect->hide();
904 free_selection_rects.front()->start_trim->hide();
905 free_selection_rects.front()->end_trim->hide();
907 selection_group->hide();
910 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
911 (*i)->hide_selection ();
916 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
918 /* find the selection rect this is for. we have the item corresponding to one
922 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
923 if ((*i)->start_trim == item || (*i)->end_trim == item) {
925 /* make one trim handle be "above" the other so that if they overlap,
926 the top one is the one last used.
929 (*i)->rect->raise_to_top ();
930 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
931 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
939 TimeAxisView::get_selection_rect (uint32_t id)
943 /* check to see if we already have a visible rect for this particular selection ID */
945 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
946 if ((*i)->id == id) {
951 /* ditto for the free rect list */
953 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
954 if ((*i)->id == id) {
955 SelectionRect* ret = (*i);
956 free_selection_rects.erase (i);
961 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
963 if (free_selection_rects.empty()) {
965 rect = new SelectionRect;
967 rect->rect = new ArdourCanvas::Rectangle (selection_group);
968 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
969 rect->rect->set_outline (false);
970 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
972 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
973 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
974 rect->start_trim->set_outline (false);
975 rect->start_trim->set_fill (false);
977 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
978 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
979 rect->end_trim->set_outline (false);
980 rect->end_trim->set_fill (false);
982 free_selection_rects.push_front (rect);
984 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
985 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
986 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
989 rect = free_selection_rects.front();
991 free_selection_rects.pop_front();
995 struct null_deleter { void operator()(void const *) const {} };
998 TimeAxisView::is_child (TimeAxisView* tav)
1000 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1004 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1006 children.push_back (child);
1010 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1012 Children::iterator i;
1014 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1019 /** Get selectable things within a given range.
1020 * @param start Start time in session frames.
1021 * @param end End time in session frames.
1022 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1023 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1024 * @param result Filled in with selectable things.
1027 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1033 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1039 TimeAxisView::add_ghost (RegionView* rv)
1041 GhostRegion* gr = rv->add_ghost (*this);
1044 ghosts.push_back(gr);
1049 TimeAxisView::remove_ghost (RegionView* rv)
1051 rv->remove_ghost_in (*this);
1055 TimeAxisView::erase_ghost (GhostRegion* gr)
1057 if (in_destructor) {
1061 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1070 TimeAxisView::touched (double top, double bot)
1072 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1073 y_position is the "origin" or "top" of the track.
1076 double mybot = _y_position + current_height();
1078 return ((_y_position <= bot && _y_position >= top) ||
1079 ((mybot <= bot) && (top < mybot)) ||
1080 (mybot >= bot && _y_position < top));
1084 TimeAxisView::set_parent (TimeAxisView& p)
1090 TimeAxisView::reset_height ()
1092 set_height (height);
1094 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1095 (*i)->set_height ((*i)->height);
1100 TimeAxisView::compute_heights ()
1102 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1103 Gtk::Table two_row_table (2, 8);
1104 Gtk::Table one_row_table (1, 8);
1106 const int border_width = 2;
1108 const int separator_height = 2;
1109 extra_height = (2 * border_width) + separator_height;
1111 window.add (one_row_table);
1113 one_row_table.set_border_width (border_width);
1114 one_row_table.set_row_spacings (0);
1115 one_row_table.set_col_spacings (0);
1116 one_row_table.set_homogeneous (true);
1118 two_row_table.set_border_width (border_width);
1119 two_row_table.set_row_spacings (0);
1120 two_row_table.set_col_spacings (0);
1121 two_row_table.set_homogeneous (true);
1123 for (int i = 0; i < 5; ++i) {
1124 buttons[i] = manage (new Button (X_("f")));
1125 buttons[i]->set_name ("TrackMuteButton");
1128 one_row_table.attach (*buttons[0], 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
1130 one_row_table.show_all ();
1131 Gtk::Requisition req(one_row_table.size_request ());
1133 // height required to show 1 row of buttons
1134 button_height = req.height;
1138 TimeAxisView::color_handler ()
1140 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1144 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1146 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1147 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1149 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1150 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1152 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1153 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1156 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1158 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1159 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1161 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1162 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1164 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1165 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1169 /** @return Pair: TimeAxisView, layer index.
1170 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1171 * does. @param y is an offset from the top of the trackview area.
1173 * If the covering object is a child axis, then the child is returned.
1174 * TimeAxisView is 0 otherwise.
1176 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1177 * and is in stacked or expanded * region display mode, otherwise 0.
1179 std::pair<TimeAxisView*, double>
1180 TimeAxisView::covers_y_position (double y) const
1183 return std::make_pair ((TimeAxisView *) 0, 0);
1186 if (_y_position <= y && y < (_y_position + height)) {
1188 /* work out the layer index if appropriate */
1190 switch (layer_display ()) {
1196 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1197 /* clamp to max layers to be on the safe side; sometimes the above calculation
1198 returns a too-high value */
1199 if (l >= view()->layers ()) {
1200 l = view()->layers() - 1;
1206 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1208 if (l >= (view()->layers() - 0.5)) {
1209 l = view()->layers() - 0.5;
1215 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1218 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1220 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1226 return std::make_pair ((TimeAxisView *) 0, 0);
1230 TimeAxisView::covered_by_y_range (double y0, double y1) const
1236 /* if either the top or bottom of the axisview is in the vertical
1237 * range, we cover it.
1240 if ((y0 < _y_position && y1 < _y_position) ||
1241 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1245 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1246 if ((*i)->covered_by_y_range (y0, y1)) {
1255 TimeAxisView::preset_height (Height h)
1259 return (button_height * 2) + extra_height + 260;
1261 return (button_height * 2) + extra_height + 160;
1263 return (button_height * 2) + extra_height + 60;
1265 return (button_height * 2) + extra_height + 10;
1267 return button_height + extra_height;
1274 /** @return Child time axis views that are not hidden */
1275 TimeAxisView::Children
1276 TimeAxisView::get_child_list ()
1280 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1281 if (!(*i)->hidden()) {
1290 TimeAxisView::build_size_menu ()
1292 if (_size_menu && _size_menu->gobj ()) {
1298 using namespace Menu_Helpers;
1300 _size_menu = new Menu;
1301 _size_menu->set_name ("ArdourContextMenu");
1302 MenuList& items = _size_menu->items();
1304 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1305 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1306 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1307 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1308 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1312 TimeAxisView::reset_visual_state ()
1314 /* this method is not required to trigger a global redraw */
1316 string str = gui_property ("height");
1319 set_height (atoi (str));
1321 set_height (preset_height (HeightNormal));
1326 TrackViewList::filter_to_unique_playlists ()
1328 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1331 for (iterator i = begin(); i != end(); ++i) {
1332 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1334 /* not a route: include it anyway */
1337 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1339 if (playlists.insert (t->playlist()).second) {
1340 /* playlist not seen yet */
1344 /* not a track: include it anyway */