2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "pbd/error.h"
28 #include "pbd/convert.h"
29 #include "pbd/stacktrace.h"
31 #include <gtkmm2ext/doi.h>
32 #include <gtkmm2ext/utils.h>
33 #include <gtkmm2ext/selector.h>
35 #include "canvas/canvas.h"
36 #include "canvas/rectangle.h"
37 #include "canvas/debug.h"
39 #include "ardour_ui.h"
40 #include "ardour_dialog.h"
41 #include "global_signals.h"
42 #include "gui_thread.h"
43 #include "public_editor.h"
44 #include "time_axis_view.h"
45 #include "region_view.h"
46 #include "ghostregion.h"
47 #include "selection.h"
49 #include "rgb_macros.h"
51 #include "streamview.h"
52 #include "editor_drag.h"
60 using namespace ARDOUR;
61 using namespace ARDOUR_UI_UTILS;
63 using namespace Editing;
64 using namespace ArdourCanvas;
65 using Gtkmm2ext::Keyboard;
67 const double trim_handle_size = 6.0; /* pixels */
68 uint32_t TimeAxisView::button_height = 0;
69 uint32_t TimeAxisView::extra_height = 0;
70 int const TimeAxisView::_max_order = 512;
71 PBD::Signal1<void,TimeAxisView*> TimeAxisView::CatchDeletion;
73 TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisView* rent, Canvas& /*canvas*/)
75 , controls_table (4, 4)
76 , controls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
77 , _name_editing (false)
84 , in_destructor (false)
92 , _effective_height (0)
93 , _resize_drag_start (-1)
94 , _preresize_cursor (0)
95 , _have_preresize_cursor (false)
96 , _ebox_release_can_act (true)
98 if (extra_height == 0) {
102 _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
103 CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
104 _canvas_display->hide(); // reveal as needed
106 selection_group = new ArdourCanvas::Container (_canvas_display);
107 CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
108 selection_group->set_data (X_("timeselection"), (void *) 1);
109 selection_group->hide();
111 _ghost_group = new ArdourCanvas::Container (_canvas_display);
112 CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
113 _ghost_group->lower_to_bottom();
114 _ghost_group->show();
116 name_label.set_name ("TrackLabel");
117 name_label.set_alignment (0.0, 0.5);
118 name_label.set_width_chars (12);
119 ARDOUR_UI::instance()->set_tip (name_label, _("Track/Bus name (double click to edit)"));
121 Gtk::Entry* an_entry = new Gtk::Entry;
122 Gtk::Requisition req;
123 an_entry->size_request (req);
124 name_label.set_size_request (-1, req.height);
125 name_label.set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
128 name_hbox.pack_end (name_label, true, true);
129 name_hbox.set_size_request(100, 0); // XXX min header width (if fader is not visible)
133 controls_table.set_row_spacings (2);
134 controls_table.set_col_spacings (2);
135 controls_table.set_border_width (2);
137 controls_table.attach (name_hbox, 4, 5, 0, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
138 controls_table.show_all ();
139 controls_table.set_no_show_all ();
141 controls_vbox.pack_start (controls_table, false, false);
142 controls_vbox.show ();
144 top_hbox.pack_start (controls_vbox, true, true);
147 //controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
148 controls_ebox.add (top_hbox);
149 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
150 Gdk::BUTTON_RELEASE_MASK|
151 Gdk::POINTER_MOTION_MASK|
152 Gdk::ENTER_NOTIFY_MASK|
153 Gdk::LEAVE_NOTIFY_MASK|
155 controls_ebox.set_flags (CAN_FOCUS);
157 /* note that this handler connects *before* the default handler */
158 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
159 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
160 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
161 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
162 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
163 controls_ebox.show ();
165 time_axis_frame.set_shadow_type (Gtk::SHADOW_OUT);
166 time_axis_frame.add(controls_ebox);
167 time_axis_frame.show();
169 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
171 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
174 TimeAxisView::~TimeAxisView()
176 in_destructor = true;
178 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
182 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
184 delete (*i)->start_trim;
185 delete (*i)->end_trim;
189 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
191 delete (*i)->start_trim;
192 delete (*i)->end_trim;
195 delete selection_group;
198 delete _canvas_display;
208 TimeAxisView::hide ()
214 _canvas_display->hide ();
216 if (control_parent) {
217 control_parent->remove (time_axis_frame);
224 /* now hide children */
226 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
230 /* if its hidden, it cannot be selected */
231 _editor.get_selection().remove (this);
232 /* and neither can its regions */
233 _editor.get_selection().remove_regions (this);
238 /** Display this TimeAxisView as the nth component of the parent box, at y.
240 * @param y y position.
241 * @param nth index for this TimeAxisView, increased if this view has children.
242 * @param parent parent component.
243 * @return height of this TimeAxisView.
246 TimeAxisView::show_at (double y, int& nth, VBox *parent)
248 if (control_parent) {
249 control_parent->reorder_child (time_axis_frame, nth);
251 control_parent = parent;
252 parent->pack_start (time_axis_frame, false, false);
253 parent->reorder_child (time_axis_frame, nth);
258 if (_y_position != y) {
259 _canvas_display->set_y_position (y);
264 _canvas_display->raise_to_top ();
265 _canvas_display->show ();
269 _effective_height = current_height ();
271 /* now show relevant children */
273 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
274 if ((*i)->marked_for_display()) {
276 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
282 return _effective_height;
286 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
288 switch (ev->direction) {
290 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
291 /* See Editor::_stepping_axis_view for notes on this hack */
292 Editor& e = dynamic_cast<Editor&> (_editor);
293 if (!e.stepping_axis_view ()) {
294 e.set_stepping_axis_view (this);
296 e.stepping_axis_view()->step_height (false);
301 case GDK_SCROLL_DOWN:
302 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
303 /* See Editor::_stepping_axis_view for notes on this hack */
304 Editor& e = dynamic_cast<Editor&> (_editor);
305 if (!e.stepping_axis_view ()) {
306 e.set_stepping_axis_view (this);
308 e.stepping_axis_view()->step_height (true);
314 /* no handling for left/right, yet */
318 /* Just forward to the normal canvas scroll method. The coordinate
319 systems are different but since the canvas is always larger than the
320 track headers, and aligned with the trackview area, this will work.
322 In the not too distant future this layout is going away anyway and
323 headers will be on the canvas.
325 return _editor.canvas_scroll_event (ev, false);
329 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
331 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
332 /* see if it is inside the name label */
333 if (name_label.is_ancestor (controls_ebox)) {
336 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
337 Gtk::Allocation a = name_label.get_allocation ();
338 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
340 _ebox_release_can_act = false;
347 _ebox_release_can_act = true;
349 if (maybe_set_cursor (event->y) > 0) {
350 _resize_drag_start = event->y_root;
357 TimeAxisView::idle_resize (uint32_t h)
364 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
366 if (_resize_drag_start >= 0) {
368 /* (ab)use the DragManager to do autoscrolling - basically we
369 * are pretending that the drag is taking place over the canvas
370 * (which perhaps in the glorious future, when track headers
371 * and the canvas are unified, will actually be true.)
374 _editor.maybe_autoscroll (false, true, true);
376 /* now schedule the actual TAV resize */
377 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
378 _editor.add_to_idle_resize (this, delta);
379 _resize_drag_start = ev->y_root;
381 /* not dragging but ... */
382 maybe_set_cursor (ev->y);
385 gdk_event_request_motions(ev);
390 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
392 if (_have_preresize_cursor) {
393 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
394 _have_preresize_cursor = false;
400 TimeAxisView::maybe_set_cursor (int y)
402 /* XXX no Gtkmm Gdk::Window::get_cursor() */
403 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
405 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
407 /* y-coordinate in lower 25% */
409 if (!_have_preresize_cursor) {
410 _preresize_cursor = gdk_window_get_cursor (win->gobj());
411 _have_preresize_cursor = true;
412 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
417 } else if (_have_preresize_cursor) {
418 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
419 _have_preresize_cursor = false;
428 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
430 if (_resize_drag_start >= 0) {
431 if (_have_preresize_cursor) {
432 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
433 _preresize_cursor = 0;
434 _have_preresize_cursor = false;
436 _editor.stop_canvas_autoscroll ();
437 _resize_drag_start = -1;
440 if (!_ebox_release_can_act) {
444 switch (ev->button) {
446 selection_click (ev);
450 popup_display_menu (ev->time);
458 TimeAxisView::selection_click (GdkEventButton* ev)
460 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
461 _editor.set_selected_track (*this, op, false);
465 /** Steps through the defined heights for this TrackView.
466 * @param coarser true if stepping should decrease in size, otherwise false.
469 TimeAxisView::step_height (bool coarser)
471 static const uint32_t step = 25;
475 if (height <= preset_height (HeightSmall)) {
477 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
478 set_height_enum (HeightSmall);
480 set_height (height - step);
485 if (height <= preset_height(HeightSmall)) {
486 set_height_enum (HeightNormal);
488 set_height (height + step);
495 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
497 if (apply_to_selection) {
498 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
500 set_height (preset_height (h));
505 TimeAxisView::set_height (uint32_t h)
507 if (h < preset_height (HeightSmall)) {
508 h = preset_height (HeightSmall);
511 time_axis_frame.property_height_request () = h;
515 snprintf (buf, sizeof (buf), "%u", height);
516 set_gui_property ("height", buf);
518 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
522 if (selection_group->visible ()) {
523 /* resize the selection rect */
524 show_selection (_editor.get_selection().time);
527 _editor.override_visible_track_count ();
531 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
533 /* steal escape, tabs from GTK */
535 switch (ev->keyval) {
537 case GDK_ISO_Left_Tab:
545 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
547 TrackViewList::iterator i;
549 switch (ev->keyval) {
551 end_name_edit (RESPONSE_CANCEL);
554 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
555 * generates a different ev->keyval, rather than setting
558 case GDK_ISO_Left_Tab:
559 end_name_edit (RESPONSE_APPLY);
563 end_name_edit (RESPONSE_ACCEPT);
573 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
575 end_name_edit (RESPONSE_OK);
580 TimeAxisView::begin_name_edit ()
586 if (can_edit_name()) {
588 name_entry = manage (new Gtkmm2ext::FocusEntry);
590 name_entry->set_width_chars(8); // min width, entry expands
592 name_entry->set_name ("EditorTrackNameDisplay");
593 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
594 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
595 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
596 name_entry->set_text (name_label.get_text());
597 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
599 if (name_label.is_ancestor (name_hbox)) {
600 name_hbox.remove (name_label);
603 name_hbox.pack_end (*name_entry, true, true);
606 name_entry->select_region (0, -1);
607 name_entry->set_state (STATE_SELECTED);
608 name_entry->grab_focus ();
609 name_entry->start_editing (0);
614 TimeAxisView::end_name_edit (int response)
620 bool edit_next = false;
621 bool edit_prev = false;
624 case RESPONSE_CANCEL:
627 name_entry_changed ();
629 case RESPONSE_ACCEPT:
630 name_entry_changed ();
633 name_entry_changed ();
637 /* this will delete the name_entry. but it will also drop focus, which
638 * will cause another callback to this function, so set name_entry = 0
639 * first to ensure we don't double-remove etc. etc.
642 Gtk::Entry* tmp = name_entry;
644 name_hbox.remove (*tmp);
646 /* put the name label back */
648 name_hbox.pack_end (name_label);
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->route()->record_enabled()) {
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 (edit_prev) {
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->route()->record_enabled()) {
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 ()
720 TimeAxisView::can_edit_name () const
726 TimeAxisView::conditionally_add_to_selection ()
728 Selection& s (_editor.get_selection ());
730 if (!s.selected (this)) {
731 _editor.set_selected_track (*this, Selection::Set);
736 TimeAxisView::popup_display_menu (guint32 when)
738 conditionally_add_to_selection ();
740 build_display_menu ();
741 display_menu->popup (1, when);
745 TimeAxisView::set_selected (bool yn)
747 if (yn == _selected) {
751 Selectable::set_selected (yn);
754 time_axis_frame.set_shadow_type (Gtk::SHADOW_IN);
755 time_axis_frame.set_name ("MixerStripSelectedFrame");
756 controls_ebox.set_name (controls_base_selected_name);
757 controls_vbox.set_name (controls_base_selected_name);
759 time_axis_frame.set_shadow_type (Gtk::SHADOW_OUT);
760 time_axis_frame.set_name (controls_base_unselected_name);
761 controls_ebox.set_name (controls_base_unselected_name);
762 controls_vbox.set_name (controls_base_unselected_name);
766 /* children will be set for the yn=true case. but when deselecting
767 the editor only has a list of top-level trackviews, so we
768 have to do this here.
771 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
772 (*i)->set_selected (false);
776 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 (framepos_t start, framepos_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)
823 SelectionRect *rect; time_axis_frame.show();
826 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
827 (*i)->show_selection (ts);
830 if (selection_group->visible ()) {
831 while (!used_selection_rects.empty()) {
832 free_selection_rects.push_front (used_selection_rects.front());
833 used_selection_rects.pop_front();
834 free_selection_rects.front()->rect->hide();
835 free_selection_rects.front()->start_trim->hide();
836 free_selection_rects.front()->end_trim->hide();
838 selection_group->hide();
841 selection_group->show();
842 selection_group->raise_to_top();
844 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
845 framepos_t start, end;
850 cnt = end - start + 1;
852 rect = get_selection_rect ((*i).id);
854 x1 = _editor.sample_to_pixel (start);
855 x2 = _editor.sample_to_pixel (start + cnt - 1);
856 y2 = current_height() - 1;
858 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
860 // trim boxes are at the top for selections
863 rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
864 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
866 rect->start_trim->show();
867 rect->end_trim->show();
869 rect->start_trim->hide();
870 rect->end_trim->hide();
874 used_selection_rects.push_back (rect);
879 TimeAxisView::reshow_selection (TimeSelection& ts)
883 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
884 (*i)->show_selection (ts);
889 TimeAxisView::hide_selection ()
891 if (selection_group->visible ()) {
892 while (!used_selection_rects.empty()) {
893 free_selection_rects.push_front (used_selection_rects.front());
894 used_selection_rects.pop_front();
895 free_selection_rects.front()->rect->hide();
896 free_selection_rects.front()->start_trim->hide();
897 free_selection_rects.front()->end_trim->hide();
899 selection_group->hide();
902 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
903 (*i)->hide_selection ();
908 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
910 /* find the selection rect this is for. we have the item corresponding to one
914 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
915 if ((*i)->start_trim == item || (*i)->end_trim == item) {
917 /* make one trim handle be "above" the other so that if they overlap,
918 the top one is the one last used.
921 (*i)->rect->raise_to_top ();
922 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
923 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
931 TimeAxisView::get_selection_rect (uint32_t id)
935 /* check to see if we already have a visible rect for this particular selection ID */
937 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
938 if ((*i)->id == id) {
943 /* ditto for the free rect list */
945 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
946 if ((*i)->id == id) {
947 SelectionRect* ret = (*i);
948 free_selection_rects.erase (i);
953 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
955 if (free_selection_rects.empty()) {
957 rect = new SelectionRect;
959 rect->rect = new ArdourCanvas::Rectangle (selection_group);
960 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
961 rect->rect->set_outline (false);
962 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
964 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
965 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
966 rect->start_trim->set_outline (false);
967 rect->start_trim->set_fill (false);
969 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
970 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
971 rect->end_trim->set_outline (false);
972 rect->end_trim->set_fill (false);
974 free_selection_rects.push_front (rect);
976 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
977 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
978 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
981 rect = free_selection_rects.front();
983 free_selection_rects.pop_front();
987 struct null_deleter { void operator()(void const *) const {} };
990 TimeAxisView::is_child (TimeAxisView* tav)
992 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
996 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
998 children.push_back (child);
1002 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1004 Children::iterator i;
1006 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1011 /** Get selectable things within a given range.
1012 * @param start Start time in session frames.
1013 * @param end End time in session frames.
1014 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1015 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1016 * @param result Filled in with selectable things.
1019 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1025 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1031 TimeAxisView::add_ghost (RegionView* rv)
1033 GhostRegion* gr = rv->add_ghost (*this);
1036 ghosts.push_back(gr);
1041 TimeAxisView::remove_ghost (RegionView* rv)
1043 rv->remove_ghost_in (*this);
1047 TimeAxisView::erase_ghost (GhostRegion* gr)
1049 if (in_destructor) {
1053 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1062 TimeAxisView::touched (double top, double bot)
1064 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1065 y_position is the "origin" or "top" of the track.
1068 double mybot = _y_position + current_height();
1070 return ((_y_position <= bot && _y_position >= top) ||
1071 ((mybot <= bot) && (top < mybot)) ||
1072 (mybot >= bot && _y_position < top));
1076 TimeAxisView::set_parent (TimeAxisView& p)
1082 TimeAxisView::reset_height ()
1084 set_height (height);
1086 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1087 (*i)->set_height ((*i)->height);
1092 TimeAxisView::compute_heights ()
1094 // TODO this function should be re-evaluated when font-scaling changes (!)
1095 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1096 Gtk::Table one_row_table (1, 8);
1097 ArdourButton* test_button = manage (new ArdourButton);
1098 const int border_width = 2;
1099 const int frame_height = 2;
1100 extra_height = (2 * border_width) + frame_height;
1102 window.add (one_row_table);
1103 test_button->set_name ("mute button");
1104 test_button->set_text (_("M"));
1106 one_row_table.set_border_width (border_width);
1107 one_row_table.set_row_spacings (2);
1108 one_row_table.set_col_spacings (2);
1110 one_row_table.attach (*test_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
1111 one_row_table.show_all ();
1113 Gtk::Requisition req(one_row_table.size_request ());
1114 button_height = req.height;
1118 TimeAxisView::color_handler ()
1120 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1124 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1126 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1127 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1129 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1130 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1132 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1133 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1136 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1138 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1139 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1141 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1142 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1144 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1145 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1149 /** @return Pair: TimeAxisView, layer index.
1150 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1151 * does. @param y is an offset from the top of the trackview area.
1153 * If the covering object is a child axis, then the child is returned.
1154 * TimeAxisView is 0 otherwise.
1156 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1157 * and is in stacked or expanded * region display mode, otherwise 0.
1159 std::pair<TimeAxisView*, double>
1160 TimeAxisView::covers_y_position (double y) const
1163 return std::make_pair ((TimeAxisView *) 0, 0);
1166 if (_y_position <= y && y < (_y_position + height)) {
1168 /* work out the layer index if appropriate */
1170 switch (layer_display ()) {
1176 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1177 /* clamp to max layers to be on the safe side; sometimes the above calculation
1178 returns a too-high value */
1179 if (l >= view()->layers ()) {
1180 l = view()->layers() - 1;
1186 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1188 if (l >= (view()->layers() - 0.5)) {
1189 l = view()->layers() - 0.5;
1195 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1198 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1200 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1206 return std::make_pair ((TimeAxisView *) 0, 0);
1210 TimeAxisView::covered_by_y_range (double y0, double y1) const
1216 /* if either the top or bottom of the axisview is in the vertical
1217 * range, we cover it.
1220 if ((y0 < _y_position && y1 < _y_position) ||
1221 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1225 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1226 if ((*i)->covered_by_y_range (y0, y1)) {
1235 TimeAxisView::preset_height (Height h)
1239 return (button_height * 2) + extra_height + 260;
1241 return (button_height * 2) + extra_height + 160;
1243 return (button_height * 2) + extra_height + 60;
1245 return (button_height * 2) + extra_height + 10;
1247 return button_height + extra_height;
1254 /** @return Child time axis views that are not hidden */
1255 TimeAxisView::Children
1256 TimeAxisView::get_child_list ()
1260 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1261 if (!(*i)->hidden()) {
1270 TimeAxisView::build_size_menu ()
1272 if (_size_menu && _size_menu->gobj ()) {
1278 using namespace Menu_Helpers;
1280 _size_menu = new Menu;
1281 _size_menu->set_name ("ArdourContextMenu");
1282 MenuList& items = _size_menu->items();
1284 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1285 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1286 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1287 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1288 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1292 TimeAxisView::reset_visual_state ()
1294 /* this method is not required to trigger a global redraw */
1296 string str = gui_property ("height");
1299 set_height (atoi (str));
1301 set_height (preset_height (HeightNormal));
1306 TrackViewList::filter_to_unique_playlists ()
1308 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1311 for (iterator i = begin(); i != end(); ++i) {
1312 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1314 /* not a route: include it anyway */
1317 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1319 if (playlists.insert (t->playlist()).second) {
1320 /* playlist not seen yet */
1324 /* not a track: include it anyway */