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>
23 #include "pbd/memento_command.h"
24 #include "pbd/stacktrace.h"
26 #include "ardour/automation_control.h"
27 #include "ardour/event_type_map.h"
28 #include "ardour/route.h"
29 #include "ardour/session.h"
31 #include "ardour_ui.h"
32 #include "automation_time_axis.h"
33 #include "automation_streamview.h"
34 #include "global_signals.h"
35 #include "gui_thread.h"
36 #include "route_time_axis.h"
37 #include "automation_line.h"
38 #include "public_editor.h"
39 #include "simplerect.h"
40 #include "selection.h"
41 #include "rgb_macros.h"
42 #include "point_selection.h"
43 #include "canvas_impl.h"
49 using namespace ARDOUR
;
52 using namespace Gtkmm2ext
;
53 using namespace Editing
;
55 Pango::FontDescription
AutomationTimeAxisView::name_font
;
56 bool AutomationTimeAxisView::have_name_font
= false;
57 const string
AutomationTimeAxisView::state_node_name
= "AutomationChild";
60 /** \a a the automatable object this time axis is to display data for.
61 * For route/track automation (e.g. gain) pass the route for both \r and \a.
62 * For route child (e.g. plugin) automation, pass the child for \a.
63 * For region automation (e.g. MIDI CC), pass null for \a.
65 AutomationTimeAxisView::AutomationTimeAxisView (
67 boost::shared_ptr
<Route
> r
,
68 boost::shared_ptr
<Automatable
> a
,
69 boost::shared_ptr
<AutomationControl
> c
,
74 ArdourCanvas::Canvas
& canvas
,
76 const string
& nomparent
79 , TimeAxisView (s
, e
, &parent
, canvas
)
85 , _view (show_regions
? new AutomationStreamView (*this) : 0)
87 , auto_button (X_("")) /* force addition of a label */
89 if (!have_name_font
) {
90 name_font
= get_font_for_style (X_("AutomationTrackName"));
91 have_name_font
= true;
94 if (_automatable
&& _control
) {
95 _controller
= AutomationController::create (_automatable
, _control
->parameter(), _control
);
103 mode_discrete_item
= 0;
106 ignore_state_request
= false;
107 first_call_to_set_height
= true;
109 _base_rect
= new SimpleRect(*_canvas_display
);
110 _base_rect
->property_x1() = 0.0;
111 _base_rect
->property_y1() = 0.0;
112 /** gnomecanvas sometimes converts this value to int or adds 2 to it, so it must be
113 set correctly to avoid overflow.
115 _base_rect
->property_x2() = INT_MAX
- 2;
116 _base_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline
.get();
118 /* outline ends and bottom */
119 _base_rect
->property_outline_what() = (guint32
) (0x1|0x2|0x8);
120 _base_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill
.get();
122 _base_rect
->set_data ("trackview", this);
124 _base_rect
->signal_event().connect (sigc::bind (
125 sigc::mem_fun (_editor
, &PublicEditor::canvas_automation_track_event
),
129 _base_rect
->lower_to_bottom();
132 hide_button
.add (*(manage (new Gtk::Image (::get_icon("hide")))));
134 auto_button
.set_name ("TrackVisualButton");
135 hide_button
.set_name ("TrackRemoveButton");
137 auto_button
.unset_flags (Gtk::CAN_FOCUS
);
138 hide_button
.unset_flags (Gtk::CAN_FOCUS
);
140 controls_table
.set_no_show_all();
142 ARDOUR_UI::instance()->set_tip(auto_button
, _("automation state"));
143 ARDOUR_UI::instance()->set_tip(hide_button
, _("hide track"));
145 /* rearrange the name display */
147 /* we never show these for automation tracks, so make
148 life easier and remove them.
153 /* keep the parameter name short */
155 string shortpname
= _name
;
157 shortpname
= fit_to_pixels (_name
, 60, name_font
, ignore_width
, true);
159 name_label
.set_text (shortpname
);
160 name_label
.set_alignment (Gtk::ALIGN_CENTER
, Gtk::ALIGN_CENTER
);
161 name_label
.set_name (X_("TrackParameterName"));
163 string tipname
= nomparent
;
164 if (!tipname
.empty()) {
168 ARDOUR_UI::instance()->set_tip(controls_ebox
, tipname
);
170 /* add the buttons */
171 controls_table
.attach (hide_button
, 0, 1, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
173 controls_table
.attach (auto_button
, 5, 8, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
176 /* add bar controller */
177 controls_table
.attach (*_controller
.get(), 0, 8, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
180 controls_table
.show_all ();
182 hide_button
.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked
));
183 auto_button
.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked
));
185 controls_base_selected_name
= X_("AutomationTrackControlsBaseSelected");
186 controls_base_unselected_name
= X_("AutomationTrackControlsBase");
187 controls_ebox
.set_name (controls_base_unselected_name
);
189 XMLNode
* xml_node
= get_state_node ();
192 set_state (*xml_node
, Stateful::loading_state_version
);
195 /* ask for notifications of any new RegionViews */
202 /* no regions, just a single line for the entire track (e.g. bus gain) */
204 boost::shared_ptr
<AutomationLine
> line (
206 ARDOUR::EventTypeMap::instance().to_symbol(_parameter
),
213 line
->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine
.get());
214 line
->queue_reset ();
218 /* make sure labels etc. are correct */
220 automation_state_changed ();
221 ColorsChanged
.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler
));
223 _route
->DropReferences
.connect (
224 _route_connections
, invalidator (*this), ui_bind (&AutomationTimeAxisView::route_going_away
, this), gui_context ()
228 AutomationTimeAxisView::~AutomationTimeAxisView ()
233 AutomationTimeAxisView::route_going_away ()
239 AutomationTimeAxisView::auto_clicked ()
241 using namespace Menu_Helpers
;
243 if (automation_menu
== 0) {
244 automation_menu
= manage (new Menu
);
245 automation_menu
->set_name ("ArdourContextMenu");
246 MenuList
& items (automation_menu
->items());
248 items
.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
249 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Off
)));
250 items
.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
251 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Play
)));
252 items
.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
253 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Write
)));
254 items
.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
255 &AutomationTimeAxisView::set_automation_state
), (AutoState
) Touch
)));
258 automation_menu
->popup (1, gtk_get_current_event_time());
262 AutomationTimeAxisView::set_automation_state (AutoState state
)
264 if (ignore_state_request
) {
269 _automatable
->set_parameter_automation_state (_parameter
, state
);
272 if (_route
== _automatable
) { // This is a time axis for route (not region) automation
273 _route
->set_parameter_automation_state (_parameter
, state
);
276 if (_control
->list()) {
277 _control
->alist()->set_automation_state(state
);
281 _view
->set_automation_state (state
);
283 /* AutomationStreamViews don't signal when their automation state changes, so handle
284 our updates `manually'.
286 automation_state_changed ();
291 AutomationTimeAxisView::automation_state_changed ()
295 /* update button label */
298 state
= _control
->alist()->automation_state ();
300 state
= _view
->automation_state ();
305 switch (state
& (Off
|Play
|Touch
|Write
)) {
307 auto_button
.set_label (_("Manual"));
309 ignore_state_request
= true;
310 auto_off_item
->set_active (true);
311 auto_play_item
->set_active (false);
312 auto_touch_item
->set_active (false);
313 auto_write_item
->set_active (false);
314 ignore_state_request
= false;
318 auto_button
.set_label (_("Play"));
319 if (auto_play_item
) {
320 ignore_state_request
= true;
321 auto_play_item
->set_active (true);
322 auto_off_item
->set_active (false);
323 auto_touch_item
->set_active (false);
324 auto_write_item
->set_active (false);
325 ignore_state_request
= false;
329 auto_button
.set_label (_("Write"));
330 if (auto_write_item
) {
331 ignore_state_request
= true;
332 auto_write_item
->set_active (true);
333 auto_off_item
->set_active (false);
334 auto_play_item
->set_active (false);
335 auto_touch_item
->set_active (false);
336 ignore_state_request
= false;
340 auto_button
.set_label (_("Touch"));
341 if (auto_touch_item
) {
342 ignore_state_request
= true;
343 auto_touch_item
->set_active (true);
344 auto_off_item
->set_active (false);
345 auto_play_item
->set_active (false);
346 auto_write_item
->set_active (false);
347 ignore_state_request
= false;
351 auto_button
.set_label (_("???"));
356 /** The interpolation style of our AutomationList has changed, so update */
358 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s
)
360 if (mode_line_item
&& mode_discrete_item
) {
361 if (s
== AutomationList::Discrete
) {
362 mode_discrete_item
->set_active(true);
363 mode_line_item
->set_active(false);
365 mode_line_item
->set_active(true);
366 mode_discrete_item
->set_active(false);
371 /** A menu item has been selected to change our interpolation mode */
373 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style
)
375 /* Tell our view's list, if we have one, otherwise tell our own.
376 * Everything else will be signalled back from that.
380 _view
->set_interpolation (style
);
382 _control
->list()->set_interpolation (style
);
387 AutomationTimeAxisView::clear_clicked ()
389 assert (_line
|| _view
);
391 _session
->begin_reversible_command (_("clear automation"));
399 _session
->commit_reversible_command ();
400 _session
->set_dirty ();
404 AutomationTimeAxisView::set_height (uint32_t h
)
406 bool const changed
= (height
!= (uint32_t) h
) || first_call_to_set_height
;
407 uint32_t const normal
= preset_height (HeightNormal
);
408 bool const changed_between_small_and_normal
= ( (height
< normal
&& h
>= normal
) || (height
>= normal
|| h
< normal
) );
410 TimeAxisView
* state_parent
= get_parent_with_state ();
411 assert(state_parent
);
412 XMLNode
* xml_node
= _control
->extra_xml ("GUI");
414 TimeAxisView::set_height (h
);
415 _base_rect
->property_y2() = h
;
418 _line
->set_height(h
);
422 _view
->set_height(h
);
423 _view
->update_contents_height();
427 snprintf (buf
, sizeof (buf
), "%u", height
);
429 xml_node
->add_property ("height", buf
);
432 if (changed_between_small_and_normal
|| first_call_to_set_height
) {
434 first_call_to_set_height
= false;
436 if (h
>= preset_height (HeightNormal
)) {
439 name_hbox
.show_all ();
442 hide_button
.show_all();
444 } else if (h
>= preset_height (HeightSmall
)) {
445 controls_table
.hide_all ();
448 name_hbox
.show_all ();
453 } else if (h
>= preset_height (HeightNormal
)) {
454 cerr
<< "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl
;
458 if (canvas_item_visible (_canvas_display
) && _route
) {
459 /* only emit the signal if the height really changed and we were visible */
460 _route
->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
466 AutomationTimeAxisView::set_samples_per_unit (double spu
)
468 TimeAxisView::set_samples_per_unit (spu
);
475 _view
->set_samples_per_unit (spu
);
480 AutomationTimeAxisView::hide_clicked ()
482 // LAME fix for refreshing the hide button
483 hide_button
.set_sensitive(false);
485 set_marked_for_display (false);
488 hide_button
.set_sensitive(true);
492 AutomationTimeAxisView::build_display_menu ()
494 using namespace Menu_Helpers
;
498 TimeAxisView::build_display_menu ();
500 /* now fill it with our stuff */
502 MenuList
& items
= display_menu
->items();
504 items
.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked
)));
505 items
.push_back (SeparatorElem());
506 items
.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked
)));
507 items
.push_back (SeparatorElem());
511 Menu
* auto_state_menu
= manage (new Menu
);
512 auto_state_menu
->set_name ("ArdourContextMenu");
513 MenuList
& as_items
= auto_state_menu
->items();
515 as_items
.push_back (CheckMenuElem (_("Manual"), sigc::bind (
516 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
518 auto_off_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
520 as_items
.push_back (CheckMenuElem (_("Play"), sigc::bind (
521 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
523 auto_play_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
525 as_items
.push_back (CheckMenuElem (_("Write"), sigc::bind (
526 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
527 (AutoState
) Write
)));
528 auto_write_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
530 as_items
.push_back (CheckMenuElem (_("Touch"), sigc::bind (
531 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state
),
532 (AutoState
) Touch
)));
533 auto_touch_item
= dynamic_cast<CheckMenuItem
*>(&as_items
.back());
535 items
.push_back (MenuElem (_("State"), *auto_state_menu
));
539 /* current interpolation state */
540 AutomationList::InterpolationStyle
const s
= _view
? _view
->interpolation() : _control
->list()->interpolation ();
542 if (EventTypeMap::instance().is_midi_parameter(_parameter
)) {
544 Menu
* auto_mode_menu
= manage (new Menu
);
545 auto_mode_menu
->set_name ("ArdourContextMenu");
546 MenuList
& am_items
= auto_mode_menu
->items();
548 RadioMenuItem::Group group
;
550 am_items
.push_back (RadioMenuElem (group
, _("Discrete"), sigc::bind (
551 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation
),
552 AutomationList::Discrete
)));
553 mode_discrete_item
= dynamic_cast<CheckMenuItem
*>(&am_items
.back());
554 mode_discrete_item
->set_active (s
== AutomationList::Discrete
);
556 am_items
.push_back (RadioMenuElem (group
, _("Linear"), sigc::bind (
557 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation
),
558 AutomationList::Linear
)));
559 mode_line_item
= dynamic_cast<CheckMenuItem
*>(&am_items
.back());
560 mode_line_item
->set_active (s
== AutomationList::Linear
);
562 items
.push_back (MenuElem (_("Mode"), *auto_mode_menu
));
565 /* make sure the automation menu state is correct */
567 automation_state_changed ();
568 interpolation_changed (s
);
572 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item
* /*item*/, GdkEvent
* /*event*/, framepos_t when
, double y
)
580 _canvas_display
->w2i (x
, y
);
582 /* compute vertical fractional position */
584 y
= 1.0 - (y
/ height
);
588 _line
->view_to_model_coord (x
, y
);
590 _session
->begin_reversible_command (_("add automation event"));
591 XMLNode
& before
= _control
->alist()->get_state();
593 _control
->alist()->add (when
, y
);
595 XMLNode
& after
= _control
->alist()->get_state();
596 _session
->commit_reversible_command (new MementoCommand
<ARDOUR::AutomationList
>(*_control
->alist(), &before
, &after
));
598 _session
->set_dirty ();
602 AutomationTimeAxisView::cut_copy_clear (Selection
& selection
, CutCopyOp op
)
604 list
<boost::shared_ptr
<AutomationLine
> > lines
;
606 lines
.push_back (_line
);
608 lines
= _view
->get_lines ();
611 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
612 cut_copy_clear_one (**i
, selection
, op
);
617 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine
& line
, Selection
& selection
, CutCopyOp op
)
619 boost::shared_ptr
<Evoral::ControlList
> what_we_got
;
620 boost::shared_ptr
<AutomationList
> alist (line
.the_list());
622 XMLNode
&before
= alist
->get_state();
624 /* convert time selection to automation list model coordinates */
625 const Evoral::TimeConverter
<double, ARDOUR::framepos_t
>& tc
= line
.time_converter ();
626 double const start
= tc
.from (selection
.time
.front().start
- tc
.origin_b ());
627 double const end
= tc
.from (selection
.time
.front().end
- tc
.origin_b ());
631 if (alist
->cut (start
, end
) != 0) {
632 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
638 if ((what_we_got
= alist
->cut (start
, end
)) != 0) {
639 _editor
.get_cut_buffer().add (what_we_got
);
640 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
644 if ((what_we_got
= alist
->copy (start
, end
)) != 0) {
645 _editor
.get_cut_buffer().add (what_we_got
);
650 if ((what_we_got
= alist
->cut (start
, end
)) != 0) {
651 _session
->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
657 for (AutomationList::iterator x
= what_we_got
->begin(); x
!= what_we_got
->end(); ++x
) {
658 double when
= (*x
)->when
;
659 double val
= (*x
)->value
;
660 line
.model_to_view_coord (when
, val
);
668 AutomationTimeAxisView::reset_objects (PointSelection
& selection
)
670 list
<boost::shared_ptr
<AutomationLine
> > lines
;
672 lines
.push_back (_line
);
674 lines
= _view
->get_lines ();
677 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
678 reset_objects_one (**i
, selection
);
683 AutomationTimeAxisView::reset_objects_one (AutomationLine
& line
, PointSelection
& selection
)
685 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
687 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), &alist
->get_state(), 0));
689 for (PointSelection::iterator i
= selection
.begin(); i
!= selection
.end(); ++i
) {
691 if ((*i
).track
!= this) {
695 alist
->reset_range ((*i
).start
, (*i
).end
);
700 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection
& selection
, CutCopyOp op
)
702 list
<boost::shared_ptr
<AutomationLine
> > lines
;
704 lines
.push_back (_line
);
706 lines
= _view
->get_lines ();
709 for (list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
710 cut_copy_clear_objects_one (**i
, selection
, op
);
715 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine
& line
, PointSelection
& selection
, CutCopyOp op
)
717 boost::shared_ptr
<Evoral::ControlList
> what_we_got
;
718 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
720 XMLNode
&before
= alist
->get_state();
722 for (PointSelection::iterator i
= selection
.begin(); i
!= selection
.end(); ++i
) {
724 if ((*i
).track
!= this) {
730 if (alist
->cut ((*i
).start
, (*i
).end
) != 0) {
731 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
735 if ((what_we_got
= alist
->cut ((*i
).start
, (*i
).end
)) != 0) {
736 _editor
.get_cut_buffer().add (what_we_got
);
737 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
741 if ((what_we_got
= alist
->copy ((*i
).start
, (*i
).end
)) != 0) {
742 _editor
.get_cut_buffer().add (what_we_got
);
747 if ((what_we_got
= alist
->cut ((*i
).start
, (*i
).end
)) != 0) {
748 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), new XMLNode (before
), &alist
->get_state()));
757 for (AutomationList::iterator x
= what_we_got
->begin(); x
!= what_we_got
->end(); ++x
) {
758 double when
= (*x
)->when
;
759 double val
= (*x
)->value
;
760 line
.model_to_view_coord (when
, val
);
767 /** Paste a selection.
768 * @param pos Position to paste to (session frames).
769 * @param times Number of times to paste.
770 * @param selection Selection to paste.
771 * @param nth Index of the AutomationList within the selection to paste from.
774 AutomationTimeAxisView::paste (framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
776 boost::shared_ptr
<AutomationLine
> line
;
781 line
= _view
->paste_line (pos
);
788 return paste_one (*line
, pos
, times
, selection
, nth
);
792 AutomationTimeAxisView::paste_one (AutomationLine
& line
, framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
794 AutomationSelection::iterator p
;
795 boost::shared_ptr
<AutomationList
> alist(line
.the_list());
797 for (p
= selection
.lines
.begin(); p
!= selection
.lines
.end() && nth
; ++p
, --nth
) {}
799 if (p
== selection
.lines
.end()) {
803 /* Make a copy of the list because we have to scale the
804 values from view coordinates to model coordinates, and we're
805 not supposed to modify the points in the selection.
808 AutomationList
copy (**p
);
810 for (AutomationList::iterator x
= copy
.begin(); x
!= copy
.end(); ++x
) {
811 double when
= (*x
)->when
;
812 double val
= (*x
)->value
;
813 line
.view_to_model_coord (when
, val
);
818 double const model_pos
= line
.time_converter().from (pos
- line
.time_converter().origin_b ());
820 XMLNode
&before
= alist
->get_state();
821 alist
->paste (copy
, model_pos
, times
);
822 _session
->add_command (new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &alist
->get_state()));
828 AutomationTimeAxisView::get_selectables (framepos_t start
, framepos_t end
, double top
, double bot
, list
<Selectable
*>& results
)
830 if (!_line
&& !_view
) {
834 if (touched (top
, bot
)) {
836 /* remember: this is X Window - coordinate space starts in upper left and moves down.
837 _y_position is the "origin" or "top" of the track.
840 /* bottom of our track */
841 double const mybot
= _y_position
+ height
;
846 if (_y_position
>= top
&& mybot
<= bot
) {
848 /* _y_position is below top, mybot is above bot, so we're fully
857 /* top and bot are within _y_position .. mybot */
859 topfrac
= 1.0 - ((top
- _y_position
) / height
);
860 botfrac
= 1.0 - ((bot
- _y_position
) / height
);
865 _line
->get_selectables (start
, end
, botfrac
, topfrac
, results
);
867 _view
->get_selectables (start
, end
, botfrac
, topfrac
, results
);
873 AutomationTimeAxisView::get_inverted_selectables (Selection
& sel
, list
<Selectable
*>& result
)
876 _line
->get_inverted_selectables (sel
, result
);
881 AutomationTimeAxisView::set_selected_points (PointSelection
& points
)
884 _line
->set_selected_points (points
);
886 _view
->set_selected_points (points
);
891 AutomationTimeAxisView::clear_lines ()
894 _list_connections
.drop_connections ();
898 AutomationTimeAxisView::add_line (boost::shared_ptr
<AutomationLine
> line
)
902 assert(line
->the_list() == _control
->list());
904 _control
->alist()->automation_state_changed
.connect (
905 _list_connections
, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed
, this), gui_context()
908 _control
->alist()->InterpolationChanged
.connect (
909 _list_connections
, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed
, this, _1
), gui_context()
913 //_controller = AutomationController::create(_session, line->the_list(), _control);
915 line
->set_height (height
);
917 /* pick up the current state */
918 automation_state_changed ();
924 AutomationTimeAxisView::entered()
927 _line
->track_entered();
932 AutomationTimeAxisView::exited ()
935 _line
->track_exited();
940 AutomationTimeAxisView::color_handler ()
948 AutomationTimeAxisView::set_state (const XMLNode
& node
, int version
)
950 TimeAxisView::set_state (node
, version
);
952 if (version
< 3000) {
953 return set_state_2X (node
, version
);
956 XMLProperty
const * prop
= node
.property ("shown");
959 set_visibility (string_is_affirmative (prop
->value()));
961 set_visibility (false);
968 AutomationTimeAxisView::set_state_2X (const XMLNode
& node
, int /*version*/)
970 if (node
.name() == X_("gain") && _parameter
== Evoral::Parameter (GainAutomation
)) {
971 XMLProperty
const * shown
= node
.property (X_("shown"));
972 if (shown
&& string_is_affirmative (shown
->value ())) {
973 set_marked_for_display (true);
974 _canvas_display
->show (); /* FIXME: necessary? show_at? */
978 if (!_marked_for_display
) {
986 AutomationTimeAxisView::get_state_node ()
988 return _control
->extra_xml ("GUI", true);
992 AutomationTimeAxisView::update_extra_xml_shown (bool shown
)
994 XMLNode
* xml_node
= get_state_node();
996 xml_node
->add_property ("shown", shown
? "yes" : "no");
1001 AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr
<Automatable
>& automatable
, set
<Evoral::Parameter
>& visible
)
1003 /* this keeps "knowledge" of how we store visibility information
1004 in XML private to this class.
1007 assert (automatable
);
1009 Automatable::Controls
& controls (automatable
->controls());
1011 for (Automatable::Controls::iterator i
= controls
.begin(); i
!= controls
.end(); ++i
) {
1013 boost::shared_ptr
<AutomationControl
> ac
= boost::dynamic_pointer_cast
<AutomationControl
> (i
->second
);
1017 const XMLNode
* gui_node
= ac
->extra_xml ("GUI");
1020 const XMLProperty
* prop
= gui_node
->property ("shown");
1022 if (string_is_affirmative (prop
->value())) {
1023 visible
.insert (i
->first
);
1032 AutomationTimeAxisView::show_at (double y
, int& nth
, Gtk::VBox
*parent
)
1034 if (!canvas_item_visible (_canvas_display
)) {
1035 update_extra_xml_shown (true);
1038 return TimeAxisView::show_at (y
, nth
, parent
);
1042 AutomationTimeAxisView::show ()
1044 if (!canvas_item_visible (_canvas_display
)) {
1045 update_extra_xml_shown (true);
1048 return TimeAxisView::show ();
1052 AutomationTimeAxisView::hide ()
1054 if (canvas_item_visible (_canvas_display
)) {
1055 update_extra_xml_shown (false);
1058 TimeAxisView::hide ();
1061 /** @return true if this view has any automation data to display */
1063 AutomationTimeAxisView::has_automation () const
1065 return ( (_line
&& _line
->npoints() > 0) || (_view
&& _view
->has_automation()) );
1068 list
<boost::shared_ptr
<AutomationLine
> >
1069 AutomationTimeAxisView::lines () const
1071 list
<boost::shared_ptr
<AutomationLine
> > lines
;
1074 lines
.push_back (_line
);
1076 lines
= _view
->get_lines ();