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.
26 #include <boost/smart_ptr/scoped_ptr.hpp>
28 #include <gtkmm/separator.h>
30 #include "pbd/error.h"
31 #include "pbd/convert.h"
32 #include "pbd/stacktrace.h"
33 #include "pbd/unwind.h"
35 #include "ardour/profile.h"
37 #include "gtkmm2ext/colors.h"
38 #include "gtkmm2ext/doi.h"
39 #include "gtkmm2ext/utils.h"
41 #include "canvas/canvas.h"
42 #include "canvas/rectangle.h"
43 #include "canvas/debug.h"
44 #include "canvas/utils.h"
46 #include "widgets/tooltips.h"
48 #include "ardour_dialog.h"
49 #include "floating_text_entry.h"
50 #include "gui_thread.h"
51 #include "public_editor.h"
52 #include "time_axis_view.h"
53 #include "region_view.h"
54 #include "ghostregion.h"
55 #include "selection.h"
57 #include "rgb_macros.h"
59 #include "streamview.h"
60 #include "editor_drag.h"
62 #include "ui_config.h"
69 using namespace ARDOUR;
71 using namespace Editing;
72 using namespace ArdourCanvas;
73 using namespace ArdourWidgets;
74 using Gtkmm2ext::Keyboard;
76 #define TOP_LEVEL_WIDGET controls_ebox
78 const double trim_handle_size = 6.0; /* pixels */
79 uint32_t TimeAxisView::button_height = 0;
80 uint32_t TimeAxisView::extra_height = 0;
81 int const TimeAxisView::_max_order = 512;
82 unsigned int TimeAxisView::name_width_px = 100;
83 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
84 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = Glib::RefPtr<Gtk::SizeGroup>();
85 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::midi_scroomer_size_group = Glib::RefPtr<Gtk::SizeGroup>();
88 TimeAxisView::setup_sizes()
90 name_width_px = ceilf (100.f * UIConfiguration::instance().get_ui_scale());
93 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
94 : controls_table (5, 4)
95 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
96 , _name_editing (false)
100 , selection_group (0)
103 , in_destructor (false)
105 , _canvas_display (0)
110 , _effective_height (0)
111 , _resize_drag_start (-1)
112 , _did_resize (false)
113 , _preresize_cursor (0)
114 , _have_preresize_cursor (false)
115 , _ebox_release_can_act (true)
117 if (!controls_meters_size_group) {
118 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
120 if (!midi_scroomer_size_group) {
121 midi_scroomer_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
123 if (extra_height == 0) {
127 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group ());
128 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
129 _canvas_display->hide(); // reveal as needed
131 _canvas_separator = new ArdourCanvas::Line(_canvas_display);
132 CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
133 _canvas_separator->set (ArdourCanvas::Duple(0.0, 0.0), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, 0.0));
134 _canvas_separator->set_outline_color(Gtkmm2ext::rgba_to_color (0, 0, 0, 1.0));
135 _canvas_separator->set_outline_width(1.0);
136 _canvas_separator->hide();
138 selection_group = new ArdourCanvas::Container (_canvas_display);
139 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
140 selection_group->set_data (X_("timeselection"), (void *) 1);
141 selection_group->hide();
143 _ghost_group = new ArdourCanvas::Container (_canvas_display);
144 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
145 _ghost_group->lower_to_bottom();
146 _ghost_group->show();
148 name_label.set_name (X_("TrackNameEditor"));
149 name_label.set_alignment (0.0, 0.5);
150 name_label.set_width_chars (12);
151 set_tooltip (name_label, _("Track/Bus name (double click to edit)"));
154 boost::scoped_ptr<Gtk::Entry> an_entry (new FocusEntry);
155 an_entry->set_name (X_("TrackNameEditor"));
156 Gtk::Requisition req;
157 an_entry->size_request (req);
159 name_label.set_size_request (-1, req.height);
160 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
163 // set min. track-header width if fader is not visible
164 name_label.set_size_request(name_width_px, -1);
168 controls_table.set_row_spacings (2);
169 controls_table.set_col_spacings (2);
170 controls_table.set_border_width (2);
172 if (ARDOUR::Profile->get_mixbus() ) {
173 controls_table.attach (name_label, 4, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
175 controls_table.attach (name_label, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
178 controls_table.show_all ();
179 controls_table.set_no_show_all ();
181 controls_vbox.pack_start (controls_table, false, false);
182 controls_vbox.show ();
184 top_hbox.pack_start (controls_vbox, true, true);
187 controls_ebox.add (time_axis_hbox);
188 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
189 Gdk::BUTTON_RELEASE_MASK|
190 Gdk::POINTER_MOTION_MASK|
191 Gdk::ENTER_NOTIFY_MASK|
192 Gdk::LEAVE_NOTIFY_MASK|
194 controls_ebox.set_flags (CAN_FOCUS);
196 /* note that this handler connects *before* the default handler */
197 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
198 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
199 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
200 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
201 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
202 controls_ebox.show ();
204 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
205 time_axis_frame.add(top_hbox);
206 time_axis_frame.show();
208 HSeparator* separator = manage (new HSeparator());
209 separator->set_name("TrackSeparator");
210 separator->set_size_request(-1, 1);
213 scroomer_placeholder.set_size_request (-1, -1);
214 scroomer_placeholder.show();
215 midi_scroomer_size_group->add_widget (scroomer_placeholder);
217 time_axis_vbox.pack_start (*separator, false, false);
218 time_axis_vbox.pack_start (time_axis_frame, true, true);
219 time_axis_vbox.show();
220 time_axis_hbox.pack_start (time_axis_vbox, true, true);
221 time_axis_hbox.show();
222 top_hbox.pack_start (scroomer_placeholder, false, false); // OR pack_end to move after meters ?
224 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
227 TimeAxisView::~TimeAxisView()
229 CatchDeletion (this);
231 in_destructor = true;
233 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
237 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
238 delete (*i)->rect; (*i)->rect=0;
239 delete (*i)->start_trim; (*i)->start_trim = 0;
240 delete (*i)->end_trim; (*i)->end_trim = 0;
244 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
245 delete (*i)->rect; (*i)->rect = 0;
246 delete (*i)->start_trim; (*i)->start_trim = 0;
247 delete (*i)->end_trim; (*i)->end_trim = 0;
250 delete selection_group;
253 delete _canvas_display;
263 TimeAxisView::hide ()
269 _canvas_display->hide ();
270 _canvas_separator->hide ();
272 if (control_parent) {
273 control_parent->remove (TOP_LEVEL_WIDGET);
280 /* now hide children */
282 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
286 /* if its hidden, it cannot be selected */
287 _editor.get_selection().remove (this);
288 /* and neither can its regions */
289 _editor.get_selection().remove_regions (this);
294 /** Display this TimeAxisView as the nth component of the parent box, at y.
296 * @param y y position.
297 * @param nth index for this TimeAxisView, increased if this view has children.
298 * @param parent parent component.
300 * @return height of this TimeAxisView.
303 TimeAxisView::show_at (double y, int& nth, VBox *parent)
305 if (control_parent) {
306 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
308 control_parent = parent;
309 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
310 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
315 if (_y_position != y) {
316 _canvas_display->set_y_position (y);
320 _canvas_display->raise_to_top ();
321 _canvas_display->show ();
325 _effective_height = current_height ();
327 /* now show relevant children */
329 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
330 if ((*i)->marked_for_display()) {
332 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
338 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
342 /* put separator at the bottom of this time axis view */
344 _canvas_separator->set (ArdourCanvas::Duple(0, height), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, height));
345 _canvas_separator->lower_to_bottom ();
346 _canvas_separator->show ();
348 return _effective_height;
352 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
354 switch (ev->direction) {
356 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
357 /* See Editor::_stepping_axis_view for notes on this hack */
358 Editor& e = dynamic_cast<Editor&> (_editor);
359 if (!e.stepping_axis_view ()) {
360 e.set_stepping_axis_view (this);
362 e.stepping_axis_view()->step_height (false);
367 case GDK_SCROLL_DOWN:
368 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
369 /* See Editor::_stepping_axis_view for notes on this hack */
370 Editor& e = dynamic_cast<Editor&> (_editor);
371 if (!e.stepping_axis_view ()) {
372 e.set_stepping_axis_view (this);
374 e.stepping_axis_view()->step_height (true);
380 /* no handling for left/right, yet */
384 /* Just forward to the normal canvas scroll method. The coordinate
385 systems are different but since the canvas is always larger than the
386 track headers, and aligned with the trackview area, this will work.
388 In the not too distant future this layout is going away anyway and
389 headers will be on the canvas.
391 return _editor.canvas_scroll_event (ev, false);
395 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
397 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
398 /* see if it is inside the name label */
399 if (name_label.is_ancestor (controls_ebox)) {
402 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
403 Gtk::Allocation a = name_label.get_allocation ();
404 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
406 _ebox_release_can_act = false;
413 _ebox_release_can_act = true;
415 if (maybe_set_cursor (event->y) > 0) {
416 _resize_drag_start = event->y_root;
423 TimeAxisView::idle_resize (int32_t h)
425 set_height (std::max(0, h));
430 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
432 if (_resize_drag_start >= 0) {
434 /* (ab)use the DragManager to do autoscrolling - basically we
435 * are pretending that the drag is taking place over the canvas
436 * (which perhaps in the glorious future, when track headers
437 * and the canvas are unified, will actually be true.)
440 _editor.maybe_autoscroll (false, true, true);
442 /* now schedule the actual TAV resize */
443 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
444 _editor.add_to_idle_resize (this, delta);
445 _resize_drag_start = ev->y_root;
448 /* not dragging but ... */
449 maybe_set_cursor (ev->y);
452 gdk_event_request_motions(ev);
457 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
459 if (_have_preresize_cursor) {
460 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
461 _have_preresize_cursor = false;
467 TimeAxisView::maybe_set_cursor (int y)
469 /* XXX no Gtkmm Gdk::Window::get_cursor() */
470 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
472 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
474 /* y-coordinate in lower 25% */
476 if (!_have_preresize_cursor) {
477 _preresize_cursor = gdk_window_get_cursor (win->gobj());
478 _have_preresize_cursor = true;
479 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
484 } else if (_have_preresize_cursor) {
485 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
486 _have_preresize_cursor = false;
495 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
497 if (_resize_drag_start >= 0) {
498 if (_have_preresize_cursor) {
499 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
500 _preresize_cursor = 0;
501 _have_preresize_cursor = false;
503 _editor.stop_canvas_autoscroll ();
504 _resize_drag_start = -1;
507 // don't change selection
512 if (!_ebox_release_can_act) {
516 switch (ev->button) {
519 selection_click (ev);
524 popup_display_menu (ev->time);
532 TimeAxisView::selection_click (GdkEventButton* ev)
534 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
535 _editor.set_selected_track (*this, op, false);
539 /** Steps through the defined heights for this TrackView.
540 * @param coarser true if stepping should decrease in size, otherwise false.
543 TimeAxisView::step_height (bool coarser)
545 static const uint32_t step = 25;
549 if (height <= preset_height (HeightSmall)) {
551 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
552 set_height_enum (HeightSmall);
554 set_height (height - step);
559 if (height <= preset_height(HeightSmall)) {
560 set_height_enum (HeightNormal);
562 set_height (height + step);
569 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
571 if (apply_to_selection) {
572 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
574 set_height (preset_height (h));
579 TimeAxisView::set_height (uint32_t h, TrackHeightMode m)
582 if (m == TotalHeight) {
583 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
584 if ( !(*i)->hidden()) ++lanes;
589 if (h < preset_height (HeightSmall)) {
590 h = preset_height (HeightSmall);
593 TOP_LEVEL_WIDGET.property_height_request () = h;
596 set_gui_property ("height", height);
598 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
602 if (selection_group->visible ()) {
603 /* resize the selection rect */
604 show_selection (_editor.get_selection().time);
608 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
609 (*i)->set_height(h, OnlySelf);
613 _editor.override_visible_track_count ();
617 TimeAxisView::begin_name_edit ()
619 if (!can_edit_name()) {
623 Gtk::Window* toplevel = (Gtk::Window*) control_parent->get_toplevel();
624 FloatingTextEntry* fte = new FloatingTextEntry (toplevel, name ());
626 fte->set_name ("TrackNameEditor");
627 fte->use_text.connect (sigc::mem_fun (*this, &TimeAxisView::end_name_edit));
629 /* We want to new toplevel window to overlay the name label, so
630 * translate the coordinates of the upper left corner of the name label
631 * into the coordinate space of the top level window.
637 name_label.translate_coordinates (*toplevel, 0, 0, x, y);
638 toplevel->get_window()->get_origin (wx, wy);
640 fte->move (wx + x, wy + y);
645 TimeAxisView::end_name_edit (std::string str, int next_dir)
647 if (!name_entry_changed (str)) {
653 TrackViewList const & allviews = _editor.get_track_views ();
654 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
656 if (i != allviews.end()) {
659 if (++i == allviews.end()) {
663 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
665 if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) {
669 if (!(*i)->hidden()) {
676 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
677 _editor.ensure_time_axis_view_is_visible (**i, false);
678 (*i)->begin_name_edit ();
681 } else if (next_dir < 0) {
683 TrackViewList const & allviews = _editor.get_track_views ();
684 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
686 if (i != allviews.begin()) {
688 if (i == allviews.begin()) {
694 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
696 if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) {
700 if (!(*i)->hidden()) {
707 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
708 _editor.ensure_time_axis_view_is_visible (**i, false);
709 (*i)->begin_name_edit ();
715 TimeAxisView::name_entry_changed (string const&)
721 TimeAxisView::can_edit_name () const
727 TimeAxisView::conditionally_add_to_selection ()
733 Selection& s (_editor.get_selection ());
735 if (!s.selected (this)) {
736 _editor.set_selected_track (*this, Selection::Set);
741 TimeAxisView::popup_display_menu (guint32 when)
743 conditionally_add_to_selection ();
745 build_display_menu ();
747 if (!display_menu->items().empty()) {
748 display_menu->popup (1, when);
753 TimeAxisView::set_selected (bool yn)
755 if (yn == selected()) {
759 AxisView::set_selected (yn);
762 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
763 time_axis_frame.set_name ("MixerStripSelectedFrame");
764 controls_ebox.set_name (controls_base_selected_name);
765 controls_vbox.set_name (controls_base_selected_name);
766 time_axis_vbox.set_name (controls_base_selected_name);
768 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
769 time_axis_frame.set_name (controls_base_unselected_name);
770 controls_ebox.set_name (controls_base_unselected_name);
771 controls_vbox.set_name (controls_base_unselected_name);
772 time_axis_vbox.set_name (controls_base_unselected_name);
777 time_axis_frame.show();
781 TimeAxisView::build_display_menu ()
783 using namespace Menu_Helpers;
787 display_menu = new Menu;
788 display_menu->set_name ("ArdourContextMenu");
790 // Just let implementing classes define what goes into the manu
794 TimeAxisView::set_samples_per_pixel (double fpp)
796 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
797 (*i)->set_samples_per_pixel (fpp);
802 TimeAxisView::show_timestretch (samplepos_t start, samplepos_t end, int layers, int layer)
804 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
805 (*i)->show_timestretch (start, end, layers, layer);
810 TimeAxisView::hide_timestretch ()
812 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
813 (*i)->hide_timestretch ();
818 TimeAxisView::show_selection (TimeSelection& ts)
825 time_axis_frame.show();
827 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
828 if (!(*i)->selected () && !(*i)->propagate_time_selection ()) {
831 (*i)->show_selection (ts);
834 if (selection_group->visible ()) {
835 while (!used_selection_rects.empty()) {
836 free_selection_rects.push_front (used_selection_rects.front());
837 used_selection_rects.pop_front();
838 free_selection_rects.front()->rect->hide();
839 free_selection_rects.front()->start_trim->hide();
840 free_selection_rects.front()->end_trim->hide();
842 selection_group->hide();
845 selection_group->show();
846 selection_group->raise_to_top();
848 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
849 samplepos_t start, end;
854 cnt = end - start + 1;
856 rect = get_selection_rect ((*i).id);
858 x1 = _editor.sample_to_pixel (start);
859 x2 = _editor.sample_to_pixel (start + cnt - 1);
860 y2 = current_height() - 1;
862 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
864 // trim boxes are at the top for selections
867 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
868 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
870 rect->start_trim->show();
871 rect->end_trim->show();
873 rect->start_trim->hide();
874 rect->end_trim->hide();
878 used_selection_rects.push_back (rect);
883 TimeAxisView::reshow_selection (TimeSelection& ts)
887 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
888 if (!(*i)->selected () && !(*i)->propagate_time_selection ()) {
891 (*i)->show_selection (ts);
896 TimeAxisView::hide_selection ()
898 if (selection_group->visible ()) {
899 while (!used_selection_rects.empty()) {
900 free_selection_rects.push_front (used_selection_rects.front());
901 used_selection_rects.pop_front();
902 free_selection_rects.front()->rect->hide();
903 free_selection_rects.front()->start_trim->hide();
904 free_selection_rects.front()->end_trim->hide();
906 selection_group->hide();
909 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
910 (*i)->hide_selection ();
915 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
917 /* find the selection rect this is for. we have the item corresponding to one
921 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
922 if ((*i)->start_trim == item || (*i)->end_trim == item) {
924 /* make one trim handle be "above" the other so that if they overlap,
925 the top one is the one last used.
928 (*i)->rect->raise_to_top ();
929 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
930 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
937 // retuned rect is pushed back into the used_selection_rects list
938 // in TimeAxisView::show_selection() which is the only caller.
940 TimeAxisView::get_selection_rect (uint32_t id)
944 /* check to see if we already have a visible rect for this particular selection ID */
946 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
947 if ((*i)->id == id) {
948 SelectionRect* ret = (*i);
949 used_selection_rects.erase (i);
954 /* ditto for the free rect list */
956 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
957 if ((*i)->id == id) {
958 SelectionRect* ret = (*i);
959 free_selection_rects.erase (i);
964 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
966 if (free_selection_rects.empty()) {
968 rect = new SelectionRect;
970 rect->rect = new ArdourCanvas::Rectangle (selection_group);
971 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
972 rect->rect->set_outline (false);
973 rect->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
975 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
976 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
977 rect->start_trim->set_outline (false);
978 rect->start_trim->set_fill (false);
980 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
981 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
982 rect->end_trim->set_outline (false);
983 rect->end_trim->set_fill (false);
985 free_selection_rects.push_front (rect);
987 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
988 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
989 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
992 rect = free_selection_rects.front();
994 free_selection_rects.pop_front();
998 struct null_deleter { void operator()(void const *) const {} };
1001 TimeAxisView::is_child (TimeAxisView* tav)
1003 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1007 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1009 children.push_back (child);
1013 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1015 Children::iterator i;
1017 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1022 /** Get selectable things within a given range.
1023 * @param start Start time in session samples.
1024 * @param end End time in session samples.
1025 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1026 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1027 * @param result Filled in with selectable things.
1030 TimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1032 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1033 if (!(*i)->hidden()) {
1034 (*i)->get_selectables (start, end, top, bot, results, within);
1040 TimeAxisView::set_selected_points (PointSelection& points)
1042 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1043 (*i)->set_selected_points (points);
1048 TimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1050 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1051 if (!(*i)->hidden()) {
1052 (*i)->get_inverted_selectables (sel, results);
1058 TimeAxisView::add_ghost (RegionView* rv)
1060 GhostRegion* gr = rv->add_ghost (*this);
1063 ghosts.push_back(gr);
1068 TimeAxisView::remove_ghost (RegionView* rv)
1070 rv->remove_ghost_in (*this);
1074 TimeAxisView::erase_ghost (GhostRegion* gr)
1076 if (in_destructor) {
1080 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1089 TimeAxisView::touched (double top, double bot)
1091 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1092 y_position is the "origin" or "top" of the track.
1095 double mybot = _y_position + current_height();
1097 return ((_y_position <= bot && _y_position >= top) ||
1098 ((mybot <= bot) && (top < mybot)) ||
1099 (mybot >= bot && _y_position < top));
1103 TimeAxisView::set_parent (TimeAxisView& p)
1109 TimeAxisView::reset_height ()
1111 set_height (height);
1113 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1114 (*i)->set_height ((*i)->height);
1119 TimeAxisView::compute_heights ()
1121 // TODO this function should be re-evaluated when font-scaling changes (!)
1122 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1123 Gtk::Table one_row_table (1, 1);
1124 ArdourButton* test_button = manage (new ArdourButton);
1125 const int border_width = 2;
1126 const int sample_height = 2;
1127 extra_height = (2 * border_width) + sample_height;
1129 window.add (one_row_table);
1130 test_button->set_name ("mute button");
1131 test_button->set_text (S_("Mute|M"));
1132 test_button->set_tweaks (ArdourButton::TrackHeader);
1134 one_row_table.set_border_width (border_width);
1135 one_row_table.set_row_spacings (2);
1136 one_row_table.set_col_spacings (2);
1138 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1139 one_row_table.show_all ();
1141 Gtk::Requisition req(one_row_table.size_request ());
1142 button_height = req.height;
1146 TimeAxisView::color_handler ()
1148 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1152 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1154 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1155 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1157 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1158 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1160 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1161 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1164 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1166 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1167 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1169 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1170 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1172 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1173 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1177 /** @return Pair: TimeAxisView, layer index.
1178 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1179 * does. @param y is an offset from the top of the trackview area.
1181 * If the covering object is a child axis, then the child is returned.
1182 * TimeAxisView is 0 otherwise.
1184 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1185 * and is in stacked or expanded * region display mode, otherwise 0.
1187 std::pair<TimeAxisView*, double>
1188 TimeAxisView::covers_y_position (double y) const
1191 return std::make_pair ((TimeAxisView *) 0, 0);
1194 if (_y_position <= y && y < (_y_position + height)) {
1196 /* work out the layer index if appropriate */
1198 switch (layer_display ()) {
1204 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1205 /* clamp to max layers to be on the safe side; sometimes the above calculation
1206 returns a too-high value */
1207 if (l >= view()->layers ()) {
1208 l = view()->layers() - 1;
1214 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1216 if (l >= (view()->layers() - 0.5)) {
1217 l = view()->layers() - 0.5;
1223 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1226 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1228 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1234 return std::make_pair ((TimeAxisView *) 0, 0);
1238 TimeAxisView::covered_by_y_range (double y0, double y1) const
1244 /* if either the top or bottom of the axisview is in the vertical
1245 * range, we cover it.
1248 if ((y0 < _y_position && y1 < _y_position) ||
1249 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1253 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1254 if ((*i)->covered_by_y_range (y0, y1)) {
1263 TimeAxisView::preset_height (Height h)
1267 return (button_height * 2) + extra_height + 260;
1269 return (button_height * 2) + extra_height + 160;
1271 return (button_height * 2) + extra_height + 60;
1273 return (button_height * 2) + extra_height + 10;
1275 return button_height + extra_height;
1278 abort(); /* NOTREACHED */
1282 /** @return Child time axis views that are not hidden */
1283 TimeAxisView::Children
1284 TimeAxisView::get_child_list () const
1288 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1289 if (!(*i)->hidden()) {
1298 TimeAxisView::build_size_menu ()
1300 if (_size_menu && _size_menu->gobj ()) {
1306 using namespace Menu_Helpers;
1308 _size_menu = new Menu;
1309 _size_menu->set_name ("ArdourContextMenu");
1310 MenuList& items = _size_menu->items();
1312 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1313 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1314 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1315 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1316 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1320 TimeAxisView::reset_visual_state ()
1322 /* this method is not required to trigger a global redraw */
1325 if (get_gui_property ("height", height)) {
1326 set_height (height);
1328 set_height (preset_height (HeightNormal));
1333 TrackViewList::filter_to_unique_playlists ()
1335 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1338 for (iterator i = begin(); i != end(); ++i) {
1339 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1341 /* not a route: include it anyway */
1344 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1346 if (playlists.insert (t->playlist()).second) {
1347 /* playlist not seen yet */
1351 /* not a track: include it anyway */