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 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
74 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
76 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
78 , controls_table (3, 3)
79 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
80 , _name_editing (false)
87 , in_destructor (false)
95 , _effective_height (0)
96 , _resize_drag_start (-1)
97 , _preresize_cursor (0)
98 , _have_preresize_cursor (false)
99 , _ebox_release_can_act (true)
101 if (extra_height == 0) {
105 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
106 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
107 _canvas_display->hide(); // reveal as needed
109 selection_group = new ArdourCanvas::Container (_canvas_display);
110 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
111 selection_group->set_data (X_("timeselection"), (void *) 1);
112 selection_group->hide();
114 _ghost_group = new ArdourCanvas::Container (_canvas_display);
115 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
116 _ghost_group->lower_to_bottom();
117 _ghost_group->show();
119 name_label.set_name ("TrackLabel");
120 name_label.set_alignment (0.0, 0.5);
121 name_label.set_width_chars (12);
122 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
124 Gtk::Entry* an_entry = new Gtk::Entry;
125 Gtk::Requisition req;
126 an_entry->size_request (req);
127 name_label.set_size_request (-1, req.height);
128 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
131 name_hbox.pack_end (name_label, true, true);
133 // set min. track-header width if fader is not visible
134 name_hbox.set_size_request(100, 0);
139 controls_table.set_row_spacings (2);
140 controls_table.set_col_spacings (2);
141 controls_table.set_border_width (2);
143 if (ARDOUR::Profile->get_mixbus() ) {
144 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
146 controls_table.attach (name_hbox, 1, 2, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
148 controls_table.show_all ();
149 controls_table.set_no_show_all ();
151 controls_vbox.pack_start (controls_table, false, false);
152 controls_vbox.show ();
154 top_hbox.pack_start (controls_vbox, true, true);
157 controls_ebox.add (top_hbox);
158 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
159 Gdk::BUTTON_RELEASE_MASK|
160 Gdk::POINTER_MOTION_MASK|
161 Gdk::ENTER_NOTIFY_MASK|
162 Gdk::LEAVE_NOTIFY_MASK|
164 controls_ebox.set_flags (CAN_FOCUS);
166 /* note that this handler connects *before* the default handler */
167 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
168 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
169 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
170 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
171 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
172 controls_ebox.show ();
174 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
175 time_axis_frame.add(controls_ebox);
176 time_axis_frame.show();
178 HSeparator* separator = manage (new HSeparator());
179 separator->set_name("TrackSeparator");
180 separator->set_size_request(-1, 1);
183 time_axis_vbox.pack_start (time_axis_frame, true, true);
184 time_axis_vbox.pack_end (*separator, false, false);
185 time_axis_vbox.show();
187 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
189 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
192 TimeAxisView::~TimeAxisView()
194 in_destructor = true;
196 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
200 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
202 delete (*i)->start_trim;
203 delete (*i)->end_trim;
207 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
209 delete (*i)->start_trim;
210 delete (*i)->end_trim;
213 delete selection_group;
216 delete _canvas_display;
226 TimeAxisView::hide ()
232 _canvas_display->hide ();
234 if (control_parent) {
235 control_parent->remove (time_axis_vbox);
242 /* now hide children */
244 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
248 /* if its hidden, it cannot be selected */
249 _editor.get_selection().remove (this);
250 /* and neither can its regions */
251 _editor.get_selection().remove_regions (this);
256 /** Display this TimeAxisView as the nth component of the parent box, at y.
258 * @param y y position.
259 * @param nth index for this TimeAxisView, increased if this view has children.
260 * @param parent parent component.
261 * @return height of this TimeAxisView.
264 TimeAxisView::show_at (double y, int& nth, VBox *parent)
266 if (control_parent) {
267 control_parent->reorder_child (time_axis_vbox, nth);
269 control_parent = parent;
270 parent->pack_start (time_axis_vbox, false, false);
271 parent->reorder_child (time_axis_vbox, nth);
276 if (_y_position != y) {
277 _canvas_display->set_y_position (y);
282 _canvas_display->raise_to_top ();
283 _canvas_display->show ();
287 _effective_height = current_height ();
289 /* now show relevant children */
291 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
292 if ((*i)->marked_for_display()) {
294 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
300 return _effective_height;
304 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
306 switch (ev->direction) {
308 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
309 /* See Editor::_stepping_axis_view for notes on this hack */
310 Editor& e = dynamic_cast<Editor&> (_editor);
311 if (!e.stepping_axis_view ()) {
312 e.set_stepping_axis_view (this);
314 e.stepping_axis_view()->step_height (false);
319 case GDK_SCROLL_DOWN:
320 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
321 /* See Editor::_stepping_axis_view for notes on this hack */
322 Editor& e = dynamic_cast<Editor&> (_editor);
323 if (!e.stepping_axis_view ()) {
324 e.set_stepping_axis_view (this);
326 e.stepping_axis_view()->step_height (true);
332 /* no handling for left/right, yet */
336 /* Just forward to the normal canvas scroll method. The coordinate
337 systems are different but since the canvas is always larger than the
338 track headers, and aligned with the trackview area, this will work.
340 In the not too distant future this layout is going away anyway and
341 headers will be on the canvas.
343 return _editor.canvas_scroll_event (ev, false);
347 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
349 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
350 /* see if it is inside the name label */
351 if (name_label.is_ancestor (controls_ebox)) {
354 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
355 Gtk::Allocation a = name_label.get_allocation ();
356 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
358 _ebox_release_can_act = false;
365 _ebox_release_can_act = true;
367 if (maybe_set_cursor (event->y) > 0) {
368 _resize_drag_start = event->y_root;
375 TimeAxisView::idle_resize (uint32_t h)
382 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
384 if (_resize_drag_start >= 0) {
386 /* (ab)use the DragManager to do autoscrolling - basically we
387 * are pretending that the drag is taking place over the canvas
388 * (which perhaps in the glorious future, when track headers
389 * and the canvas are unified, will actually be true.)
392 _editor.maybe_autoscroll (false, true, true);
394 /* now schedule the actual TAV resize */
395 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
396 _editor.add_to_idle_resize (this, delta);
397 _resize_drag_start = ev->y_root;
399 /* not dragging but ... */
400 maybe_set_cursor (ev->y);
403 gdk_event_request_motions(ev);
408 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
410 if (_have_preresize_cursor) {
411 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
412 _have_preresize_cursor = false;
418 TimeAxisView::maybe_set_cursor (int y)
420 /* XXX no Gtkmm Gdk::Window::get_cursor() */
421 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
423 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
425 /* y-coordinate in lower 25% */
427 if (!_have_preresize_cursor) {
428 _preresize_cursor = gdk_window_get_cursor (win->gobj());
429 _have_preresize_cursor = true;
430 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
435 } else if (_have_preresize_cursor) {
436 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
437 _have_preresize_cursor = false;
446 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
448 if (_resize_drag_start >= 0) {
449 if (_have_preresize_cursor) {
450 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
451 _preresize_cursor = 0;
452 _have_preresize_cursor = false;
454 _editor.stop_canvas_autoscroll ();
455 _resize_drag_start = -1;
458 if (!_ebox_release_can_act) {
462 switch (ev->button) {
464 selection_click (ev);
468 popup_display_menu (ev->time);
476 TimeAxisView::selection_click (GdkEventButton* ev)
478 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
479 _editor.set_selected_track (*this, op, false);
483 /** Steps through the defined heights for this TrackView.
484 * @param coarser true if stepping should decrease in size, otherwise false.
487 TimeAxisView::step_height (bool coarser)
489 static const uint32_t step = 25;
493 if (height <= preset_height (HeightSmall)) {
495 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
496 set_height_enum (HeightSmall);
498 set_height (height - step);
503 if (height <= preset_height(HeightSmall)) {
504 set_height_enum (HeightNormal);
506 set_height (height + step);
513 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
515 if (apply_to_selection) {
516 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
518 set_height (preset_height (h));
523 TimeAxisView::set_height (uint32_t h)
525 if (h < preset_height (HeightSmall)) {
526 h = preset_height (HeightSmall);
529 time_axis_vbox.property_height_request () = h;
533 snprintf (buf, sizeof (buf), "%u", height);
534 set_gui_property ("height", buf);
536 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
540 if (selection_group->visible ()) {
541 /* resize the selection rect */
542 show_selection (_editor.get_selection().time);
545 _editor.override_visible_track_count ();
549 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
551 /* steal escape, tabs from GTK */
553 switch (ev->keyval) {
555 case GDK_ISO_Left_Tab:
563 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
565 TrackViewList::iterator i;
567 switch (ev->keyval) {
569 end_name_edit (RESPONSE_CANCEL);
572 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
573 * generates a different ev->keyval, rather than setting
576 case GDK_ISO_Left_Tab:
577 end_name_edit (RESPONSE_APPLY);
581 end_name_edit (RESPONSE_ACCEPT);
591 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
593 end_name_edit (RESPONSE_OK);
598 TimeAxisView::begin_name_edit ()
604 if (can_edit_name()) {
606 name_entry = manage (new Gtkmm2ext::FocusEntry);
608 name_entry->set_width_chars(8); // min width, entry expands
610 name_entry->set_name ("EditorTrackNameDisplay");
611 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
612 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
613 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
614 name_entry->set_text (name_label.get_text());
615 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
617 if (name_label.is_ancestor (name_hbox)) {
618 name_hbox.remove (name_label);
621 name_hbox.pack_end (*name_entry, true, true);
624 name_entry->select_region (0, -1);
625 name_entry->set_state (STATE_SELECTED);
626 name_entry->grab_focus ();
627 name_entry->start_editing (0);
632 TimeAxisView::end_name_edit (int response)
638 bool edit_next = false;
639 bool edit_prev = false;
642 case RESPONSE_CANCEL:
645 name_entry_changed ();
647 case RESPONSE_ACCEPT:
648 name_entry_changed ();
651 name_entry_changed ();
655 /* this will delete the name_entry. but it will also drop focus, which
656 * will cause another callback to this function, so set name_entry = 0
657 * first to ensure we don't double-remove etc. etc.
660 Gtk::Entry* tmp = name_entry;
662 name_hbox.remove (*tmp);
664 /* put the name label back */
666 name_hbox.pack_end (name_label);
671 TrackViewList const & allviews = _editor.get_track_views ();
672 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
674 if (i != allviews.end()) {
677 if (++i == allviews.end()) {
681 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
683 if (rtav && rtav->route()->record_enabled()) {
687 if (!(*i)->hidden()) {
694 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
695 _editor.ensure_time_axis_view_is_visible (**i, false);
696 (*i)->begin_name_edit ();
699 } else if (edit_prev) {
701 TrackViewList const & allviews = _editor.get_track_views ();
702 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
704 if (i != allviews.begin()) {
706 if (i == allviews.begin()) {
712 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
714 if (rtav && rtav->route()->record_enabled()) {
718 if (!(*i)->hidden()) {
725 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
726 _editor.ensure_time_axis_view_is_visible (**i, false);
727 (*i)->begin_name_edit ();
733 TimeAxisView::name_entry_changed ()
738 TimeAxisView::can_edit_name () const
744 TimeAxisView::conditionally_add_to_selection ()
746 Selection& s (_editor.get_selection ());
748 if (!s.selected (this)) {
749 _editor.set_selected_track (*this, Selection::Set);
754 TimeAxisView::popup_display_menu (guint32 when)
756 conditionally_add_to_selection ();
758 build_display_menu ();
759 display_menu->popup (1, when);
763 TimeAxisView::set_selected (bool yn)
765 if (yn == _selected) {
769 Selectable::set_selected (yn);
772 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
773 time_axis_frame.set_name ("MixerStripSelectedFrame");
774 controls_ebox.set_name (controls_base_selected_name);
775 controls_vbox.set_name (controls_base_selected_name);
776 time_axis_vbox.set_name (controls_base_selected_name);
778 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
779 time_axis_frame.set_name (controls_base_unselected_name);
780 controls_ebox.set_name (controls_base_unselected_name);
781 controls_vbox.set_name (controls_base_unselected_name);
782 time_axis_vbox.set_name (controls_base_unselected_name);
786 /* children will be set for the yn=true case. but when deselecting
787 the editor only has a list of top-level trackviews, so we
788 have to do this here.
791 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
792 (*i)->set_selected (false);
796 time_axis_frame.show();
801 TimeAxisView::build_display_menu ()
803 using namespace Menu_Helpers;
807 display_menu = new Menu;
808 display_menu->set_name ("ArdourContextMenu");
810 // Just let implementing classes define what goes into the manu
814 TimeAxisView::set_samples_per_pixel (double fpp)
816 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
817 (*i)->set_samples_per_pixel (fpp);
822 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
824 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
825 (*i)->show_timestretch (start, end, layers, layer);
830 TimeAxisView::hide_timestretch ()
832 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
833 (*i)->hide_timestretch ();
838 TimeAxisView::show_selection (TimeSelection& ts)
843 SelectionRect *rect; time_axis_frame.show();
846 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
847 (*i)->show_selection (ts);
850 if (selection_group->visible ()) {
851 while (!used_selection_rects.empty()) {
852 free_selection_rects.push_front (used_selection_rects.front());
853 used_selection_rects.pop_front();
854 free_selection_rects.front()->rect->hide();
855 free_selection_rects.front()->start_trim->hide();
856 free_selection_rects.front()->end_trim->hide();
858 selection_group->hide();
861 selection_group->show();
862 selection_group->raise_to_top();
864 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
865 framepos_t start, end;
870 cnt = end - start + 1;
872 rect = get_selection_rect ((*i).id);
874 x1 = _editor.sample_to_pixel (start);
875 x2 = _editor.sample_to_pixel (start + cnt - 1);
876 y2 = current_height() - 1;
878 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
880 // trim boxes are at the top for selections
883 rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
884 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
886 rect->start_trim->show();
887 rect->end_trim->show();
889 rect->start_trim->hide();
890 rect->end_trim->hide();
894 used_selection_rects.push_back (rect);
899 TimeAxisView::reshow_selection (TimeSelection& ts)
903 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
904 (*i)->show_selection (ts);
909 TimeAxisView::hide_selection ()
911 if (selection_group->visible ()) {
912 while (!used_selection_rects.empty()) {
913 free_selection_rects.push_front (used_selection_rects.front());
914 used_selection_rects.pop_front();
915 free_selection_rects.front()->rect->hide();
916 free_selection_rects.front()->start_trim->hide();
917 free_selection_rects.front()->end_trim->hide();
919 selection_group->hide();
922 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
923 (*i)->hide_selection ();
928 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
930 /* find the selection rect this is for. we have the item corresponding to one
934 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
935 if ((*i)->start_trim == item || (*i)->end_trim == item) {
937 /* make one trim handle be "above" the other so that if they overlap,
938 the top one is the one last used.
941 (*i)->rect->raise_to_top ();
942 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
943 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
951 TimeAxisView::get_selection_rect (uint32_t id)
955 /* check to see if we already have a visible rect for this particular selection ID */
957 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
958 if ((*i)->id == id) {
963 /* ditto for the free rect list */
965 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
966 if ((*i)->id == id) {
967 SelectionRect* ret = (*i);
968 free_selection_rects.erase (i);
973 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
975 if (free_selection_rects.empty()) {
977 rect = new SelectionRect;
979 rect->rect = new ArdourCanvas::Rectangle (selection_group);
980 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
981 rect->rect->set_outline (false);
982 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
984 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
985 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
986 rect->start_trim->set_outline (false);
987 rect->start_trim->set_fill (false);
989 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
990 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
991 rect->end_trim->set_outline (false);
992 rect->end_trim->set_fill (false);
994 free_selection_rects.push_front (rect);
996 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
997 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
998 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1001 rect = free_selection_rects.front();
1003 free_selection_rects.pop_front();
1007 struct null_deleter { void operator()(void const *) const {} };
1010 TimeAxisView::is_child (TimeAxisView* tav)
1012 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1016 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1018 children.push_back (child);
1022 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1024 Children::iterator i;
1026 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1031 /** Get selectable things within a given range.
1032 * @param start Start time in session frames.
1033 * @param end End time in session frames.
1034 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1035 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1036 * @param result Filled in with selectable things.
1039 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1045 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1051 TimeAxisView::add_ghost (RegionView* rv)
1053 GhostRegion* gr = rv->add_ghost (*this);
1056 ghosts.push_back(gr);
1061 TimeAxisView::remove_ghost (RegionView* rv)
1063 rv->remove_ghost_in (*this);
1067 TimeAxisView::erase_ghost (GhostRegion* gr)
1069 if (in_destructor) {
1073 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1082 TimeAxisView::touched (double top, double bot)
1084 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1085 y_position is the "origin" or "top" of the track.
1088 double mybot = _y_position + current_height();
1090 return ((_y_position <= bot && _y_position >= top) ||
1091 ((mybot <= bot) && (top < mybot)) ||
1092 (mybot >= bot && _y_position < top));
1096 TimeAxisView::set_parent (TimeAxisView& p)
1102 TimeAxisView::reset_height ()
1104 set_height (height);
1106 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1107 (*i)->set_height ((*i)->height);
1112 TimeAxisView::compute_heights ()
1114 // TODO this function should be re-evaluated when font-scaling changes (!)
1115 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1116 Gtk::Table one_row_table (1, 1);
1117 ArdourButton* test_button = manage (new ArdourButton);
1118 const int border_width = 2;
1119 const int frame_height = 2;
1120 extra_height = (2 * border_width) + frame_height;
1122 window.add (one_row_table);
1123 test_button->set_name ("mute button");
1124 test_button->set_text (_("M"));
1126 one_row_table.set_border_width (border_width);
1127 one_row_table.set_row_spacings (2);
1128 one_row_table.set_col_spacings (2);
1130 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1131 one_row_table.show_all ();
1133 Gtk::Requisition req(one_row_table.size_request ());
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 */