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/profile.h"
41 #include "ardour_ui.h"
42 #include "ardour_dialog.h"
43 #include "global_signals.h"
44 #include "gui_thread.h"
45 #include "public_editor.h"
46 #include "time_axis_view.h"
47 #include "region_view.h"
48 #include "ghostregion.h"
49 #include "selection.h"
51 #include "rgb_macros.h"
53 #include "streamview.h"
54 #include "editor_drag.h"
62 using namespace ARDOUR;
63 using namespace ARDOUR_UI_UTILS;
65 using namespace Editing;
66 using namespace ArdourCanvas;
67 using Gtkmm2ext::Keyboard;
69 #define TOP_LEVEL_WIDGET controls_ebox
71 const double trim_handle_size = 6.0; /* pixels */
72 uint32_t TimeAxisView::button_height = 0;
73 uint32_t TimeAxisView::extra_height = 0;
74 int const TimeAxisView::_max_order = 512;
75 unsigned int TimeAxisView::name_width_px = 100; // TODO adjust with font-scaling on style-change
76 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
77 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = Glib::RefPtr<Gtk::SizeGroup>();
79 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
81 , controls_table (3, 3)
82 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
83 , _name_editing (false)
90 , in_destructor (false)
98 , _effective_height (0)
99 , _resize_drag_start (-1)
100 , _preresize_cursor (0)
101 , _have_preresize_cursor (false)
102 , _ebox_release_can_act (true)
104 if (!controls_meters_size_group) {
105 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
107 if (extra_height == 0) {
111 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (1.0, 0.0));
112 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
113 _canvas_display->hide(); // reveal as needed
115 _canvas_separator = new ArdourCanvas::Line(ed.get_trackview_group ());
116 CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
117 _canvas_separator->set_outline_color(RGBA_TO_UINT (0, 0, 0, 255));
118 _canvas_separator->set_outline_width(1.0);
119 _canvas_separator->hide();
121 selection_group = new ArdourCanvas::Container (_canvas_display);
122 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
123 selection_group->set_data (X_("timeselection"), (void *) 1);
124 selection_group->hide();
126 _ghost_group = new ArdourCanvas::Container (_canvas_display);
127 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
128 _ghost_group->lower_to_bottom();
129 _ghost_group->show();
131 name_label.set_name ("TrackLabel");
132 name_label.set_alignment (0.0, 0.5);
133 name_label.set_width_chars (12);
134 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
136 Gtk::Entry* an_entry = new Gtk::Entry;
137 Gtk::Requisition req;
138 an_entry->size_request (req);
139 name_label.set_size_request (-1, req.height);
140 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
143 name_hbox.pack_end (name_label, true, true);
145 // set min. track-header width if fader is not visible
146 name_hbox.set_size_request(name_width_px, -1);
151 controls_table.set_row_spacings (2);
152 controls_table.set_col_spacings (2);
153 controls_table.set_border_width (2);
155 if (ARDOUR::Profile->get_mixbus() ) {
156 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
158 controls_table.attach (name_hbox, 1, 2, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
160 controls_table.show_all ();
161 controls_table.set_no_show_all ();
163 controls_vbox.pack_start (controls_table, false, false);
164 controls_vbox.show ();
166 top_hbox.pack_start (controls_vbox, true, true);
169 controls_ebox.add (time_axis_hbox);
170 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
171 Gdk::BUTTON_RELEASE_MASK|
172 Gdk::POINTER_MOTION_MASK|
173 Gdk::ENTER_NOTIFY_MASK|
174 Gdk::LEAVE_NOTIFY_MASK|
176 controls_ebox.set_flags (CAN_FOCUS);
178 /* note that this handler connects *before* the default handler */
179 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
180 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
181 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
182 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
183 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
184 controls_ebox.show ();
186 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
187 time_axis_frame.add(top_hbox);
188 time_axis_frame.show();
190 HSeparator* separator = manage (new HSeparator());
191 separator->set_name("TrackSeparator");
192 separator->set_size_request(-1, 1);
195 time_axis_vbox.pack_start (*separator, false, false);
196 time_axis_vbox.pack_start (time_axis_frame, true, true);
197 time_axis_vbox.show();
198 time_axis_hbox.pack_start (time_axis_vbox, true, true);
199 time_axis_hbox.show();
201 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
203 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
206 TimeAxisView::~TimeAxisView()
208 in_destructor = true;
210 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
214 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
216 delete (*i)->start_trim;
217 delete (*i)->end_trim;
221 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
223 delete (*i)->start_trim;
224 delete (*i)->end_trim;
227 delete selection_group;
230 delete _canvas_display;
233 delete _canvas_separator;
234 _canvas_separator = 0;
243 TimeAxisView::hide ()
249 _canvas_display->hide ();
250 _canvas_separator->hide ();
252 if (control_parent) {
253 control_parent->remove (TOP_LEVEL_WIDGET);
260 /* now hide children */
262 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
266 /* if its hidden, it cannot be selected */
267 _editor.get_selection().remove (this);
268 /* and neither can its regions */
269 _editor.get_selection().remove_regions (this);
274 /** Display this TimeAxisView as the nth component of the parent box, at y.
276 * @param y y position.
277 * @param nth index for this TimeAxisView, increased if this view has children.
278 * @param parent parent component.
279 * @return height of this TimeAxisView.
282 TimeAxisView::show_at (double y, int& nth, VBox *parent)
284 if (control_parent) {
285 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
287 control_parent = parent;
288 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
289 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
294 if (_y_position != y) {
295 _canvas_separator->set (ArdourCanvas::Duple(0, y), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, y));
296 _canvas_display->set_y_position (y + 1);
300 _canvas_display->raise_to_top ();
301 _canvas_display->show ();
303 _canvas_separator->raise_to_top ();
304 _canvas_separator->show ();
308 _effective_height = current_height ();
310 /* now show relevant children */
312 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
313 if ((*i)->marked_for_display()) {
315 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
321 return _effective_height;
325 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
327 switch (ev->direction) {
329 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
330 /* See Editor::_stepping_axis_view for notes on this hack */
331 Editor& e = dynamic_cast<Editor&> (_editor);
332 if (!e.stepping_axis_view ()) {
333 e.set_stepping_axis_view (this);
335 e.stepping_axis_view()->step_height (false);
340 case GDK_SCROLL_DOWN:
341 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
342 /* See Editor::_stepping_axis_view for notes on this hack */
343 Editor& e = dynamic_cast<Editor&> (_editor);
344 if (!e.stepping_axis_view ()) {
345 e.set_stepping_axis_view (this);
347 e.stepping_axis_view()->step_height (true);
353 /* no handling for left/right, yet */
357 /* Just forward to the normal canvas scroll method. The coordinate
358 systems are different but since the canvas is always larger than the
359 track headers, and aligned with the trackview area, this will work.
361 In the not too distant future this layout is going away anyway and
362 headers will be on the canvas.
364 return _editor.canvas_scroll_event (ev, false);
368 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
370 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
371 /* see if it is inside the name label */
372 if (name_label.is_ancestor (controls_ebox)) {
375 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
376 Gtk::Allocation a = name_label.get_allocation ();
377 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
379 _ebox_release_can_act = false;
386 _ebox_release_can_act = true;
388 if (maybe_set_cursor (event->y) > 0) {
389 _resize_drag_start = event->y_root;
396 TimeAxisView::idle_resize (uint32_t h)
403 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
405 if (_resize_drag_start >= 0) {
407 /* (ab)use the DragManager to do autoscrolling - basically we
408 * are pretending that the drag is taking place over the canvas
409 * (which perhaps in the glorious future, when track headers
410 * and the canvas are unified, will actually be true.)
413 _editor.maybe_autoscroll (false, true, true);
415 /* now schedule the actual TAV resize */
416 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
417 _editor.add_to_idle_resize (this, delta);
418 _resize_drag_start = ev->y_root;
420 /* not dragging but ... */
421 maybe_set_cursor (ev->y);
424 gdk_event_request_motions(ev);
429 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
431 if (_have_preresize_cursor) {
432 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
433 _have_preresize_cursor = false;
439 TimeAxisView::maybe_set_cursor (int y)
441 /* XXX no Gtkmm Gdk::Window::get_cursor() */
442 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
444 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
446 /* y-coordinate in lower 25% */
448 if (!_have_preresize_cursor) {
449 _preresize_cursor = gdk_window_get_cursor (win->gobj());
450 _have_preresize_cursor = true;
451 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
456 } else if (_have_preresize_cursor) {
457 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
458 _have_preresize_cursor = false;
467 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
469 if (_resize_drag_start >= 0) {
470 if (_have_preresize_cursor) {
471 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
472 _preresize_cursor = 0;
473 _have_preresize_cursor = false;
475 _editor.stop_canvas_autoscroll ();
476 _resize_drag_start = -1;
479 if (!_ebox_release_can_act) {
483 switch (ev->button) {
485 selection_click (ev);
489 popup_display_menu (ev->time);
497 TimeAxisView::selection_click (GdkEventButton* ev)
499 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
500 _editor.set_selected_track (*this, op, false);
504 /** Steps through the defined heights for this TrackView.
505 * @param coarser true if stepping should decrease in size, otherwise false.
508 TimeAxisView::step_height (bool coarser)
510 static const uint32_t step = 25;
514 if (height <= preset_height (HeightSmall)) {
516 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
517 set_height_enum (HeightSmall);
519 set_height (height - step);
524 if (height <= preset_height(HeightSmall)) {
525 set_height_enum (HeightNormal);
527 set_height (height + step);
534 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
536 if (apply_to_selection) {
537 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
539 set_height (preset_height (h));
544 TimeAxisView::set_height (uint32_t h)
546 if (h < preset_height (HeightSmall)) {
547 h = preset_height (HeightSmall);
550 TOP_LEVEL_WIDGET.property_height_request () = h;
554 snprintf (buf, sizeof (buf), "%u", height);
555 set_gui_property ("height", buf);
557 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
561 if (selection_group->visible ()) {
562 /* resize the selection rect */
563 show_selection (_editor.get_selection().time);
566 _editor.override_visible_track_count ();
570 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
572 /* steal escape, tabs from GTK */
574 switch (ev->keyval) {
576 case GDK_ISO_Left_Tab:
584 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
586 TrackViewList::iterator i;
588 switch (ev->keyval) {
590 end_name_edit (RESPONSE_CANCEL);
593 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
594 * generates a different ev->keyval, rather than setting
597 case GDK_ISO_Left_Tab:
598 end_name_edit (RESPONSE_APPLY);
602 end_name_edit (RESPONSE_ACCEPT);
612 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
614 end_name_edit (RESPONSE_OK);
619 TimeAxisView::begin_name_edit ()
625 if (can_edit_name()) {
627 name_entry = manage (new Gtkmm2ext::FocusEntry);
629 name_entry->set_width_chars(8); // min width, entry expands
631 name_entry->set_name ("EditorTrackNameDisplay");
632 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
633 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
634 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
635 name_entry->set_text (name_label.get_text());
636 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
638 if (name_label.is_ancestor (name_hbox)) {
639 name_hbox.remove (name_label);
642 name_hbox.pack_end (*name_entry, true, true);
645 name_entry->select_region (0, -1);
646 name_entry->set_state (STATE_SELECTED);
647 name_entry->grab_focus ();
648 name_entry->start_editing (0);
653 TimeAxisView::end_name_edit (int response)
659 bool edit_next = false;
660 bool edit_prev = false;
663 case RESPONSE_CANCEL:
666 name_entry_changed ();
668 case RESPONSE_ACCEPT:
669 name_entry_changed ();
672 name_entry_changed ();
676 /* this will delete the name_entry. but it will also drop focus, which
677 * will cause another callback to this function, so set name_entry = 0
678 * first to ensure we don't double-remove etc. etc.
681 Gtk::Entry* tmp = name_entry;
683 name_hbox.remove (*tmp);
685 /* put the name label back */
687 name_hbox.pack_end (name_label);
692 TrackViewList const & allviews = _editor.get_track_views ();
693 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
695 if (i != allviews.end()) {
698 if (++i == allviews.end()) {
702 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
704 if (rtav && rtav->route()->record_enabled()) {
708 if (!(*i)->hidden()) {
715 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
716 _editor.ensure_time_axis_view_is_visible (**i, false);
717 (*i)->begin_name_edit ();
720 } else if (edit_prev) {
722 TrackViewList const & allviews = _editor.get_track_views ();
723 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
725 if (i != allviews.begin()) {
727 if (i == allviews.begin()) {
733 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
735 if (rtav && rtav->route()->record_enabled()) {
739 if (!(*i)->hidden()) {
746 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
747 _editor.ensure_time_axis_view_is_visible (**i, false);
748 (*i)->begin_name_edit ();
754 TimeAxisView::name_entry_changed ()
759 TimeAxisView::can_edit_name () const
765 TimeAxisView::conditionally_add_to_selection ()
767 Selection& s (_editor.get_selection ());
769 if (!s.selected (this)) {
770 _editor.set_selected_track (*this, Selection::Set);
775 TimeAxisView::popup_display_menu (guint32 when)
777 conditionally_add_to_selection ();
779 build_display_menu ();
780 display_menu->popup (1, when);
784 TimeAxisView::set_selected (bool yn)
786 if (can_edit_name() && name_entry && name_entry->get_visible()) {
787 end_name_edit (RESPONSE_CANCEL);
790 if (yn == _selected) {
794 Selectable::set_selected (yn);
797 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
798 time_axis_frame.set_name ("MixerStripSelectedFrame");
799 controls_ebox.set_name (controls_base_selected_name);
800 controls_vbox.set_name (controls_base_selected_name);
801 time_axis_vbox.set_name (controls_base_selected_name);
803 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
804 time_axis_frame.set_name (controls_base_unselected_name);
805 controls_ebox.set_name (controls_base_unselected_name);
806 controls_vbox.set_name (controls_base_unselected_name);
807 time_axis_vbox.set_name (controls_base_unselected_name);
811 /* children will be set for the yn=true case. but when deselecting
812 the editor only has a list of top-level trackviews, so we
813 have to do this here.
816 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
817 (*i)->set_selected (false);
821 time_axis_frame.show();
826 TimeAxisView::build_display_menu ()
828 using namespace Menu_Helpers;
832 display_menu = new Menu;
833 display_menu->set_name ("ArdourContextMenu");
835 // Just let implementing classes define what goes into the manu
839 TimeAxisView::set_samples_per_pixel (double fpp)
841 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
842 (*i)->set_samples_per_pixel (fpp);
847 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
849 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
850 (*i)->show_timestretch (start, end, layers, layer);
855 TimeAxisView::hide_timestretch ()
857 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
858 (*i)->hide_timestretch ();
863 TimeAxisView::show_selection (TimeSelection& ts)
868 SelectionRect *rect; time_axis_frame.show();
871 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
872 (*i)->show_selection (ts);
875 if (selection_group->visible ()) {
876 while (!used_selection_rects.empty()) {
877 free_selection_rects.push_front (used_selection_rects.front());
878 used_selection_rects.pop_front();
879 free_selection_rects.front()->rect->hide();
880 free_selection_rects.front()->start_trim->hide();
881 free_selection_rects.front()->end_trim->hide();
883 selection_group->hide();
886 selection_group->show();
887 selection_group->raise_to_top();
889 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
890 framepos_t start, end;
895 cnt = end - start + 1;
897 rect = get_selection_rect ((*i).id);
899 x1 = _editor.sample_to_pixel (start);
900 x2 = _editor.sample_to_pixel (start + cnt - 1);
901 y2 = current_height() - 1;
903 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
905 // trim boxes are at the top for selections
908 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
909 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
911 rect->start_trim->show();
912 rect->end_trim->show();
914 rect->start_trim->hide();
915 rect->end_trim->hide();
919 used_selection_rects.push_back (rect);
924 TimeAxisView::reshow_selection (TimeSelection& ts)
928 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
929 (*i)->show_selection (ts);
934 TimeAxisView::hide_selection ()
936 if (selection_group->visible ()) {
937 while (!used_selection_rects.empty()) {
938 free_selection_rects.push_front (used_selection_rects.front());
939 used_selection_rects.pop_front();
940 free_selection_rects.front()->rect->hide();
941 free_selection_rects.front()->start_trim->hide();
942 free_selection_rects.front()->end_trim->hide();
944 selection_group->hide();
947 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
948 (*i)->hide_selection ();
953 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
955 /* find the selection rect this is for. we have the item corresponding to one
959 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
960 if ((*i)->start_trim == item || (*i)->end_trim == item) {
962 /* make one trim handle be "above" the other so that if they overlap,
963 the top one is the one last used.
966 (*i)->rect->raise_to_top ();
967 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
968 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
976 TimeAxisView::get_selection_rect (uint32_t id)
980 /* check to see if we already have a visible rect for this particular selection ID */
982 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
983 if ((*i)->id == id) {
988 /* ditto for the free rect list */
990 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
991 if ((*i)->id == id) {
992 SelectionRect* ret = (*i);
993 free_selection_rects.erase (i);
998 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
1000 if (free_selection_rects.empty()) {
1002 rect = new SelectionRect;
1004 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1005 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1006 rect->rect->set_outline (false);
1007 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1009 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1010 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1011 rect->start_trim->set_outline (false);
1012 rect->start_trim->set_fill (false);
1014 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1015 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1016 rect->end_trim->set_outline (false);
1017 rect->end_trim->set_fill (false);
1019 free_selection_rects.push_front (rect);
1021 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1022 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1023 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1026 rect = free_selection_rects.front();
1028 free_selection_rects.pop_front();
1032 struct null_deleter { void operator()(void const *) const {} };
1035 TimeAxisView::is_child (TimeAxisView* tav)
1037 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1041 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1043 children.push_back (child);
1047 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1049 Children::iterator i;
1051 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1056 /** Get selectable things within a given range.
1057 * @param start Start time in session frames.
1058 * @param end End time in session frames.
1059 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1060 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1061 * @param result Filled in with selectable things.
1064 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1070 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1076 TimeAxisView::add_ghost (RegionView* rv)
1078 GhostRegion* gr = rv->add_ghost (*this);
1081 ghosts.push_back(gr);
1086 TimeAxisView::remove_ghost (RegionView* rv)
1088 rv->remove_ghost_in (*this);
1092 TimeAxisView::erase_ghost (GhostRegion* gr)
1094 if (in_destructor) {
1098 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1107 TimeAxisView::touched (double top, double bot)
1109 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1110 y_position is the "origin" or "top" of the track.
1113 double mybot = _y_position + current_height();
1115 return ((_y_position <= bot && _y_position >= top) ||
1116 ((mybot <= bot) && (top < mybot)) ||
1117 (mybot >= bot && _y_position < top));
1121 TimeAxisView::set_parent (TimeAxisView& p)
1127 TimeAxisView::reset_height ()
1129 set_height (height);
1131 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1132 (*i)->set_height ((*i)->height);
1137 TimeAxisView::compute_heights ()
1139 // TODO this function should be re-evaluated when font-scaling changes (!)
1140 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1141 Gtk::Table one_row_table (1, 1);
1142 ArdourButton* test_button = manage (new ArdourButton);
1143 const int border_width = 2;
1144 const int frame_height = 2;
1145 extra_height = (2 * border_width) + frame_height;
1147 window.add (one_row_table);
1148 test_button->set_name ("mute button");
1149 test_button->set_text (_("M"));
1150 test_button->set_tweaks (ArdourButton::Square);
1152 one_row_table.set_border_width (border_width);
1153 one_row_table.set_row_spacings (2);
1154 one_row_table.set_col_spacings (2);
1156 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1157 one_row_table.show_all ();
1159 Gtk::Requisition req(one_row_table.size_request ());
1160 button_height = req.height;
1164 TimeAxisView::color_handler ()
1166 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1170 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1172 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1173 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1175 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1176 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1178 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1179 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1182 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1184 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1185 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1187 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1188 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1190 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1191 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1195 /** @return Pair: TimeAxisView, layer index.
1196 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1197 * does. @param y is an offset from the top of the trackview area.
1199 * If the covering object is a child axis, then the child is returned.
1200 * TimeAxisView is 0 otherwise.
1202 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1203 * and is in stacked or expanded * region display mode, otherwise 0.
1205 std::pair<TimeAxisView*, double>
1206 TimeAxisView::covers_y_position (double y) const
1209 return std::make_pair ((TimeAxisView *) 0, 0);
1212 if (_y_position <= y && y < (_y_position + height)) {
1214 /* work out the layer index if appropriate */
1216 switch (layer_display ()) {
1222 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1223 /* clamp to max layers to be on the safe side; sometimes the above calculation
1224 returns a too-high value */
1225 if (l >= view()->layers ()) {
1226 l = view()->layers() - 1;
1232 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1234 if (l >= (view()->layers() - 0.5)) {
1235 l = view()->layers() - 0.5;
1241 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1244 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1246 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1252 return std::make_pair ((TimeAxisView *) 0, 0);
1256 TimeAxisView::covered_by_y_range (double y0, double y1) const
1262 /* if either the top or bottom of the axisview is in the vertical
1263 * range, we cover it.
1266 if ((y0 < _y_position && y1 < _y_position) ||
1267 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1271 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1272 if ((*i)->covered_by_y_range (y0, y1)) {
1281 TimeAxisView::preset_height (Height h)
1285 return (button_height * 2) + extra_height + 260;
1287 return (button_height * 2) + extra_height + 160;
1289 return (button_height * 2) + extra_height + 60;
1291 return (button_height * 2) + extra_height + 10;
1293 return button_height + extra_height;
1300 /** @return Child time axis views that are not hidden */
1301 TimeAxisView::Children
1302 TimeAxisView::get_child_list ()
1306 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1307 if (!(*i)->hidden()) {
1316 TimeAxisView::build_size_menu ()
1318 if (_size_menu && _size_menu->gobj ()) {
1324 using namespace Menu_Helpers;
1326 _size_menu = new Menu;
1327 _size_menu->set_name ("ArdourContextMenu");
1328 MenuList& items = _size_menu->items();
1330 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1331 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1332 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1333 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1334 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1338 TimeAxisView::reset_visual_state ()
1340 /* this method is not required to trigger a global redraw */
1342 string str = gui_property ("height");
1345 set_height (atoi (str));
1347 set_height (preset_height (HeightNormal));
1352 TrackViewList::filter_to_unique_playlists ()
1354 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1357 for (iterator i = begin(); i != end(); ++i) {
1358 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1360 /* not a route: include it anyway */
1363 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1365 if (playlists.insert (t->playlist()).second) {
1366 /* playlist not seen yet */
1370 /* not a track: include it anyway */