2 Copyright (C) 2000-2007 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.
21 #include <gtkmm2ext/barcontroller.h>
22 #include <gtkmm2ext/utils.h>
23 #include <boost/algorithm/string.hpp>
24 #include <boost/lexical_cast.hpp>
26 #include "pbd/memento_command.h"
27 #include "pbd/stacktrace.h"
29 #include "ardour/automation_control.h"
30 #include "ardour/event_type_map.h"
31 #include "ardour/route.h"
32 #include "ardour/session.h"
34 #include "ardour_ui.h"
35 #include "automation_time_axis.h"
36 #include "automation_streamview.h"
37 #include "global_signals.h"
38 #include "gui_thread.h"
39 #include "route_time_axis.h"
40 #include "automation_line.h"
41 #include "public_editor.h"
42 #include "simplerect.h"
43 #include "selection.h"
44 #include "rgb_macros.h"
45 #include "point_selection.h"
46 #include "canvas_impl.h"
52 using namespace ARDOUR
;
55 using namespace Gtkmm2ext
;
56 using namespace Editing
;
58 Pango::FontDescription
AutomationTimeAxisView::name_font
;
59 bool AutomationTimeAxisView::have_name_font
= false;
62 /** \a a the automatable object this time axis is to display data for.
63 * For route/track automation (e.g. gain) pass the route for both \r and \a.
64 * For route child (e.g. plugin) automation, pass the child for \a.
65 * For region automation (e.g. MIDI CC), pass null for \a.
67 AutomationTimeAxisView::AutomationTimeAxisView (
69 boost::shared_ptr
<Route
> r
,
70 boost::shared_ptr
<Automatable
> a
,
71 boost::shared_ptr
<AutomationControl
> c
,
76 ArdourCanvas::Canvas
& canvas
,
78 const string
& nomparent
81 , TimeAxisView (s
, e
, &parent
, canvas
)
87 , _view (show_regions
? new AutomationStreamView (*this) : 0)
89 , auto_button (X_("")) /* force addition of a label */
91 if (!have_name_font
) {
92 name_font
= get_font_for_style (X_("AutomationTrackName"));
93 have_name_font
= true;
96 if (_automatable
&& _control
) {
97 _controller
= AutomationController::create (_automatable
, _control
->parameter(), _control
);
105 mode_discrete_item
= 0;
108 ignore_state_request
= false;
109 first_call_to_set_height
= true;
111 _base_rect
= new SimpleRect(*_canvas_display
);
112 _base_rect
->property_x1() = 0.0;
113 _base_rect
->property_y1() = 0.0;
114 /** gnomecanvas sometimes converts this value to int or adds 2 to it, so it must be
115 set correctly to avoid overflow.
117 _base_rect
->property_x2() = INT_MAX
- 2;
118 _base_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline
.get();
120 /* outline ends and bottom */
121 _base_rect
->property_outline_what() = (guint32
) (0x1|0x2|0x8);
122 _base_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill
.get();
124 _base_rect
->set_data ("trackview", this);
126 _base_rect
->signal_event().connect (sigc::bind (
127 sigc::mem_fun (_editor
, &PublicEditor::canvas_automation_track_event
),
131 _base_rect
->lower_to_bottom();
134 hide_button
.add (*(manage (new Gtk::Image (::get_icon("hide")))));
136 auto_button
.set_name ("TrackVisualButton");
137 hide_button
.set_name ("TrackRemoveButton");
139 auto_button
.unset_flags (Gtk::CAN_FOCUS
);
140 hide_button
.unset_flags (Gtk::CAN_FOCUS
);
142 controls_table
.set_no_show_all();
144 ARDOUR_UI::instance()->set_tip(auto_button
, _("automation state"));
145 ARDOUR_UI::instance()->set_tip(hide_button
, _("hide track"));
147 string str
= gui_property ("height");
149 set_height (atoi (str
));
151 set_height (preset_height (HeightNormal
));
154 /* rearrange the name display */
156 controls_table
.remove (name_hbox
);
157 controls_table
.attach (name_hbox
, 1, 6, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 3, 0);
159 /* we never show these for automation tracks, so make
160 life easier and remove them.
165 name_label
.set_text (_name
);
166 name_label
.set_alignment (Gtk::ALIGN_CENTER
, Gtk::ALIGN_CENTER
);
167 name_label
.set_name (X_("TrackParameterName"));
168 name_label
.set_ellipsize (Pango::ELLIPSIZE_END
);
170 string tipname
= nomparent
;
171 if (!tipname
.empty()) {
175 ARDOUR_UI::instance()->set_tip(controls_ebox
, tipname
);
177 /* add the buttons */
178 controls_table
.attach (hide_button
, 0, 1, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
179 controls_table
.attach (auto_button
, 6, 8, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
182 /* add bar controller */
183 controls_table
.attach (*_controller
.get(), 0, 8, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
186 controls_table
.show_all ();
188 hide_button
.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked
));
189 auto_button
.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked
));
191 controls_base_selected_name
= X_("AutomationTrackControlsBaseSelected");
192 controls_base_unselected_name
= X_("AutomationTrackControlsBase");
193 controls_ebox
.set_name (controls_base_unselected_name
);
195 /* ask for notifications of any new RegionViews */
202 /* no regions, just a single line for the entire track (e.g. bus gain) */
206 boost::shared_ptr
<AutomationLine
> line (
208 ARDOUR::EventTypeMap::instance().to_symbol(_parameter
),
215 line
->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine
.get());
216 line
->queue_reset ();
220 /* make sure labels etc. are correct */
222 automation_state_changed ();
223 ColorsChanged
.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler
));
225 _route
->DropReferences
.connect (
226 _route_connections
, invalidator (*this), ui_bind (&AutomationTimeAxisView::route_going_away
, this), gui_context ()
230 AutomationTimeAxisView::~AutomationTimeAxisView ()
236 AutomationTimeAxisView::route_going_away ()
242 AutomationTimeAxisView::auto_clicked ()
244 using namespace Menu_Helpers
;
246 if (automation_menu
== 0) {
247 automation_menu
= manage (new Menu
);
248 automation_menu
->set_name ("ArdourContextMenu");
249 MenuList
& items (automation_menu
->items());
251 items
.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
252 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Off
)));
253 items
.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
254 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Play
)));
255 items
.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
256 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Write
)));
257 items
.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
258 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Touch
)));
261 automation_menu
->popup (1, gtk_get_current_event_time());
265 AutomationTimeAxisView::set_automation_state (AutoState state
)
267 if (ignore_state_request
) {
272 _automatable
->set_parameter_automation_state (_parameter
, state
);
276 _view
->set_automation_state (state
);
278 /* AutomationStreamViews don't signal when their automation state changes, so handle
279 our updates `manually'.
281 automation_state_changed ();
286 AutomationTimeAxisView::automation_state_changed ()
290 /* update button label */
293 state
= _view
->automation_state ();
296 state
= _control
->alist()->automation_state ();
301 switch (state
& (Off
|Play
|Touch
|Write
)) {
303 auto_button
.set_label (_("Manual"));
305 ignore_state_request
= true;
306 auto_off_item
->set_active (true);
307 auto_play_item
->set_active (false);
308 auto_touch_item
->set_active (false);
309 auto_write_item
->set_active (false);
310 ignore_state_request
= false;
314 auto_button
.set_label (_("Play"));
315 if (auto_play_item
) {
316 ignore_state_request
= true;
317 auto_play_item
->set_active (true);
318 auto_off_item
->set_active (false);
319 auto_touch_item
->set_active (false);
320 auto_write_item
->set_active (false);
321 ignore_state_request
= false;
325 auto_button
.set_label (_("Write"));
326 if (auto_write_item
) {
327 ignore_state_request
= true;
328 auto_write_item
->set_active (true);
329 auto_off_item
->set_active (false);
330 auto_play_item
->set_active (false);
331 auto_touch_item
->set_active (false);
332 ignore_state_request
= false;
336 auto_button
.set_label (_("Touch"));
337 if (auto_touch_item
) {
338 ignore_state_request
= true;
339 auto_touch_item
->set_active (true);
340 auto_off_item
->set_active (false);
341 auto_play_item
->set_active (false);
342 auto_write_item
->set_active (false);
343 ignore_state_request
= false;
347 auto_button
.set_label (_("???"));
352 /** The interpolation style of our AutomationList has changed, so update */
354 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s
)
356 if (mode_line_item
&& mode_discrete_item
) {
357 if (s
== AutomationList::Discrete
) {
358 mode_discrete_item
->set_active(true);
359 mode_line_item
->set_active(false);
361 mode_line_item
->set_active(true);
362 mode_discrete_item
->set_active(false);
367 /** A menu item has been selected to change our interpolation mode */
369 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style
)
371 /* Tell our view's list, if we have one, otherwise tell our own.
372 * Everything else will be signalled back from that.
376 _view
->set_interpolation (style
);
379 _control
->list()->set_interpolation (style
);
384 AutomationTimeAxisView::clear_clicked ()
386 assert (_line
|| _view
);
388 _session
->begin_reversible_command (_("clear automation"));
396 _session
->commit_reversible_command ();
397 _session
->set_dirty ();
401 AutomationTimeAxisView::set_height (uint32_t h
)
403 bool const changed
= (height
!= (uint32_t) h
) || first_call_to_set_height
;
404 uint32_t const normal
= preset_height (HeightNormal
);
405 bool const changed_between_small_and_normal
= ( (height
< normal
&& h
>= normal
) || (height
>= normal
|| h
< normal
) );
407 TimeAxisView::set_height (h
);
409 _base_rect
->property_y2() = h
;
412 _line
->set_height(h
);
416 _view
->set_height(h
);
417 _view
->update_contents_height();
420 if (changed_between_small_and_normal
|| first_call_to_set_height
) {
422 first_call_to_set_height
= false;
424 if (h
>= preset_height (HeightNormal
)) {
427 name_hbox
.show_all ();
430 hide_button
.show_all();
432 } else if (h
>= preset_height (HeightSmall
)) {
433 controls_table
.hide_all ();
436 name_hbox
.show_all ();
441 } else if (h
>= preset_height (HeightNormal
)) {
442 cerr
<< "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl
;
446 if (canvas_item_visible (_canvas_display
) && _route
) {
447 /* only emit the signal if the height really changed and we were visible */
448 _route
->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
454 AutomationTimeAxisView::set_samples_per_unit (double spu
)
456 TimeAxisView::set_samples_per_unit (spu
);
463 _view
->set_samples_per_unit (spu
);
468 AutomationTimeAxisView::hide_clicked ()
470 hide_button
.set_sensitive(false);
471 set_marked_for_display (false);
472 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(parent
);
474 rtv
->request_redraw ();
476 hide_button
.set_sensitive(true);
480 AutomationTimeAxisView::build_display_menu ()
482 using namespace Menu_Helpers
;
486 TimeAxisView::build_display_menu ();
488 /* now fill it with our stuff */
490 MenuList
& items
= display_menu
->items();
492 items
.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked
)));
493 items
.push_back (SeparatorElem());
494 items
.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked
)));
495 items
.push_back (SeparatorElem());
499 Menu
* auto_state_menu
= manage (new Menu
);
500 auto_state_menu
->set_name ("ArdourContextMenu");
501 MenuList
& as_items
= auto_state_menu
->items();
503 as_items
.push_back (CheckMenuElem (_("Manual"), sigc::bind (
504 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
506 auto_off_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
508 as_items
.push_back (CheckMenuElem (_("Play"), sigc::bind (
509 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
511 auto_play_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
513 as_items
.push_back (CheckMenuElem (_("Write"), sigc::bind (
514 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
515 (AutoState
) Write
)));
516 auto_write_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
518 as_items
.push_back (CheckMenuElem (_("Touch"), sigc::bind (
519 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
520 (AutoState
) Touch
)));
521 auto_touch_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
523 items
.push_back (MenuElem (_("State"), *auto_state_menu
));
527 /* current interpolation state */
528 AutomationList::InterpolationStyle
const s
= _view
? _view
->interpolation() : _control
->list()->interpolation ();
530 if (EventTypeMap::instance().is_midi_parameter(_parameter
)) {
532 Menu
* auto_mode_menu
= manage (new Menu
);
533 auto_mode_menu
->set_name ("ArdourContextMenu");
534 MenuList
& am_items
= auto_mode_menu
->items();
536 RadioMenuItem::Group group
;
538 am_items
.push_back (RadioMenuElem (group
, _("Discrete"), sigc::bind (
539 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation
),
540 AutomationList::Discrete
)));
541 mode_discrete_item
= dynamic_cast<CheckMenuItem
*>(&am_items
.back());
542 mode_discrete_item
->set_active (s
== AutomationList::Discrete
);
544 am_items
.push_back (RadioMenuElem (group
, _("Linear"), sigc::bind (
545 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation
),
546 AutomationList::Linear
)));
547 mode_line_item
= dynamic_cast<CheckMenuItem
*>(&am_items
.back());
548 mode_line_item
->set_active (s
== AutomationList::Linear
);
550 items
.push_back (MenuElem (_("Mode"), *auto_mode_menu
));
553 /* make sure the automation menu state is correct */
555 automation_state_changed ();
556 interpolation_changed (s
);
560 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item
* /*item*/, GdkEvent
* /*event*/, framepos_t when
, double y
)
568 _canvas_display
->w2i (x
, y
);
570 /* compute vertical fractional position */
572 y
= 1.0 - (y
/ height
);
576 _line
->view_to_model_coord (x
, y
);
578 boost::shared_ptr
<AutomationList
> list
= _line
->the_list ();
580 _session
->begin_reversible_command (_("add automation event"));
581 XMLNode
& before
= list
->get_state();
585 XMLNode
& after
= list
->get_state();
586 _session
->commit_reversible_command (new MementoCommand
<ARDOUR::AutomationList
> (*list
, &before
, &after
));
587 _session
->set_dirty ();
591 AutomationTimeAxisView::cut_copy_clear (Selection
& selection
, CutCopyOp op
)
593 list
<boost::shared_ptr
<AutomationLine
> > lines
;
595 lines
.push_back (_line
);
597 lines
= _view
->get_lines ();
600 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
601 cut_copy_clear_one (**i
, selection
, op
);
606 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine
& line
, Selection
& selection
, CutCopyOp op
)
608 boost::shared_ptr
<Evoral::ControlList
> what_we_got
;
609 boost::shared_ptr
<AutomationList
> alist (line
.the_list());
611 XMLNode
&before
= alist
->get_state();
613 /* convert time selection to automation list model coordinates */
614 const Evoral::TimeConverter
<double, ARDOUR::framepos_t
>& tc
= line
.time_converter ();
615 double const start
= tc
.from (selection
.time
.front().start
- tc
.origin_b ());
616 double const end
= tc
.from (selection
.time
.front().end
- tc
.origin_b ());
620 if (alist
->cut (start
, end
) != 0) {
621 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
627 if ((what_we_got
= alist
->cut (start
, end
)) != 0) {
628 _editor
.get_cut_buffer().add (what_we_got
);
629 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
633 if ((what_we_got
= alist
->copy (start
, end
)) != 0) {
634 _editor
.get_cut_buffer().add (what_we_got
);
639 if ((what_we_got
= alist
->cut (start
, end
)) != 0) {
640 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
646 for (AutomationList::iterator x
= what_we_got
->begin(); x
!= what_we_got
->end(); ++x
) {
647 double when
= (*x
)->when
;
648 double val
= (*x
)->value
;
649 line
.model_to_view_coord (when
, val
);
657 AutomationTimeAxisView::reset_objects (PointSelection
& selection
)
659 list
<boost::shared_ptr
<AutomationLine
> > lines
;
661 lines
.push_back (_line
);
663 lines
= _view
->get_lines ();
666 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
667 reset_objects_one (**i
, selection
);
672 AutomationTimeAxisView::reset_objects_one (AutomationLine
& line
, PointSelection
& selection
)
674 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
676 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), &alist
->get_state(), 0));
678 for (PointSelection::iterator i
= selection
.begin(); i
!= selection
.end(); ++i
) {
680 if ((*i
).track
!= this) {
684 alist
->reset_range ((*i
).start
, (*i
).end
);
689 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection
& selection
, CutCopyOp op
)
691 list
<boost::shared_ptr
<AutomationLine
> > lines
;
693 lines
.push_back (_line
);
695 lines
= _view
->get_lines ();
698 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
699 cut_copy_clear_objects_one (**i
, selection
, op
);
704 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine
& line
, PointSelection
& selection
, CutCopyOp op
)
706 boost::shared_ptr
<Evoral::ControlList
> what_we_got
;
707 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
709 XMLNode
&before
= alist
->get_state();
711 for (PointSelection::iterator i
= selection
.begin(); i
!= selection
.end(); ++i
) {
713 if ((*i
).track
!= this) {
719 if (alist
->cut ((*i
).start
, (*i
).end
) != 0) {
720 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
724 if ((what_we_got
= alist
->cut ((*i
).start
, (*i
).end
)) != 0) {
725 _editor
.get_cut_buffer().add (what_we_got
);
726 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
730 if ((what_we_got
= alist
->copy ((*i
).start
, (*i
).end
)) != 0) {
731 _editor
.get_cut_buffer().add (what_we_got
);
736 if ((what_we_got
= alist
->cut ((*i
).start
, (*i
).end
)) != 0) {
737 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
746 for (AutomationList::iterator x
= what_we_got
->begin(); x
!= what_we_got
->end(); ++x
) {
747 double when
= (*x
)->when
;
748 double val
= (*x
)->value
;
749 line
.model_to_view_coord (when
, val
);
756 /** Paste a selection.
757 * @param pos Position to paste to (session frames).
758 * @param times Number of times to paste.
759 * @param selection Selection to paste.
760 * @param nth Index of the AutomationList within the selection to paste from.
763 AutomationTimeAxisView::paste (framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
765 boost::shared_ptr
<AutomationLine
> line
;
770 line
= _view
->paste_line (pos
);
777 return paste_one (*line
, pos
, times
, selection
, nth
);
781 AutomationTimeAxisView::paste_one (AutomationLine
& line
, framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
783 AutomationSelection::iterator p
;
784 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
786 for (p
= selection
.lines
.begin(); p
!= selection
.lines
.end() && nth
; ++p
, --nth
) {}
788 if (p
== selection
.lines
.end()) {
792 /* Make a copy of the list because we have to scale the
793 values from view coordinates to model coordinates, and we're
794 not supposed to modify the points in the selection.
797 AutomationList
copy (**p
);
799 for (AutomationList::iterator x
= copy
.begin(); x
!= copy
.end(); ++x
) {
800 double when
= (*x
)->when
;
801 double val
= (*x
)->value
;
802 line
.view_to_model_coord (when
, val
);
807 double const model_pos
= line
.time_converter().from (pos
- line
.time_converter().origin_b ());
809 XMLNode
&before
= alist
->get_state();
810 alist
->paste (copy
, model_pos
, times
);
811 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
817 AutomationTimeAxisView::get_selectables (framepos_t start
, framepos_t end
, double top
, double bot
, list
<Selectable
*>& results
)
819 if (!_line
&& !_view
) {
823 if (touched (top
, bot
)) {
825 /* remember: this is X Window - coordinate space starts in upper left and moves down.
826 _y_position is the "origin" or "top" of the track.
829 /* bottom of our track */
830 double const mybot
= _y_position
+ height
;
835 if (_y_position
>= top
&& mybot
<= bot
) {
837 /* _y_position is below top, mybot is above bot, so we're fully
846 /* top and bot are within _y_position .. mybot */
848 topfrac
= 1.0 - ((top
- _y_position
) / height
);
849 botfrac
= 1.0 - ((bot
- _y_position
) / height
);
854 _line
->get_selectables (start
, end
, botfrac
, topfrac
, results
);
856 _view
->get_selectables (start
, end
, botfrac
, topfrac
, results
);
862 AutomationTimeAxisView::get_inverted_selectables (Selection
& sel
, list
<Selectable
*>& result
)
865 _line
->get_inverted_selectables (sel
, result
);
870 AutomationTimeAxisView::set_selected_points (PointSelection
& points
)
873 _line
->set_selected_points (points
);
875 _view
->set_selected_points (points
);
880 AutomationTimeAxisView::clear_lines ()
883 _list_connections
.drop_connections ();
887 AutomationTimeAxisView::add_line (boost::shared_ptr
<AutomationLine
> line
)
892 assert(line
->the_list() == _control
->list());
894 _control
->alist()->automation_state_changed
.connect (
895 _list_connections
, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed
, this), gui_context()
898 _control
->alist()->InterpolationChanged
.connect (
899 _list_connections
, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed
, this, _1
), gui_context()
905 line
->set_height (height
);
907 /* pick up the current state */
908 automation_state_changed ();
914 AutomationTimeAxisView::entered()
917 _line
->track_entered();
922 AutomationTimeAxisView::exited ()
925 _line
->track_exited();
930 AutomationTimeAxisView::color_handler ()
938 AutomationTimeAxisView::set_state_2X (const XMLNode
& node
, int /*version*/)
940 if (node
.name() == X_("gain") && _parameter
== Evoral::Parameter (GainAutomation
)) {
941 XMLProperty
const * shown
= node
.property (X_("shown"));
943 bool yn
= string_is_affirmative (shown
->value ());
945 _canvas_display
->show (); /* FIXME: necessary? show_at? */
947 set_gui_property ("visible", yn
);
949 set_gui_property ("visible", false);
957 AutomationTimeAxisView::set_state (const XMLNode
& node
, int /*version*/)
963 AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr
<Automatable
>& automatable
, set
<Evoral::Parameter
>& visible
)
965 /* this keeps "knowledge" of how we store visibility information
966 in XML private to this class.
969 assert (automatable
);
971 Automatable::Controls
& controls (automatable
->controls());
973 for (Automatable::Controls::iterator i
= controls
.begin(); i
!= controls
.end(); ++i
) {
975 boost::shared_ptr
<AutomationControl
> ac
= boost::dynamic_pointer_cast
<AutomationControl
> (i
->second
);
979 const XMLNode
* gui_node
= ac
->extra_xml ("GUI");
982 const XMLProperty
* prop
= gui_node
->property ("shown");
984 if (string_is_affirmative (prop
->value())) {
985 visible
.insert (i
->first
);
994 /** @return true if this view has any automation data to display */
996 AutomationTimeAxisView::has_automation () const
998 return ( (_line
&& _line
->npoints() > 0) || (_view
&& _view
->has_automation()) );
1001 list
<boost::shared_ptr
<AutomationLine
> >
1002 AutomationTimeAxisView::lines () const
1004 list
<boost::shared_ptr
<AutomationLine
> > lines
;
1007 lines
.push_back (_line
);
1009 lines
= _view
->get_lines ();
1016 AutomationTimeAxisView::state_id() const
1019 return string_compose ("automation %1", _control
->id().to_s());
1021 assert (_parameter
);
1022 return string_compose ("automation %1 %2/%3/%4",
1026 (int) _parameter
.channel());
1030 /** Given a state id string, see if it is one generated by
1031 * this class. If so, parse it into its components.
1032 * @param state_id State ID string to parse.
1033 * @param route_id Filled in with the route's ID if the state ID string is parsed.
1034 * @param has_parameter Filled in with true if the state ID has a parameter, otherwise false.
1035 * @param parameter Filled in with the state ID's parameter, if it has one.
1036 * @return true if this is a state ID generated by this class, otherwise false.
1040 AutomationTimeAxisView::parse_state_id (
1041 string
const & state_id
,
1043 bool & has_parameter
,
1044 Evoral::Parameter
& parameter
)
1052 if (a
!= X_("automation")) {
1056 route_id
= PBD::ID (b
);
1059 has_parameter
= false;
1063 has_parameter
= true;
1066 boost::split (p
, c
, boost::is_any_of ("/"));
1068 assert (p
.size() == 3);
1070 parameter
= Evoral::Parameter (
1071 boost::lexical_cast
<int> (p
[0]),
1072 boost::lexical_cast
<int> (p
[2]),
1073 boost::lexical_cast
<int> (p
[1])