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::controls_button_size_group = Glib::RefPtr<Gtk::SizeGroup>();
77 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::track_number_v_size_group = Glib::RefPtr<Gtk::SizeGroup>();
79 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
81 , controls_table (3, 3)
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 (!controls_button_size_group) {
110 controls_button_size_group = SizeGroup::create (Gtk::SIZE_GROUP_BOTH);
112 if (extra_height == 0) {
116 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (1.0, 0.0));
117 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
118 _canvas_display->hide(); // reveal as needed
120 _canvas_separator = new ArdourCanvas::Line(ed.get_trackview_group ());
121 CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
122 _canvas_separator->set_outline_color(RGBA_TO_UINT (0, 0, 0, 255));
123 _canvas_separator->set_outline_width(1.0);
124 _canvas_separator->hide();
126 selection_group = new ArdourCanvas::Container (_canvas_display);
127 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
128 selection_group->set_data (X_("timeselection"), (void *) 1);
129 selection_group->hide();
131 _ghost_group = new ArdourCanvas::Container (_canvas_display);
132 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
133 _ghost_group->lower_to_bottom();
134 _ghost_group->show();
136 name_label.set_name ("TrackLabel");
137 name_label.set_alignment (0.0, 0.5);
138 name_label.set_width_chars (12);
139 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
141 Gtk::Entry* an_entry = new Gtk::Entry;
142 Gtk::Requisition req;
143 an_entry->size_request (req);
144 name_label.set_size_request (-1, req.height);
145 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
148 name_hbox.pack_end (name_label, true, true);
150 // set min. track-header width if fader is not visible
151 name_hbox.set_size_request(name_width_px, -1);
156 controls_table.set_row_spacings (2);
157 controls_table.set_col_spacings (2);
158 controls_table.set_border_width (2);
160 if (ARDOUR::Profile->get_mixbus() ) {
161 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
163 controls_table.attach (name_hbox, 1, 2, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
165 controls_table.show_all ();
166 controls_table.set_no_show_all ();
168 controls_vbox.pack_start (controls_table, false, false);
169 controls_vbox.show ();
171 top_hbox.pack_start (controls_vbox, true, true);
174 controls_ebox.add (top_hbox);
175 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
176 Gdk::BUTTON_RELEASE_MASK|
177 Gdk::POINTER_MOTION_MASK|
178 Gdk::ENTER_NOTIFY_MASK|
179 Gdk::LEAVE_NOTIFY_MASK|
181 controls_ebox.set_flags (CAN_FOCUS);
183 /* note that this handler connects *before* the default handler */
184 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
185 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
186 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
187 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
188 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
189 controls_ebox.show ();
191 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
192 time_axis_frame.add(controls_ebox);
193 time_axis_frame.show();
195 HSeparator* separator = manage (new HSeparator());
196 separator->set_name("TrackSeparator");
197 separator->set_size_request(-1, 1);
200 time_axis_vbox.pack_start (*separator, false, false);
201 time_axis_vbox.pack_start (time_axis_frame, true, true);
202 time_axis_vbox.show();
203 time_axis_hbox.pack_start (time_axis_vbox, true, true);
204 time_axis_hbox.show();
206 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
208 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
211 TimeAxisView::~TimeAxisView()
213 in_destructor = true;
215 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
219 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
221 delete (*i)->start_trim;
222 delete (*i)->end_trim;
226 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
228 delete (*i)->start_trim;
229 delete (*i)->end_trim;
232 delete selection_group;
235 delete _canvas_display;
238 delete _canvas_separator;
239 _canvas_separator = 0;
248 TimeAxisView::hide ()
254 _canvas_display->hide ();
255 _canvas_separator->hide ();
257 if (control_parent) {
258 control_parent->remove (time_axis_hbox);
265 /* now hide children */
267 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
271 /* if its hidden, it cannot be selected */
272 _editor.get_selection().remove (this);
273 /* and neither can its regions */
274 _editor.get_selection().remove_regions (this);
279 /** Display this TimeAxisView as the nth component of the parent box, at y.
281 * @param y y position.
282 * @param nth index for this TimeAxisView, increased if this view has children.
283 * @param parent parent component.
284 * @return height of this TimeAxisView.
287 TimeAxisView::show_at (double y, int& nth, VBox *parent)
289 if (control_parent) {
290 control_parent->reorder_child (time_axis_hbox, nth);
292 control_parent = parent;
293 parent->pack_start (time_axis_hbox, false, false);
294 parent->reorder_child (time_axis_hbox, nth);
299 if (_y_position != y) {
300 _canvas_separator->set (ArdourCanvas::Duple(0, y), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, y));
301 _canvas_display->set_y_position (y + 1);
305 _canvas_display->raise_to_top ();
306 _canvas_display->show ();
308 _canvas_separator->raise_to_top ();
309 _canvas_separator->show ();
313 _effective_height = current_height ();
315 /* now show relevant children */
317 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
318 if ((*i)->marked_for_display()) {
320 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
326 return _effective_height;
330 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
332 switch (ev->direction) {
334 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
335 /* See Editor::_stepping_axis_view for notes on this hack */
336 Editor& e = dynamic_cast<Editor&> (_editor);
337 if (!e.stepping_axis_view ()) {
338 e.set_stepping_axis_view (this);
340 e.stepping_axis_view()->step_height (false);
345 case GDK_SCROLL_DOWN:
346 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
347 /* See Editor::_stepping_axis_view for notes on this hack */
348 Editor& e = dynamic_cast<Editor&> (_editor);
349 if (!e.stepping_axis_view ()) {
350 e.set_stepping_axis_view (this);
352 e.stepping_axis_view()->step_height (true);
358 /* no handling for left/right, yet */
362 /* Just forward to the normal canvas scroll method. The coordinate
363 systems are different but since the canvas is always larger than the
364 track headers, and aligned with the trackview area, this will work.
366 In the not too distant future this layout is going away anyway and
367 headers will be on the canvas.
369 return _editor.canvas_scroll_event (ev, false);
373 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
375 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
376 /* see if it is inside the name label */
377 if (name_label.is_ancestor (controls_ebox)) {
380 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
381 Gtk::Allocation a = name_label.get_allocation ();
382 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
384 _ebox_release_can_act = false;
391 _ebox_release_can_act = true;
393 if (maybe_set_cursor (event->y) > 0) {
394 _resize_drag_start = event->y_root;
401 TimeAxisView::idle_resize (uint32_t h)
408 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
410 if (_resize_drag_start >= 0) {
412 /* (ab)use the DragManager to do autoscrolling - basically we
413 * are pretending that the drag is taking place over the canvas
414 * (which perhaps in the glorious future, when track headers
415 * and the canvas are unified, will actually be true.)
418 _editor.maybe_autoscroll (false, true, true);
420 /* now schedule the actual TAV resize */
421 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
422 _editor.add_to_idle_resize (this, delta);
423 _resize_drag_start = ev->y_root;
425 /* not dragging but ... */
426 maybe_set_cursor (ev->y);
429 gdk_event_request_motions(ev);
434 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
436 if (_have_preresize_cursor) {
437 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
438 _have_preresize_cursor = false;
444 TimeAxisView::maybe_set_cursor (int y)
446 /* XXX no Gtkmm Gdk::Window::get_cursor() */
447 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
449 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
451 /* y-coordinate in lower 25% */
453 if (!_have_preresize_cursor) {
454 _preresize_cursor = gdk_window_get_cursor (win->gobj());
455 _have_preresize_cursor = true;
456 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
461 } else if (_have_preresize_cursor) {
462 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
463 _have_preresize_cursor = false;
472 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
474 if (_resize_drag_start >= 0) {
475 if (_have_preresize_cursor) {
476 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
477 _preresize_cursor = 0;
478 _have_preresize_cursor = false;
480 _editor.stop_canvas_autoscroll ();
481 _resize_drag_start = -1;
484 if (!_ebox_release_can_act) {
488 switch (ev->button) {
490 selection_click (ev);
494 popup_display_menu (ev->time);
502 TimeAxisView::selection_click (GdkEventButton* ev)
504 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
505 _editor.set_selected_track (*this, op, false);
509 /** Steps through the defined heights for this TrackView.
510 * @param coarser true if stepping should decrease in size, otherwise false.
513 TimeAxisView::step_height (bool coarser)
515 static const uint32_t step = 25;
519 if (height <= preset_height (HeightSmall)) {
521 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
522 set_height_enum (HeightSmall);
524 set_height (height - step);
529 if (height <= preset_height(HeightSmall)) {
530 set_height_enum (HeightNormal);
532 set_height (height + step);
539 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
541 if (apply_to_selection) {
542 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
544 set_height (preset_height (h));
549 TimeAxisView::set_height (uint32_t h)
551 if (h < preset_height (HeightSmall)) {
552 h = preset_height (HeightSmall);
555 time_axis_hbox.property_height_request () = h;
559 snprintf (buf, sizeof (buf), "%u", height);
560 set_gui_property ("height", buf);
562 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
566 if (selection_group->visible ()) {
567 /* resize the selection rect */
568 show_selection (_editor.get_selection().time);
571 _editor.override_visible_track_count ();
575 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
577 /* steal escape, tabs from GTK */
579 switch (ev->keyval) {
581 case GDK_ISO_Left_Tab:
589 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
591 TrackViewList::iterator i;
593 switch (ev->keyval) {
595 end_name_edit (RESPONSE_CANCEL);
598 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
599 * generates a different ev->keyval, rather than setting
602 case GDK_ISO_Left_Tab:
603 end_name_edit (RESPONSE_APPLY);
607 end_name_edit (RESPONSE_ACCEPT);
617 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
619 end_name_edit (RESPONSE_OK);
624 TimeAxisView::begin_name_edit ()
630 if (can_edit_name()) {
632 name_entry = manage (new Gtkmm2ext::FocusEntry);
634 name_entry->set_width_chars(8); // min width, entry expands
636 name_entry->set_name ("EditorTrackNameDisplay");
637 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
638 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
639 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
640 name_entry->set_text (name_label.get_text());
641 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
643 if (name_label.is_ancestor (name_hbox)) {
644 name_hbox.remove (name_label);
647 name_hbox.pack_end (*name_entry, true, true);
650 name_entry->select_region (0, -1);
651 name_entry->set_state (STATE_SELECTED);
652 name_entry->grab_focus ();
653 name_entry->start_editing (0);
658 TimeAxisView::end_name_edit (int response)
664 bool edit_next = false;
665 bool edit_prev = false;
668 case RESPONSE_CANCEL:
671 name_entry_changed ();
673 case RESPONSE_ACCEPT:
674 name_entry_changed ();
677 name_entry_changed ();
681 /* this will delete the name_entry. but it will also drop focus, which
682 * will cause another callback to this function, so set name_entry = 0
683 * first to ensure we don't double-remove etc. etc.
686 Gtk::Entry* tmp = name_entry;
688 name_hbox.remove (*tmp);
690 /* put the name label back */
692 name_hbox.pack_end (name_label);
697 TrackViewList const & allviews = _editor.get_track_views ();
698 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
700 if (i != allviews.end()) {
703 if (++i == allviews.end()) {
707 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
709 if (rtav && rtav->route()->record_enabled()) {
713 if (!(*i)->hidden()) {
720 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
721 _editor.ensure_time_axis_view_is_visible (**i, false);
722 (*i)->begin_name_edit ();
725 } else if (edit_prev) {
727 TrackViewList const & allviews = _editor.get_track_views ();
728 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
730 if (i != allviews.begin()) {
732 if (i == allviews.begin()) {
738 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
740 if (rtav && rtav->route()->record_enabled()) {
744 if (!(*i)->hidden()) {
751 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
752 _editor.ensure_time_axis_view_is_visible (**i, false);
753 (*i)->begin_name_edit ();
759 TimeAxisView::name_entry_changed ()
764 TimeAxisView::can_edit_name () const
770 TimeAxisView::conditionally_add_to_selection ()
772 Selection& s (_editor.get_selection ());
774 if (!s.selected (this)) {
775 _editor.set_selected_track (*this, Selection::Set);
780 TimeAxisView::popup_display_menu (guint32 when)
782 conditionally_add_to_selection ();
784 build_display_menu ();
785 display_menu->popup (1, when);
789 TimeAxisView::set_selected (bool yn)
791 if (can_edit_name() && name_entry && name_entry->get_visible()) {
792 end_name_edit (RESPONSE_CANCEL);
795 if (yn == _selected) {
799 Selectable::set_selected (yn);
802 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
803 time_axis_frame.set_name ("MixerStripSelectedFrame");
804 controls_ebox.set_name (controls_base_selected_name);
805 controls_vbox.set_name (controls_base_selected_name);
806 time_axis_vbox.set_name (controls_base_selected_name);
808 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
809 time_axis_frame.set_name (controls_base_unselected_name);
810 controls_ebox.set_name (controls_base_unselected_name);
811 controls_vbox.set_name (controls_base_unselected_name);
812 time_axis_vbox.set_name (controls_base_unselected_name);
816 /* children will be set for the yn=true case. but when deselecting
817 the editor only has a list of top-level trackviews, so we
818 have to do this here.
821 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
822 (*i)->set_selected (false);
826 time_axis_frame.show();
831 TimeAxisView::build_display_menu ()
833 using namespace Menu_Helpers;
837 display_menu = new Menu;
838 display_menu->set_name ("ArdourContextMenu");
840 // Just let implementing classes define what goes into the manu
844 TimeAxisView::set_samples_per_pixel (double fpp)
846 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
847 (*i)->set_samples_per_pixel (fpp);
852 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
854 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
855 (*i)->show_timestretch (start, end, layers, layer);
860 TimeAxisView::hide_timestretch ()
862 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
863 (*i)->hide_timestretch ();
868 TimeAxisView::show_selection (TimeSelection& ts)
873 SelectionRect *rect; time_axis_frame.show();
876 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
877 (*i)->show_selection (ts);
880 if (selection_group->visible ()) {
881 while (!used_selection_rects.empty()) {
882 free_selection_rects.push_front (used_selection_rects.front());
883 used_selection_rects.pop_front();
884 free_selection_rects.front()->rect->hide();
885 free_selection_rects.front()->start_trim->hide();
886 free_selection_rects.front()->end_trim->hide();
888 selection_group->hide();
891 selection_group->show();
892 selection_group->raise_to_top();
894 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
895 framepos_t start, end;
900 cnt = end - start + 1;
902 rect = get_selection_rect ((*i).id);
904 x1 = _editor.sample_to_pixel (start);
905 x2 = _editor.sample_to_pixel (start + cnt - 1);
906 y2 = current_height() - 1;
908 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
910 // trim boxes are at the top for selections
913 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
914 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
916 rect->start_trim->show();
917 rect->end_trim->show();
919 rect->start_trim->hide();
920 rect->end_trim->hide();
924 used_selection_rects.push_back (rect);
929 TimeAxisView::reshow_selection (TimeSelection& ts)
933 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
934 (*i)->show_selection (ts);
939 TimeAxisView::hide_selection ()
941 if (selection_group->visible ()) {
942 while (!used_selection_rects.empty()) {
943 free_selection_rects.push_front (used_selection_rects.front());
944 used_selection_rects.pop_front();
945 free_selection_rects.front()->rect->hide();
946 free_selection_rects.front()->start_trim->hide();
947 free_selection_rects.front()->end_trim->hide();
949 selection_group->hide();
952 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
953 (*i)->hide_selection ();
958 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
960 /* find the selection rect this is for. we have the item corresponding to one
964 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
965 if ((*i)->start_trim == item || (*i)->end_trim == item) {
967 /* make one trim handle be "above" the other so that if they overlap,
968 the top one is the one last used.
971 (*i)->rect->raise_to_top ();
972 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
973 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
981 TimeAxisView::get_selection_rect (uint32_t id)
985 /* check to see if we already have a visible rect for this particular selection ID */
987 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
988 if ((*i)->id == id) {
993 /* ditto for the free rect list */
995 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
996 if ((*i)->id == id) {
997 SelectionRect* ret = (*i);
998 free_selection_rects.erase (i);
1003 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
1005 if (free_selection_rects.empty()) {
1007 rect = new SelectionRect;
1009 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1010 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1011 rect->rect->set_outline (false);
1012 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1014 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1015 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1016 rect->start_trim->set_outline (false);
1017 rect->start_trim->set_fill (false);
1019 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1020 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1021 rect->end_trim->set_outline (false);
1022 rect->end_trim->set_fill (false);
1024 free_selection_rects.push_front (rect);
1026 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1027 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1028 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1031 rect = free_selection_rects.front();
1033 free_selection_rects.pop_front();
1037 struct null_deleter { void operator()(void const *) const {} };
1040 TimeAxisView::is_child (TimeAxisView* tav)
1042 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1046 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1048 children.push_back (child);
1052 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1054 Children::iterator i;
1056 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1061 /** Get selectable things within a given range.
1062 * @param start Start time in session frames.
1063 * @param end End time in session frames.
1064 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1065 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1066 * @param result Filled in with selectable things.
1069 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1075 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1081 TimeAxisView::add_ghost (RegionView* rv)
1083 GhostRegion* gr = rv->add_ghost (*this);
1086 ghosts.push_back(gr);
1091 TimeAxisView::remove_ghost (RegionView* rv)
1093 rv->remove_ghost_in (*this);
1097 TimeAxisView::erase_ghost (GhostRegion* gr)
1099 if (in_destructor) {
1103 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1112 TimeAxisView::touched (double top, double bot)
1114 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1115 y_position is the "origin" or "top" of the track.
1118 double mybot = _y_position + current_height();
1120 return ((_y_position <= bot && _y_position >= top) ||
1121 ((mybot <= bot) && (top < mybot)) ||
1122 (mybot >= bot && _y_position < top));
1126 TimeAxisView::set_parent (TimeAxisView& p)
1132 TimeAxisView::reset_height ()
1134 set_height (height);
1136 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1137 (*i)->set_height ((*i)->height);
1142 TimeAxisView::compute_heights ()
1144 // TODO this function should be re-evaluated when font-scaling changes (!)
1145 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1146 Gtk::Table one_row_table (1, 1);
1147 ArdourButton* test_button = manage (new ArdourButton);
1148 const int border_width = 2;
1149 const int frame_height = 2;
1150 extra_height = (2 * border_width) + frame_height;
1152 window.add (one_row_table);
1153 test_button->set_name ("mute button");
1154 test_button->set_text (_("M"));
1156 one_row_table.set_border_width (border_width);
1157 one_row_table.set_row_spacings (2);
1158 one_row_table.set_col_spacings (2);
1160 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1161 one_row_table.show_all ();
1163 Gtk::Requisition req(one_row_table.size_request ());
1164 button_height = req.height;
1168 TimeAxisView::color_handler ()
1170 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1174 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1176 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1177 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1179 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1180 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1182 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1183 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1186 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1188 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1189 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1191 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1192 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1194 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1195 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1199 /** @return Pair: TimeAxisView, layer index.
1200 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1201 * does. @param y is an offset from the top of the trackview area.
1203 * If the covering object is a child axis, then the child is returned.
1204 * TimeAxisView is 0 otherwise.
1206 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1207 * and is in stacked or expanded * region display mode, otherwise 0.
1209 std::pair<TimeAxisView*, double>
1210 TimeAxisView::covers_y_position (double y) const
1213 return std::make_pair ((TimeAxisView *) 0, 0);
1216 if (_y_position <= y && y < (_y_position + height)) {
1218 /* work out the layer index if appropriate */
1220 switch (layer_display ()) {
1226 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1227 /* clamp to max layers to be on the safe side; sometimes the above calculation
1228 returns a too-high value */
1229 if (l >= view()->layers ()) {
1230 l = view()->layers() - 1;
1236 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1238 if (l >= (view()->layers() - 0.5)) {
1239 l = view()->layers() - 0.5;
1245 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1248 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1250 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1256 return std::make_pair ((TimeAxisView *) 0, 0);
1260 TimeAxisView::covered_by_y_range (double y0, double y1) const
1266 /* if either the top or bottom of the axisview is in the vertical
1267 * range, we cover it.
1270 if ((y0 < _y_position && y1 < _y_position) ||
1271 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1275 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1276 if ((*i)->covered_by_y_range (y0, y1)) {
1285 TimeAxisView::preset_height (Height h)
1289 return (button_height * 2) + extra_height + 260;
1291 return (button_height * 2) + extra_height + 160;
1293 return (button_height * 2) + extra_height + 60;
1295 return (button_height * 2) + extra_height + 10;
1297 return button_height + extra_height;
1304 /** @return Child time axis views that are not hidden */
1305 TimeAxisView::Children
1306 TimeAxisView::get_child_list ()
1310 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1311 if (!(*i)->hidden()) {
1320 TimeAxisView::build_size_menu ()
1322 if (_size_menu && _size_menu->gobj ()) {
1328 using namespace Menu_Helpers;
1330 _size_menu = new Menu;
1331 _size_menu->set_name ("ArdourContextMenu");
1332 MenuList& items = _size_menu->items();
1334 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1335 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1336 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1337 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1338 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1342 TimeAxisView::reset_visual_state ()
1344 /* this method is not required to trigger a global redraw */
1346 string str = gui_property ("height");
1349 set_height (atoi (str));
1351 set_height (preset_height (HeightNormal));
1356 TrackViewList::filter_to_unique_playlists ()
1358 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1361 for (iterator i = begin(); i != end(); ++i) {
1362 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1364 /* not a route: include it anyway */
1367 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1369 if (playlists.insert (t->playlist()).second) {
1370 /* playlist not seen yet */
1374 /* not a track: include it anyway */