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>();
78 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::track_number_v_size_group = Glib::RefPtr<Gtk::SizeGroup>();
80 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
82 , controls_table (3, 3)
83 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
84 , _name_editing (false)
91 , in_destructor (false)
99 , _effective_height (0)
100 , _resize_drag_start (-1)
101 , _preresize_cursor (0)
102 , _have_preresize_cursor (false)
103 , _ebox_release_can_act (true)
105 if (!controls_meters_size_group) {
106 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
108 if (!track_number_v_size_group) {
109 track_number_v_size_group = SizeGroup::create (SIZE_GROUP_VERTICAL);
111 if (extra_height == 0) {
115 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (1.0, 0.0));
116 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
117 _canvas_display->hide(); // reveal as needed
119 _canvas_separator = new ArdourCanvas::Line(ed.get_trackview_group ());
120 CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
121 _canvas_separator->set_outline_color(RGBA_TO_UINT (0, 0, 0, 255));
122 _canvas_separator->set_outline_width(1.0);
123 _canvas_separator->hide();
125 selection_group = new ArdourCanvas::Container (_canvas_display);
126 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
127 selection_group->set_data (X_("timeselection"), (void *) 1);
128 selection_group->hide();
130 _ghost_group = new ArdourCanvas::Container (_canvas_display);
131 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
132 _ghost_group->lower_to_bottom();
133 _ghost_group->show();
135 name_label.set_name ("TrackLabel");
136 name_label.set_alignment (0.0, 0.5);
137 name_label.set_width_chars (12);
138 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
140 Gtk::Entry* an_entry = new Gtk::Entry;
141 Gtk::Requisition req;
142 an_entry->size_request (req);
143 name_label.set_size_request (-1, req.height);
144 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
147 name_hbox.pack_end (name_label, true, true);
149 // set min. track-header width if fader is not visible
150 name_hbox.set_size_request(name_width_px, -1);
155 controls_table.set_row_spacings (2);
156 controls_table.set_col_spacings (2);
157 controls_table.set_border_width (2);
159 if (ARDOUR::Profile->get_mixbus() ) {
160 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
162 controls_table.attach (name_hbox, 1, 2, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
164 controls_table.show_all ();
165 controls_table.set_no_show_all ();
167 controls_vbox.pack_start (controls_table, false, false);
168 controls_vbox.show ();
170 top_hbox.pack_start (controls_vbox, true, true);
173 controls_ebox.add (time_axis_hbox);
174 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
175 Gdk::BUTTON_RELEASE_MASK|
176 Gdk::POINTER_MOTION_MASK|
177 Gdk::ENTER_NOTIFY_MASK|
178 Gdk::LEAVE_NOTIFY_MASK|
180 controls_ebox.set_flags (CAN_FOCUS);
182 /* note that this handler connects *before* the default handler */
183 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
184 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
185 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
186 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
187 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
188 controls_ebox.show ();
190 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
191 time_axis_frame.add(top_hbox);
192 time_axis_frame.show();
194 HSeparator* separator = manage (new HSeparator());
195 separator->set_name("TrackSeparator");
196 separator->set_size_request(-1, 1);
199 time_axis_vbox.pack_start (*separator, false, false);
200 time_axis_vbox.pack_start (time_axis_frame, true, true);
201 time_axis_vbox.show();
202 time_axis_hbox.pack_start (time_axis_vbox, true, true);
203 time_axis_hbox.show();
205 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
207 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
210 TimeAxisView::~TimeAxisView()
212 in_destructor = true;
214 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
218 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
220 delete (*i)->start_trim;
221 delete (*i)->end_trim;
225 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
227 delete (*i)->start_trim;
228 delete (*i)->end_trim;
231 delete selection_group;
234 delete _canvas_display;
237 delete _canvas_separator;
238 _canvas_separator = 0;
247 TimeAxisView::hide ()
253 _canvas_display->hide ();
254 _canvas_separator->hide ();
256 if (control_parent) {
257 control_parent->remove (TOP_LEVEL_WIDGET);
264 /* now hide children */
266 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
270 /* if its hidden, it cannot be selected */
271 _editor.get_selection().remove (this);
272 /* and neither can its regions */
273 _editor.get_selection().remove_regions (this);
278 /** Display this TimeAxisView as the nth component of the parent box, at y.
280 * @param y y position.
281 * @param nth index for this TimeAxisView, increased if this view has children.
282 * @param parent parent component.
283 * @return height of this TimeAxisView.
286 TimeAxisView::show_at (double y, int& nth, VBox *parent)
288 if (control_parent) {
289 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
291 control_parent = parent;
292 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
293 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
298 if (_y_position != y) {
299 _canvas_separator->set (ArdourCanvas::Duple(0, y), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, y));
300 _canvas_display->set_y_position (y + 1);
304 _canvas_display->raise_to_top ();
305 _canvas_display->show ();
307 _canvas_separator->raise_to_top ();
308 _canvas_separator->show ();
312 _effective_height = current_height ();
314 /* now show relevant children */
316 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
317 if ((*i)->marked_for_display()) {
319 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
325 return _effective_height;
329 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
331 switch (ev->direction) {
333 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
334 /* See Editor::_stepping_axis_view for notes on this hack */
335 Editor& e = dynamic_cast<Editor&> (_editor);
336 if (!e.stepping_axis_view ()) {
337 e.set_stepping_axis_view (this);
339 e.stepping_axis_view()->step_height (false);
344 case GDK_SCROLL_DOWN:
345 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
346 /* See Editor::_stepping_axis_view for notes on this hack */
347 Editor& e = dynamic_cast<Editor&> (_editor);
348 if (!e.stepping_axis_view ()) {
349 e.set_stepping_axis_view (this);
351 e.stepping_axis_view()->step_height (true);
357 /* no handling for left/right, yet */
361 /* Just forward to the normal canvas scroll method. The coordinate
362 systems are different but since the canvas is always larger than the
363 track headers, and aligned with the trackview area, this will work.
365 In the not too distant future this layout is going away anyway and
366 headers will be on the canvas.
368 return _editor.canvas_scroll_event (ev, false);
372 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
374 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
375 /* see if it is inside the name label */
376 if (name_label.is_ancestor (controls_ebox)) {
379 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
380 Gtk::Allocation a = name_label.get_allocation ();
381 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
383 _ebox_release_can_act = false;
390 _ebox_release_can_act = true;
392 if (maybe_set_cursor (event->y) > 0) {
393 _resize_drag_start = event->y_root;
400 TimeAxisView::idle_resize (uint32_t h)
407 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
409 if (_resize_drag_start >= 0) {
411 /* (ab)use the DragManager to do autoscrolling - basically we
412 * are pretending that the drag is taking place over the canvas
413 * (which perhaps in the glorious future, when track headers
414 * and the canvas are unified, will actually be true.)
417 _editor.maybe_autoscroll (false, true, true);
419 /* now schedule the actual TAV resize */
420 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
421 _editor.add_to_idle_resize (this, delta);
422 _resize_drag_start = ev->y_root;
424 /* not dragging but ... */
425 maybe_set_cursor (ev->y);
428 gdk_event_request_motions(ev);
433 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
435 if (_have_preresize_cursor) {
436 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
437 _have_preresize_cursor = false;
443 TimeAxisView::maybe_set_cursor (int y)
445 /* XXX no Gtkmm Gdk::Window::get_cursor() */
446 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
448 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
450 /* y-coordinate in lower 25% */
452 if (!_have_preresize_cursor) {
453 _preresize_cursor = gdk_window_get_cursor (win->gobj());
454 _have_preresize_cursor = true;
455 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
460 } else if (_have_preresize_cursor) {
461 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
462 _have_preresize_cursor = false;
471 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
473 if (_resize_drag_start >= 0) {
474 if (_have_preresize_cursor) {
475 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
476 _preresize_cursor = 0;
477 _have_preresize_cursor = false;
479 _editor.stop_canvas_autoscroll ();
480 _resize_drag_start = -1;
483 if (!_ebox_release_can_act) {
487 switch (ev->button) {
489 selection_click (ev);
493 popup_display_menu (ev->time);
501 TimeAxisView::selection_click (GdkEventButton* ev)
503 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
504 _editor.set_selected_track (*this, op, false);
508 /** Steps through the defined heights for this TrackView.
509 * @param coarser true if stepping should decrease in size, otherwise false.
512 TimeAxisView::step_height (bool coarser)
514 static const uint32_t step = 25;
518 if (height <= preset_height (HeightSmall)) {
520 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
521 set_height_enum (HeightSmall);
523 set_height (height - step);
528 if (height <= preset_height(HeightSmall)) {
529 set_height_enum (HeightNormal);
531 set_height (height + step);
538 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
540 if (apply_to_selection) {
541 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
543 set_height (preset_height (h));
548 TimeAxisView::set_height (uint32_t h)
550 if (h < preset_height (HeightSmall)) {
551 h = preset_height (HeightSmall);
554 TOP_LEVEL_WIDGET.property_height_request () = h;
558 snprintf (buf, sizeof (buf), "%u", height);
559 set_gui_property ("height", buf);
561 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
565 if (selection_group->visible ()) {
566 /* resize the selection rect */
567 show_selection (_editor.get_selection().time);
570 _editor.override_visible_track_count ();
574 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
576 /* steal escape, tabs from GTK */
578 switch (ev->keyval) {
580 case GDK_ISO_Left_Tab:
588 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
590 TrackViewList::iterator i;
592 switch (ev->keyval) {
594 end_name_edit (RESPONSE_CANCEL);
597 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
598 * generates a different ev->keyval, rather than setting
601 case GDK_ISO_Left_Tab:
602 end_name_edit (RESPONSE_APPLY);
606 end_name_edit (RESPONSE_ACCEPT);
616 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
618 end_name_edit (RESPONSE_OK);
623 TimeAxisView::begin_name_edit ()
629 if (can_edit_name()) {
631 name_entry = manage (new Gtkmm2ext::FocusEntry);
633 name_entry->set_width_chars(8); // min width, entry expands
635 name_entry->set_name ("EditorTrackNameDisplay");
636 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
637 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
638 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
639 name_entry->set_text (name_label.get_text());
640 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
642 if (name_label.is_ancestor (name_hbox)) {
643 name_hbox.remove (name_label);
646 name_hbox.pack_end (*name_entry, true, true);
649 name_entry->select_region (0, -1);
650 name_entry->set_state (STATE_SELECTED);
651 name_entry->grab_focus ();
652 name_entry->start_editing (0);
657 TimeAxisView::end_name_edit (int response)
663 bool edit_next = false;
664 bool edit_prev = false;
667 case RESPONSE_CANCEL:
670 name_entry_changed ();
672 case RESPONSE_ACCEPT:
673 name_entry_changed ();
676 name_entry_changed ();
680 /* this will delete the name_entry. but it will also drop focus, which
681 * will cause another callback to this function, so set name_entry = 0
682 * first to ensure we don't double-remove etc. etc.
685 Gtk::Entry* tmp = name_entry;
687 name_hbox.remove (*tmp);
689 /* put the name label back */
691 name_hbox.pack_end (name_label);
696 TrackViewList const & allviews = _editor.get_track_views ();
697 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
699 if (i != allviews.end()) {
702 if (++i == allviews.end()) {
706 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
708 if (rtav && rtav->route()->record_enabled()) {
712 if (!(*i)->hidden()) {
719 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
720 _editor.ensure_time_axis_view_is_visible (**i, false);
721 (*i)->begin_name_edit ();
724 } else if (edit_prev) {
726 TrackViewList const & allviews = _editor.get_track_views ();
727 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
729 if (i != allviews.begin()) {
731 if (i == allviews.begin()) {
737 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
739 if (rtav && rtav->route()->record_enabled()) {
743 if (!(*i)->hidden()) {
750 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
751 _editor.ensure_time_axis_view_is_visible (**i, false);
752 (*i)->begin_name_edit ();
758 TimeAxisView::name_entry_changed ()
763 TimeAxisView::can_edit_name () const
769 TimeAxisView::conditionally_add_to_selection ()
771 Selection& s (_editor.get_selection ());
773 if (!s.selected (this)) {
774 _editor.set_selected_track (*this, Selection::Set);
779 TimeAxisView::popup_display_menu (guint32 when)
781 conditionally_add_to_selection ();
783 build_display_menu ();
784 display_menu->popup (1, when);
788 TimeAxisView::set_selected (bool yn)
790 if (can_edit_name() && name_entry && name_entry->get_visible()) {
791 end_name_edit (RESPONSE_CANCEL);
794 if (yn == _selected) {
798 Selectable::set_selected (yn);
801 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
802 time_axis_frame.set_name ("MixerStripSelectedFrame");
803 controls_ebox.set_name (controls_base_selected_name);
804 controls_vbox.set_name (controls_base_selected_name);
805 time_axis_vbox.set_name (controls_base_selected_name);
807 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
808 time_axis_frame.set_name (controls_base_unselected_name);
809 controls_ebox.set_name (controls_base_unselected_name);
810 controls_vbox.set_name (controls_base_unselected_name);
811 time_axis_vbox.set_name (controls_base_unselected_name);
815 /* children will be set for the yn=true case. but when deselecting
816 the editor only has a list of top-level trackviews, so we
817 have to do this here.
820 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
821 (*i)->set_selected (false);
825 time_axis_frame.show();
830 TimeAxisView::build_display_menu ()
832 using namespace Menu_Helpers;
836 display_menu = new Menu;
837 display_menu->set_name ("ArdourContextMenu");
839 // Just let implementing classes define what goes into the manu
843 TimeAxisView::set_samples_per_pixel (double fpp)
845 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
846 (*i)->set_samples_per_pixel (fpp);
851 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
853 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
854 (*i)->show_timestretch (start, end, layers, layer);
859 TimeAxisView::hide_timestretch ()
861 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
862 (*i)->hide_timestretch ();
867 TimeAxisView::show_selection (TimeSelection& ts)
872 SelectionRect *rect; time_axis_frame.show();
875 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
876 (*i)->show_selection (ts);
879 if (selection_group->visible ()) {
880 while (!used_selection_rects.empty()) {
881 free_selection_rects.push_front (used_selection_rects.front());
882 used_selection_rects.pop_front();
883 free_selection_rects.front()->rect->hide();
884 free_selection_rects.front()->start_trim->hide();
885 free_selection_rects.front()->end_trim->hide();
887 selection_group->hide();
890 selection_group->show();
891 selection_group->raise_to_top();
893 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
894 framepos_t start, end;
899 cnt = end - start + 1;
901 rect = get_selection_rect ((*i).id);
903 x1 = _editor.sample_to_pixel (start);
904 x2 = _editor.sample_to_pixel (start + cnt - 1);
905 y2 = current_height() - 1;
907 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
909 // trim boxes are at the top for selections
912 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
913 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
915 rect->start_trim->show();
916 rect->end_trim->show();
918 rect->start_trim->hide();
919 rect->end_trim->hide();
923 used_selection_rects.push_back (rect);
928 TimeAxisView::reshow_selection (TimeSelection& ts)
932 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
933 (*i)->show_selection (ts);
938 TimeAxisView::hide_selection ()
940 if (selection_group->visible ()) {
941 while (!used_selection_rects.empty()) {
942 free_selection_rects.push_front (used_selection_rects.front());
943 used_selection_rects.pop_front();
944 free_selection_rects.front()->rect->hide();
945 free_selection_rects.front()->start_trim->hide();
946 free_selection_rects.front()->end_trim->hide();
948 selection_group->hide();
951 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
952 (*i)->hide_selection ();
957 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
959 /* find the selection rect this is for. we have the item corresponding to one
963 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
964 if ((*i)->start_trim == item || (*i)->end_trim == item) {
966 /* make one trim handle be "above" the other so that if they overlap,
967 the top one is the one last used.
970 (*i)->rect->raise_to_top ();
971 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
972 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
980 TimeAxisView::get_selection_rect (uint32_t id)
984 /* check to see if we already have a visible rect for this particular selection ID */
986 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
987 if ((*i)->id == id) {
992 /* ditto for the free rect list */
994 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
995 if ((*i)->id == id) {
996 SelectionRect* ret = (*i);
997 free_selection_rects.erase (i);
1002 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
1004 if (free_selection_rects.empty()) {
1006 rect = new SelectionRect;
1008 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1009 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1010 rect->rect->set_outline (false);
1011 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1013 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1014 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1015 rect->start_trim->set_outline (false);
1016 rect->start_trim->set_fill (false);
1018 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1019 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1020 rect->end_trim->set_outline (false);
1021 rect->end_trim->set_fill (false);
1023 free_selection_rects.push_front (rect);
1025 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1026 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1027 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1030 rect = free_selection_rects.front();
1032 free_selection_rects.pop_front();
1036 struct null_deleter { void operator()(void const *) const {} };
1039 TimeAxisView::is_child (TimeAxisView* tav)
1041 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1045 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1047 children.push_back (child);
1051 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1053 Children::iterator i;
1055 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1060 /** Get selectable things within a given range.
1061 * @param start Start time in session frames.
1062 * @param end End time in session frames.
1063 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1064 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1065 * @param result Filled in with selectable things.
1068 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1074 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1080 TimeAxisView::add_ghost (RegionView* rv)
1082 GhostRegion* gr = rv->add_ghost (*this);
1085 ghosts.push_back(gr);
1090 TimeAxisView::remove_ghost (RegionView* rv)
1092 rv->remove_ghost_in (*this);
1096 TimeAxisView::erase_ghost (GhostRegion* gr)
1098 if (in_destructor) {
1102 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1111 TimeAxisView::touched (double top, double bot)
1113 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1114 y_position is the "origin" or "top" of the track.
1117 double mybot = _y_position + current_height();
1119 return ((_y_position <= bot && _y_position >= top) ||
1120 ((mybot <= bot) && (top < mybot)) ||
1121 (mybot >= bot && _y_position < top));
1125 TimeAxisView::set_parent (TimeAxisView& p)
1131 TimeAxisView::reset_height ()
1133 set_height (height);
1135 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1136 (*i)->set_height ((*i)->height);
1141 TimeAxisView::compute_heights ()
1143 // TODO this function should be re-evaluated when font-scaling changes (!)
1144 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1145 Gtk::Table one_row_table (1, 1);
1146 ArdourButton* test_button = manage (new ArdourButton);
1147 const int border_width = 2;
1148 const int frame_height = 2;
1149 extra_height = (2 * border_width) + frame_height;
1151 window.add (one_row_table);
1152 test_button->set_name ("mute button");
1153 test_button->set_text (_("M"));
1155 one_row_table.set_border_width (border_width);
1156 one_row_table.set_row_spacings (2);
1157 one_row_table.set_col_spacings (2);
1159 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1160 one_row_table.show_all ();
1162 Gtk::Requisition req(one_row_table.size_request ());
1163 button_height = req.height;
1167 TimeAxisView::color_handler ()
1169 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1173 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1175 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1176 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1178 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1179 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1181 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1182 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1185 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1187 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1188 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1190 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1191 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1193 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1194 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1198 /** @return Pair: TimeAxisView, layer index.
1199 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1200 * does. @param y is an offset from the top of the trackview area.
1202 * If the covering object is a child axis, then the child is returned.
1203 * TimeAxisView is 0 otherwise.
1205 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1206 * and is in stacked or expanded * region display mode, otherwise 0.
1208 std::pair<TimeAxisView*, double>
1209 TimeAxisView::covers_y_position (double y) const
1212 return std::make_pair ((TimeAxisView *) 0, 0);
1215 if (_y_position <= y && y < (_y_position + height)) {
1217 /* work out the layer index if appropriate */
1219 switch (layer_display ()) {
1225 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1226 /* clamp to max layers to be on the safe side; sometimes the above calculation
1227 returns a too-high value */
1228 if (l >= view()->layers ()) {
1229 l = view()->layers() - 1;
1235 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1237 if (l >= (view()->layers() - 0.5)) {
1238 l = view()->layers() - 0.5;
1244 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1247 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1249 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1255 return std::make_pair ((TimeAxisView *) 0, 0);
1259 TimeAxisView::covered_by_y_range (double y0, double y1) const
1265 /* if either the top or bottom of the axisview is in the vertical
1266 * range, we cover it.
1269 if ((y0 < _y_position && y1 < _y_position) ||
1270 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1274 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1275 if ((*i)->covered_by_y_range (y0, y1)) {
1284 TimeAxisView::preset_height (Height h)
1288 return (button_height * 2) + extra_height + 260;
1290 return (button_height * 2) + extra_height + 160;
1292 return (button_height * 2) + extra_height + 60;
1294 return (button_height * 2) + extra_height + 10;
1296 return button_height + extra_height;
1303 /** @return Child time axis views that are not hidden */
1304 TimeAxisView::Children
1305 TimeAxisView::get_child_list ()
1309 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1310 if (!(*i)->hidden()) {
1319 TimeAxisView::build_size_menu ()
1321 if (_size_menu && _size_menu->gobj ()) {
1327 using namespace Menu_Helpers;
1329 _size_menu = new Menu;
1330 _size_menu->set_name ("ArdourContextMenu");
1331 MenuList& items = _size_menu->items();
1333 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1334 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1335 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1336 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1337 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1341 TimeAxisView::reset_visual_state ()
1343 /* this method is not required to trigger a global redraw */
1345 string str = gui_property ("height");
1348 set_height (atoi (str));
1350 set_height (preset_height (HeightNormal));
1355 TrackViewList::filter_to_unique_playlists ()
1357 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1360 for (iterator i = begin(); i != end(); ++i) {
1361 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1363 /* not a route: include it anyway */
1366 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1368 if (playlists.insert (t->playlist()).second) {
1369 /* playlist not seen yet */
1373 /* not a track: include it anyway */