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 = an_entry->size_request ();
158 name_label.set_size_request (-1, req.height);
159 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
162 // set min. track-header width if fader is not visible
163 name_label.set_size_request(name_width_px, -1);
167 controls_table.set_row_spacings (2);
168 controls_table.set_col_spacings (2);
169 controls_table.set_border_width (2);
171 if (ARDOUR::Profile->get_mixbus() ) {
172 controls_table.attach (name_label, 4, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
174 controls_table.attach (name_label, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK, 0, 0);
177 controls_table.show_all ();
178 controls_table.set_no_show_all ();
180 controls_vbox.pack_start (controls_table, false, false);
181 controls_vbox.show ();
183 top_hbox.pack_start (controls_vbox, true, true);
186 controls_ebox.add (time_axis_hbox);
187 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
188 Gdk::BUTTON_RELEASE_MASK|
189 Gdk::POINTER_MOTION_MASK|
190 Gdk::ENTER_NOTIFY_MASK|
191 Gdk::LEAVE_NOTIFY_MASK|
193 controls_ebox.set_flags (CAN_FOCUS);
195 /* note that this handler connects *before* the default handler */
196 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
197 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
198 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
199 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
200 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
201 controls_ebox.show ();
203 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
204 time_axis_frame.add(top_hbox);
205 time_axis_frame.show();
207 HSeparator* separator = manage (new HSeparator());
208 separator->set_name("TrackSeparator");
209 separator->set_size_request(-1, 1);
212 scroomer_placeholder.set_size_request (-1, -1);
213 scroomer_placeholder.show();
214 midi_scroomer_size_group->add_widget (scroomer_placeholder);
216 time_axis_vbox.pack_start (*separator, false, false);
217 time_axis_vbox.pack_start (time_axis_frame, true, true);
218 time_axis_vbox.show();
219 time_axis_hbox.pack_start (time_axis_vbox, true, true);
220 time_axis_hbox.show();
221 top_hbox.pack_start (scroomer_placeholder, false, false); // OR pack_end to move after meters ?
223 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
226 TimeAxisView::~TimeAxisView()
228 CatchDeletion (this);
230 in_destructor = true;
232 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
236 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
237 delete (*i)->rect; (*i)->rect=0;
238 delete (*i)->start_trim; (*i)->start_trim = 0;
239 delete (*i)->end_trim; (*i)->end_trim = 0;
243 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
244 delete (*i)->rect; (*i)->rect = 0;
245 delete (*i)->start_trim; (*i)->start_trim = 0;
246 delete (*i)->end_trim; (*i)->end_trim = 0;
249 delete selection_group;
252 delete _canvas_display;
262 TimeAxisView::hide ()
268 _canvas_display->hide ();
269 _canvas_separator->hide ();
271 if (control_parent) {
272 control_parent->remove (TOP_LEVEL_WIDGET);
279 /* now hide children */
281 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
285 /* if its hidden, it cannot be selected */
286 _editor.get_selection().remove (this);
287 /* and neither can its regions */
288 _editor.get_selection().remove_regions (this);
293 /** Display this TimeAxisView as the nth component of the parent box, at y.
295 * @param y y position.
296 * @param nth index for this TimeAxisView, increased if this view has children.
297 * @param parent parent component.
299 * @return height of this TimeAxisView.
302 TimeAxisView::show_at (double y, int& nth, VBox *parent)
304 if (control_parent) {
305 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
307 control_parent = parent;
308 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
309 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
314 if (_y_position != y) {
315 _canvas_display->set_y_position (y);
319 _canvas_display->raise_to_top ();
320 _canvas_display->show ();
324 _effective_height = current_height ();
326 /* now show relevant children */
328 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
329 if ((*i)->marked_for_display()) {
331 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
337 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
341 /* put separator at the bottom of this time axis view */
343 _canvas_separator->set (ArdourCanvas::Duple(0, height), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, height));
344 _canvas_separator->lower_to_bottom ();
345 _canvas_separator->show ();
347 return _effective_height;
351 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
353 switch (ev->direction) {
355 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
356 /* See Editor::_stepping_axis_view for notes on this hack */
357 Editor& e = dynamic_cast<Editor&> (_editor);
358 if (!e.stepping_axis_view ()) {
359 e.set_stepping_axis_view (this);
361 e.stepping_axis_view()->step_height (false);
366 case GDK_SCROLL_DOWN:
367 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
368 /* See Editor::_stepping_axis_view for notes on this hack */
369 Editor& e = dynamic_cast<Editor&> (_editor);
370 if (!e.stepping_axis_view ()) {
371 e.set_stepping_axis_view (this);
373 e.stepping_axis_view()->step_height (true);
379 /* no handling for left/right, yet */
383 /* Just forward to the normal canvas scroll method. The coordinate
384 systems are different but since the canvas is always larger than the
385 track headers, and aligned with the trackview area, this will work.
387 In the not too distant future this layout is going away anyway and
388 headers will be on the canvas.
390 return _editor.canvas_scroll_event (ev, false);
394 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
396 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
397 /* see if it is inside the name label */
398 if (name_label.is_ancestor (controls_ebox)) {
401 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
402 Gtk::Allocation a = name_label.get_allocation ();
403 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
405 _ebox_release_can_act = false;
412 _ebox_release_can_act = true;
414 if (maybe_set_cursor (event->y) > 0) {
415 _resize_drag_start = event->y_root;
422 TimeAxisView::idle_resize (int32_t h)
424 set_height (std::max(0, h));
429 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
431 if (_resize_drag_start >= 0) {
433 /* (ab)use the DragManager to do autoscrolling - basically we
434 * are pretending that the drag is taking place over the canvas
435 * (which perhaps in the glorious future, when track headers
436 * and the canvas are unified, will actually be true.)
439 _editor.maybe_autoscroll (false, true, true);
441 /* now schedule the actual TAV resize */
442 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
443 _editor.add_to_idle_resize (this, delta);
444 _resize_drag_start = ev->y_root;
447 /* not dragging but ... */
448 maybe_set_cursor (ev->y);
451 gdk_event_request_motions(ev);
456 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
458 if (_have_preresize_cursor) {
459 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
460 _have_preresize_cursor = false;
466 TimeAxisView::maybe_set_cursor (int y)
468 /* XXX no Gtkmm Gdk::Window::get_cursor() */
469 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
471 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
473 /* y-coordinate in lower 25% */
475 if (!_have_preresize_cursor) {
476 _preresize_cursor = gdk_window_get_cursor (win->gobj());
477 _have_preresize_cursor = true;
478 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
483 } else if (_have_preresize_cursor) {
484 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
485 _have_preresize_cursor = false;
494 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
496 if (_resize_drag_start >= 0) {
497 if (_have_preresize_cursor) {
498 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
499 _preresize_cursor = 0;
500 _have_preresize_cursor = false;
502 _editor.stop_canvas_autoscroll ();
503 _resize_drag_start = -1;
506 // don't change selection
511 if (!_ebox_release_can_act) {
515 switch (ev->button) {
518 selection_click (ev);
523 popup_display_menu (ev->time);
531 TimeAxisView::selection_click (GdkEventButton* ev)
533 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
534 _editor.set_selected_track (*this, op, false);
538 /** Steps through the defined heights for this TrackView.
539 * @param coarser true if stepping should decrease in size, otherwise false.
542 TimeAxisView::step_height (bool coarser)
544 static const uint32_t step = 25;
548 if (height <= preset_height (HeightSmall)) {
550 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
551 set_height_enum (HeightSmall);
553 set_height (height - step);
558 if (height <= preset_height(HeightSmall)) {
559 set_height_enum (HeightNormal);
561 set_height (height + step);
568 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
570 if (apply_to_selection) {
571 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
573 set_height (preset_height (h));
578 TimeAxisView::set_height (uint32_t h, TrackHeightMode m)
581 if (m == TotalHeight) {
582 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
583 if ( !(*i)->hidden()) ++lanes;
588 if (h < preset_height (HeightSmall)) {
589 h = preset_height (HeightSmall);
592 TOP_LEVEL_WIDGET.property_height_request () = h;
595 set_gui_property ("height", height);
597 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
601 if (selection_group->visible ()) {
602 /* resize the selection rect */
603 show_selection (_editor.get_selection().time);
607 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
608 (*i)->set_height(h, OnlySelf);
612 _editor.override_visible_track_count ();
616 TimeAxisView::begin_name_edit ()
618 if (!can_edit_name()) {
622 Gtk::Window* toplevel = (Gtk::Window*) control_parent->get_toplevel();
623 FloatingTextEntry* fte = new FloatingTextEntry (toplevel, name ());
625 fte->set_name ("TrackNameEditor");
626 fte->use_text.connect (sigc::mem_fun (*this, &TimeAxisView::end_name_edit));
628 /* We want to new toplevel window to overlay the name label, so
629 * translate the coordinates of the upper left corner of the name label
630 * into the coordinate space of the top level window.
636 name_label.translate_coordinates (*toplevel, 0, 0, x, y);
637 toplevel->get_window()->get_origin (wx, wy);
639 fte->move (wx + x, wy + y);
644 TimeAxisView::end_name_edit (std::string str, int next_dir)
646 if (!name_entry_changed (str)) {
652 TrackViewList const & allviews = _editor.get_track_views ();
653 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
655 if (i != allviews.end()) {
658 if (++i == allviews.end()) {
662 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
664 if (rtav && rtav->is_track() && rtav->track()->rec_enable_control()->get_value()) {
668 if (!(*i)->hidden()) {
675 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
676 _editor.ensure_time_axis_view_is_visible (**i, false);
677 (*i)->begin_name_edit ();
680 } else if (next_dir < 0) {
682 TrackViewList const & allviews = _editor.get_track_views ();
683 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
685 if (i != allviews.begin()) {
687 if (i == allviews.begin()) {
693 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
695 if (rtav && rtav->is_track() && rtav->track()->rec_enable_control()->get_value()) {
699 if (!(*i)->hidden()) {
706 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
707 _editor.ensure_time_axis_view_is_visible (**i, false);
708 (*i)->begin_name_edit ();
714 TimeAxisView::name_entry_changed (string const&)
720 TimeAxisView::can_edit_name () const
726 TimeAxisView::conditionally_add_to_selection ()
732 Selection& s (_editor.get_selection ());
734 if (!s.selected (this)) {
735 _editor.set_selected_track (*this, Selection::Set);
740 TimeAxisView::popup_display_menu (guint32 when)
742 conditionally_add_to_selection ();
744 build_display_menu ();
746 if (!display_menu->items().empty()) {
747 display_menu->popup (1, when);
752 TimeAxisView::set_selected (bool yn)
754 if (yn == selected()) {
758 AxisView::set_selected (yn);
761 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
762 time_axis_frame.set_name ("MixerStripSelectedFrame");
763 controls_ebox.set_name (controls_base_selected_name);
764 controls_vbox.set_name (controls_base_selected_name);
765 time_axis_vbox.set_name (controls_base_selected_name);
767 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
768 time_axis_frame.set_name (controls_base_unselected_name);
769 controls_ebox.set_name (controls_base_unselected_name);
770 controls_vbox.set_name (controls_base_unselected_name);
771 time_axis_vbox.set_name (controls_base_unselected_name);
776 time_axis_frame.show();
780 TimeAxisView::build_display_menu ()
782 using namespace Menu_Helpers;
786 display_menu = new Menu;
787 display_menu->set_name ("ArdourContextMenu");
789 // Just let implementing classes define what goes into the manu
793 TimeAxisView::set_samples_per_pixel (double fpp)
795 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
796 (*i)->set_samples_per_pixel (fpp);
801 TimeAxisView::show_timestretch (samplepos_t start, samplepos_t end, int layers, int layer)
803 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
804 (*i)->show_timestretch (start, end, layers, layer);
809 TimeAxisView::hide_timestretch ()
811 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
812 (*i)->hide_timestretch ();
817 TimeAxisView::show_selection (TimeSelection& ts)
824 time_axis_frame.show();
826 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
827 if (!(*i)->selected () && !(*i)->propagate_time_selection ()) {
830 (*i)->show_selection (ts);
833 if (selection_group->visible ()) {
834 while (!used_selection_rects.empty()) {
835 free_selection_rects.push_front (used_selection_rects.front());
836 used_selection_rects.pop_front();
837 free_selection_rects.front()->rect->hide();
838 free_selection_rects.front()->start_trim->hide();
839 free_selection_rects.front()->end_trim->hide();
841 selection_group->hide();
844 selection_group->show();
845 selection_group->raise_to_top();
847 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
848 samplepos_t start, end;
853 cnt = end - start + 1;
855 rect = get_selection_rect ((*i).id);
857 x1 = _editor.sample_to_pixel (start);
858 x2 = _editor.sample_to_pixel (start + cnt - 1);
859 y2 = current_height() - 1;
861 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
863 // trim boxes are at the top for selections
866 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
867 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
869 rect->start_trim->show();
870 rect->end_trim->show();
872 rect->start_trim->hide();
873 rect->end_trim->hide();
877 used_selection_rects.push_back (rect);
882 TimeAxisView::reshow_selection (TimeSelection& ts)
886 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
887 if (!(*i)->selected () && !(*i)->propagate_time_selection ()) {
890 (*i)->show_selection (ts);
895 TimeAxisView::hide_selection ()
897 if (selection_group->visible ()) {
898 while (!used_selection_rects.empty()) {
899 free_selection_rects.push_front (used_selection_rects.front());
900 used_selection_rects.pop_front();
901 free_selection_rects.front()->rect->hide();
902 free_selection_rects.front()->start_trim->hide();
903 free_selection_rects.front()->end_trim->hide();
905 selection_group->hide();
908 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
909 (*i)->hide_selection ();
914 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
916 /* find the selection rect this is for. we have the item corresponding to one
920 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
921 if ((*i)->start_trim == item || (*i)->end_trim == item) {
923 /* make one trim handle be "above" the other so that if they overlap,
924 the top one is the one last used.
927 (*i)->rect->raise_to_top ();
928 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
929 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
936 // retuned rect is pushed back into the used_selection_rects list
937 // in TimeAxisView::show_selection() which is the only caller.
939 TimeAxisView::get_selection_rect (uint32_t id)
943 /* check to see if we already have a visible rect for this particular selection ID */
945 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
946 if ((*i)->id == id) {
947 SelectionRect* ret = (*i);
948 used_selection_rects.erase (i);
953 /* ditto for the free rect list */
955 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
956 if ((*i)->id == id) {
957 SelectionRect* ret = (*i);
958 free_selection_rects.erase (i);
963 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
965 if (free_selection_rects.empty()) {
967 rect = new SelectionRect;
969 rect->rect = new ArdourCanvas::Rectangle (selection_group);
970 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
971 rect->rect->set_outline (false);
972 rect->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
974 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
975 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
976 rect->start_trim->set_outline (false);
977 rect->start_trim->set_fill (false);
979 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
980 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
981 rect->end_trim->set_outline (false);
982 rect->end_trim->set_fill (false);
984 free_selection_rects.push_front (rect);
986 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
987 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
988 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
991 rect = free_selection_rects.front();
993 free_selection_rects.pop_front();
997 struct null_deleter { void operator()(void const *) const {} };
1000 TimeAxisView::is_child (TimeAxisView* tav)
1002 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1006 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1008 children.push_back (child);
1012 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1014 Children::iterator i;
1016 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1021 /** Get selectable things within a given range.
1022 * @param start Start time in session samples.
1023 * @param end End time in session samples.
1024 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1025 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1026 * @param result Filled in with selectable things.
1029 TimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1031 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1032 if (!(*i)->hidden()) {
1033 (*i)->get_selectables (start, end, top, bot, results, within);
1039 TimeAxisView::set_selected_points (PointSelection& points)
1041 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1042 (*i)->set_selected_points (points);
1047 TimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1049 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1050 if (!(*i)->hidden()) {
1051 (*i)->get_inverted_selectables (sel, results);
1057 TimeAxisView::add_ghost (RegionView* rv)
1059 GhostRegion* gr = rv->add_ghost (*this);
1062 ghosts.push_back(gr);
1067 TimeAxisView::remove_ghost (RegionView* rv)
1069 rv->remove_ghost_in (*this);
1073 TimeAxisView::erase_ghost (GhostRegion* gr)
1075 if (in_destructor) {
1079 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1088 TimeAxisView::touched (double top, double bot)
1090 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1091 y_position is the "origin" or "top" of the track.
1094 double mybot = _y_position + current_height();
1096 return ((_y_position <= bot && _y_position >= top) ||
1097 ((mybot <= bot) && (top < mybot)) ||
1098 (mybot >= bot && _y_position < top));
1102 TimeAxisView::set_parent (TimeAxisView& p)
1108 TimeAxisView::reset_height ()
1110 set_height (height);
1112 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1113 (*i)->set_height ((*i)->height);
1118 TimeAxisView::compute_heights ()
1120 // TODO this function should be re-evaluated when font-scaling changes (!)
1121 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1122 Gtk::Table one_row_table (1, 1);
1123 ArdourButton* test_button = manage (new ArdourButton);
1124 const int border_width = 2;
1125 const int sample_height = 2;
1126 extra_height = (2 * border_width) + sample_height;
1128 window.add (one_row_table);
1129 test_button->set_name ("mute button");
1130 test_button->set_text (S_("Mute|M"));
1131 test_button->set_tweaks (ArdourButton::TrackHeader);
1133 one_row_table.set_border_width (border_width);
1134 one_row_table.set_row_spacings (2);
1135 one_row_table.set_col_spacings (2);
1137 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1138 one_row_table.show_all ();
1140 Gtk::Requisition req(one_row_table.size_request ());
1141 button_height = req.height;
1145 TimeAxisView::color_handler ()
1147 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1151 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1153 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1154 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1156 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1157 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1159 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1160 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1163 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1165 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1166 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1168 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1169 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1171 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1172 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1176 /** @return Pair: TimeAxisView, layer index.
1177 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1178 * does. @param y is an offset from the top of the trackview area.
1180 * If the covering object is a child axis, then the child is returned.
1181 * TimeAxisView is 0 otherwise.
1183 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1184 * and is in stacked or expanded * region display mode, otherwise 0.
1186 std::pair<TimeAxisView*, double>
1187 TimeAxisView::covers_y_position (double y) const
1190 return std::make_pair ((TimeAxisView *) 0, 0);
1193 if (_y_position <= y && y < (_y_position + height)) {
1195 /* work out the layer index if appropriate */
1197 switch (layer_display ()) {
1203 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1204 /* clamp to max layers to be on the safe side; sometimes the above calculation
1205 returns a too-high value */
1206 if (l >= view()->layers ()) {
1207 l = view()->layers() - 1;
1213 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1215 if (l >= (view()->layers() - 0.5)) {
1216 l = view()->layers() - 0.5;
1222 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1225 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1227 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1233 return std::make_pair ((TimeAxisView *) 0, 0);
1237 TimeAxisView::covered_by_y_range (double y0, double y1) const
1243 /* if either the top or bottom of the axisview is in the vertical
1244 * range, we cover it.
1247 if ((y0 < _y_position && y1 < _y_position) ||
1248 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1252 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1253 if ((*i)->covered_by_y_range (y0, y1)) {
1262 TimeAxisView::preset_height (Height h)
1266 return (button_height * 2) + extra_height + 260;
1268 return (button_height * 2) + extra_height + 160;
1270 return (button_height * 2) + extra_height + 60;
1272 return (button_height * 2) + extra_height + 10;
1274 return button_height + extra_height;
1277 abort(); /* NOTREACHED */
1281 /** @return Child time axis views that are not hidden */
1282 TimeAxisView::Children
1283 TimeAxisView::get_child_list () const
1287 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1288 if (!(*i)->hidden()) {
1297 TimeAxisView::build_size_menu ()
1299 if (_size_menu && _size_menu->gobj ()) {
1305 using namespace Menu_Helpers;
1307 _size_menu = new Menu;
1308 _size_menu->set_name ("ArdourContextMenu");
1309 MenuList& items = _size_menu->items();
1311 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1312 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1313 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1314 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1315 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1319 TimeAxisView::reset_visual_state ()
1321 /* this method is not required to trigger a global redraw */
1324 if (get_gui_property ("height", height)) {
1325 set_height (height);
1327 set_height (preset_height (HeightNormal));
1332 TrackViewList::filter_to_unique_playlists ()
1334 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1337 for (iterator i = begin(); i != end(); ++i) {
1338 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1340 /* not a route: include it anyway */
1343 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1345 if (playlists.insert (t->playlist()).second) {
1346 /* playlist not seen yet */
1350 /* not a track: include it anyway */