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>();
76 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::track_number_v_size_group = Glib::RefPtr<Gtk::SizeGroup>();
78 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
80 , controls_table (3, 3)
81 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
82 , _name_editing (false)
89 , in_destructor (false)
97 , _effective_height (0)
98 , _resize_drag_start (-1)
99 , _preresize_cursor (0)
100 , _have_preresize_cursor (false)
101 , _ebox_release_can_act (true)
103 if (!controls_meters_size_group) {
104 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
106 if (!track_number_v_size_group) {
107 track_number_v_size_group = SizeGroup::create (SIZE_GROUP_VERTICAL);
109 if (extra_height == 0) {
113 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (1.0, 0.0));
114 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
115 _canvas_display->hide(); // reveal as needed
117 _canvas_separator = new ArdourCanvas::Line(ed.get_trackview_group ());
118 CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
119 _canvas_separator->set_outline_color(RGBA_TO_UINT (0, 0, 0, 255));
120 _canvas_separator->set_outline_width(1.0);
121 _canvas_separator->hide();
123 selection_group = new ArdourCanvas::Container (_canvas_display);
124 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
125 selection_group->set_data (X_("timeselection"), (void *) 1);
126 selection_group->hide();
128 _ghost_group = new ArdourCanvas::Container (_canvas_display);
129 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
130 _ghost_group->lower_to_bottom();
131 _ghost_group->show();
133 name_label.set_name ("TrackLabel");
134 name_label.set_alignment (0.0, 0.5);
135 name_label.set_width_chars (12);
136 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
138 Gtk::Entry* an_entry = new Gtk::Entry;
139 Gtk::Requisition req;
140 an_entry->size_request (req);
141 name_label.set_size_request (-1, req.height);
142 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
145 name_hbox.pack_end (name_label, true, true);
147 // set min. track-header width if fader is not visible
148 name_hbox.set_size_request(name_width_px, -1);
153 controls_table.set_row_spacings (2);
154 controls_table.set_col_spacings (2);
155 controls_table.set_border_width (2);
157 if (ARDOUR::Profile->get_mixbus() ) {
158 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
160 controls_table.attach (name_hbox, 1, 2, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
162 controls_table.show_all ();
163 controls_table.set_no_show_all ();
165 controls_vbox.pack_start (controls_table, false, false);
166 controls_vbox.show ();
168 top_hbox.pack_start (controls_vbox, true, true);
171 controls_ebox.add (top_hbox);
172 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
173 Gdk::BUTTON_RELEASE_MASK|
174 Gdk::POINTER_MOTION_MASK|
175 Gdk::ENTER_NOTIFY_MASK|
176 Gdk::LEAVE_NOTIFY_MASK|
178 controls_ebox.set_flags (CAN_FOCUS);
180 /* note that this handler connects *before* the default handler */
181 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
182 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
183 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
184 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
185 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
186 controls_ebox.show ();
188 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
189 time_axis_frame.add(controls_ebox);
190 time_axis_frame.show();
192 HSeparator* separator = manage (new HSeparator());
193 separator->set_name("TrackSeparator");
194 separator->set_size_request(-1, 1);
197 time_axis_vbox.pack_start (*separator, false, false);
198 time_axis_vbox.pack_start (time_axis_frame, true, true);
199 time_axis_vbox.show();
200 time_axis_hbox.pack_start (time_axis_vbox, true, true);
201 time_axis_hbox.show();
203 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
205 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
208 TimeAxisView::~TimeAxisView()
210 in_destructor = true;
212 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
216 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
218 delete (*i)->start_trim;
219 delete (*i)->end_trim;
223 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
225 delete (*i)->start_trim;
226 delete (*i)->end_trim;
229 delete selection_group;
232 delete _canvas_display;
235 delete _canvas_separator;
236 _canvas_separator = 0;
245 TimeAxisView::hide ()
251 _canvas_display->hide ();
252 _canvas_separator->hide ();
254 if (control_parent) {
255 control_parent->remove (time_axis_hbox);
262 /* now hide children */
264 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
268 /* if its hidden, it cannot be selected */
269 _editor.get_selection().remove (this);
270 /* and neither can its regions */
271 _editor.get_selection().remove_regions (this);
276 /** Display this TimeAxisView as the nth component of the parent box, at y.
278 * @param y y position.
279 * @param nth index for this TimeAxisView, increased if this view has children.
280 * @param parent parent component.
281 * @return height of this TimeAxisView.
284 TimeAxisView::show_at (double y, int& nth, VBox *parent)
286 if (control_parent) {
287 control_parent->reorder_child (time_axis_hbox, nth);
289 control_parent = parent;
290 parent->pack_start (time_axis_hbox, false, false);
291 parent->reorder_child (time_axis_hbox, nth);
296 if (_y_position != y) {
297 _canvas_separator->set (ArdourCanvas::Duple(0, y), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, y));
298 _canvas_display->set_y_position (y + 1);
302 _canvas_display->raise_to_top ();
303 _canvas_display->show ();
305 _canvas_separator->raise_to_top ();
306 _canvas_separator->show ();
310 _effective_height = current_height ();
312 /* now show relevant children */
314 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
315 if ((*i)->marked_for_display()) {
317 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
323 return _effective_height;
327 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
329 switch (ev->direction) {
331 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
332 /* See Editor::_stepping_axis_view for notes on this hack */
333 Editor& e = dynamic_cast<Editor&> (_editor);
334 if (!e.stepping_axis_view ()) {
335 e.set_stepping_axis_view (this);
337 e.stepping_axis_view()->step_height (false);
342 case GDK_SCROLL_DOWN:
343 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
344 /* See Editor::_stepping_axis_view for notes on this hack */
345 Editor& e = dynamic_cast<Editor&> (_editor);
346 if (!e.stepping_axis_view ()) {
347 e.set_stepping_axis_view (this);
349 e.stepping_axis_view()->step_height (true);
355 /* no handling for left/right, yet */
359 /* Just forward to the normal canvas scroll method. The coordinate
360 systems are different but since the canvas is always larger than the
361 track headers, and aligned with the trackview area, this will work.
363 In the not too distant future this layout is going away anyway and
364 headers will be on the canvas.
366 return _editor.canvas_scroll_event (ev, false);
370 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
372 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
373 /* see if it is inside the name label */
374 if (name_label.is_ancestor (controls_ebox)) {
377 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
378 Gtk::Allocation a = name_label.get_allocation ();
379 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
381 _ebox_release_can_act = false;
388 _ebox_release_can_act = true;
390 if (maybe_set_cursor (event->y) > 0) {
391 _resize_drag_start = event->y_root;
398 TimeAxisView::idle_resize (uint32_t h)
405 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
407 if (_resize_drag_start >= 0) {
409 /* (ab)use the DragManager to do autoscrolling - basically we
410 * are pretending that the drag is taking place over the canvas
411 * (which perhaps in the glorious future, when track headers
412 * and the canvas are unified, will actually be true.)
415 _editor.maybe_autoscroll (false, true, true);
417 /* now schedule the actual TAV resize */
418 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
419 _editor.add_to_idle_resize (this, delta);
420 _resize_drag_start = ev->y_root;
422 /* not dragging but ... */
423 maybe_set_cursor (ev->y);
426 gdk_event_request_motions(ev);
431 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
433 if (_have_preresize_cursor) {
434 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
435 _have_preresize_cursor = false;
441 TimeAxisView::maybe_set_cursor (int y)
443 /* XXX no Gtkmm Gdk::Window::get_cursor() */
444 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
446 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
448 /* y-coordinate in lower 25% */
450 if (!_have_preresize_cursor) {
451 _preresize_cursor = gdk_window_get_cursor (win->gobj());
452 _have_preresize_cursor = true;
453 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
458 } else if (_have_preresize_cursor) {
459 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
460 _have_preresize_cursor = false;
469 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
471 if (_resize_drag_start >= 0) {
472 if (_have_preresize_cursor) {
473 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
474 _preresize_cursor = 0;
475 _have_preresize_cursor = false;
477 _editor.stop_canvas_autoscroll ();
478 _resize_drag_start = -1;
481 if (!_ebox_release_can_act) {
485 switch (ev->button) {
487 selection_click (ev);
491 popup_display_menu (ev->time);
499 TimeAxisView::selection_click (GdkEventButton* ev)
501 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
502 _editor.set_selected_track (*this, op, false);
506 /** Steps through the defined heights for this TrackView.
507 * @param coarser true if stepping should decrease in size, otherwise false.
510 TimeAxisView::step_height (bool coarser)
512 static const uint32_t step = 25;
516 if (height <= preset_height (HeightSmall)) {
518 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
519 set_height_enum (HeightSmall);
521 set_height (height - step);
526 if (height <= preset_height(HeightSmall)) {
527 set_height_enum (HeightNormal);
529 set_height (height + step);
536 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
538 if (apply_to_selection) {
539 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
541 set_height (preset_height (h));
546 TimeAxisView::set_height (uint32_t h)
548 if (h < preset_height (HeightSmall)) {
549 h = preset_height (HeightSmall);
552 time_axis_hbox.property_height_request () = h;
556 snprintf (buf, sizeof (buf), "%u", height);
557 set_gui_property ("height", buf);
559 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
563 if (selection_group->visible ()) {
564 /* resize the selection rect */
565 show_selection (_editor.get_selection().time);
568 _editor.override_visible_track_count ();
572 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
574 /* steal escape, tabs from GTK */
576 switch (ev->keyval) {
578 case GDK_ISO_Left_Tab:
586 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
588 TrackViewList::iterator i;
590 switch (ev->keyval) {
592 end_name_edit (RESPONSE_CANCEL);
595 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
596 * generates a different ev->keyval, rather than setting
599 case GDK_ISO_Left_Tab:
600 end_name_edit (RESPONSE_APPLY);
604 end_name_edit (RESPONSE_ACCEPT);
614 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
616 end_name_edit (RESPONSE_OK);
621 TimeAxisView::begin_name_edit ()
627 if (can_edit_name()) {
629 name_entry = manage (new Gtkmm2ext::FocusEntry);
631 name_entry->set_width_chars(8); // min width, entry expands
633 name_entry->set_name ("EditorTrackNameDisplay");
634 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
635 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
636 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
637 name_entry->set_text (name_label.get_text());
638 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
640 if (name_label.is_ancestor (name_hbox)) {
641 name_hbox.remove (name_label);
644 name_hbox.pack_end (*name_entry, true, true);
647 name_entry->select_region (0, -1);
648 name_entry->set_state (STATE_SELECTED);
649 name_entry->grab_focus ();
650 name_entry->start_editing (0);
655 TimeAxisView::end_name_edit (int response)
661 bool edit_next = false;
662 bool edit_prev = false;
665 case RESPONSE_CANCEL:
668 name_entry_changed ();
670 case RESPONSE_ACCEPT:
671 name_entry_changed ();
674 name_entry_changed ();
678 /* this will delete the name_entry. but it will also drop focus, which
679 * will cause another callback to this function, so set name_entry = 0
680 * first to ensure we don't double-remove etc. etc.
683 Gtk::Entry* tmp = name_entry;
685 name_hbox.remove (*tmp);
687 /* put the name label back */
689 name_hbox.pack_end (name_label);
694 TrackViewList const & allviews = _editor.get_track_views ();
695 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
697 if (i != allviews.end()) {
700 if (++i == allviews.end()) {
704 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
706 if (rtav && rtav->route()->record_enabled()) {
710 if (!(*i)->hidden()) {
717 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
718 _editor.ensure_time_axis_view_is_visible (**i, false);
719 (*i)->begin_name_edit ();
722 } else if (edit_prev) {
724 TrackViewList const & allviews = _editor.get_track_views ();
725 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
727 if (i != allviews.begin()) {
729 if (i == allviews.begin()) {
735 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
737 if (rtav && rtav->route()->record_enabled()) {
741 if (!(*i)->hidden()) {
748 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
749 _editor.ensure_time_axis_view_is_visible (**i, false);
750 (*i)->begin_name_edit ();
756 TimeAxisView::name_entry_changed ()
761 TimeAxisView::can_edit_name () const
767 TimeAxisView::conditionally_add_to_selection ()
769 Selection& s (_editor.get_selection ());
771 if (!s.selected (this)) {
772 _editor.set_selected_track (*this, Selection::Set);
777 TimeAxisView::popup_display_menu (guint32 when)
779 conditionally_add_to_selection ();
781 build_display_menu ();
782 display_menu->popup (1, when);
786 TimeAxisView::set_selected (bool yn)
788 if (can_edit_name() && name_entry && name_entry->get_visible()) {
789 end_name_edit (RESPONSE_CANCEL);
792 if (yn == _selected) {
796 Selectable::set_selected (yn);
799 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
800 time_axis_frame.set_name ("MixerStripSelectedFrame");
801 controls_ebox.set_name (controls_base_selected_name);
802 controls_vbox.set_name (controls_base_selected_name);
803 time_axis_vbox.set_name (controls_base_selected_name);
805 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
806 time_axis_frame.set_name (controls_base_unselected_name);
807 controls_ebox.set_name (controls_base_unselected_name);
808 controls_vbox.set_name (controls_base_unselected_name);
809 time_axis_vbox.set_name (controls_base_unselected_name);
813 /* children will be set for the yn=true case. but when deselecting
814 the editor only has a list of top-level trackviews, so we
815 have to do this here.
818 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
819 (*i)->set_selected (false);
823 time_axis_frame.show();
828 TimeAxisView::build_display_menu ()
830 using namespace Menu_Helpers;
834 display_menu = new Menu;
835 display_menu->set_name ("ArdourContextMenu");
837 // Just let implementing classes define what goes into the manu
841 TimeAxisView::set_samples_per_pixel (double fpp)
843 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
844 (*i)->set_samples_per_pixel (fpp);
849 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
851 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
852 (*i)->show_timestretch (start, end, layers, layer);
857 TimeAxisView::hide_timestretch ()
859 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
860 (*i)->hide_timestretch ();
865 TimeAxisView::show_selection (TimeSelection& ts)
870 SelectionRect *rect; time_axis_frame.show();
873 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
874 (*i)->show_selection (ts);
877 if (selection_group->visible ()) {
878 while (!used_selection_rects.empty()) {
879 free_selection_rects.push_front (used_selection_rects.front());
880 used_selection_rects.pop_front();
881 free_selection_rects.front()->rect->hide();
882 free_selection_rects.front()->start_trim->hide();
883 free_selection_rects.front()->end_trim->hide();
885 selection_group->hide();
888 selection_group->show();
889 selection_group->raise_to_top();
891 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
892 framepos_t start, end;
897 cnt = end - start + 1;
899 rect = get_selection_rect ((*i).id);
901 x1 = _editor.sample_to_pixel (start);
902 x2 = _editor.sample_to_pixel (start + cnt - 1);
903 y2 = current_height() - 1;
905 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
907 // trim boxes are at the top for selections
910 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
911 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
913 rect->start_trim->show();
914 rect->end_trim->show();
916 rect->start_trim->hide();
917 rect->end_trim->hide();
921 used_selection_rects.push_back (rect);
926 TimeAxisView::reshow_selection (TimeSelection& ts)
930 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
931 (*i)->show_selection (ts);
936 TimeAxisView::hide_selection ()
938 if (selection_group->visible ()) {
939 while (!used_selection_rects.empty()) {
940 free_selection_rects.push_front (used_selection_rects.front());
941 used_selection_rects.pop_front();
942 free_selection_rects.front()->rect->hide();
943 free_selection_rects.front()->start_trim->hide();
944 free_selection_rects.front()->end_trim->hide();
946 selection_group->hide();
949 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
950 (*i)->hide_selection ();
955 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
957 /* find the selection rect this is for. we have the item corresponding to one
961 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
962 if ((*i)->start_trim == item || (*i)->end_trim == item) {
964 /* make one trim handle be "above" the other so that if they overlap,
965 the top one is the one last used.
968 (*i)->rect->raise_to_top ();
969 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
970 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
978 TimeAxisView::get_selection_rect (uint32_t id)
982 /* check to see if we already have a visible rect for this particular selection ID */
984 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
985 if ((*i)->id == id) {
990 /* ditto for the free rect list */
992 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
993 if ((*i)->id == id) {
994 SelectionRect* ret = (*i);
995 free_selection_rects.erase (i);
1000 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
1002 if (free_selection_rects.empty()) {
1004 rect = new SelectionRect;
1006 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1007 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1008 rect->rect->set_outline (false);
1009 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1011 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1012 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1013 rect->start_trim->set_outline (false);
1014 rect->start_trim->set_fill (false);
1016 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1017 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1018 rect->end_trim->set_outline (false);
1019 rect->end_trim->set_fill (false);
1021 free_selection_rects.push_front (rect);
1023 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1024 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1025 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1028 rect = free_selection_rects.front();
1030 free_selection_rects.pop_front();
1034 struct null_deleter { void operator()(void const *) const {} };
1037 TimeAxisView::is_child (TimeAxisView* tav)
1039 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1043 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1045 children.push_back (child);
1049 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1051 Children::iterator i;
1053 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1058 /** Get selectable things within a given range.
1059 * @param start Start time in session frames.
1060 * @param end End time in session frames.
1061 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1062 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1063 * @param result Filled in with selectable things.
1066 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1072 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1078 TimeAxisView::add_ghost (RegionView* rv)
1080 GhostRegion* gr = rv->add_ghost (*this);
1083 ghosts.push_back(gr);
1088 TimeAxisView::remove_ghost (RegionView* rv)
1090 rv->remove_ghost_in (*this);
1094 TimeAxisView::erase_ghost (GhostRegion* gr)
1096 if (in_destructor) {
1100 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1109 TimeAxisView::touched (double top, double bot)
1111 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1112 y_position is the "origin" or "top" of the track.
1115 double mybot = _y_position + current_height();
1117 return ((_y_position <= bot && _y_position >= top) ||
1118 ((mybot <= bot) && (top < mybot)) ||
1119 (mybot >= bot && _y_position < top));
1123 TimeAxisView::set_parent (TimeAxisView& p)
1129 TimeAxisView::reset_height ()
1131 set_height (height);
1133 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1134 (*i)->set_height ((*i)->height);
1139 TimeAxisView::compute_heights ()
1141 // TODO this function should be re-evaluated when font-scaling changes (!)
1142 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1143 Gtk::Table one_row_table (1, 1);
1144 ArdourButton* test_button = manage (new ArdourButton);
1145 const int border_width = 2;
1146 const int frame_height = 2;
1147 extra_height = (2 * border_width) + frame_height;
1149 window.add (one_row_table);
1150 test_button->set_name ("mute button");
1151 test_button->set_text (_("M"));
1153 one_row_table.set_border_width (border_width);
1154 one_row_table.set_row_spacings (2);
1155 one_row_table.set_col_spacings (2);
1157 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1158 one_row_table.show_all ();
1160 Gtk::Requisition req(one_row_table.size_request ());
1161 button_height = req.height;
1165 TimeAxisView::color_handler ()
1167 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1171 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1173 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1174 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1176 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1177 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1179 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1180 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1183 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1185 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1186 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1188 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1189 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1191 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1192 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1196 /** @return Pair: TimeAxisView, layer index.
1197 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1198 * does. @param y is an offset from the top of the trackview area.
1200 * If the covering object is a child axis, then the child is returned.
1201 * TimeAxisView is 0 otherwise.
1203 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1204 * and is in stacked or expanded * region display mode, otherwise 0.
1206 std::pair<TimeAxisView*, double>
1207 TimeAxisView::covers_y_position (double y) const
1210 return std::make_pair ((TimeAxisView *) 0, 0);
1213 if (_y_position <= y && y < (_y_position + height)) {
1215 /* work out the layer index if appropriate */
1217 switch (layer_display ()) {
1223 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1224 /* clamp to max layers to be on the safe side; sometimes the above calculation
1225 returns a too-high value */
1226 if (l >= view()->layers ()) {
1227 l = view()->layers() - 1;
1233 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1235 if (l >= (view()->layers() - 0.5)) {
1236 l = view()->layers() - 0.5;
1242 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1245 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1247 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1253 return std::make_pair ((TimeAxisView *) 0, 0);
1257 TimeAxisView::covered_by_y_range (double y0, double y1) const
1263 /* if either the top or bottom of the axisview is in the vertical
1264 * range, we cover it.
1267 if ((y0 < _y_position && y1 < _y_position) ||
1268 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1272 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1273 if ((*i)->covered_by_y_range (y0, y1)) {
1282 TimeAxisView::preset_height (Height h)
1286 return (button_height * 2) + extra_height + 260;
1288 return (button_height * 2) + extra_height + 160;
1290 return (button_height * 2) + extra_height + 60;
1292 return (button_height * 2) + extra_height + 10;
1294 return button_height + extra_height;
1301 /** @return Child time axis views that are not hidden */
1302 TimeAxisView::Children
1303 TimeAxisView::get_child_list ()
1307 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1308 if (!(*i)->hidden()) {
1317 TimeAxisView::build_size_menu ()
1319 if (_size_menu && _size_menu->gobj ()) {
1325 using namespace Menu_Helpers;
1327 _size_menu = new Menu;
1328 _size_menu->set_name ("ArdourContextMenu");
1329 MenuList& items = _size_menu->items();
1331 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1332 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1333 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1334 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1335 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1339 TimeAxisView::reset_visual_state ()
1341 /* this method is not required to trigger a global redraw */
1343 string str = gui_property ("height");
1346 set_height (atoi (str));
1348 set_height (preset_height (HeightNormal));
1353 TrackViewList::filter_to_unique_playlists ()
1355 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1358 for (iterator i = begin(); i != end(); ++i) {
1359 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1361 /* not a route: include it anyway */
1364 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1366 if (playlists.insert (t->playlist()).second) {
1367 /* playlist not seen yet */
1371 /* not a track: include it anyway */