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 "audio_time_axis.h"
50 #include "floating_text_entry.h"
51 #include "gui_thread.h"
52 #include "public_editor.h"
53 #include "time_axis_view.h"
54 #include "region_view.h"
55 #include "ghostregion.h"
56 #include "selection.h"
58 #include "rgb_macros.h"
60 #include "streamview.h"
61 #include "editor_drag.h"
63 #include "ui_config.h"
70 using namespace ARDOUR;
72 using namespace Editing;
73 using namespace ArdourCanvas;
74 using namespace ArdourWidgets;
75 using Gtkmm2ext::Keyboard;
77 #define TOP_LEVEL_WIDGET controls_ebox
79 const double trim_handle_size = 6.0; /* pixels */
80 uint32_t TimeAxisView::button_height = 0;
81 uint32_t TimeAxisView::extra_height = 0;
82 int const TimeAxisView::_max_order = 512;
83 unsigned int TimeAxisView::name_width_px = 100;
84 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
85 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::controls_meters_size_group = Glib::RefPtr<Gtk::SizeGroup>();
86 Glib::RefPtr<Gtk::SizeGroup> TimeAxisView::midi_scroomer_size_group = Glib::RefPtr<Gtk::SizeGroup>();
89 TimeAxisView::setup_sizes()
91 name_width_px = ceilf (100.f * UIConfiguration::instance().get_ui_scale());
94 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
95 : controls_table (5, 4)
96 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
97 , _name_editing (false)
101 , selection_group (0)
104 , in_destructor (false)
106 , _canvas_display (0)
111 , _effective_height (0)
112 , _resize_drag_start (-1)
113 , _did_resize (false)
114 , _preresize_cursor (0)
115 , _have_preresize_cursor (false)
116 , _ebox_release_can_act (true)
118 if (!controls_meters_size_group) {
119 controls_meters_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
121 if (!midi_scroomer_size_group) {
122 midi_scroomer_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
124 if (extra_height == 0) {
128 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group ());
129 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
130 _canvas_display->hide(); // reveal as needed
132 _canvas_separator = new ArdourCanvas::Line(_canvas_display);
133 CANVAS_DEBUG_NAME (_canvas_separator, "separator for TAV");
134 _canvas_separator->set (ArdourCanvas::Duple(0.0, 0.0), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, 0.0));
135 _canvas_separator->set_outline_color(Gtkmm2ext::rgba_to_color (0, 0, 0, 1.0));
136 _canvas_separator->set_outline_width(1.0);
137 _canvas_separator->hide();
139 selection_group = new ArdourCanvas::Container (_canvas_display);
140 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
141 selection_group->set_data (X_("timeselection"), (void *) 1);
142 selection_group->hide();
144 _ghost_group = new ArdourCanvas::Container (_canvas_display);
145 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
146 _ghost_group->lower_to_bottom();
147 _ghost_group->show();
149 name_label.set_name (X_("TrackNameEditor"));
150 name_label.set_alignment (0.0, 0.5);
151 name_label.set_width_chars (12);
152 set_tooltip (name_label, _("Track/Bus name (double click to edit)"));
155 boost::scoped_ptr<Gtk::Entry> an_entry (new FocusEntry);
156 an_entry->set_name (X_("TrackNameEditor"));
157 Gtk::Requisition req = an_entry->size_request ();
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));
225 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &TimeAxisView::parameter_changed));
228 TimeAxisView::~TimeAxisView()
230 CatchDeletion (this);
232 in_destructor = true;
234 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
238 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
239 delete (*i)->rect; (*i)->rect=0;
240 delete (*i)->start_trim; (*i)->start_trim = 0;
241 delete (*i)->end_trim; (*i)->end_trim = 0;
245 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
246 delete (*i)->rect; (*i)->rect = 0;
247 delete (*i)->start_trim; (*i)->start_trim = 0;
248 delete (*i)->end_trim; (*i)->end_trim = 0;
251 delete selection_group;
254 delete _canvas_display;
264 TimeAxisView::hide ()
270 _canvas_display->hide ();
271 _canvas_separator->hide ();
273 if (control_parent) {
274 control_parent->remove (TOP_LEVEL_WIDGET);
281 /* now hide children */
283 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
287 /* if its hidden, it cannot be selected */
288 _editor.get_selection().remove (this);
289 /* and neither can its regions */
290 _editor.get_selection().remove_regions (this);
295 /** Display this TimeAxisView as the nth component of the parent box, at y.
297 * @param y y position.
298 * @param nth index for this TimeAxisView, increased if this view has children.
299 * @param parent parent component.
301 * @return height of this TimeAxisView.
304 TimeAxisView::show_at (double y, int& nth, VBox *parent)
306 if (control_parent) {
307 control_parent->reorder_child (TOP_LEVEL_WIDGET, nth);
309 control_parent = parent;
310 parent->pack_start (TOP_LEVEL_WIDGET, false, false);
311 parent->reorder_child (TOP_LEVEL_WIDGET, nth);
316 if (_y_position != y) {
317 _canvas_display->set_y_position (y);
321 _canvas_display->raise_to_top ();
322 _canvas_display->show ();
326 _effective_height = current_height ();
328 /* now show relevant children */
330 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
331 if ((*i)->marked_for_display()) {
333 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
339 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
343 /* put separator at the bottom of this time axis view */
345 _canvas_separator->set (ArdourCanvas::Duple(0, height), ArdourCanvas::Duple(ArdourCanvas::COORD_MAX, height));
346 _canvas_separator->lower_to_bottom ();
347 _canvas_separator->show ();
349 return _effective_height;
353 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
355 switch (ev->direction) {
357 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
358 /* See Editor::_stepping_axis_view for notes on this hack */
359 Editor& e = dynamic_cast<Editor&> (_editor);
360 if (!e.stepping_axis_view ()) {
361 e.set_stepping_axis_view (this);
363 e.stepping_axis_view()->step_height (false);
368 case GDK_SCROLL_DOWN:
369 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
370 /* See Editor::_stepping_axis_view for notes on this hack */
371 Editor& e = dynamic_cast<Editor&> (_editor);
372 if (!e.stepping_axis_view ()) {
373 e.set_stepping_axis_view (this);
375 e.stepping_axis_view()->step_height (true);
381 /* no handling for left/right, yet */
385 /* Just forward to the normal canvas scroll method. The coordinate
386 systems are different but since the canvas is always larger than the
387 track headers, and aligned with the trackview area, this will work.
389 In the not too distant future this layout is going away anyway and
390 headers will be on the canvas.
392 return _editor.canvas_scroll_event (ev, false);
396 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
398 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
399 /* see if it is inside the name label */
400 if (name_label.is_ancestor (controls_ebox)) {
403 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
404 Gtk::Allocation a = name_label.get_allocation ();
405 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
407 _ebox_release_can_act = false;
414 _ebox_release_can_act = true;
416 if (maybe_set_cursor (event->y) > 0) {
417 _resize_drag_start = event->y_root;
424 TimeAxisView::idle_resize (int32_t h)
426 set_height (std::max(0, h));
431 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
433 if (_resize_drag_start >= 0) {
435 /* (ab)use the DragManager to do autoscrolling - basically we
436 * are pretending that the drag is taking place over the canvas
437 * (which perhaps in the glorious future, when track headers
438 * and the canvas are unified, will actually be true.)
441 _editor.maybe_autoscroll (false, true, true);
443 /* now schedule the actual TAV resize */
444 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
445 _editor.add_to_idle_resize (this, delta);
446 _resize_drag_start = ev->y_root;
449 /* not dragging but ... */
450 maybe_set_cursor (ev->y);
453 gdk_event_request_motions(ev);
458 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
460 if (_have_preresize_cursor) {
461 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
462 _have_preresize_cursor = false;
468 TimeAxisView::maybe_set_cursor (int y)
470 /* XXX no Gtkmm Gdk::Window::get_cursor() */
471 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
473 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
475 /* y-coordinate in lower 25% */
477 if (!_have_preresize_cursor) {
478 _preresize_cursor = gdk_window_get_cursor (win->gobj());
479 _have_preresize_cursor = true;
480 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
485 } else if (_have_preresize_cursor) {
486 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
487 _have_preresize_cursor = false;
496 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
498 if (_resize_drag_start >= 0) {
499 if (_have_preresize_cursor) {
500 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
501 _preresize_cursor = 0;
502 _have_preresize_cursor = false;
504 _editor.stop_canvas_autoscroll ();
505 _resize_drag_start = -1;
508 // don't change selection
513 if (!_ebox_release_can_act) {
517 switch (ev->button) {
520 selection_click (ev);
525 popup_display_menu (ev->time);
533 TimeAxisView::selection_click (GdkEventButton* ev)
535 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
536 _editor.set_selected_track (*this, op, false);
540 /** Steps through the defined heights for this TrackView.
541 * @param coarser true if stepping should decrease in size, otherwise false.
544 TimeAxisView::step_height (bool coarser)
546 static const uint32_t step = 25;
550 if (height <= preset_height (HeightSmall)) {
552 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
553 set_height_enum (HeightSmall);
555 set_height (height - step);
560 if (height <= preset_height(HeightSmall)) {
561 set_height_enum (HeightNormal);
563 set_height (height + step);
570 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
572 if (apply_to_selection) {
573 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
575 set_height (preset_height (h));
580 TimeAxisView::set_height (uint32_t h, TrackHeightMode m)
583 if (m == TotalHeight) {
584 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
585 if (!(*i)->hidden()) {
592 if (h < preset_height (HeightSmall)) {
593 h = preset_height (HeightSmall);
596 TOP_LEVEL_WIDGET.property_height_request () = h;
599 set_gui_property ("height", height);
601 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
605 if (selection_group->visible ()) {
606 /* resize the selection rect */
607 show_selection (_editor.get_selection().time);
611 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
612 (*i)->set_height(h, OnlySelf);
616 _editor.override_visible_track_count ();
620 TimeAxisView::begin_name_edit ()
622 if (!can_edit_name()) {
626 Gtk::Window* toplevel = (Gtk::Window*) control_parent->get_toplevel();
627 FloatingTextEntry* fte = new FloatingTextEntry (toplevel, name ());
629 fte->set_name ("TrackNameEditor");
630 fte->use_text.connect (sigc::mem_fun (*this, &TimeAxisView::end_name_edit));
632 /* We want to new toplevel window to overlay the name label, so
633 * translate the coordinates of the upper left corner of the name label
634 * into the coordinate space of the top level window.
640 name_label.translate_coordinates (*toplevel, 0, 0, x, y);
641 toplevel->get_window()->get_origin (wx, wy);
643 fte->move (wx + x, wy + y);
648 TimeAxisView::end_name_edit (std::string str, int next_dir)
650 if (!name_entry_changed (str)) {
656 TrackViewList const & allviews = _editor.get_track_views ();
657 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
659 if (i != allviews.end()) {
662 if (++i == allviews.end()) {
666 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
668 if (rtav && rtav->is_track() && rtav->track()->rec_enable_control()->get_value()) {
672 if (!(*i)->hidden()) {
679 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
680 _editor.ensure_time_axis_view_is_visible (**i, false);
681 (*i)->begin_name_edit ();
684 } else if (next_dir < 0) {
686 TrackViewList const & allviews = _editor.get_track_views ();
687 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
689 if (i != allviews.begin()) {
691 if (i == allviews.begin()) {
697 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
699 if (rtav && rtav->is_track() && rtav->track()->rec_enable_control()->get_value()) {
703 if (!(*i)->hidden()) {
710 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
711 _editor.ensure_time_axis_view_is_visible (**i, false);
712 (*i)->begin_name_edit ();
718 TimeAxisView::name_entry_changed (string const&)
724 TimeAxisView::can_edit_name () const
730 TimeAxisView::conditionally_add_to_selection ()
736 Selection& s (_editor.get_selection ());
738 if (!s.selected (this)) {
739 _editor.set_selected_track (*this, Selection::Set);
744 TimeAxisView::popup_display_menu (guint32 when)
746 conditionally_add_to_selection ();
748 build_display_menu ();
750 if (!display_menu->items().empty()) {
751 display_menu->popup (1, when);
756 TimeAxisView::set_selected (bool yn)
758 if (yn == selected()) {
762 AxisView::set_selected (yn);
765 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
766 time_axis_frame.set_name ("MixerStripSelectedFrame");
767 controls_ebox.set_name (controls_base_selected_name);
768 controls_vbox.set_name (controls_base_selected_name);
769 time_axis_vbox.set_name (controls_base_selected_name);
771 time_axis_frame.set_shadow_type (Gtk::SHADOW_NONE);
772 time_axis_frame.set_name (controls_base_unselected_name);
773 controls_ebox.set_name (controls_base_unselected_name);
774 controls_vbox.set_name (controls_base_unselected_name);
775 time_axis_vbox.set_name (controls_base_unselected_name);
780 time_axis_frame.show();
784 TimeAxisView::build_display_menu ()
786 using namespace Menu_Helpers;
790 display_menu = new Menu;
791 display_menu->set_name ("ArdourContextMenu");
793 // Just let implementing classes define what goes into the manu
797 TimeAxisView::set_samples_per_pixel (double fpp)
799 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
800 (*i)->set_samples_per_pixel (fpp);
805 TimeAxisView::show_timestretch (samplepos_t start, samplepos_t end, int layers, int layer)
807 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
808 (*i)->show_timestretch (start, end, layers, layer);
813 TimeAxisView::hide_timestretch ()
815 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
816 (*i)->hide_timestretch ();
821 TimeAxisView::show_selection (TimeSelection& ts)
828 time_axis_frame.show();
830 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
831 if (!(*i)->selected () && !(*i)->propagate_time_selection ()) {
834 (*i)->show_selection (ts);
837 if (selection_group->visible ()) {
838 while (!used_selection_rects.empty()) {
839 free_selection_rects.push_front (used_selection_rects.front());
840 used_selection_rects.pop_front();
841 free_selection_rects.front()->rect->hide();
842 free_selection_rects.front()->start_trim->hide();
843 free_selection_rects.front()->end_trim->hide();
845 selection_group->hide();
848 selection_group->show();
849 selection_group->raise_to_top();
851 uint32_t gap = UIConfiguration::instance().get_vertical_region_gap ();
852 float ui_scale = UIConfiguration::instance().get_ui_scale ();
853 if (gap > 0 && ui_scale > 0) {
854 gap = ceil (gap * ui_scale);
857 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
858 samplepos_t start, end;
863 cnt = end - start + 1;
865 rect = get_selection_rect ((*i).id);
867 x1 = _editor.sample_to_pixel (start);
868 x2 = _editor.sample_to_pixel (start + cnt - 1);
869 y2 = current_height() - 1;
871 if (dynamic_cast<AudioTimeAxisView*>(this)) {
879 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
881 // trim boxes are at the top for selections
884 rect->start_trim->set (ArdourCanvas::Rect (x1, 0, x1 + trim_handle_size, y2));
885 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
887 rect->start_trim->show();
888 rect->end_trim->show();
890 rect->start_trim->hide();
891 rect->end_trim->hide();
895 used_selection_rects.push_back (rect);
900 TimeAxisView::reshow_selection (TimeSelection& ts)
904 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
905 if (!(*i)->selected () && !(*i)->propagate_time_selection ()) {
908 (*i)->show_selection (ts);
913 TimeAxisView::hide_selection ()
915 if (selection_group->visible ()) {
916 while (!used_selection_rects.empty()) {
917 free_selection_rects.push_front (used_selection_rects.front());
918 used_selection_rects.pop_front();
919 free_selection_rects.front()->rect->hide();
920 free_selection_rects.front()->start_trim->hide();
921 free_selection_rects.front()->end_trim->hide();
923 selection_group->hide();
926 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
927 (*i)->hide_selection ();
932 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
934 /* find the selection rect this is for. we have the item corresponding to one
938 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
939 if ((*i)->start_trim == item || (*i)->end_trim == item) {
941 /* make one trim handle be "above" the other so that if they overlap,
942 the top one is the one last used.
945 (*i)->rect->raise_to_top ();
946 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
947 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
954 // retuned rect is pushed back into the used_selection_rects list
955 // in TimeAxisView::show_selection() which is the only caller.
957 TimeAxisView::get_selection_rect (uint32_t id)
961 /* check to see if we already have a visible rect for this particular selection ID */
963 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
964 if ((*i)->id == id) {
965 SelectionRect* ret = (*i);
966 used_selection_rects.erase (i);
971 /* ditto for the free rect list */
973 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
974 if ((*i)->id == id) {
975 SelectionRect* ret = (*i);
976 free_selection_rects.erase (i);
981 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
983 if (free_selection_rects.empty()) {
985 rect = new SelectionRect;
987 rect->rect = new ArdourCanvas::Rectangle (selection_group);
988 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
989 rect->rect->set_outline (false);
990 rect->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
992 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
993 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
994 rect->start_trim->set_outline (false);
995 rect->start_trim->set_fill (false);
997 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
998 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
999 rect->end_trim->set_outline (false);
1000 rect->end_trim->set_fill (false);
1002 free_selection_rects.push_front (rect);
1004 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1005 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1006 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1009 rect = free_selection_rects.front();
1011 free_selection_rects.pop_front();
1015 struct null_deleter { void operator()(void const *) const {} };
1018 TimeAxisView::is_child (TimeAxisView* tav)
1020 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1024 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1026 children.push_back (child);
1030 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1032 Children::iterator i;
1034 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1039 /** Get selectable things within a given range.
1040 * @param start Start time in session samples.
1041 * @param end End time in session samples.
1042 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1043 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1044 * @param result Filled in with selectable things.
1047 TimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1049 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1050 if (!(*i)->hidden()) {
1051 (*i)->get_selectables (start, end, top, bot, results, within);
1057 TimeAxisView::set_selected_points (PointSelection& points)
1059 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1060 (*i)->set_selected_points (points);
1065 TimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1067 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1068 if (!(*i)->hidden()) {
1069 (*i)->get_inverted_selectables (sel, results);
1075 TimeAxisView::add_ghost (RegionView* rv)
1077 GhostRegion* gr = rv->add_ghost (*this);
1080 ghosts.push_back(gr);
1085 TimeAxisView::remove_ghost (RegionView* rv)
1087 rv->remove_ghost_in (*this);
1091 TimeAxisView::erase_ghost (GhostRegion* gr)
1093 if (in_destructor) {
1097 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1106 TimeAxisView::touched (double top, double bot)
1108 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1109 y_position is the "origin" or "top" of the track.
1112 double mybot = _y_position + current_height();
1114 return ((_y_position <= bot && _y_position >= top) ||
1115 ((mybot <= bot) && (top < mybot)) ||
1116 (mybot >= bot && _y_position < top));
1120 TimeAxisView::set_parent (TimeAxisView& p)
1126 TimeAxisView::reset_height ()
1128 set_height (height);
1130 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1131 (*i)->set_height ((*i)->height);
1136 TimeAxisView::compute_heights ()
1138 // TODO this function should be re-evaluated when font-scaling changes (!)
1139 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1140 Gtk::Table one_row_table (1, 1);
1141 ArdourButton* test_button = manage (new ArdourButton);
1142 const int border_width = 2;
1143 const int frame_height = 2;
1144 extra_height = (2 * border_width) + frame_height;
1146 window.add (one_row_table);
1147 test_button->set_name ("mute button");
1148 test_button->set_text (S_("Mute|M"));
1149 test_button->set_tweaks (ArdourButton::TrackHeader);
1151 one_row_table.set_border_width (border_width);
1152 one_row_table.set_row_spacings (2);
1153 one_row_table.set_col_spacings (2);
1155 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1156 one_row_table.show_all ();
1158 Gtk::Requisition req(one_row_table.size_request ());
1159 button_height = req.height;
1163 TimeAxisView::color_handler ()
1165 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1169 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1171 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1172 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1174 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1175 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1177 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1178 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1181 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1183 (*i)->rect->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
1184 (*i)->rect->set_outline_color (UIConfiguration::instance().color ("selection"));
1186 (*i)->start_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1187 (*i)->start_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1189 (*i)->end_trim->set_fill_color (UIConfiguration::instance().color ("selection"));
1190 (*i)->end_trim->set_outline_color (UIConfiguration::instance().color ("selection"));
1195 TimeAxisView::parameter_changed (string const & what_changed)
1197 if (what_changed == "vertical-region-gap") {
1199 show_selection (_editor.get_selection().time);
1204 view()->parameter_changed (what_changed);
1208 /** @return Pair: TimeAxisView, layer index.
1209 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1210 * does. @param y is an offset from the top of the trackview area.
1212 * If the covering object is a child axis, then the child is returned.
1213 * TimeAxisView is 0 otherwise.
1215 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1216 * and is in stacked or expanded * region display mode, otherwise 0.
1218 std::pair<TimeAxisView*, double>
1219 TimeAxisView::covers_y_position (double y) const
1222 return std::make_pair ((TimeAxisView *) 0, 0);
1225 if (_y_position <= y && y < (_y_position + height)) {
1227 /* work out the layer index if appropriate */
1229 switch (layer_display ()) {
1235 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1236 /* clamp to max layers to be on the safe side; sometimes the above calculation
1237 returns a too-high value */
1238 if (l >= view()->layers ()) {
1239 l = view()->layers() - 1;
1245 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1247 if (l >= (view()->layers() - 0.5)) {
1248 l = view()->layers() - 0.5;
1254 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1257 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1259 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1265 return std::make_pair ((TimeAxisView *) 0, 0);
1269 TimeAxisView::covered_by_y_range (double y0, double y1) const
1275 /* if either the top or bottom of the axisview is in the vertical
1276 * range, we cover it.
1279 if ((y0 < _y_position && y1 < _y_position) ||
1280 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1284 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1285 if ((*i)->covered_by_y_range (y0, y1)) {
1294 TimeAxisView::preset_height (Height h)
1298 return (button_height * 2) + extra_height + 260;
1300 return (button_height * 2) + extra_height + 160;
1302 return (button_height * 2) + extra_height + 60;
1304 return (button_height * 2) + extra_height + 10;
1306 return button_height + extra_height;
1309 abort(); /* NOTREACHED */
1313 /** @return Child time axis views that are not hidden */
1314 TimeAxisView::Children
1315 TimeAxisView::get_child_list () const
1319 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1320 if (!(*i)->hidden()) {
1329 TimeAxisView::build_size_menu ()
1331 if (_size_menu && _size_menu->gobj ()) {
1337 using namespace Menu_Helpers;
1339 _size_menu = new Menu;
1340 _size_menu->set_name ("ArdourContextMenu");
1341 MenuList& items = _size_menu->items();
1343 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1344 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1345 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1346 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1347 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1351 TimeAxisView::reset_visual_state ()
1353 /* this method is not required to trigger a global redraw */
1356 if (get_gui_property ("height", height)) {
1357 set_height (height);
1359 set_height (preset_height (HeightNormal));
1364 TrackViewList::filter_to_unique_playlists ()
1366 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1369 for (iterator i = begin(); i != end(); ++i) {
1370 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1372 /* not a route: include it anyway */
1375 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1377 if (playlists.insert (t->playlist()).second) {
1378 /* playlist not seen yet */
1382 /* not a track: include it anyway */