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::midi_scroomer_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 (!midi_scroomer_size_group) {
109 midi_scroomer_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
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 scroomer_placeholder.set_size_request (-1, -1);
200 scroomer_placeholder.show();
201 midi_scroomer_size_group->add_widget (scroomer_placeholder);
203 time_axis_vbox.pack_start (*separator, false, false);
204 time_axis_vbox.pack_start (time_axis_frame, true, true);
205 time_axis_vbox.show();
206 time_axis_hbox.pack_start (time_axis_vbox, true, true);
207 time_axis_hbox.show();
208 top_hbox.pack_start (scroomer_placeholder, false, false);
210 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
212 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
215 TimeAxisView::~TimeAxisView()
217 in_destructor = true;
219 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
223 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
225 delete (*i)->start_trim;
226 delete (*i)->end_trim;
230 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
232 delete (*i)->start_trim;
233 delete (*i)->end_trim;
236 delete selection_group;
239 delete _canvas_display;
242 delete _canvas_separator;
243 _canvas_separator = 0;
252 TimeAxisView::hide ()
258 _canvas_display->hide ();
259 _canvas_separator->hide ();
261 if (control_parent) {
262 control_parent->remove (TOP_LEVEL_WIDGET);
269 /* now hide children */
271 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
275 /* if its hidden, it cannot be selected */
276 _editor.get_selection().remove (this);
277 /* and neither can its regions */
278 _editor.get_selection().remove_regions (this);
283 /** Display this TimeAxisView as the nth component of the parent box, at y.
285 * @param y y position.
286 * @param nth index for this TimeAxisView, increased if this view has children.
287 * @param parent parent component.
288 * @return height of this TimeAxisView.
291 TimeAxisView::show_at (double y, int& nth, VBox *parent)
293 if (control_parent) {
294 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
296 control_parent = parent;
297 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
298 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
303 if (_y_position != y) {
304 _canvas_separator->set (ArdourCanvas::Duple(0, y), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, y));
305 _canvas_display->set_y_position (y + 1);
309 _canvas_display->raise_to_top ();
310 _canvas_display->show ();
312 _canvas_separator->raise_to_top ();
313 _canvas_separator->show ();
317 _effective_height = current_height ();
319 /* now show relevant children */
321 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
322 if ((*i)->marked_for_display()) {
324 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
330 return _effective_height;
334 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
336 switch (ev->direction) {
338 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
339 /* See Editor::_stepping_axis_view for notes on this hack */
340 Editor& e = dynamic_cast<Editor&> (_editor);
341 if (!e.stepping_axis_view ()) {
342 e.set_stepping_axis_view (this);
344 e.stepping_axis_view()->step_height (false);
349 case GDK_SCROLL_DOWN:
350 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
351 /* See Editor::_stepping_axis_view for notes on this hack */
352 Editor& e = dynamic_cast<Editor&> (_editor);
353 if (!e.stepping_axis_view ()) {
354 e.set_stepping_axis_view (this);
356 e.stepping_axis_view()->step_height (true);
362 /* no handling for left/right, yet */
366 /* Just forward to the normal canvas scroll method. The coordinate
367 systems are different but since the canvas is always larger than the
368 track headers, and aligned with the trackview area, this will work.
370 In the not too distant future this layout is going away anyway and
371 headers will be on the canvas.
373 return _editor.canvas_scroll_event (ev, false);
377 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
379 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
380 /* see if it is inside the name label */
381 if (name_label.is_ancestor (controls_ebox)) {
384 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
385 Gtk::Allocation a = name_label.get_allocation ();
386 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
388 _ebox_release_can_act = false;
395 _ebox_release_can_act = true;
397 if (maybe_set_cursor (event->y) > 0) {
398 _resize_drag_start = event->y_root;
405 TimeAxisView::idle_resize (uint32_t h)
412 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
414 if (_resize_drag_start >= 0) {
416 /* (ab)use the DragManager to do autoscrolling - basically we
417 * are pretending that the drag is taking place over the canvas
418 * (which perhaps in the glorious future, when track headers
419 * and the canvas are unified, will actually be true.)
422 _editor.maybe_autoscroll (false, true, true);
424 /* now schedule the actual TAV resize */
425 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
426 _editor.add_to_idle_resize (this, delta);
427 _resize_drag_start = ev->y_root;
429 /* not dragging but ... */
430 maybe_set_cursor (ev->y);
433 gdk_event_request_motions(ev);
438 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
440 if (_have_preresize_cursor) {
441 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
442 _have_preresize_cursor = false;
448 TimeAxisView::maybe_set_cursor (int y)
450 /* XXX no Gtkmm Gdk::Window::get_cursor() */
451 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
453 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
455 /* y-coordinate in lower 25% */
457 if (!_have_preresize_cursor) {
458 _preresize_cursor = gdk_window_get_cursor (win->gobj());
459 _have_preresize_cursor = true;
460 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
465 } else if (_have_preresize_cursor) {
466 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
467 _have_preresize_cursor = false;
476 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
478 if (_resize_drag_start >= 0) {
479 if (_have_preresize_cursor) {
480 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
481 _preresize_cursor = 0;
482 _have_preresize_cursor = false;
484 _editor.stop_canvas_autoscroll ();
485 _resize_drag_start = -1;
488 if (!_ebox_release_can_act) {
492 switch (ev->button) {
494 selection_click (ev);
498 popup_display_menu (ev->time);
506 TimeAxisView::selection_click (GdkEventButton* ev)
508 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
509 _editor.set_selected_track (*this, op, false);
513 /** Steps through the defined heights for this TrackView.
514 * @param coarser true if stepping should decrease in size, otherwise false.
517 TimeAxisView::step_height (bool coarser)
519 static const uint32_t step = 25;
523 if (height <= preset_height (HeightSmall)) {
525 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
526 set_height_enum (HeightSmall);
528 set_height (height - step);
533 if (height <= preset_height(HeightSmall)) {
534 set_height_enum (HeightNormal);
536 set_height (height + step);
543 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
545 if (apply_to_selection) {
546 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
548 set_height (preset_height (h));
553 TimeAxisView::set_height (uint32_t h)
555 if (h < preset_height (HeightSmall)) {
556 h = preset_height (HeightSmall);
559 TOP_LEVEL_WIDGET.property_height_request () = h;
563 snprintf (buf, sizeof (buf), "%u", height);
564 set_gui_property ("height", buf);
566 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
570 if (selection_group->visible ()) {
571 /* resize the selection rect */
572 show_selection (_editor.get_selection().time);
575 _editor.override_visible_track_count ();
579 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
581 /* steal escape, tabs from GTK */
583 switch (ev->keyval) {
585 case GDK_ISO_Left_Tab:
593 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
595 TrackViewList::iterator i;
597 switch (ev->keyval) {
599 end_name_edit (RESPONSE_CANCEL);
602 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
603 * generates a different ev->keyval, rather than setting
606 case GDK_ISO_Left_Tab:
607 end_name_edit (RESPONSE_APPLY);
611 end_name_edit (RESPONSE_ACCEPT);
621 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
623 end_name_edit (RESPONSE_OK);
628 TimeAxisView::begin_name_edit ()
634 if (can_edit_name()) {
636 name_entry = manage (new Gtkmm2ext::FocusEntry);
638 name_entry->set_width_chars(8); // min width, entry expands
640 name_entry->set_name ("EditorTrackNameDisplay");
641 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
642 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
643 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
644 name_entry->set_text (name_label.get_text());
645 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
647 if (name_label.is_ancestor (name_hbox)) {
648 name_hbox.remove (name_label);
651 name_hbox.pack_end (*name_entry, true, true);
654 name_entry->select_region (0, -1);
655 name_entry->set_state (STATE_SELECTED);
656 name_entry->grab_focus ();
657 name_entry->start_editing (0);
662 TimeAxisView::end_name_edit (int response)
668 bool edit_next = false;
669 bool edit_prev = false;
672 case RESPONSE_CANCEL:
675 name_entry_changed ();
677 case RESPONSE_ACCEPT:
678 name_entry_changed ();
681 name_entry_changed ();
685 /* this will delete the name_entry. but it will also drop focus, which
686 * will cause another callback to this function, so set name_entry = 0
687 * first to ensure we don't double-remove etc. etc.
690 Gtk::Entry* tmp = name_entry;
692 name_hbox.remove (*tmp);
694 /* put the name label back */
696 name_hbox.pack_end (name_label);
701 TrackViewList const & allviews = _editor.get_track_views ();
702 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
704 if (i != allviews.end()) {
707 if (++i == allviews.end()) {
711 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
713 if (rtav && rtav->route()->record_enabled()) {
717 if (!(*i)->hidden()) {
724 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
725 _editor.ensure_time_axis_view_is_visible (**i, false);
726 (*i)->begin_name_edit ();
729 } else if (edit_prev) {
731 TrackViewList const & allviews = _editor.get_track_views ();
732 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
734 if (i != allviews.begin()) {
736 if (i == allviews.begin()) {
742 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
744 if (rtav && rtav->route()->record_enabled()) {
748 if (!(*i)->hidden()) {
755 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
756 _editor.ensure_time_axis_view_is_visible (**i, false);
757 (*i)->begin_name_edit ();
763 TimeAxisView::name_entry_changed ()
768 TimeAxisView::can_edit_name () const
774 TimeAxisView::conditionally_add_to_selection ()
776 Selection& s (_editor.get_selection ());
778 if (!s.selected (this)) {
779 _editor.set_selected_track (*this, Selection::Set);
784 TimeAxisView::popup_display_menu (guint32 when)
786 conditionally_add_to_selection ();
788 build_display_menu ();
789 display_menu->popup (1, when);
793 TimeAxisView::set_selected (bool yn)
795 if (can_edit_name() && name_entry && name_entry->get_visible()) {
796 end_name_edit (RESPONSE_CANCEL);
799 if (yn == _selected) {
803 Selectable::set_selected (yn);
806 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
807 time_axis_frame.set_name ("MixerStripSelectedFrame");
808 controls_ebox.set_name (controls_base_selected_name);
809 controls_vbox.set_name (controls_base_selected_name);
810 time_axis_vbox.set_name (controls_base_selected_name);
812 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
813 time_axis_frame.set_name (controls_base_unselected_name);
814 controls_ebox.set_name (controls_base_unselected_name);
815 controls_vbox.set_name (controls_base_unselected_name);
816 time_axis_vbox.set_name (controls_base_unselected_name);
820 /* children will be set for the yn=true case. but when deselecting
821 the editor only has a list of top-level trackviews, so we
822 have to do this here.
825 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
826 (*i)->set_selected (false);
830 time_axis_frame.show();
835 TimeAxisView::build_display_menu ()
837 using namespace Menu_Helpers;
841 display_menu = new Menu;
842 display_menu->set_name ("ArdourContextMenu");
844 // Just let implementing classes define what goes into the manu
848 TimeAxisView::set_samples_per_pixel (double fpp)
850 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
851 (*i)->set_samples_per_pixel (fpp);
856 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
858 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
859 (*i)->show_timestretch (start, end, layers, layer);
864 TimeAxisView::hide_timestretch ()
866 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
867 (*i)->hide_timestretch ();
872 TimeAxisView::show_selection (TimeSelection& ts)
877 SelectionRect *rect; time_axis_frame.show();
880 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
881 (*i)->show_selection (ts);
884 if (selection_group->visible ()) {
885 while (!used_selection_rects.empty()) {
886 free_selection_rects.push_front (used_selection_rects.front());
887 used_selection_rects.pop_front();
888 free_selection_rects.front()->rect->hide();
889 free_selection_rects.front()->start_trim->hide();
890 free_selection_rects.front()->end_trim->hide();
892 selection_group->hide();
895 selection_group->show();
896 selection_group->raise_to_top();
898 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
899 framepos_t start, end;
904 cnt = end - start + 1;
906 rect = get_selection_rect ((*i).id);
908 x1 = _editor.sample_to_pixel (start);
909 x2 = _editor.sample_to_pixel (start + cnt - 1);
910 y2 = current_height() - 1;
912 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
914 // trim boxes are at the top for selections
917 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
918 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
920 rect->start_trim->show();
921 rect->end_trim->show();
923 rect->start_trim->hide();
924 rect->end_trim->hide();
928 used_selection_rects.push_back (rect);
933 TimeAxisView::reshow_selection (TimeSelection& ts)
937 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
938 (*i)->show_selection (ts);
943 TimeAxisView::hide_selection ()
945 if (selection_group->visible ()) {
946 while (!used_selection_rects.empty()) {
947 free_selection_rects.push_front (used_selection_rects.front());
948 used_selection_rects.pop_front();
949 free_selection_rects.front()->rect->hide();
950 free_selection_rects.front()->start_trim->hide();
951 free_selection_rects.front()->end_trim->hide();
953 selection_group->hide();
956 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
957 (*i)->hide_selection ();
962 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
964 /* find the selection rect this is for. we have the item corresponding to one
968 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
969 if ((*i)->start_trim == item || (*i)->end_trim == item) {
971 /* make one trim handle be "above" the other so that if they overlap,
972 the top one is the one last used.
975 (*i)->rect->raise_to_top ();
976 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
977 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
985 TimeAxisView::get_selection_rect (uint32_t id)
989 /* check to see if we already have a visible rect for this particular selection ID */
991 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
992 if ((*i)->id == id) {
997 /* ditto for the free rect list */
999 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1000 if ((*i)->id == id) {
1001 SelectionRect* ret = (*i);
1002 free_selection_rects.erase (i);
1007 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
1009 if (free_selection_rects.empty()) {
1011 rect = new SelectionRect;
1013 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1014 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1015 rect->rect->set_outline (false);
1016 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1018 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1019 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1020 rect->start_trim->set_outline (false);
1021 rect->start_trim->set_fill (false);
1023 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1024 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1025 rect->end_trim->set_outline (false);
1026 rect->end_trim->set_fill (false);
1028 free_selection_rects.push_front (rect);
1030 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1031 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1032 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1035 rect = free_selection_rects.front();
1037 free_selection_rects.pop_front();
1041 struct null_deleter { void operator()(void const *) const {} };
1044 TimeAxisView::is_child (TimeAxisView* tav)
1046 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1050 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1052 children.push_back (child);
1056 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1058 Children::iterator i;
1060 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1065 /** Get selectable things within a given range.
1066 * @param start Start time in session frames.
1067 * @param end End time in session frames.
1068 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1069 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1070 * @param result Filled in with selectable things.
1073 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1079 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1085 TimeAxisView::add_ghost (RegionView* rv)
1087 GhostRegion* gr = rv->add_ghost (*this);
1090 ghosts.push_back(gr);
1095 TimeAxisView::remove_ghost (RegionView* rv)
1097 rv->remove_ghost_in (*this);
1101 TimeAxisView::erase_ghost (GhostRegion* gr)
1103 if (in_destructor) {
1107 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1116 TimeAxisView::touched (double top, double bot)
1118 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1119 y_position is the "origin" or "top" of the track.
1122 double mybot = _y_position + current_height();
1124 return ((_y_position <= bot && _y_position >= top) ||
1125 ((mybot <= bot) && (top < mybot)) ||
1126 (mybot >= bot && _y_position < top));
1130 TimeAxisView::set_parent (TimeAxisView& p)
1136 TimeAxisView::reset_height ()
1138 set_height (height);
1140 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1141 (*i)->set_height ((*i)->height);
1146 TimeAxisView::compute_heights ()
1148 // TODO this function should be re-evaluated when font-scaling changes (!)
1149 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1150 Gtk::Table one_row_table (1, 1);
1151 ArdourButton* test_button = manage (new ArdourButton);
1152 const int border_width = 2;
1153 const int frame_height = 2;
1154 extra_height = (2 * border_width) + frame_height;
1156 window.add (one_row_table);
1157 test_button->set_name ("mute button");
1158 test_button->set_text (_("M"));
1159 test_button->set_tweaks (ArdourButton::TrackHeader);
1161 one_row_table.set_border_width (border_width);
1162 one_row_table.set_row_spacings (2);
1163 one_row_table.set_col_spacings (2);
1165 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1166 one_row_table.show_all ();
1168 Gtk::Requisition req(one_row_table.size_request ());
1169 button_height = req.height;
1173 TimeAxisView::color_handler ()
1175 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1179 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1181 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1182 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1184 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1185 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1187 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1188 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1191 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1193 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1194 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1196 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1197 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1199 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1200 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1204 /** @return Pair: TimeAxisView, layer index.
1205 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1206 * does. @param y is an offset from the top of the trackview area.
1208 * If the covering object is a child axis, then the child is returned.
1209 * TimeAxisView is 0 otherwise.
1211 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1212 * and is in stacked or expanded * region display mode, otherwise 0.
1214 std::pair<TimeAxisView*, double>
1215 TimeAxisView::covers_y_position (double y) const
1218 return std::make_pair ((TimeAxisView *) 0, 0);
1221 if (_y_position <= y && y < (_y_position + height)) {
1223 /* work out the layer index if appropriate */
1225 switch (layer_display ()) {
1231 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1232 /* clamp to max layers to be on the safe side; sometimes the above calculation
1233 returns a too-high value */
1234 if (l >= view()->layers ()) {
1235 l = view()->layers() - 1;
1241 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1243 if (l >= (view()->layers() - 0.5)) {
1244 l = view()->layers() - 0.5;
1250 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1253 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1255 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1261 return std::make_pair ((TimeAxisView *) 0, 0);
1265 TimeAxisView::covered_by_y_range (double y0, double y1) const
1271 /* if either the top or bottom of the axisview is in the vertical
1272 * range, we cover it.
1275 if ((y0 < _y_position && y1 < _y_position) ||
1276 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1280 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1281 if ((*i)->covered_by_y_range (y0, y1)) {
1290 TimeAxisView::preset_height (Height h)
1294 return (button_height * 2) + extra_height + 260;
1296 return (button_height * 2) + extra_height + 160;
1298 return (button_height * 2) + extra_height + 60;
1300 return (button_height * 2) + extra_height + 10;
1302 return button_height + extra_height;
1309 /** @return Child time axis views that are not hidden */
1310 TimeAxisView::Children
1311 TimeAxisView::get_child_list ()
1315 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1316 if (!(*i)->hidden()) {
1325 TimeAxisView::build_size_menu ()
1327 if (_size_menu && _size_menu->gobj ()) {
1333 using namespace Menu_Helpers;
1335 _size_menu = new Menu;
1336 _size_menu->set_name ("ArdourContextMenu");
1337 MenuList& items = _size_menu->items();
1339 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1340 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1341 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1342 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1343 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1347 TimeAxisView::reset_visual_state ()
1349 /* this method is not required to trigger a global redraw */
1351 string str = gui_property ("height");
1354 set_height (atoi (str));
1356 set_height (preset_height (HeightNormal));
1361 TrackViewList::filter_to_unique_playlists ()
1363 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1366 for (iterator i = begin(); i != end(); ++i) {
1367 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1369 /* not a route: include it anyway */
1372 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1374 if (playlists.insert (t->playlist()).second) {
1375 /* playlist not seen yet */
1379 /* not a track: include it anyway */