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 const double trim_handle_size = 6.0; /* pixels */
70 uint32_t TimeAxisView::button_height = 0;
71 uint32_t TimeAxisView::extra_height = 0;
72 int const TimeAxisView::_max_order = 512;
73 unsigned int TimeAxisView::name_width_px = 100; // TODO adjust with font-scaling on style-change
74 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
75 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = Glib::RefPtr<Gtk::SizeGroup>();
77 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
79 , controls_table (3, 3)
80 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
81 , _name_editing (false)
88 , in_destructor (false)
96 , _effective_height (0)
97 , _resize_drag_start (-1)
98 , _preresize_cursor (0)
99 , _have_preresize_cursor (false)
100 , _ebox_release_can_act (true)
102 if (!controls_meters_size_group) {
103 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
105 if (extra_height == 0) {
109 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
110 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
111 _canvas_display->hide(); // reveal as needed
113 selection_group = new ArdourCanvas::Container (_canvas_display);
114 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
115 selection_group->set_data (X_("timeselection"), (void *) 1);
116 selection_group->hide();
118 _ghost_group = new ArdourCanvas::Container (_canvas_display);
119 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
120 _ghost_group->lower_to_bottom();
121 _ghost_group->show();
123 name_label.set_name ("TrackLabel");
124 name_label.set_alignment (0.0, 0.5);
125 name_label.set_width_chars (12);
126 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
128 Gtk::Entry* an_entry = new Gtk::Entry;
129 Gtk::Requisition req;
130 an_entry->size_request (req);
131 name_label.set_size_request (-1, req.height);
132 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
135 name_hbox.pack_end (name_label, true, true);
137 // set min. track-header width if fader is not visible
138 name_hbox.set_size_request(name_width_px, 0);
143 controls_table.set_row_spacings (2);
144 controls_table.set_col_spacings (2);
145 controls_table.set_border_width (2);
147 if (ARDOUR::Profile->get_mixbus() ) {
148 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
150 controls_table.attach (name_hbox, 1, 2, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
152 controls_table.show_all ();
153 controls_table.set_no_show_all ();
155 controls_vbox.pack_start (controls_table, false, false);
156 controls_vbox.show ();
158 top_hbox.pack_start (controls_vbox, true, true);
161 controls_ebox.add (top_hbox);
162 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
163 Gdk::BUTTON_RELEASE_MASK|
164 Gdk::POINTER_MOTION_MASK|
165 Gdk::ENTER_NOTIFY_MASK|
166 Gdk::LEAVE_NOTIFY_MASK|
168 controls_ebox.set_flags (CAN_FOCUS);
170 /* note that this handler connects *before* the default handler */
171 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
172 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
173 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
174 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
175 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
176 controls_ebox.show ();
178 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
179 time_axis_frame.add(controls_ebox);
180 time_axis_frame.show();
182 HSeparator* separator = manage (new HSeparator());
183 separator->set_name("TrackSeparator");
184 separator->set_size_request(-1, 1);
187 time_axis_vbox.pack_start (*separator, false, false);
188 time_axis_vbox.pack_start (time_axis_frame, true, true);
189 time_axis_vbox.show();
190 time_axis_hbox.pack_start (time_axis_vbox, true, true);
191 time_axis_hbox.show();
193 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
195 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
198 TimeAxisView::~TimeAxisView()
200 in_destructor = true;
202 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
206 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
208 delete (*i)->start_trim;
209 delete (*i)->end_trim;
213 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
215 delete (*i)->start_trim;
216 delete (*i)->end_trim;
219 delete selection_group;
222 delete _canvas_display;
232 TimeAxisView::hide ()
238 _canvas_display->hide ();
240 if (control_parent) {
241 control_parent->remove (time_axis_hbox);
248 /* now hide children */
250 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
254 /* if its hidden, it cannot be selected */
255 _editor.get_selection().remove (this);
256 /* and neither can its regions */
257 _editor.get_selection().remove_regions (this);
262 /** Display this TimeAxisView as the nth component of the parent box, at y.
264 * @param y y position.
265 * @param nth index for this TimeAxisView, increased if this view has children.
266 * @param parent parent component.
267 * @return height of this TimeAxisView.
270 TimeAxisView::show_at (double y, int& nth, VBox *parent)
272 if (control_parent) {
273 control_parent->reorder_child (time_axis_hbox, nth);
275 control_parent = parent;
276 parent->pack_start (time_axis_hbox, false, false);
277 parent->reorder_child (time_axis_hbox, nth);
282 if (_y_position != y) {
283 _canvas_display->set_y_position (y);
288 _canvas_display->raise_to_top ();
289 _canvas_display->show ();
293 _effective_height = current_height ();
295 /* now show relevant children */
297 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
298 if ((*i)->marked_for_display()) {
300 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
306 return _effective_height;
310 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
312 switch (ev->direction) {
314 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
315 /* See Editor::_stepping_axis_view for notes on this hack */
316 Editor& e = dynamic_cast<Editor&> (_editor);
317 if (!e.stepping_axis_view ()) {
318 e.set_stepping_axis_view (this);
320 e.stepping_axis_view()->step_height (false);
325 case GDK_SCROLL_DOWN:
326 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
327 /* See Editor::_stepping_axis_view for notes on this hack */
328 Editor& e = dynamic_cast<Editor&> (_editor);
329 if (!e.stepping_axis_view ()) {
330 e.set_stepping_axis_view (this);
332 e.stepping_axis_view()->step_height (true);
338 /* no handling for left/right, yet */
342 /* Just forward to the normal canvas scroll method. The coordinate
343 systems are different but since the canvas is always larger than the
344 track headers, and aligned with the trackview area, this will work.
346 In the not too distant future this layout is going away anyway and
347 headers will be on the canvas.
349 return _editor.canvas_scroll_event (ev, false);
353 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
355 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
356 /* see if it is inside the name label */
357 if (name_label.is_ancestor (controls_ebox)) {
360 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
361 Gtk::Allocation a = name_label.get_allocation ();
362 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
364 _ebox_release_can_act = false;
371 _ebox_release_can_act = true;
373 if (maybe_set_cursor (event->y) > 0) {
374 _resize_drag_start = event->y_root;
381 TimeAxisView::idle_resize (uint32_t h)
388 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
390 if (_resize_drag_start >= 0) {
392 /* (ab)use the DragManager to do autoscrolling - basically we
393 * are pretending that the drag is taking place over the canvas
394 * (which perhaps in the glorious future, when track headers
395 * and the canvas are unified, will actually be true.)
398 _editor.maybe_autoscroll (false, true, true);
400 /* now schedule the actual TAV resize */
401 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
402 _editor.add_to_idle_resize (this, delta);
403 _resize_drag_start = ev->y_root;
405 /* not dragging but ... */
406 maybe_set_cursor (ev->y);
409 gdk_event_request_motions(ev);
414 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
416 if (_have_preresize_cursor) {
417 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
418 _have_preresize_cursor = false;
424 TimeAxisView::maybe_set_cursor (int y)
426 /* XXX no Gtkmm Gdk::Window::get_cursor() */
427 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
429 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
431 /* y-coordinate in lower 25% */
433 if (!_have_preresize_cursor) {
434 _preresize_cursor = gdk_window_get_cursor (win->gobj());
435 _have_preresize_cursor = true;
436 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
441 } else if (_have_preresize_cursor) {
442 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
443 _have_preresize_cursor = false;
452 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
454 if (_resize_drag_start >= 0) {
455 if (_have_preresize_cursor) {
456 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
457 _preresize_cursor = 0;
458 _have_preresize_cursor = false;
460 _editor.stop_canvas_autoscroll ();
461 _resize_drag_start = -1;
464 if (!_ebox_release_can_act) {
468 switch (ev->button) {
470 selection_click (ev);
474 popup_display_menu (ev->time);
482 TimeAxisView::selection_click (GdkEventButton* ev)
484 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
485 _editor.set_selected_track (*this, op, false);
489 /** Steps through the defined heights for this TrackView.
490 * @param coarser true if stepping should decrease in size, otherwise false.
493 TimeAxisView::step_height (bool coarser)
495 static const uint32_t step = 25;
499 if (height <= preset_height (HeightSmall)) {
501 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
502 set_height_enum (HeightSmall);
504 set_height (height - step);
509 if (height <= preset_height(HeightSmall)) {
510 set_height_enum (HeightNormal);
512 set_height (height + step);
519 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
521 if (apply_to_selection) {
522 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
524 set_height (preset_height (h));
529 TimeAxisView::set_height (uint32_t h)
531 if (h < preset_height (HeightSmall)) {
532 h = preset_height (HeightSmall);
535 time_axis_hbox.property_height_request () = h;
539 snprintf (buf, sizeof (buf), "%u", height);
540 set_gui_property ("height", buf);
542 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
546 if (selection_group->visible ()) {
547 /* resize the selection rect */
548 show_selection (_editor.get_selection().time);
551 _editor.override_visible_track_count ();
555 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
557 /* steal escape, tabs from GTK */
559 switch (ev->keyval) {
561 case GDK_ISO_Left_Tab:
569 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
571 TrackViewList::iterator i;
573 switch (ev->keyval) {
575 end_name_edit (RESPONSE_CANCEL);
578 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
579 * generates a different ev->keyval, rather than setting
582 case GDK_ISO_Left_Tab:
583 end_name_edit (RESPONSE_APPLY);
587 end_name_edit (RESPONSE_ACCEPT);
597 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
599 end_name_edit (RESPONSE_OK);
604 TimeAxisView::begin_name_edit ()
610 if (can_edit_name()) {
612 name_entry = manage (new Gtkmm2ext::FocusEntry);
614 name_entry->set_width_chars(8); // min width, entry expands
616 name_entry->set_name ("EditorTrackNameDisplay");
617 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
618 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
619 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
620 name_entry->set_text (name_label.get_text());
621 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
623 if (name_label.is_ancestor (name_hbox)) {
624 name_hbox.remove (name_label);
627 name_hbox.pack_end (*name_entry, true, true);
630 name_entry->select_region (0, -1);
631 name_entry->set_state (STATE_SELECTED);
632 name_entry->grab_focus ();
633 name_entry->start_editing (0);
638 TimeAxisView::end_name_edit (int response)
644 bool edit_next = false;
645 bool edit_prev = false;
648 case RESPONSE_CANCEL:
651 name_entry_changed ();
653 case RESPONSE_ACCEPT:
654 name_entry_changed ();
657 name_entry_changed ();
661 /* this will delete the name_entry. but it will also drop focus, which
662 * will cause another callback to this function, so set name_entry = 0
663 * first to ensure we don't double-remove etc. etc.
666 Gtk::Entry* tmp = name_entry;
668 name_hbox.remove (*tmp);
670 /* put the name label back */
672 name_hbox.pack_end (name_label);
677 TrackViewList const & allviews = _editor.get_track_views ();
678 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
680 if (i != allviews.end()) {
683 if (++i == allviews.end()) {
687 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
689 if (rtav && rtav->route()->record_enabled()) {
693 if (!(*i)->hidden()) {
700 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
701 _editor.ensure_time_axis_view_is_visible (**i, false);
702 (*i)->begin_name_edit ();
705 } else if (edit_prev) {
707 TrackViewList const & allviews = _editor.get_track_views ();
708 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
710 if (i != allviews.begin()) {
712 if (i == allviews.begin()) {
718 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
720 if (rtav && rtav->route()->record_enabled()) {
724 if (!(*i)->hidden()) {
731 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
732 _editor.ensure_time_axis_view_is_visible (**i, false);
733 (*i)->begin_name_edit ();
739 TimeAxisView::name_entry_changed ()
744 TimeAxisView::can_edit_name () const
750 TimeAxisView::conditionally_add_to_selection ()
752 Selection& s (_editor.get_selection ());
754 if (!s.selected (this)) {
755 _editor.set_selected_track (*this, Selection::Set);
760 TimeAxisView::popup_display_menu (guint32 when)
762 conditionally_add_to_selection ();
764 build_display_menu ();
765 display_menu->popup (1, when);
769 TimeAxisView::set_selected (bool yn)
771 if (yn == _selected) {
775 Selectable::set_selected (yn);
778 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
779 time_axis_frame.set_name ("MixerStripSelectedFrame");
780 controls_ebox.set_name (controls_base_selected_name);
781 controls_vbox.set_name (controls_base_selected_name);
782 time_axis_vbox.set_name (controls_base_selected_name);
784 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
785 time_axis_frame.set_name (controls_base_unselected_name);
786 controls_ebox.set_name (controls_base_unselected_name);
787 controls_vbox.set_name (controls_base_unselected_name);
788 time_axis_vbox.set_name (controls_base_unselected_name);
792 /* children will be set for the yn=true case. but when deselecting
793 the editor only has a list of top-level trackviews, so we
794 have to do this here.
797 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
798 (*i)->set_selected (false);
802 time_axis_frame.show();
807 TimeAxisView::build_display_menu ()
809 using namespace Menu_Helpers;
813 display_menu = new Menu;
814 display_menu->set_name ("ArdourContextMenu");
816 // Just let implementing classes define what goes into the manu
820 TimeAxisView::set_samples_per_pixel (double fpp)
822 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
823 (*i)->set_samples_per_pixel (fpp);
828 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
830 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
831 (*i)->show_timestretch (start, end, layers, layer);
836 TimeAxisView::hide_timestretch ()
838 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
839 (*i)->hide_timestretch ();
844 TimeAxisView::show_selection (TimeSelection& ts)
849 SelectionRect *rect; time_axis_frame.show();
852 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
853 (*i)->show_selection (ts);
856 if (selection_group->visible ()) {
857 while (!used_selection_rects.empty()) {
858 free_selection_rects.push_front (used_selection_rects.front());
859 used_selection_rects.pop_front();
860 free_selection_rects.front()->rect->hide();
861 free_selection_rects.front()->start_trim->hide();
862 free_selection_rects.front()->end_trim->hide();
864 selection_group->hide();
867 selection_group->show();
868 selection_group->raise_to_top();
870 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
871 framepos_t start, end;
876 cnt = end - start + 1;
878 rect = get_selection_rect ((*i).id);
880 x0 = _editor.sample_to_pixel (start);
881 x1 = _editor.sample_to_pixel (start + cnt - 1);
882 y1 = current_height();
884 rect->rect->set (ArdourCanvas::Rect (x0, 1, x1, y1));
886 // trim boxes are at the top for selections
889 rect->start_trim->set (ArdourCanvas::Rect (x0, 1, x0 + trim_handle_size, y1));
890 rect->end_trim->set (ArdourCanvas::Rect (x1 - trim_handle_size, 1, x1, y1));
892 rect->start_trim->show();
893 rect->end_trim->show();
895 rect->start_trim->hide();
896 rect->end_trim->hide();
900 used_selection_rects.push_back (rect);
905 TimeAxisView::reshow_selection (TimeSelection& ts)
909 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
910 (*i)->show_selection (ts);
915 TimeAxisView::hide_selection ()
917 if (selection_group->visible ()) {
918 while (!used_selection_rects.empty()) {
919 free_selection_rects.push_front (used_selection_rects.front());
920 used_selection_rects.pop_front();
921 free_selection_rects.front()->rect->hide();
922 free_selection_rects.front()->start_trim->hide();
923 free_selection_rects.front()->end_trim->hide();
925 selection_group->hide();
928 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
929 (*i)->hide_selection ();
934 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
936 /* find the selection rect this is for. we have the item corresponding to one
940 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
941 if ((*i)->start_trim == item || (*i)->end_trim == item) {
943 /* make one trim handle be "above" the other so that if they overlap,
944 the top one is the one last used.
947 (*i)->rect->raise_to_top ();
948 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
949 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
957 TimeAxisView::get_selection_rect (uint32_t id)
961 /* check to see if we already have a visible rect for this particular selection ID */
963 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
964 if ((*i)->id == id) {
969 /* ditto for the free rect list */
971 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
972 if ((*i)->id == id) {
973 SelectionRect* ret = (*i);
974 free_selection_rects.erase (i);
979 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
981 if (free_selection_rects.empty()) {
983 rect = new SelectionRect;
985 rect->rect = new ArdourCanvas::Rectangle (selection_group);
986 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
987 rect->rect->set_outline (false);
988 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
990 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
991 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
992 rect->start_trim->set_outline (false);
993 rect->start_trim->set_fill (false);
995 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
996 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
997 rect->end_trim->set_outline (false);
998 rect->end_trim->set_fill (false);
1000 free_selection_rects.push_front (rect);
1002 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1003 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1004 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1007 rect = free_selection_rects.front();
1009 free_selection_rects.pop_front();
1013 struct null_deleter { void operator()(void const *) const {} };
1016 TimeAxisView::is_child (TimeAxisView* tav)
1018 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1022 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1024 children.push_back (child);
1028 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1030 Children::iterator i;
1032 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1037 /** Get selectable things within a given range.
1038 * @param start Start time in session frames.
1039 * @param end End time in session frames.
1040 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1041 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1042 * @param result Filled in with selectable things.
1045 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1051 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1057 TimeAxisView::add_ghost (RegionView* rv)
1059 GhostRegion* gr = rv->add_ghost (*this);
1062 ghosts.push_back(gr);
1067 TimeAxisView::remove_ghost (RegionView* rv)
1069 rv->remove_ghost_in (*this);
1073 TimeAxisView::erase_ghost (GhostRegion* gr)
1075 if (in_destructor) {
1079 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1088 TimeAxisView::touched (double top, double bot)
1090 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1091 y_position is the "origin" or "top" of the track.
1094 double mybot = _y_position + current_height();
1096 return ((_y_position <= bot && _y_position >= top) ||
1097 ((mybot <= bot) && (top < mybot)) ||
1098 (mybot >= bot && _y_position < top));
1102 TimeAxisView::set_parent (TimeAxisView& p)
1108 TimeAxisView::reset_height ()
1110 set_height (height);
1112 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1113 (*i)->set_height ((*i)->height);
1118 TimeAxisView::compute_heights ()
1120 // TODO this function should be re-evaluated when font-scaling changes (!)
1121 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1122 Gtk::Table one_row_table (1, 1);
1123 ArdourButton* test_button = manage (new ArdourButton);
1124 const int border_width = 2;
1125 const int frame_height = 2;
1126 extra_height = (2 * border_width) + frame_height;
1128 window.add (one_row_table);
1129 test_button->set_name ("mute button");
1130 test_button->set_text (_("M"));
1132 one_row_table.set_border_width (border_width);
1133 one_row_table.set_row_spacings (2);
1134 one_row_table.set_col_spacings (2);
1136 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1137 one_row_table.show_all ();
1139 Gtk::Requisition req(one_row_table.size_request ());
1140 button_height = req.height;
1144 TimeAxisView::color_handler ()
1146 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1150 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1152 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1153 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1155 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1156 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1158 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1159 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1162 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1164 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1165 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1167 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1168 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1170 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1171 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1175 /** @return Pair: TimeAxisView, layer index.
1176 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1177 * does. @param y is an offset from the top of the trackview area.
1179 * If the covering object is a child axis, then the child is returned.
1180 * TimeAxisView is 0 otherwise.
1182 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1183 * and is in stacked or expanded * region display mode, otherwise 0.
1185 std::pair<TimeAxisView*, double>
1186 TimeAxisView::covers_y_position (double y) const
1189 return std::make_pair ((TimeAxisView *) 0, 0);
1192 if (_y_position <= y && y < (_y_position + height)) {
1194 /* work out the layer index if appropriate */
1196 switch (layer_display ()) {
1202 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1203 /* clamp to max layers to be on the safe side; sometimes the above calculation
1204 returns a too-high value */
1205 if (l >= view()->layers ()) {
1206 l = view()->layers() - 1;
1212 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1214 if (l >= (view()->layers() - 0.5)) {
1215 l = view()->layers() - 0.5;
1221 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1224 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1226 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1232 return std::make_pair ((TimeAxisView *) 0, 0);
1236 TimeAxisView::covered_by_y_range (double y0, double y1) const
1242 /* if either the top or bottom of the axisview is in the vertical
1243 * range, we cover it.
1246 if ((y0 < _y_position && y1 < _y_position) ||
1247 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1251 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1252 if ((*i)->covered_by_y_range (y0, y1)) {
1261 TimeAxisView::preset_height (Height h)
1265 return (button_height * 2) + extra_height + 260;
1267 return (button_height * 2) + extra_height + 160;
1269 return (button_height * 2) + extra_height + 60;
1271 return (button_height * 2) + extra_height + 10;
1273 return button_height + extra_height;
1280 /** @return Child time axis views that are not hidden */
1281 TimeAxisView::Children
1282 TimeAxisView::get_child_list ()
1286 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1287 if (!(*i)->hidden()) {
1296 TimeAxisView::build_size_menu ()
1298 if (_size_menu && _size_menu->gobj ()) {
1304 using namespace Menu_Helpers;
1306 _size_menu = new Menu;
1307 _size_menu->set_name ("ArdourContextMenu");
1308 MenuList& items = _size_menu->items();
1310 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1311 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1312 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1313 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1314 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1318 TimeAxisView::reset_visual_state ()
1320 /* this method is not required to trigger a global redraw */
1322 string str = gui_property ("height");
1325 set_height (atoi (str));
1327 set_height (preset_height (HeightNormal));
1332 TrackViewList::filter_to_unique_playlists ()
1334 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1337 for (iterator i = begin(); i != end(); ++i) {
1338 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1340 /* not a route: include it anyway */
1343 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1345 if (playlists.insert (t->playlist()).second) {
1346 /* playlist not seen yet */
1350 /* not a track: include it anyway */