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 Gtkmm2ext::FocusEntry;
141 an_entry->set_name ("EditorTrackNameDisplay");
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 (time_axis_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(top_hbox);
193 time_axis_frame.show();
195 HSeparator* separator = manage (new HSeparator());
196 separator->set_name("TrackSeparator");
197 separator->set_size_request(-1, 1);
200 scroomer_placeholder.set_size_request (-1, -1);
201 scroomer_placeholder.show();
202 midi_scroomer_size_group->add_widget (scroomer_placeholder);
204 time_axis_vbox.pack_start (*separator, false, false);
205 time_axis_vbox.pack_start (time_axis_frame, true, true);
206 time_axis_vbox.show();
207 time_axis_hbox.pack_start (time_axis_vbox, true, true);
208 time_axis_hbox.show();
209 top_hbox.pack_start (scroomer_placeholder, false, false); // OR pack_end to move after meters ?
211 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
213 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
216 TimeAxisView::~TimeAxisView()
218 in_destructor = true;
220 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
224 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
226 delete (*i)->start_trim;
227 delete (*i)->end_trim;
231 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
233 delete (*i)->start_trim;
234 delete (*i)->end_trim;
237 delete selection_group;
240 delete _canvas_display;
243 delete _canvas_separator;
244 _canvas_separator = 0;
253 TimeAxisView::hide ()
259 _canvas_display->hide ();
260 _canvas_separator->hide ();
262 if (control_parent) {
263 control_parent->remove (TOP_LEVEL_WIDGET);
270 /* now hide children */
272 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
276 /* if its hidden, it cannot be selected */
277 _editor.get_selection().remove (this);
278 /* and neither can its regions */
279 _editor.get_selection().remove_regions (this);
284 /** Display this TimeAxisView as the nth component of the parent box, at y.
286 * @param y y position.
287 * @param nth index for this TimeAxisView, increased if this view has children.
288 * @param parent parent component.
289 * @return height of this TimeAxisView.
292 TimeAxisView::show_at (double y, int& nth, VBox *parent)
294 if (control_parent) {
295 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
297 control_parent = parent;
298 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
299 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
304 if (_y_position != y) {
305 _canvas_separator->set (ArdourCanvas::Duple(0, y), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, y));
306 _canvas_display->set_y_position (y + 1);
310 _canvas_display->raise_to_top ();
311 _canvas_display->show ();
313 _canvas_separator->raise_to_top ();
314 _canvas_separator->show ();
318 _effective_height = current_height ();
320 /* now show relevant children */
322 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
323 if ((*i)->marked_for_display()) {
325 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
331 return _effective_height;
335 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
337 switch (ev->direction) {
339 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
340 /* See Editor::_stepping_axis_view for notes on this hack */
341 Editor& e = dynamic_cast<Editor&> (_editor);
342 if (!e.stepping_axis_view ()) {
343 e.set_stepping_axis_view (this);
345 e.stepping_axis_view()->step_height (false);
350 case GDK_SCROLL_DOWN:
351 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
352 /* See Editor::_stepping_axis_view for notes on this hack */
353 Editor& e = dynamic_cast<Editor&> (_editor);
354 if (!e.stepping_axis_view ()) {
355 e.set_stepping_axis_view (this);
357 e.stepping_axis_view()->step_height (true);
363 /* no handling for left/right, yet */
367 /* Just forward to the normal canvas scroll method. The coordinate
368 systems are different but since the canvas is always larger than the
369 track headers, and aligned with the trackview area, this will work.
371 In the not too distant future this layout is going away anyway and
372 headers will be on the canvas.
374 return _editor.canvas_scroll_event (ev, false);
378 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
380 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
381 /* see if it is inside the name label */
382 if (name_label.is_ancestor (controls_ebox)) {
385 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
386 Gtk::Allocation a = name_label.get_allocation ();
387 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
389 _ebox_release_can_act = false;
396 _ebox_release_can_act = true;
398 if (maybe_set_cursor (event->y) > 0) {
399 _resize_drag_start = event->y_root;
406 TimeAxisView::idle_resize (uint32_t h)
413 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
415 if (_resize_drag_start >= 0) {
417 /* (ab)use the DragManager to do autoscrolling - basically we
418 * are pretending that the drag is taking place over the canvas
419 * (which perhaps in the glorious future, when track headers
420 * and the canvas are unified, will actually be true.)
423 _editor.maybe_autoscroll (false, true, true);
425 /* now schedule the actual TAV resize */
426 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
427 _editor.add_to_idle_resize (this, delta);
428 _resize_drag_start = ev->y_root;
430 /* not dragging but ... */
431 maybe_set_cursor (ev->y);
434 gdk_event_request_motions(ev);
439 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
441 if (_have_preresize_cursor) {
442 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
443 _have_preresize_cursor = false;
449 TimeAxisView::maybe_set_cursor (int y)
451 /* XXX no Gtkmm Gdk::Window::get_cursor() */
452 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
454 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
456 /* y-coordinate in lower 25% */
458 if (!_have_preresize_cursor) {
459 _preresize_cursor = gdk_window_get_cursor (win->gobj());
460 _have_preresize_cursor = true;
461 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
466 } else if (_have_preresize_cursor) {
467 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
468 _have_preresize_cursor = false;
477 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
479 if (_resize_drag_start >= 0) {
480 if (_have_preresize_cursor) {
481 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
482 _preresize_cursor = 0;
483 _have_preresize_cursor = false;
485 _editor.stop_canvas_autoscroll ();
486 _resize_drag_start = -1;
489 if (!_ebox_release_can_act) {
493 switch (ev->button) {
495 selection_click (ev);
499 popup_display_menu (ev->time);
507 TimeAxisView::selection_click (GdkEventButton* ev)
509 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
510 _editor.set_selected_track (*this, op, false);
514 /** Steps through the defined heights for this TrackView.
515 * @param coarser true if stepping should decrease in size, otherwise false.
518 TimeAxisView::step_height (bool coarser)
520 static const uint32_t step = 25;
524 if (height <= preset_height (HeightSmall)) {
526 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
527 set_height_enum (HeightSmall);
529 set_height (height - step);
534 if (height <= preset_height(HeightSmall)) {
535 set_height_enum (HeightNormal);
537 set_height (height + step);
544 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
546 if (apply_to_selection) {
547 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
549 set_height (preset_height (h));
554 TimeAxisView::set_height (uint32_t h)
556 if (h < preset_height (HeightSmall)) {
557 h = preset_height (HeightSmall);
560 TOP_LEVEL_WIDGET.property_height_request () = h;
564 snprintf (buf, sizeof (buf), "%u", height);
565 set_gui_property ("height", buf);
567 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
571 if (selection_group->visible ()) {
572 /* resize the selection rect */
573 show_selection (_editor.get_selection().time);
576 _editor.override_visible_track_count ();
580 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
582 /* steal escape, tabs from GTK */
584 switch (ev->keyval) {
586 case GDK_ISO_Left_Tab:
594 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
596 TrackViewList::iterator i;
598 switch (ev->keyval) {
600 end_name_edit (RESPONSE_CANCEL);
603 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
604 * generates a different ev->keyval, rather than setting
607 case GDK_ISO_Left_Tab:
608 end_name_edit (RESPONSE_APPLY);
612 end_name_edit (RESPONSE_ACCEPT);
622 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
624 end_name_edit (RESPONSE_OK);
629 TimeAxisView::begin_name_edit ()
635 if (can_edit_name()) {
637 name_entry = manage (new Gtkmm2ext::FocusEntry);
639 name_entry->set_width_chars(8); // min width, entry expands
641 name_entry->set_name ("EditorTrackNameDisplay");
642 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
643 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
644 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
645 name_entry->set_text (name_label.get_text());
646 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
648 if (name_label.is_ancestor (name_hbox)) {
649 name_hbox.remove (name_label);
652 name_hbox.pack_end (*name_entry, true, true);
655 name_entry->select_region (0, -1);
656 name_entry->set_state (STATE_SELECTED);
657 name_entry->grab_focus ();
658 name_entry->start_editing (0);
663 TimeAxisView::end_name_edit (int response)
669 bool edit_next = false;
670 bool edit_prev = false;
673 case RESPONSE_CANCEL:
676 name_entry_changed ();
678 case RESPONSE_ACCEPT:
679 name_entry_changed ();
682 name_entry_changed ();
686 /* this will delete the name_entry. but it will also drop focus, which
687 * will cause another callback to this function, so set name_entry = 0
688 * first to ensure we don't double-remove etc. etc.
691 Gtk::Entry* tmp = name_entry;
693 name_hbox.remove (*tmp);
695 /* put the name label back */
697 name_hbox.pack_end (name_label);
702 TrackViewList const & allviews = _editor.get_track_views ();
703 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
705 if (i != allviews.end()) {
708 if (++i == allviews.end()) {
712 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
714 if (rtav && rtav->route()->record_enabled()) {
718 if (!(*i)->hidden()) {
725 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
726 _editor.ensure_time_axis_view_is_visible (**i, false);
727 (*i)->begin_name_edit ();
730 } else if (edit_prev) {
732 TrackViewList const & allviews = _editor.get_track_views ();
733 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
735 if (i != allviews.begin()) {
737 if (i == allviews.begin()) {
743 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
745 if (rtav && rtav->route()->record_enabled()) {
749 if (!(*i)->hidden()) {
756 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
757 _editor.ensure_time_axis_view_is_visible (**i, false);
758 (*i)->begin_name_edit ();
764 TimeAxisView::name_entry_changed ()
769 TimeAxisView::can_edit_name () const
775 TimeAxisView::conditionally_add_to_selection ()
777 Selection& s (_editor.get_selection ());
779 if (!s.selected (this)) {
780 _editor.set_selected_track (*this, Selection::Set);
785 TimeAxisView::popup_display_menu (guint32 when)
787 conditionally_add_to_selection ();
789 build_display_menu ();
790 display_menu->popup (1, when);
794 TimeAxisView::set_selected (bool yn)
796 if (can_edit_name() && name_entry && name_entry->get_visible()) {
797 end_name_edit (RESPONSE_CANCEL);
800 if (yn == _selected) {
804 Selectable::set_selected (yn);
807 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
808 time_axis_frame.set_name ("MixerStripSelectedFrame");
809 controls_ebox.set_name (controls_base_selected_name);
810 controls_vbox.set_name (controls_base_selected_name);
811 time_axis_vbox.set_name (controls_base_selected_name);
813 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
814 time_axis_frame.set_name (controls_base_unselected_name);
815 controls_ebox.set_name (controls_base_unselected_name);
816 controls_vbox.set_name (controls_base_unselected_name);
817 time_axis_vbox.set_name (controls_base_unselected_name);
821 /* children will be set for the yn=true case. but when deselecting
822 the editor only has a list of top-level trackviews, so we
823 have to do this here.
826 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
827 (*i)->set_selected (false);
831 time_axis_frame.show();
836 TimeAxisView::build_display_menu ()
838 using namespace Menu_Helpers;
842 display_menu = new Menu;
843 display_menu->set_name ("ArdourContextMenu");
845 // Just let implementing classes define what goes into the manu
849 TimeAxisView::set_samples_per_pixel (double fpp)
851 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
852 (*i)->set_samples_per_pixel (fpp);
857 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
859 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
860 (*i)->show_timestretch (start, end, layers, layer);
865 TimeAxisView::hide_timestretch ()
867 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
868 (*i)->hide_timestretch ();
873 TimeAxisView::show_selection (TimeSelection& ts)
878 SelectionRect *rect; time_axis_frame.show();
881 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
882 (*i)->show_selection (ts);
885 if (selection_group->visible ()) {
886 while (!used_selection_rects.empty()) {
887 free_selection_rects.push_front (used_selection_rects.front());
888 used_selection_rects.pop_front();
889 free_selection_rects.front()->rect->hide();
890 free_selection_rects.front()->start_trim->hide();
891 free_selection_rects.front()->end_trim->hide();
893 selection_group->hide();
896 selection_group->show();
897 selection_group->raise_to_top();
899 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
900 framepos_t start, end;
905 cnt = end - start + 1;
907 rect = get_selection_rect ((*i).id);
909 x1 = _editor.sample_to_pixel (start);
910 x2 = _editor.sample_to_pixel (start + cnt - 1);
911 y2 = current_height() - 1;
913 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
915 // trim boxes are at the top for selections
918 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
919 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
921 rect->start_trim->show();
922 rect->end_trim->show();
924 rect->start_trim->hide();
925 rect->end_trim->hide();
929 used_selection_rects.push_back (rect);
934 TimeAxisView::reshow_selection (TimeSelection& ts)
938 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
939 (*i)->show_selection (ts);
944 TimeAxisView::hide_selection ()
946 if (selection_group->visible ()) {
947 while (!used_selection_rects.empty()) {
948 free_selection_rects.push_front (used_selection_rects.front());
949 used_selection_rects.pop_front();
950 free_selection_rects.front()->rect->hide();
951 free_selection_rects.front()->start_trim->hide();
952 free_selection_rects.front()->end_trim->hide();
954 selection_group->hide();
957 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
958 (*i)->hide_selection ();
963 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
965 /* find the selection rect this is for. we have the item corresponding to one
969 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
970 if ((*i)->start_trim == item || (*i)->end_trim == item) {
972 /* make one trim handle be "above" the other so that if they overlap,
973 the top one is the one last used.
976 (*i)->rect->raise_to_top ();
977 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
978 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
986 TimeAxisView::get_selection_rect (uint32_t id)
990 /* check to see if we already have a visible rect for this particular selection ID */
992 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
993 if ((*i)->id == id) {
998 /* ditto for the free rect list */
1000 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1001 if ((*i)->id == id) {
1002 SelectionRect* ret = (*i);
1003 free_selection_rects.erase (i);
1008 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
1010 if (free_selection_rects.empty()) {
1012 rect = new SelectionRect;
1014 rect->rect = new ArdourCanvas::Rectangle (selection_group);
1015 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
1016 rect->rect->set_outline (false);
1017 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1019 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
1020 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
1021 rect->start_trim->set_outline (false);
1022 rect->start_trim->set_fill (false);
1024 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
1025 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
1026 rect->end_trim->set_outline (false);
1027 rect->end_trim->set_fill (false);
1029 free_selection_rects.push_front (rect);
1031 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1032 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1033 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1036 rect = free_selection_rects.front();
1038 free_selection_rects.pop_front();
1042 struct null_deleter { void operator()(void const *) const {} };
1045 TimeAxisView::is_child (TimeAxisView* tav)
1047 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1051 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1053 children.push_back (child);
1057 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1059 Children::iterator i;
1061 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1066 /** Get selectable things within a given range.
1067 * @param start Start time in session frames.
1068 * @param end End time in session frames.
1069 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1070 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1071 * @param result Filled in with selectable things.
1074 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1080 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1086 TimeAxisView::add_ghost (RegionView* rv)
1088 GhostRegion* gr = rv->add_ghost (*this);
1091 ghosts.push_back(gr);
1096 TimeAxisView::remove_ghost (RegionView* rv)
1098 rv->remove_ghost_in (*this);
1102 TimeAxisView::erase_ghost (GhostRegion* gr)
1104 if (in_destructor) {
1108 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1117 TimeAxisView::touched (double top, double bot)
1119 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1120 y_position is the "origin" or "top" of the track.
1123 double mybot = _y_position + current_height();
1125 return ((_y_position <= bot && _y_position >= top) ||
1126 ((mybot <= bot) && (top < mybot)) ||
1127 (mybot >= bot && _y_position < top));
1131 TimeAxisView::set_parent (TimeAxisView& p)
1137 TimeAxisView::reset_height ()
1139 set_height (height);
1141 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1142 (*i)->set_height ((*i)->height);
1147 TimeAxisView::compute_heights ()
1149 // TODO this function should be re-evaluated when font-scaling changes (!)
1150 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1151 Gtk::Table one_row_table (1, 1);
1152 ArdourButton* test_button = manage (new ArdourButton);
1153 const int border_width = 2;
1154 const int frame_height = 2;
1155 extra_height = (2 * border_width) + frame_height;
1157 window.add (one_row_table);
1158 test_button->set_name ("mute button");
1159 test_button->set_text (_("M"));
1160 test_button->set_tweaks (ArdourButton::TrackHeader);
1162 one_row_table.set_border_width (border_width);
1163 one_row_table.set_row_spacings (2);
1164 one_row_table.set_col_spacings (2);
1166 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1167 one_row_table.show_all ();
1169 Gtk::Requisition req(one_row_table.size_request ());
1170 button_height = req.height;
1174 TimeAxisView::color_handler ()
1176 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1180 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1182 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1183 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1185 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1186 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1188 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1189 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1192 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1194 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1195 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1197 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1198 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1200 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1201 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1205 /** @return Pair: TimeAxisView, layer index.
1206 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1207 * does. @param y is an offset from the top of the trackview area.
1209 * If the covering object is a child axis, then the child is returned.
1210 * TimeAxisView is 0 otherwise.
1212 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1213 * and is in stacked or expanded * region display mode, otherwise 0.
1215 std::pair<TimeAxisView*, double>
1216 TimeAxisView::covers_y_position (double y) const
1219 return std::make_pair ((TimeAxisView *) 0, 0);
1222 if (_y_position <= y && y < (_y_position + height)) {
1224 /* work out the layer index if appropriate */
1226 switch (layer_display ()) {
1232 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1233 /* clamp to max layers to be on the safe side; sometimes the above calculation
1234 returns a too-high value */
1235 if (l >= view()->layers ()) {
1236 l = view()->layers() - 1;
1242 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1244 if (l >= (view()->layers() - 0.5)) {
1245 l = view()->layers() - 0.5;
1251 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1254 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1256 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1262 return std::make_pair ((TimeAxisView *) 0, 0);
1266 TimeAxisView::covered_by_y_range (double y0, double y1) const
1272 /* if either the top or bottom of the axisview is in the vertical
1273 * range, we cover it.
1276 if ((y0 < _y_position && y1 < _y_position) ||
1277 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1281 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1282 if ((*i)->covered_by_y_range (y0, y1)) {
1291 TimeAxisView::preset_height (Height h)
1295 return (button_height * 2) + extra_height + 260;
1297 return (button_height * 2) + extra_height + 160;
1299 return (button_height * 2) + extra_height + 60;
1301 return (button_height * 2) + extra_height + 10;
1303 return button_height + extra_height;
1310 /** @return Child time axis views that are not hidden */
1311 TimeAxisView::Children
1312 TimeAxisView::get_child_list ()
1316 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1317 if (!(*i)->hidden()) {
1326 TimeAxisView::build_size_menu ()
1328 if (_size_menu && _size_menu->gobj ()) {
1334 using namespace Menu_Helpers;
1336 _size_menu = new Menu;
1337 _size_menu->set_name ("ArdourContextMenu");
1338 MenuList& items = _size_menu->items();
1340 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1341 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1342 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1343 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1344 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1348 TimeAxisView::reset_visual_state ()
1350 /* this method is not required to trigger a global redraw */
1352 string str = gui_property ("height");
1355 set_height (atoi (str));
1357 set_height (preset_height (HeightNormal));
1362 TrackViewList::filter_to_unique_playlists ()
1364 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1367 for (iterator i = begin(); i != end(); ++i) {
1368 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1370 /* not a route: include it anyway */
1373 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1375 if (playlists.insert (t->playlist()).second) {
1376 /* playlist not seen yet */
1380 /* not a track: include it anyway */