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
));
250 AutomationTimeAxisView::~AutomationTimeAxisView ()
255 AutomationTimeAxisView::auto_clicked ()
257 using namespace Menu_Helpers
;
259 if (automation_menu
== 0) {
260 automation_menu
= manage (new Menu
);
261 automation_menu
->set_name ("ArdourContextMenu");
262 MenuList
& items (automation_menu
->items());
264 items
.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
265 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Off
)));
266 items
.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
267 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Play
)));
268 items
.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
269 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Write
)));
270 items
.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
271 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Touch
)));
274 automation_menu
->popup (1, gtk_get_current_event_time());
278 AutomationTimeAxisView::set_automation_state (AutoState state
)
280 if (ignore_state_request
) {
285 _automatable
->set_parameter_automation_state (_parameter
, state
);
288 if (_route
== _automatable
) { // This is a time axis for route (not region) automation
289 _route
->set_parameter_automation_state (_parameter
, state
);
292 if (_control
->list()) {
293 _control
->alist()->set_automation_state(state
);
297 _view
->set_automation_state (state
);
299 /* AutomationStreamViews don't signal when their automation state changes, so handle
300 our updates `manually'.
302 automation_state_changed ();
307 AutomationTimeAxisView::automation_state_changed ()
311 /* update button label */
314 state
= _control
->alist()->automation_state ();
316 state
= _view
->automation_state ();
321 switch (state
& (Off
|Play
|Touch
|Write
)) {
323 auto_button
.set_label (_("Manual"));
325 ignore_state_request
= true;
326 auto_off_item
->set_active (true);
327 auto_play_item
->set_active (false);
328 auto_touch_item
->set_active (false);
329 auto_write_item
->set_active (false);
330 ignore_state_request
= false;
334 auto_button
.set_label (_("Play"));
335 if (auto_play_item
) {
336 ignore_state_request
= true;
337 auto_play_item
->set_active (true);
338 auto_off_item
->set_active (false);
339 auto_touch_item
->set_active (false);
340 auto_write_item
->set_active (false);
341 ignore_state_request
= false;
345 auto_button
.set_label (_("Write"));
346 if (auto_write_item
) {
347 ignore_state_request
= true;
348 auto_write_item
->set_active (true);
349 auto_off_item
->set_active (false);
350 auto_play_item
->set_active (false);
351 auto_touch_item
->set_active (false);
352 ignore_state_request
= false;
356 auto_button
.set_label (_("Touch"));
357 if (auto_touch_item
) {
358 ignore_state_request
= true;
359 auto_touch_item
->set_active (true);
360 auto_off_item
->set_active (false);
361 auto_play_item
->set_active (false);
362 auto_write_item
->set_active (false);
363 ignore_state_request
= false;
367 auto_button
.set_label (_("???"));
372 /** The interpolation style of our AutomationList has changed, so update */
374 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s
)
376 if (mode_line_item
&& mode_discrete_item
) {
377 if (s
== AutomationList::Discrete
) {
378 mode_discrete_item
->set_active(true);
379 mode_line_item
->set_active(false);
381 mode_line_item
->set_active(true);
382 mode_discrete_item
->set_active(false);
387 /** A menu item has been selected to change our interpolation mode */
389 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style
)
391 /* Tell our view's list, if we have one, otherwise tell our own.
392 * Everything else will be signalled back from that.
396 _view
->set_interpolation (style
);
398 _control
->list()->set_interpolation (style
);
403 AutomationTimeAxisView::clear_clicked ()
405 assert (_line
|| _view
);
407 _session
->begin_reversible_command (_("clear automation"));
415 _session
->commit_reversible_command ();
416 _session
->set_dirty ();
420 AutomationTimeAxisView::set_height (uint32_t h
)
422 bool const changed
= (height
!= (uint32_t) h
) || first_call_to_set_height
;
423 uint32_t const normal
= preset_height (HeightNormal
);
424 bool const changed_between_small_and_normal
= ( (height
< normal
&& h
>= normal
) || (height
>= normal
|| h
< normal
) );
426 TimeAxisView
* state_parent
= get_parent_with_state ();
427 assert(state_parent
);
428 XMLNode
* xml_node
= state_parent
->get_automation_child_xml_node (_parameter
);
430 TimeAxisView::set_height (h
);
431 _base_rect
->property_y2() = h
;
434 _line
->set_height(h
);
438 _view
->set_height(h
);
439 _view
->update_contents_height();
443 snprintf (buf
, sizeof (buf
), "%u", height
);
445 xml_node
->add_property ("height", buf
);
448 if (changed_between_small_and_normal
|| first_call_to_set_height
) {
450 first_call_to_set_height
= false;
452 if (h
>= preset_height (HeightNormal
)) {
453 controls_table
.remove (name_hbox
);
456 if (plugname_packed
) {
457 controls_table
.remove (*plugname
);
458 plugname_packed
= false;
460 controls_table
.attach (*plugname
, 1, 5, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
461 plugname_packed
= true;
462 controls_table
.attach (name_hbox
, 1, 5, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
464 controls_table
.attach (name_hbox
, 1, 5, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
468 name_hbox
.show_all ();
471 hide_button
.show_all();
473 } else if (h
>= preset_height (HeightSmall
)) {
474 controls_table
.remove (name_hbox
);
476 if (plugname_packed
) {
477 controls_table
.remove (*plugname
);
478 plugname_packed
= false;
481 controls_table
.attach (name_hbox
, 1, 5, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
482 controls_table
.hide_all ();
485 name_hbox
.show_all ();
490 } else if (h
>= preset_height (HeightNormal
)) {
491 cerr
<< "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl
;
495 if (canvas_item_visible (_canvas_display
)) {
496 /* only emit the signal if the height really changed and we were visible */
497 _route
->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
503 AutomationTimeAxisView::set_samples_per_unit (double spu
)
505 TimeAxisView::set_samples_per_unit (spu
);
512 _view
->set_samples_per_unit (spu
);
517 AutomationTimeAxisView::hide_clicked ()
519 // LAME fix for refreshing the hide button
520 hide_button
.set_sensitive(false);
522 set_marked_for_display (false);
525 hide_button
.set_sensitive(true);
529 AutomationTimeAxisView::build_display_menu ()
531 using namespace Menu_Helpers
;
535 TimeAxisView::build_display_menu ();
537 /* now fill it with our stuff */
539 MenuList
& items
= display_menu
->items();
541 items
.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked
)));
542 items
.push_back (SeparatorElem());
543 items
.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked
)));
544 items
.push_back (SeparatorElem());
548 Menu
* auto_state_menu
= manage (new Menu
);
549 auto_state_menu
->set_name ("ArdourContextMenu");
550 MenuList
& as_items
= auto_state_menu
->items();
552 as_items
.push_back (CheckMenuElem (_("Manual"), sigc::bind (
553 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
555 auto_off_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
557 as_items
.push_back (CheckMenuElem (_("Play"), sigc::bind (
558 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
560 auto_play_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
562 as_items
.push_back (CheckMenuElem (_("Write"), sigc::bind (
563 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
564 (AutoState
) Write
)));
565 auto_write_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
567 as_items
.push_back (CheckMenuElem (_("Touch"), sigc::bind (
568 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
569 (AutoState
) Touch
)));
570 auto_touch_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
572 items
.push_back (MenuElem (_("State"), *auto_state_menu
));
576 /* current interpolation state */
577 AutomationList::InterpolationStyle
const s
= _view
? _view
->interpolation() : _control
->list()->interpolation ();
579 if (EventTypeMap::instance().is_midi_parameter(_parameter
)) {
581 Menu
* auto_mode_menu
= manage (new Menu
);
582 auto_mode_menu
->set_name ("ArdourContextMenu");
583 MenuList
& am_items
= auto_mode_menu
->items();
585 RadioMenuItem::Group group
;
587 am_items
.push_back (RadioMenuElem (group
, _("Discrete"), sigc::bind (
588 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation
),
589 AutomationList::Discrete
)));
590 mode_discrete_item
= dynamic_cast<CheckMenuItem
*>(&am_items
.back());
591 mode_discrete_item
->set_active (s
== AutomationList::Discrete
);
593 am_items
.push_back (RadioMenuElem (group
, _("Linear"), sigc::bind (
594 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation
),
595 AutomationList::Linear
)));
596 mode_line_item
= dynamic_cast<CheckMenuItem
*>(&am_items
.back());
597 mode_line_item
->set_active (s
== AutomationList::Linear
);
599 items
.push_back (MenuElem (_("Mode"), *auto_mode_menu
));
602 /* make sure the automation menu state is correct */
604 automation_state_changed ();
605 interpolation_changed (s
);
609 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item
* /*item*/, GdkEvent
* /*event*/, framepos_t when
, double y
)
617 _canvas_display
->w2i (x
, y
);
619 /* compute vertical fractional position */
621 y
= 1.0 - (y
/ height
);
625 _line
->view_to_model_coord (x
, y
);
627 _session
->begin_reversible_command (_("add automation event"));
628 XMLNode
& before
= _control
->alist()->get_state();
630 _control
->alist()->add (when
, y
);
632 XMLNode
& after
= _control
->alist()->get_state();
633 _session
->commit_reversible_command (new MementoCommand
<ARDOUR::AutomationList
>(*_control
->alist(), &before
, &after
));
635 _session
->set_dirty ();
639 AutomationTimeAxisView::cut_copy_clear (Selection
& selection
, CutCopyOp op
)
641 list
<boost::shared_ptr
<AutomationLine
> > lines
;
643 lines
.push_back (_line
);
645 lines
= _view
->get_lines ();
648 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
649 cut_copy_clear_one (**i
, selection
, op
);
654 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine
& line
, Selection
& selection
, CutCopyOp op
)
656 boost::shared_ptr
<Evoral::ControlList
> what_we_got
;
657 boost::shared_ptr
<AutomationList
> alist (line
.the_list());
659 XMLNode
&before
= alist
->get_state();
661 /* convert time selection to automation list model coordinates */
662 const Evoral::TimeConverter
<double, ARDOUR::framepos_t
>& tc
= line
.time_converter ();
663 double const start
= tc
.from (selection
.time
.front().start
- tc
.origin_b ());
664 double const end
= tc
.from (selection
.time
.front().end
- tc
.origin_b ());
669 if ((what_we_got
= alist
->cut (start
, end
)) != 0) {
670 _editor
.get_cut_buffer().add (what_we_got
);
671 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
675 if ((what_we_got
= alist
->copy (start
, end
)) != 0) {
676 _editor
.get_cut_buffer().add (what_we_got
);
681 if ((what_we_got
= alist
->cut (start
, end
)) != 0) {
682 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
688 for (AutomationList::iterator x
= what_we_got
->begin(); x
!= what_we_got
->end(); ++x
) {
689 double when
= (*x
)->when
;
690 double val
= (*x
)->value
;
691 line
.model_to_view_coord (when
, val
);
699 AutomationTimeAxisView::reset_objects (PointSelection
& selection
)
701 list
<boost::shared_ptr
<AutomationLine
> > lines
;
703 lines
.push_back (_line
);
705 lines
= _view
->get_lines ();
708 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
709 reset_objects_one (**i
, selection
);
714 AutomationTimeAxisView::reset_objects_one (AutomationLine
& line
, PointSelection
& selection
)
716 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
718 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), &alist
->get_state(), 0));
720 for (PointSelection::iterator i
= selection
.begin(); i
!= selection
.end(); ++i
) {
722 if ((*i
).track
!= this) {
726 alist
->reset_range ((*i
).start
, (*i
).end
);
731 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection
& selection
, CutCopyOp op
)
733 list
<boost::shared_ptr
<AutomationLine
> > lines
;
735 lines
.push_back (_line
);
737 lines
= _view
->get_lines ();
740 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
741 cut_copy_clear_objects_one (**i
, selection
, op
);
746 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine
& line
, PointSelection
& selection
, CutCopyOp op
)
748 boost::shared_ptr
<Evoral::ControlList
> what_we_got
;
749 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
751 XMLNode
&before
= alist
->get_state();
753 for (PointSelection::iterator i
= selection
.begin(); i
!= selection
.end(); ++i
) {
755 if ((*i
).track
!= this) {
761 if ((what_we_got
= alist
->cut ((*i
).start
, (*i
).end
)) != 0) {
762 _editor
.get_cut_buffer().add (what_we_got
);
763 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
767 if ((what_we_got
= alist
->copy ((*i
).start
, (*i
).end
)) != 0) {
768 _editor
.get_cut_buffer().add (what_we_got
);
773 if ((what_we_got
= alist
->cut ((*i
).start
, (*i
).end
)) != 0) {
774 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
783 for (AutomationList::iterator x
= what_we_got
->begin(); x
!= what_we_got
->end(); ++x
) {
784 double when
= (*x
)->when
;
785 double val
= (*x
)->value
;
786 line
.model_to_view_coord (when
, val
);
793 /** Paste a selection.
794 * @param pos Position to paste to (session frames).
795 * @param times Number of times to paste.
796 * @param selection Selection to paste.
797 * @param nth Index of the AutomationList within the selection to paste from.
800 AutomationTimeAxisView::paste (framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
802 boost::shared_ptr
<AutomationLine
> line
;
807 line
= _view
->paste_line (pos
);
814 return paste_one (*line
, pos
, times
, selection
, nth
);
818 AutomationTimeAxisView::paste_one (AutomationLine
& line
, framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
820 AutomationSelection::iterator p
;
821 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
823 for (p
= selection
.lines
.begin(); p
!= selection
.lines
.end() && nth
; ++p
, --nth
) {}
825 if (p
== selection
.lines
.end()) {
829 /* Make a copy of the list because we have to scale the
830 values from view coordinates to model coordinates, and we're
831 not supposed to modify the points in the selection.
834 AutomationList
copy (**p
);
836 for (AutomationList::iterator x
= copy
.begin(); x
!= copy
.end(); ++x
) {
837 double when
= (*x
)->when
;
838 double val
= (*x
)->value
;
839 line
.view_to_model_coord (when
, val
);
844 double const model_pos
= line
.time_converter().from (pos
- line
.time_converter().origin_b ());
846 XMLNode
&before
= alist
->get_state();
847 alist
->paste (copy
, model_pos
, times
);
848 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
854 AutomationTimeAxisView::get_selectables (framepos_t start
, framepos_t end
, double top
, double bot
, list
<Selectable
*>& results
)
856 if (!_line
&& !_view
) {
860 if (touched (top
, bot
)) {
862 /* remember: this is X Window - coordinate space starts in upper left and moves down.
863 _y_position is the "origin" or "top" of the track.
866 /* bottom of our track */
867 double const mybot
= _y_position
+ height
;
872 if (_y_position
>= top
&& mybot
<= bot
) {
874 /* _y_position is below top, mybot is above bot, so we're fully
883 /* top and bot are within _y_position .. mybot */
885 topfrac
= 1.0 - ((top
- _y_position
) / height
);
886 botfrac
= 1.0 - ((bot
- _y_position
) / height
);
891 _line
->get_selectables (start
, end
, botfrac
, topfrac
, results
);
893 _view
->get_selectables (start
, end
, botfrac
, topfrac
, results
);
899 AutomationTimeAxisView::get_inverted_selectables (Selection
& sel
, list
<Selectable
*>& result
)
902 _line
->get_inverted_selectables (sel
, result
);
907 AutomationTimeAxisView::set_selected_points (PointSelection
& points
)
910 _line
->set_selected_points (points
);
912 _view
->set_selected_points (points
);
917 AutomationTimeAxisView::clear_lines ()
920 _list_connections
.drop_connections ();
924 AutomationTimeAxisView::add_line (boost::shared_ptr
<AutomationLine
> line
)
928 assert(line
->the_list() == _control
->list());
930 _control
->alist()->automation_state_changed
.connect (
931 _list_connections
, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed
, this), gui_context()
934 _control
->alist()->InterpolationChanged
.connect (
935 _list_connections
, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed
, this, _1
), gui_context()
939 //_controller = AutomationController::create(_session, line->the_list(), _control);
941 line
->set_height (height
);
943 /* pick up the current state */
944 automation_state_changed ();
950 AutomationTimeAxisView::entered()
953 _line
->track_entered();
958 AutomationTimeAxisView::exited ()
961 _line
->track_exited();
966 AutomationTimeAxisView::color_handler ()
974 AutomationTimeAxisView::set_state (const XMLNode
& node
, int version
)
976 TimeAxisView::set_state (node
, version
);
978 if (version
< 3000) {
979 return set_state_2X (node
, version
);
982 XMLProperty
const * type
= node
.property ("automation-id");
983 if (type
&& type
->value () == ARDOUR::EventTypeMap::instance().to_symbol (_parameter
)) {
984 XMLProperty
const * shown
= node
.property ("shown");
985 if (shown
&& shown
->value () == "yes") {
986 set_marked_for_display (true);
987 _canvas_display
->show (); /* FIXME: necessary? show_at? */
991 if (!_marked_for_display
) {
1000 AutomationTimeAxisView::set_state_2X (const XMLNode
& node
, int /*version*/)
1002 if (node
.name() == X_("gain") && _parameter
== Evoral::Parameter (GainAutomation
)) {
1003 XMLProperty
const * shown
= node
.property (X_("shown"));
1004 if (shown
&& string_is_affirmative (shown
->value ())) {
1005 set_marked_for_display (true);
1006 _canvas_display
->show (); /* FIXME: necessary? show_at? */
1010 if (!_marked_for_display
) {
1018 AutomationTimeAxisView::get_state_node ()
1020 TimeAxisView
* state_parent
= get_parent_with_state ();
1023 return state_parent
->get_automation_child_xml_node (_parameter
);
1030 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown
)
1032 XMLNode
* xml_node
= get_state_node();
1034 xml_node
->add_property ("shown", editor_shown
? "yes" : "no");
1039 AutomationTimeAxisView::show_at (double y
, int& nth
, Gtk::VBox
*parent
)
1041 update_extra_xml_shown (true);
1043 return TimeAxisView::show_at (y
, nth
, parent
);
1047 AutomationTimeAxisView::hide ()
1049 update_extra_xml_shown (false);
1051 TimeAxisView::hide ();
1055 AutomationTimeAxisView::set_visibility (bool yn
)
1057 bool changed
= TimeAxisView::set_visibility (yn
);
1060 get_state_node()->add_property ("shown", yn
? X_("yes") : X_("no"));
1066 /** @return true if this view has any automation data to display */
1068 AutomationTimeAxisView::has_automation () const
1070 return ( (_line
&& _line
->npoints() > 0) || (_view
&& _view
->has_automation()) );
1073 list
<boost::shared_ptr
<AutomationLine
> >
1074 AutomationTimeAxisView::lines () const
1076 list
<boost::shared_ptr
<AutomationLine
> > lines
;
1079 lines
.push_back (_line
);
1081 lines
= _view
->get_lines ();