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 (2, 3)
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);
132 controls_table.set_row_spacings (2);
133 controls_table.set_col_spacings (2);
134 controls_table.set_border_width (2);
135 controls_table.set_homogeneous (true);
137 controls_table.show_all ();
138 controls_table.set_no_show_all ();
140 name_table.attach (name_hbox, 0, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
141 name_table.show_all ();
142 name_table.set_no_show_all ();
144 HSeparator* separator = manage (new HSeparator());
145 separator->set_name("TrackSeparator");
146 separator->set_size_request(-1, 1);
149 name_vbox.pack_start (name_table, false, false);
152 controls_hbox.pack_start (controls_table, false, false);
153 controls_hbox.show ();
155 controls_hbox.pack_start (name_vbox, true, true);
156 controls_hbox.show ();
158 controls_vbox.pack_start (controls_hbox, false, false);
159 controls_vbox.show ();
161 top_hbox.pack_start (controls_vbox, true, true);
164 //controls_ebox.set_name ("TimeAxisViewControlsBaseUnselected");
165 controls_ebox.add (top_hbox);
166 controls_ebox.add_events (Gdk::BUTTON_PRESS_MASK|
167 Gdk::BUTTON_RELEASE_MASK|
168 Gdk::POINTER_MOTION_MASK|
169 Gdk::ENTER_NOTIFY_MASK|
170 Gdk::LEAVE_NOTIFY_MASK|
172 controls_ebox.set_flags (CAN_FOCUS);
174 /* note that this handler connects *before* the default handler */
175 controls_ebox.signal_scroll_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_scroll), true);
176 controls_ebox.signal_button_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_press));
177 controls_ebox.signal_button_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_button_release));
178 controls_ebox.signal_motion_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_motion));
179 controls_ebox.signal_leave_notify_event().connect (sigc::mem_fun (*this, &TimeAxisView::controls_ebox_leave));
180 controls_ebox.show ();
182 time_axis_vbox.pack_start (controls_ebox, true, true, 0);
183 // time_axis_vbox.pack_end (*separator, false, false);
184 time_axis_vbox.show();
186 time_axis_frame.add(time_axis_vbox);
187 time_axis_frame.show();
189 ColorsChanged.connect (sigc::mem_fun (*this, &TimeAxisView::color_handler));
191 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&TimeAxisView::erase_ghost, this, _1), gui_context());
194 TimeAxisView::~TimeAxisView()
196 in_destructor = true;
198 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
202 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
204 delete (*i)->start_trim;
205 delete (*i)->end_trim;
209 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
211 delete (*i)->start_trim;
212 delete (*i)->end_trim;
215 delete selection_group;
218 delete _canvas_display;
228 TimeAxisView::hide ()
234 _canvas_display->hide ();
236 if (control_parent) {
237 control_parent->remove (time_axis_frame);
244 /* now hide children */
246 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
250 /* if its hidden, it cannot be selected */
251 _editor.get_selection().remove (this);
252 /* and neither can its regions */
253 _editor.get_selection().remove_regions (this);
258 /** Display this TimeAxisView as the nth component of the parent box, at y.
260 * @param y y position.
261 * @param nth index for this TimeAxisView, increased if this view has children.
262 * @param parent parent component.
263 * @return height of this TimeAxisView.
266 TimeAxisView::show_at (double y, int& nth, VBox *parent)
268 if (control_parent) {
269 control_parent->reorder_child (time_axis_frame, nth);
271 control_parent = parent;
272 parent->pack_start (time_axis_frame, false, false);
273 parent->reorder_child (time_axis_frame, nth);
278 if (_y_position != y) {
279 _canvas_display->set_y_position (y);
284 _canvas_display->raise_to_top ();
285 _canvas_display->show ();
289 _effective_height = current_height ();
291 /* now show relevant children */
293 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
294 if ((*i)->marked_for_display()) {
296 _effective_height += (*i)->show_at (y + _effective_height, nth, parent);
302 return _effective_height;
306 TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
308 switch (ev->direction) {
310 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
311 /* See Editor::_stepping_axis_view for notes on this hack */
312 Editor& e = dynamic_cast<Editor&> (_editor);
313 if (!e.stepping_axis_view ()) {
314 e.set_stepping_axis_view (this);
316 e.stepping_axis_view()->step_height (false);
321 case GDK_SCROLL_DOWN:
322 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
323 /* See Editor::_stepping_axis_view for notes on this hack */
324 Editor& e = dynamic_cast<Editor&> (_editor);
325 if (!e.stepping_axis_view ()) {
326 e.set_stepping_axis_view (this);
328 e.stepping_axis_view()->step_height (true);
334 /* no handling for left/right, yet */
338 /* Just forward to the normal canvas scroll method. The coordinate
339 systems are different but since the canvas is always larger than the
340 track headers, and aligned with the trackview area, this will work.
342 In the not too distant future this layout is going away anyway and
343 headers will be on the canvas.
345 return _editor.canvas_scroll_event (ev, false);
349 TimeAxisView::controls_ebox_button_press (GdkEventButton* event)
351 if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (event)) {
352 /* see if it is inside the name label */
353 if (name_label.is_ancestor (controls_ebox)) {
356 controls_ebox.translate_coordinates (name_label, event->x, event->y, nlx, nly);
357 Gtk::Allocation a = name_label.get_allocation ();
358 if (nlx > 0 && nlx < a.get_width() && nly > 0 && nly < a.get_height()) {
360 _ebox_release_can_act = false;
367 _ebox_release_can_act = true;
369 if (maybe_set_cursor (event->y) > 0) {
370 _resize_drag_start = event->y_root;
377 TimeAxisView::idle_resize (uint32_t h)
384 TimeAxisView::controls_ebox_motion (GdkEventMotion* ev)
386 if (_resize_drag_start >= 0) {
388 /* (ab)use the DragManager to do autoscrolling - basically we
389 * are pretending that the drag is taking place over the canvas
390 * (which perhaps in the glorious future, when track headers
391 * and the canvas are unified, will actually be true.)
394 _editor.maybe_autoscroll (false, true, true);
396 /* now schedule the actual TAV resize */
397 int32_t const delta = (int32_t) floor (ev->y_root - _resize_drag_start);
398 _editor.add_to_idle_resize (this, delta);
399 _resize_drag_start = ev->y_root;
401 /* not dragging but ... */
402 maybe_set_cursor (ev->y);
405 gdk_event_request_motions(ev);
410 TimeAxisView::controls_ebox_leave (GdkEventCrossing*)
412 if (_have_preresize_cursor) {
413 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
414 _have_preresize_cursor = false;
420 TimeAxisView::maybe_set_cursor (int y)
422 /* XXX no Gtkmm Gdk::Window::get_cursor() */
423 Glib::RefPtr<Gdk::Window> win = controls_ebox.get_window();
425 if (y > (gint) floor (controls_ebox.get_height() * 0.75)) {
427 /* y-coordinate in lower 25% */
429 if (!_have_preresize_cursor) {
430 _preresize_cursor = gdk_window_get_cursor (win->gobj());
431 _have_preresize_cursor = true;
432 win->set_cursor (Gdk::Cursor(Gdk::SB_V_DOUBLE_ARROW));
437 } else if (_have_preresize_cursor) {
438 gdk_window_set_cursor (win->gobj(), _preresize_cursor);
439 _have_preresize_cursor = false;
448 TimeAxisView::controls_ebox_button_release (GdkEventButton* ev)
450 if (_resize_drag_start >= 0) {
451 if (_have_preresize_cursor) {
452 gdk_window_set_cursor (controls_ebox.get_window()->gobj(), _preresize_cursor);
453 _preresize_cursor = 0;
454 _have_preresize_cursor = false;
456 _editor.stop_canvas_autoscroll ();
457 _resize_drag_start = -1;
460 if (!_ebox_release_can_act) {
464 switch (ev->button) {
466 selection_click (ev);
470 popup_display_menu (ev->time);
478 TimeAxisView::selection_click (GdkEventButton* ev)
480 Selection::Operation op = ArdourKeyboard::selection_type (ev->state);
481 _editor.set_selected_track (*this, op, false);
485 /** Steps through the defined heights for this TrackView.
486 * @param coarser true if stepping should decrease in size, otherwise false.
489 TimeAxisView::step_height (bool coarser)
491 static const uint32_t step = 25;
495 if (height <= preset_height (HeightSmall)) {
497 } else if (height <= preset_height (HeightNormal) && height > preset_height (HeightSmall)) {
498 set_height_enum (HeightSmall);
500 set_height (height - step);
505 if (height <= preset_height(HeightSmall)) {
506 set_height_enum (HeightNormal);
508 set_height (height + step);
515 TimeAxisView::set_height_enum (Height h, bool apply_to_selection)
517 if (apply_to_selection) {
518 _editor.get_selection().tracks.foreach_time_axis (boost::bind (&TimeAxisView::set_height_enum, _1, h, false));
520 set_height (preset_height (h));
525 TimeAxisView::set_height (uint32_t h)
527 if (h < preset_height (HeightSmall)) {
528 h = preset_height (HeightSmall);
531 time_axis_frame.property_height_request () = h;
535 snprintf (buf, sizeof (buf), "%u", height);
536 set_gui_property ("height", buf);
538 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
542 if (selection_group->visible ()) {
543 /* resize the selection rect */
544 show_selection (_editor.get_selection().time);
547 _editor.override_visible_track_count ();
551 TimeAxisView::name_entry_key_press (GdkEventKey* ev)
553 /* steal escape, tabs from GTK */
555 switch (ev->keyval) {
557 case GDK_ISO_Left_Tab:
565 TimeAxisView::name_entry_key_release (GdkEventKey* ev)
567 TrackViewList::iterator i;
569 switch (ev->keyval) {
571 end_name_edit (RESPONSE_CANCEL);
574 /* Shift+Tab Keys Pressed. Note that for Shift+Tab, GDK actually
575 * generates a different ev->keyval, rather than setting
578 case GDK_ISO_Left_Tab:
579 end_name_edit (RESPONSE_APPLY);
583 end_name_edit (RESPONSE_ACCEPT);
593 TimeAxisView::name_entry_focus_out (GdkEventFocus*)
595 end_name_edit (RESPONSE_OK);
600 TimeAxisView::begin_name_edit ()
606 if (can_edit_name()) {
608 name_entry = manage (new Gtkmm2ext::FocusEntry);
610 name_entry->set_width_chars(15);
612 name_entry->set_name ("EditorTrackNameDisplay");
613 name_entry->signal_key_press_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_press), false);
614 name_entry->signal_key_release_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_key_release), false);
615 name_entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &TimeAxisView::name_entry_focus_out));
616 name_entry->set_text (name_label.get_text());
617 name_entry->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisView::end_name_edit), RESPONSE_OK));
619 if (name_label.is_ancestor (name_hbox)) {
620 name_hbox.remove (name_label);
623 name_hbox.pack_end (*name_entry, false, false);
626 name_entry->select_region (0, -1);
627 name_entry->set_state (STATE_SELECTED);
628 name_entry->grab_focus ();
629 name_entry->start_editing (0);
634 TimeAxisView::end_name_edit (int response)
640 bool edit_next = false;
641 bool edit_prev = false;
644 case RESPONSE_CANCEL:
647 name_entry_changed ();
649 case RESPONSE_ACCEPT:
650 name_entry_changed ();
653 name_entry_changed ();
657 /* this will delete the name_entry. but it will also drop focus, which
658 * will cause another callback to this function, so set name_entry = 0
659 * first to ensure we don't double-remove etc. etc.
662 Gtk::Entry* tmp = name_entry;
664 name_hbox.remove (*tmp);
666 /* put the name label back */
668 name_hbox.pack_end (name_label);
673 TrackViewList const & allviews = _editor.get_track_views ();
674 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
676 if (i != allviews.end()) {
679 if (++i == allviews.end()) {
683 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
685 if (rtav && rtav->route()->record_enabled()) {
689 if (!(*i)->hidden()) {
696 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
697 _editor.ensure_time_axis_view_is_visible (**i, false);
698 (*i)->begin_name_edit ();
701 } else if (edit_prev) {
703 TrackViewList const & allviews = _editor.get_track_views ();
704 TrackViewList::const_iterator i = find (allviews.begin(), allviews.end(), this);
706 if (i != allviews.begin()) {
708 if (i == allviews.begin()) {
714 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
716 if (rtav && rtav->route()->record_enabled()) {
720 if (!(*i)->hidden()) {
727 if ((i != allviews.end()) && (*i != this) && !(*i)->hidden()) {
728 _editor.ensure_time_axis_view_is_visible (**i, false);
729 (*i)->begin_name_edit ();
735 TimeAxisView::name_entry_changed ()
740 TimeAxisView::can_edit_name () const
746 TimeAxisView::conditionally_add_to_selection ()
748 Selection& s (_editor.get_selection ());
750 if (!s.selected (this)) {
751 _editor.set_selected_track (*this, Selection::Set);
756 TimeAxisView::popup_display_menu (guint32 when)
758 conditionally_add_to_selection ();
760 build_display_menu ();
761 display_menu->popup (1, when);
765 TimeAxisView::set_selected (bool yn)
767 if (yn == _selected) {
771 Selectable::set_selected (yn);
774 time_axis_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
775 time_axis_frame.set_name ("MixerStripSelectedFrame");
777 // time_axis_frame.set_name (controls_base_selected_name);
778 // controls_ebox.set_name (controls_base_selected_name);
779 // time_axis_vbox.set_name (controls_base_selected_name);
780 // controls_vbox.set_name (controls_base_selected_name);
782 time_axis_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
783 time_axis_frame.set_name (controls_base_unselected_name);
785 // time_axis_frame.set_name (controls_base_unselected_name);
786 // controls_ebox.set_name (controls_base_unselected_name);
787 // time_axis_vbox.set_name (controls_base_unselected_name);
788 // controls_vbox.set_name (controls_base_unselected_name);
791 /* children will be set for the yn=true case. but when deselecting
792 the editor only has a list of top-level trackviews, so we
793 have to do this here.
796 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
797 (*i)->set_selected (false);
801 time_axis_frame.show();
806 TimeAxisView::build_display_menu ()
808 using namespace Menu_Helpers;
812 display_menu = new Menu;
813 display_menu->set_name ("ArdourContextMenu");
815 // Just let implementing classes define what goes into the manu
819 TimeAxisView::set_samples_per_pixel (double fpp)
821 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
822 (*i)->set_samples_per_pixel (fpp);
827 TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
829 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
830 (*i)->show_timestretch (start, end, layers, layer);
835 TimeAxisView::hide_timestretch ()
837 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
838 (*i)->hide_timestretch ();
843 TimeAxisView::show_selection (TimeSelection& ts)
848 SelectionRect *rect; time_axis_frame.show();
851 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
852 (*i)->show_selection (ts);
855 if (selection_group->visible ()) {
856 while (!used_selection_rects.empty()) {
857 free_selection_rects.push_front (used_selection_rects.front());
858 used_selection_rects.pop_front();
859 free_selection_rects.front()->rect->hide();
860 free_selection_rects.front()->start_trim->hide();
861 free_selection_rects.front()->end_trim->hide();
863 selection_group->hide();
866 selection_group->show();
867 selection_group->raise_to_top();
869 for (list<AudioRange>::iterator i = ts.begin(); i != ts.end(); ++i) {
870 framepos_t start, end;
875 cnt = end - start + 1;
877 rect = get_selection_rect ((*i).id);
879 x1 = _editor.sample_to_pixel (start);
880 x2 = _editor.sample_to_pixel (start + cnt - 1);
881 y2 = current_height() - 1;
883 rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
885 // trim boxes are at the top for selections
888 rect->start_trim->set (ArdourCanvas::Rect (x1, 1, x1 + trim_handle_size, y2));
889 rect->end_trim->set (ArdourCanvas::Rect (x2 - trim_handle_size, 1, x2, y2));
891 rect->start_trim->show();
892 rect->end_trim->show();
894 rect->start_trim->hide();
895 rect->end_trim->hide();
899 used_selection_rects.push_back (rect);
904 TimeAxisView::reshow_selection (TimeSelection& ts)
908 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
909 (*i)->show_selection (ts);
914 TimeAxisView::hide_selection ()
916 if (selection_group->visible ()) {
917 while (!used_selection_rects.empty()) {
918 free_selection_rects.push_front (used_selection_rects.front());
919 used_selection_rects.pop_front();
920 free_selection_rects.front()->rect->hide();
921 free_selection_rects.front()->start_trim->hide();
922 free_selection_rects.front()->end_trim->hide();
924 selection_group->hide();
927 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
928 (*i)->hide_selection ();
933 TimeAxisView::order_selection_trims (ArdourCanvas::Item *item, bool put_start_on_top)
935 /* find the selection rect this is for. we have the item corresponding to one
939 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
940 if ((*i)->start_trim == item || (*i)->end_trim == item) {
942 /* make one trim handle be "above" the other so that if they overlap,
943 the top one is the one last used.
946 (*i)->rect->raise_to_top ();
947 (put_start_on_top ? (*i)->start_trim : (*i)->end_trim)->raise_to_top ();
948 (put_start_on_top ? (*i)->end_trim : (*i)->start_trim)->raise_to_top ();
956 TimeAxisView::get_selection_rect (uint32_t id)
960 /* check to see if we already have a visible rect for this particular selection ID */
962 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
963 if ((*i)->id == id) {
968 /* ditto for the free rect list */
970 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
971 if ((*i)->id == id) {
972 SelectionRect* ret = (*i);
973 free_selection_rects.erase (i);
978 /* no existing matching rect, so go get a new one from the free list, or create one if there are none */
980 if (free_selection_rects.empty()) {
982 rect = new SelectionRect;
984 rect->rect = new ArdourCanvas::Rectangle (selection_group);
985 CANVAS_DEBUG_NAME (rect->rect, "selection rect");
986 rect->rect->set_outline (false);
987 rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
989 rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
990 CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
991 rect->start_trim->set_outline (false);
992 rect->start_trim->set_fill (false);
994 rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
995 CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
996 rect->end_trim->set_outline (false);
997 rect->end_trim->set_fill (false);
999 free_selection_rects.push_front (rect);
1001 rect->rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_rect_event), rect->rect, rect));
1002 rect->start_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_start_trim_event), rect->rect, rect));
1003 rect->end_trim->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_selection_end_trim_event), rect->rect, rect));
1006 rect = free_selection_rects.front();
1008 free_selection_rects.pop_front();
1012 struct null_deleter { void operator()(void const *) const {} };
1015 TimeAxisView::is_child (TimeAxisView* tav)
1017 return find (children.begin(), children.end(), boost::shared_ptr<TimeAxisView>(tav, null_deleter())) != children.end();
1021 TimeAxisView::add_child (boost::shared_ptr<TimeAxisView> child)
1023 children.push_back (child);
1027 TimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> child)
1029 Children::iterator i;
1031 if ((i = find (children.begin(), children.end(), child)) != children.end()) {
1036 /** Get selectable things within a given range.
1037 * @param start Start time in session frames.
1038 * @param end End time in session frames.
1039 * @param top Top y range, in trackview coordinates (ie 0 is the top of the track view)
1040 * @param bot Bottom y range, in trackview coordinates (ie 0 is the top of the track view)
1041 * @param result Filled in with selectable things.
1044 TimeAxisView::get_selectables (framepos_t /*start*/, framepos_t /*end*/, double /*top*/, double /*bot*/, list<Selectable*>& /*result*/)
1050 TimeAxisView::get_inverted_selectables (Selection& /*sel*/, list<Selectable*>& /*result*/)
1056 TimeAxisView::add_ghost (RegionView* rv)
1058 GhostRegion* gr = rv->add_ghost (*this);
1061 ghosts.push_back(gr);
1066 TimeAxisView::remove_ghost (RegionView* rv)
1068 rv->remove_ghost_in (*this);
1072 TimeAxisView::erase_ghost (GhostRegion* gr)
1074 if (in_destructor) {
1078 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1087 TimeAxisView::touched (double top, double bot)
1089 /* remember: this is X Window - coordinate space starts in upper left and moves down.
1090 y_position is the "origin" or "top" of the track.
1093 double mybot = _y_position + current_height();
1095 return ((_y_position <= bot && _y_position >= top) ||
1096 ((mybot <= bot) && (top < mybot)) ||
1097 (mybot >= bot && _y_position < top));
1101 TimeAxisView::set_parent (TimeAxisView& p)
1107 TimeAxisView::reset_height ()
1109 set_height (height);
1111 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1112 (*i)->set_height ((*i)->height);
1117 TimeAxisView::compute_heights ()
1119 Gtk::Window window (Gtk::WINDOW_TOPLEVEL);
1120 Gtk::Table two_row_table (2, 8);
1121 Gtk::Table one_row_table (1, 8);
1123 const int border_width = 2;
1125 const int separator_height = 2;
1126 extra_height = (2 * border_width) + separator_height;
1128 window.add (one_row_table);
1130 one_row_table.set_border_width (border_width);
1131 one_row_table.set_row_spacings (0);
1132 one_row_table.set_col_spacings (0);
1133 one_row_table.set_homogeneous (true);
1135 two_row_table.set_border_width (border_width);
1136 two_row_table.set_row_spacings (0);
1137 two_row_table.set_col_spacings (0);
1138 two_row_table.set_homogeneous (true);
1140 for (int i = 0; i < 5; ++i) {
1141 buttons[i] = manage (new Button (X_("f")));
1142 buttons[i]->set_name ("TrackMuteButton");
1145 one_row_table.attach (*buttons[0], 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
1147 one_row_table.show_all ();
1148 Gtk::Requisition req(one_row_table.size_request ());
1150 // height required to show 1 row of buttons
1151 button_height = req.height;
1155 TimeAxisView::color_handler ()
1157 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); i++) {
1161 for (list<SelectionRect*>::iterator i = used_selection_rects.begin(); i != used_selection_rects.end(); ++i) {
1163 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1164 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1166 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1167 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1169 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1170 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1173 for (list<SelectionRect*>::iterator i = free_selection_rects.begin(); i != free_selection_rects.end(); ++i) {
1175 (*i)->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
1176 (*i)->rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1178 (*i)->start_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1179 (*i)->start_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1181 (*i)->end_trim->set_fill_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1182 (*i)->end_trim->set_outline_color (ARDOUR_UI::config()->get_canvasvar_Selection());
1186 /** @return Pair: TimeAxisView, layer index.
1187 * TimeAxisView is non-0 if this object covers @param y, or one of its children
1188 * does. @param y is an offset from the top of the trackview area.
1190 * If the covering object is a child axis, then the child is returned.
1191 * TimeAxisView is 0 otherwise.
1193 * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
1194 * and is in stacked or expanded * region display mode, otherwise 0.
1196 std::pair<TimeAxisView*, double>
1197 TimeAxisView::covers_y_position (double y) const
1200 return std::make_pair ((TimeAxisView *) 0, 0);
1203 if (_y_position <= y && y < (_y_position + height)) {
1205 /* work out the layer index if appropriate */
1207 switch (layer_display ()) {
1213 l = layer_t ((_y_position + height - y) / (view()->child_height ()));
1214 /* clamp to max layers to be on the safe side; sometimes the above calculation
1215 returns a too-high value */
1216 if (l >= view()->layers ()) {
1217 l = view()->layers() - 1;
1223 int const n = floor ((_y_position + height - y) / (view()->child_height ()));
1225 if (l >= (view()->layers() - 0.5)) {
1226 l = view()->layers() - 0.5;
1232 return std::make_pair (const_cast<TimeAxisView*>(this), l);
1235 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1237 std::pair<TimeAxisView*, int> const r = (*i)->covers_y_position (y);
1243 return std::make_pair ((TimeAxisView *) 0, 0);
1247 TimeAxisView::covered_by_y_range (double y0, double y1) const
1253 /* if either the top or bottom of the axisview is in the vertical
1254 * range, we cover it.
1257 if ((y0 < _y_position && y1 < _y_position) ||
1258 (y0 >= _y_position + height && y1 >= _y_position + height)) {
1262 for (Children::const_iterator i = children.begin(); i != children.end(); ++i) {
1263 if ((*i)->covered_by_y_range (y0, y1)) {
1272 TimeAxisView::preset_height (Height h)
1276 return (button_height * 2) + extra_height + 260;
1278 return (button_height * 2) + extra_height + 160;
1280 return (button_height * 2) + extra_height + 60;
1282 return (button_height * 2) + extra_height + 10;
1284 return button_height + extra_height;
1291 /** @return Child time axis views that are not hidden */
1292 TimeAxisView::Children
1293 TimeAxisView::get_child_list ()
1297 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1298 if (!(*i)->hidden()) {
1307 TimeAxisView::build_size_menu ()
1309 if (_size_menu && _size_menu->gobj ()) {
1315 using namespace Menu_Helpers;
1317 _size_menu = new Menu;
1318 _size_menu->set_name ("ArdourContextMenu");
1319 MenuList& items = _size_menu->items();
1321 items.push_back (MenuElem (_("Largest"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLargest, true)));
1322 items.push_back (MenuElem (_("Larger"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarger, true)));
1323 items.push_back (MenuElem (_("Large"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightLarge, true)));
1324 items.push_back (MenuElem (_("Normal"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightNormal, true)));
1325 items.push_back (MenuElem (_("Small"), sigc::bind (sigc::mem_fun (*this, &TimeAxisView::set_height_enum), HeightSmall, true)));
1329 TimeAxisView::reset_visual_state ()
1331 /* this method is not required to trigger a global redraw */
1333 string str = gui_property ("height");
1336 set_height (atoi (str));
1338 set_height (preset_height (HeightNormal));
1343 TrackViewList::filter_to_unique_playlists ()
1345 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists;
1348 for (iterator i = begin(); i != end(); ++i) {
1349 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1351 /* not a route: include it anyway */
1354 boost::shared_ptr<ARDOUR::Track> t = rtav->track();
1356 if (playlists.insert (t->playlist()).second) {
1357 /* playlist not seen yet */
1361 /* not a track: include it anyway */