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 "pbd/memento_command.h"
23 #include "ardour/automation_control.h"
24 #include "ardour/event_type_map.h"
25 #include "ardour/route.h"
26 #include "ardour/session.h"
28 #include "ardour_ui.h"
29 #include "automation_time_axis.h"
30 #include "automation_streamview.h"
31 #include "global_signals.h"
32 #include "gui_thread.h"
33 #include "route_time_axis.h"
34 #include "automation_line.h"
35 #include "public_editor.h"
36 #include "simplerect.h"
37 #include "selection.h"
38 #include "rgb_macros.h"
39 #include "point_selection.h"
40 #include "canvas_impl.h"
46 using namespace ARDOUR
;
49 using namespace Gtkmm2ext
;
50 using namespace Editing
;
52 Pango::FontDescription
* AutomationTimeAxisView::name_font
= 0;
53 bool AutomationTimeAxisView::have_name_font
= false;
54 const string
AutomationTimeAxisView::state_node_name
= "AutomationChild";
57 /** \a a the automatable object this time axis is to display data for.
58 * For route/track automation (e.g. gain) pass the route for both \r and \a.
59 * For route child (e.g. plugin) automation, pass the child for \a.
60 * For region automation (e.g. MIDI CC), pass null for \a.
62 AutomationTimeAxisView::AutomationTimeAxisView (
64 boost::shared_ptr
<Route
> r
,
65 boost::shared_ptr
<Automatable
> a
,
66 boost::shared_ptr
<AutomationControl
> c
,
71 ArdourCanvas::Canvas
& canvas
,
73 const string
& nomparent
76 , TimeAxisView (s
, e
, &parent
, canvas
)
82 , _view (show_regions
? new AutomationStreamView (*this) : 0)
84 , auto_button (X_("")) /* force addition of a label */
86 if (!have_name_font
) {
87 name_font
= get_font_for_style (X_("AutomationTrackName"));
88 have_name_font
= true;
91 if (_automatable
&& _control
) {
92 _controller
= AutomationController::create (_automatable
, _control
->parameter(), _control
);
100 mode_discrete_item
= 0;
103 ignore_state_request
= false;
104 first_call_to_set_height
= true;
106 _base_rect
= new SimpleRect(*_canvas_display
);
107 _base_rect
->property_x1() = 0.0;
108 _base_rect
->property_y1() = 0.0;
109 /** gnomecanvas sometimes converts this value to int or adds 2 to it, so it must be
110 set correctly to avoid overflow.
112 _base_rect
->property_x2() = INT_MAX
- 2;
113 _base_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline
.get();
115 /* outline ends and bottom */
116 _base_rect
->property_outline_what() = (guint32
) (0x1|0x2|0x8);
117 _base_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill
.get();
119 _base_rect
->set_data ("trackview", this);
121 _base_rect
->signal_event().connect (sigc::bind (
122 sigc::mem_fun (_editor
, &PublicEditor::canvas_automation_track_event
),
126 _base_rect
->lower_to_bottom();
129 hide_button
.add (*(manage (new Gtk::Image (::get_icon("hide")))));
131 auto_button
.set_name ("TrackVisualButton");
132 hide_button
.set_name ("TrackRemoveButton");
134 auto_button
.unset_flags (Gtk::CAN_FOCUS
);
135 hide_button
.unset_flags (Gtk::CAN_FOCUS
);
137 controls_table
.set_no_show_all();
139 ARDOUR_UI::instance()->set_tip(auto_button
, _("automation state"));
140 ARDOUR_UI::instance()->set_tip(hide_button
, _("hide track"));
142 /* rearrange the name display */
144 /* we never show these for automation tracks, so make
145 life easier and remove them.
150 /* move the name label over a bit */
152 string shortpname
= _name
;
153 bool shortened
= false;
156 shortpname
= fit_to_pixels (_name
, 60, *name_font
, ignore_width
, true);
158 if (shortpname
!= _name
){
162 name_label
.set_text (shortpname
);
163 name_label
.set_alignment (Gtk::ALIGN_CENTER
, Gtk::ALIGN_CENTER
);
164 name_label
.set_name (X_("TrackParameterName"));
166 if (nomparent
.length()) {
168 /* limit the plug name string */
170 string pname
= fit_to_pixels (nomparent
, 60, *name_font
, ignore_width
, true);
171 if (pname
!= nomparent
) {
175 plugname
= new Label (pname
);
176 plugname
->set_name (X_("TrackPlugName"));
178 controls_table
.remove (name_hbox
);
179 controls_table
.attach (*plugname
, 1, 5, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
180 plugname_packed
= true;
181 controls_table
.attach (name_hbox
, 1, 5, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
184 plugname_packed
= false;
188 string tipname
= nomparent
;
189 if (!tipname
.empty()) {
193 ARDOUR_UI::instance()->set_tip(controls_ebox
, tipname
);
196 /* add the buttons */
197 controls_table
.attach (hide_button
, 0, 1, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
199 controls_table
.attach (auto_button
, 5, 8, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
202 /* add bar controller */
203 controls_table
.attach (*_controller
.get(), 0, 8, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
206 controls_table
.show_all ();
208 hide_button
.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked
));
209 auto_button
.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked
));
211 controls_base_selected_name
= X_("AutomationTrackControlsBaseSelected");
212 controls_base_unselected_name
= X_("AutomationTrackControlsBase");
213 controls_ebox
.set_name (controls_base_unselected_name
);
215 XMLNode
* xml_node
= get_parent_with_state()->get_automation_child_xml_node (_parameter
);
218 set_state (*xml_node
, Stateful::loading_state_version
);
221 /* ask for notifications of any new RegionViews */
228 /* no regions, just a single line for the entire track (e.g. bus gain) */
230 boost::shared_ptr
<AutomationLine
> line (
232 ARDOUR::EventTypeMap::instance().to_symbol(_parameter
),
239 line
->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine
.get());
240 line
->queue_reset ();
244 /* make sure labels etc. are correct */
246 automation_state_changed ();
247 ColorsChanged
.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler
));
249 _route
->DropReferences
.connect (
250 _route_connections
, invalidator (*this), ui_bind (&AutomationTimeAxisView::route_going_away
, this), gui_context ()
254 AutomationTimeAxisView::~AutomationTimeAxisView ()
259 AutomationTimeAxisView::route_going_away ()
265 AutomationTimeAxisView::auto_clicked ()
267 using namespace Menu_Helpers
;
269 if (automation_menu
== 0) {
270 automation_menu
= manage (new Menu
);
271 automation_menu
->set_name ("ArdourContextMenu");
272 MenuList
& items (automation_menu
->items());
274 items
.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
275 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Off
)));
276 items
.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
277 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Play
)));
278 items
.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
279 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Write
)));
280 items
.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
281 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Touch
)));
284 automation_menu
->popup (1, gtk_get_current_event_time());
288 AutomationTimeAxisView::set_automation_state (AutoState state
)
290 if (ignore_state_request
) {
295 _automatable
->set_parameter_automation_state (_parameter
, state
);
298 if (_route
== _automatable
) { // This is a time axis for route (not region) automation
299 _route
->set_parameter_automation_state (_parameter
, state
);
302 if (_control
->list()) {
303 _control
->alist()->set_automation_state(state
);
307 _view
->set_automation_state (state
);
309 /* AutomationStreamViews don't signal when their automation state changes, so handle
310 our updates `manually'.
312 automation_state_changed ();
317 AutomationTimeAxisView::automation_state_changed ()
321 /* update button label */
324 state
= _control
->alist()->automation_state ();
326 state
= _view
->automation_state ();
331 switch (state
& (Off
|Play
|Touch
|Write
)) {
333 auto_button
.set_label (_("Manual"));
335 ignore_state_request
= true;
336 auto_off_item
->set_active (true);
337 auto_play_item
->set_active (false);
338 auto_touch_item
->set_active (false);
339 auto_write_item
->set_active (false);
340 ignore_state_request
= false;
344 auto_button
.set_label (_("Play"));
345 if (auto_play_item
) {
346 ignore_state_request
= true;
347 auto_play_item
->set_active (true);
348 auto_off_item
->set_active (false);
349 auto_touch_item
->set_active (false);
350 auto_write_item
->set_active (false);
351 ignore_state_request
= false;
355 auto_button
.set_label (_("Write"));
356 if (auto_write_item
) {
357 ignore_state_request
= true;
358 auto_write_item
->set_active (true);
359 auto_off_item
->set_active (false);
360 auto_play_item
->set_active (false);
361 auto_touch_item
->set_active (false);
362 ignore_state_request
= false;
366 auto_button
.set_label (_("Touch"));
367 if (auto_touch_item
) {
368 ignore_state_request
= true;
369 auto_touch_item
->set_active (true);
370 auto_off_item
->set_active (false);
371 auto_play_item
->set_active (false);
372 auto_write_item
->set_active (false);
373 ignore_state_request
= false;
377 auto_button
.set_label (_("???"));
382 /** The interpolation style of our AutomationList has changed, so update */
384 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s
)
386 if (mode_line_item
&& mode_discrete_item
) {
387 if (s
== AutomationList::Discrete
) {
388 mode_discrete_item
->set_active(true);
389 mode_line_item
->set_active(false);
391 mode_line_item
->set_active(true);
392 mode_discrete_item
->set_active(false);
397 /** A menu item has been selected to change our interpolation mode */
399 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style
)
401 /* Tell our view's list, if we have one, otherwise tell our own.
402 * Everything else will be signalled back from that.
406 _view
->set_interpolation (style
);
408 _control
->list()->set_interpolation (style
);
413 AutomationTimeAxisView::clear_clicked ()
415 assert (_line
|| _view
);
417 _session
->begin_reversible_command (_("clear automation"));
425 _session
->commit_reversible_command ();
426 _session
->set_dirty ();
430 AutomationTimeAxisView::set_height (uint32_t h
)
432 bool const changed
= (height
!= (uint32_t) h
) || first_call_to_set_height
;
433 uint32_t const normal
= preset_height (HeightNormal
);
434 bool const changed_between_small_and_normal
= ( (height
< normal
&& h
>= normal
) || (height
>= normal
|| h
< normal
) );
436 TimeAxisView
* state_parent
= get_parent_with_state ();
437 assert(state_parent
);
438 XMLNode
* xml_node
= state_parent
->get_automation_child_xml_node (_parameter
);
440 TimeAxisView::set_height (h
);
441 _base_rect
->property_y2() = h
;
444 _line
->set_height(h
);
448 _view
->set_height(h
);
449 _view
->update_contents_height();
453 snprintf (buf
, sizeof (buf
), "%u", height
);
455 xml_node
->add_property ("height", buf
);
458 if (changed_between_small_and_normal
|| first_call_to_set_height
) {
460 first_call_to_set_height
= false;
462 if (h
>= preset_height (HeightNormal
)) {
463 controls_table
.remove (name_hbox
);
466 if (plugname_packed
) {
467 controls_table
.remove (*plugname
);
468 plugname_packed
= false;
470 controls_table
.attach (*plugname
, 1, 5, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
471 plugname_packed
= true;
472 controls_table
.attach (name_hbox
, 1, 5, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
474 controls_table
.attach (name_hbox
, 1, 5, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
478 name_hbox
.show_all ();
481 hide_button
.show_all();
483 } else if (h
>= preset_height (HeightSmall
)) {
484 controls_table
.remove (name_hbox
);
486 if (plugname_packed
) {
487 controls_table
.remove (*plugname
);
488 plugname_packed
= false;
491 controls_table
.attach (name_hbox
, 1, 5, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
492 controls_table
.hide_all ();
495 name_hbox
.show_all ();
500 } else if (h
>= preset_height (HeightNormal
)) {
501 cerr
<< "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl
;
505 if (canvas_item_visible (_canvas_display
) && _route
) {
506 /* only emit the signal if the height really changed and we were visible */
507 _route
->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
513 AutomationTimeAxisView::set_samples_per_unit (double spu
)
515 TimeAxisView::set_samples_per_unit (spu
);
522 _view
->set_samples_per_unit (spu
);
527 AutomationTimeAxisView::hide_clicked ()
529 // LAME fix for refreshing the hide button
530 hide_button
.set_sensitive(false);
532 set_marked_for_display (false);
535 hide_button
.set_sensitive(true);
539 AutomationTimeAxisView::build_display_menu ()
541 using namespace Menu_Helpers
;
545 TimeAxisView::build_display_menu ();
547 /* now fill it with our stuff */
549 MenuList
& items
= display_menu
->items();
551 items
.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked
)));
552 items
.push_back (SeparatorElem());
553 items
.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked
)));
554 items
.push_back (SeparatorElem());
558 Menu
* auto_state_menu
= manage (new Menu
);
559 auto_state_menu
->set_name ("ArdourContextMenu");
560 MenuList
& as_items
= auto_state_menu
->items();
562 as_items
.push_back (CheckMenuElem (_("Manual"), sigc::bind (
563 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
565 auto_off_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
567 as_items
.push_back (CheckMenuElem (_("Play"), sigc::bind (
568 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
570 auto_play_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
572 as_items
.push_back (CheckMenuElem (_("Write"), sigc::bind (
573 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
574 (AutoState
) Write
)));
575 auto_write_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
577 as_items
.push_back (CheckMenuElem (_("Touch"), sigc::bind (
578 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
579 (AutoState
) Touch
)));
580 auto_touch_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
582 items
.push_back (MenuElem (_("State"), *auto_state_menu
));
586 /* current interpolation state */
587 AutomationList::InterpolationStyle
const s
= _view
? _view
->interpolation() : _control
->list()->interpolation ();
589 if (EventTypeMap::instance().is_midi_parameter(_parameter
)) {
591 Menu
* auto_mode_menu
= manage (new Menu
);
592 auto_mode_menu
->set_name ("ArdourContextMenu");
593 MenuList
& am_items
= auto_mode_menu
->items();
595 RadioMenuItem::Group group
;
597 am_items
.push_back (RadioMenuElem (group
, _("Discrete"), sigc::bind (
598 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation
),
599 AutomationList::Discrete
)));
600 mode_discrete_item
= dynamic_cast<CheckMenuItem
*>(&am_items
.back());
601 mode_discrete_item
->set_active (s
== AutomationList::Discrete
);
603 am_items
.push_back (RadioMenuElem (group
, _("Linear"), sigc::bind (
604 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation
),
605 AutomationList::Linear
)));
606 mode_line_item
= dynamic_cast<CheckMenuItem
*>(&am_items
.back());
607 mode_line_item
->set_active (s
== AutomationList::Linear
);
609 items
.push_back (MenuElem (_("Mode"), *auto_mode_menu
));
612 /* make sure the automation menu state is correct */
614 automation_state_changed ();
615 interpolation_changed (s
);
619 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item
* /*item*/, GdkEvent
* /*event*/, framepos_t when
, double y
)
627 _canvas_display
->w2i (x
, y
);
629 /* compute vertical fractional position */
631 y
= 1.0 - (y
/ height
);
635 _line
->view_to_model_coord (x
, y
);
637 _session
->begin_reversible_command (_("add automation event"));
638 XMLNode
& before
= _control
->alist()->get_state();
640 _control
->alist()->add (when
, y
);
642 XMLNode
& after
= _control
->alist()->get_state();
643 _session
->commit_reversible_command (new MementoCommand
<ARDOUR::AutomationList
>(*_control
->alist(), &before
, &after
));
645 _session
->set_dirty ();
649 AutomationTimeAxisView::cut_copy_clear (Selection
& selection
, CutCopyOp op
)
651 list
<boost::shared_ptr
<AutomationLine
> > lines
;
653 lines
.push_back (_line
);
655 lines
= _view
->get_lines ();
658 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
659 cut_copy_clear_one (**i
, selection
, op
);
664 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine
& line
, Selection
& selection
, CutCopyOp op
)
666 boost::shared_ptr
<Evoral::ControlList
> what_we_got
;
667 boost::shared_ptr
<AutomationList
> alist (line
.the_list());
669 XMLNode
&before
= alist
->get_state();
671 /* convert time selection to automation list model coordinates */
672 const Evoral::TimeConverter
<double, ARDOUR::framepos_t
>& tc
= line
.time_converter ();
673 double const start
= tc
.from (selection
.time
.front().start
- tc
.origin_b ());
674 double const end
= tc
.from (selection
.time
.front().end
- tc
.origin_b ());
679 if ((what_we_got
= alist
->cut (start
, end
)) != 0) {
680 _editor
.get_cut_buffer().add (what_we_got
);
681 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
685 if ((what_we_got
= alist
->copy (start
, end
)) != 0) {
686 _editor
.get_cut_buffer().add (what_we_got
);
691 if ((what_we_got
= alist
->cut (start
, end
)) != 0) {
692 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
698 for (AutomationList::iterator x
= what_we_got
->begin(); x
!= what_we_got
->end(); ++x
) {
699 double when
= (*x
)->when
;
700 double val
= (*x
)->value
;
701 line
.model_to_view_coord (when
, val
);
709 AutomationTimeAxisView::reset_objects (PointSelection
& selection
)
711 list
<boost::shared_ptr
<AutomationLine
> > lines
;
713 lines
.push_back (_line
);
715 lines
= _view
->get_lines ();
718 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
719 reset_objects_one (**i
, selection
);
724 AutomationTimeAxisView::reset_objects_one (AutomationLine
& line
, PointSelection
& selection
)
726 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
728 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), &alist
->get_state(), 0));
730 for (PointSelection::iterator i
= selection
.begin(); i
!= selection
.end(); ++i
) {
732 if ((*i
).track
!= this) {
736 alist
->reset_range ((*i
).start
, (*i
).end
);
741 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection
& selection
, CutCopyOp op
)
743 list
<boost::shared_ptr
<AutomationLine
> > lines
;
745 lines
.push_back (_line
);
747 lines
= _view
->get_lines ();
750 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
751 cut_copy_clear_objects_one (**i
, selection
, op
);
756 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine
& line
, PointSelection
& selection
, CutCopyOp op
)
758 boost::shared_ptr
<Evoral::ControlList
> what_we_got
;
759 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
761 XMLNode
&before
= alist
->get_state();
763 for (PointSelection::iterator i
= selection
.begin(); i
!= selection
.end(); ++i
) {
765 if ((*i
).track
!= this) {
771 if ((what_we_got
= alist
->cut ((*i
).start
, (*i
).end
)) != 0) {
772 _editor
.get_cut_buffer().add (what_we_got
);
773 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
777 if ((what_we_got
= alist
->copy ((*i
).start
, (*i
).end
)) != 0) {
778 _editor
.get_cut_buffer().add (what_we_got
);
783 if ((what_we_got
= alist
->cut ((*i
).start
, (*i
).end
)) != 0) {
784 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
793 for (AutomationList::iterator x
= what_we_got
->begin(); x
!= what_we_got
->end(); ++x
) {
794 double when
= (*x
)->when
;
795 double val
= (*x
)->value
;
796 line
.model_to_view_coord (when
, val
);
803 /** Paste a selection.
804 * @param pos Position to paste to (session frames).
805 * @param times Number of times to paste.
806 * @param selection Selection to paste.
807 * @param nth Index of the AutomationList within the selection to paste from.
810 AutomationTimeAxisView::paste (framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
812 boost::shared_ptr
<AutomationLine
> line
;
817 line
= _view
->paste_line (pos
);
824 return paste_one (*line
, pos
, times
, selection
, nth
);
828 AutomationTimeAxisView::paste_one (AutomationLine
& line
, framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
830 AutomationSelection::iterator p
;
831 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
833 for (p
= selection
.lines
.begin(); p
!= selection
.lines
.end() && nth
; ++p
, --nth
) {}
835 if (p
== selection
.lines
.end()) {
839 /* Make a copy of the list because we have to scale the
840 values from view coordinates to model coordinates, and we're
841 not supposed to modify the points in the selection.
844 AutomationList
copy (**p
);
846 for (AutomationList::iterator x
= copy
.begin(); x
!= copy
.end(); ++x
) {
847 double when
= (*x
)->when
;
848 double val
= (*x
)->value
;
849 line
.view_to_model_coord (when
, val
);
854 double const model_pos
= line
.time_converter().from (pos
- line
.time_converter().origin_b ());
856 XMLNode
&before
= alist
->get_state();
857 alist
->paste (copy
, model_pos
, times
);
858 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
864 AutomationTimeAxisView::get_selectables (framepos_t start
, framepos_t end
, double top
, double bot
, list
<Selectable
*>& results
)
866 if (!_line
&& !_view
) {
870 if (touched (top
, bot
)) {
872 /* remember: this is X Window - coordinate space starts in upper left and moves down.
873 _y_position is the "origin" or "top" of the track.
876 /* bottom of our track */
877 double const mybot
= _y_position
+ height
;
882 if (_y_position
>= top
&& mybot
<= bot
) {
884 /* _y_position is below top, mybot is above bot, so we're fully
893 /* top and bot are within _y_position .. mybot */
895 topfrac
= 1.0 - ((top
- _y_position
) / height
);
896 botfrac
= 1.0 - ((bot
- _y_position
) / height
);
901 _line
->get_selectables (start
, end
, botfrac
, topfrac
, results
);
903 _view
->get_selectables (start
, end
, botfrac
, topfrac
, results
);
909 AutomationTimeAxisView::get_inverted_selectables (Selection
& sel
, list
<Selectable
*>& result
)
912 _line
->get_inverted_selectables (sel
, result
);
917 AutomationTimeAxisView::set_selected_points (PointSelection
& points
)
920 _line
->set_selected_points (points
);
922 _view
->set_selected_points (points
);
927 AutomationTimeAxisView::clear_lines ()
930 _list_connections
.drop_connections ();
934 AutomationTimeAxisView::add_line (boost::shared_ptr
<AutomationLine
> line
)
938 assert(line
->the_list() == _control
->list());
940 _control
->alist()->automation_state_changed
.connect (
941 _list_connections
, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed
, this), gui_context()
944 _control
->alist()->InterpolationChanged
.connect (
945 _list_connections
, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed
, this, _1
), gui_context()
949 //_controller = AutomationController::create(_session, line->the_list(), _control);
951 line
->set_height (height
);
953 /* pick up the current state */
954 automation_state_changed ();
960 AutomationTimeAxisView::entered()
963 _line
->track_entered();
968 AutomationTimeAxisView::exited ()
971 _line
->track_exited();
976 AutomationTimeAxisView::color_handler ()
984 AutomationTimeAxisView::set_state (const XMLNode
& node
, int version
)
986 TimeAxisView::set_state (node
, version
);
988 if (version
< 3000) {
989 return set_state_2X (node
, version
);
992 XMLProperty
const * type
= node
.property ("automation-id");
993 if (type
&& type
->value () == ARDOUR::EventTypeMap::instance().to_symbol (_parameter
)) {
994 XMLProperty
const * shown
= node
.property ("shown");
995 if (shown
&& shown
->value () == "yes") {
996 set_marked_for_display (true);
997 _canvas_display
->show (); /* FIXME: necessary? show_at? */
1001 if (!_marked_for_display
) {
1010 AutomationTimeAxisView::set_state_2X (const XMLNode
& node
, int /*version*/)
1012 if (node
.name() == X_("gain") && _parameter
== Evoral::Parameter (GainAutomation
)) {
1013 XMLProperty
const * shown
= node
.property (X_("shown"));
1014 if (shown
&& string_is_affirmative (shown
->value ())) {
1015 set_marked_for_display (true);
1016 _canvas_display
->show (); /* FIXME: necessary? show_at? */
1020 if (!_marked_for_display
) {
1028 AutomationTimeAxisView::get_state_node ()
1030 TimeAxisView
* state_parent
= get_parent_with_state ();
1033 return state_parent
->get_automation_child_xml_node (_parameter
);
1040 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown
)
1042 XMLNode
* xml_node
= get_state_node();
1044 xml_node
->add_property ("shown", editor_shown
? "yes" : "no");
1049 AutomationTimeAxisView::show_at (double y
, int& nth
, Gtk::VBox
*parent
)
1051 update_extra_xml_shown (true);
1053 return TimeAxisView::show_at (y
, nth
, parent
);
1057 AutomationTimeAxisView::hide ()
1059 update_extra_xml_shown (false);
1061 TimeAxisView::hide ();
1065 AutomationTimeAxisView::set_visibility (bool yn
)
1067 bool changed
= TimeAxisView::set_visibility (yn
);
1070 get_state_node()->add_property ("shown", yn
? X_("yes") : X_("no"));
1076 /** @return true if this view has any automation data to display */
1078 AutomationTimeAxisView::has_automation () const
1080 return ( (_line
&& _line
->npoints() > 0) || (_view
&& _view
->has_automation()) );
1083 list
<boost::shared_ptr
<AutomationLine
> >
1084 AutomationTimeAxisView::lines () const
1086 list
<boost::shared_ptr
<AutomationLine
> > lines
;
1089 lines
.push_back (_line
);
1091 lines
= _view
->get_lines ();