Make discrete mode work for MIDI automation views. Fixes #3178.
[ArdourMidi.git] / gtk2_ardour / automation_time_axis.cc
blob51923089ad6084b586c408e55f64a092741582bb
1 /*
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.
20 #include <utility>
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 "gui_thread.h"
32 #include "route_time_axis.h"
33 #include "automation_line.h"
34 #include "public_editor.h"
35 #include "simplerect.h"
36 #include "selection.h"
37 #include "rgb_macros.h"
38 #include "automation_selectable.h"
39 #include "point_selection.h"
40 #include "canvas_impl.h"
41 #include "utils.h"
43 #include "i18n.h"
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
48 using namespace Gtk;
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 (Session* s, boost::shared_ptr<Route> r,
63 boost::shared_ptr<Automatable> a, boost::shared_ptr<AutomationControl> c,
64 PublicEditor& e, TimeAxisView& parent, bool show_regions,
65 ArdourCanvas::Canvas& canvas, const string & nom, const string & nomparent)
66 : AxisView (s),
67 TimeAxisView (s, e, &parent, canvas),
68 _route (r),
69 _control (c),
70 _automatable (a),
71 _controller(AutomationController::create(a, c->parameter(), c)),
72 _base_rect (0),
73 _view (show_regions ? new AutomationStreamView(*this) : NULL),
74 _name (nom),
75 auto_button (X_("")) /* force addition of a label */
77 if (!have_name_font) {
78 name_font = get_font_for_style (X_("AutomationTrackName"));
79 have_name_font = true;
82 automation_menu = 0;
83 auto_off_item = 0;
84 auto_touch_item = 0;
85 auto_write_item = 0;
86 auto_play_item = 0;
87 mode_discrete_item = 0;
88 mode_line_item = 0;
90 ignore_state_request = false;
91 first_call_to_set_height = true;
93 _base_rect = new SimpleRect(*_canvas_display);
94 _base_rect->property_x1() = 0.0;
95 _base_rect->property_y1() = 0.0;
96 _base_rect->property_x2() = LONG_MAX - 2;
97 _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
99 /* outline ends and bottom */
100 _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
101 _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
103 _base_rect->set_data ("trackview", this);
105 _base_rect->signal_event().connect (sigc::bind (
106 sigc::mem_fun (_editor, &PublicEditor::canvas_automation_track_event),
107 _base_rect, this));
109 if (!a) {
110 _base_rect->lower_to_bottom();
113 hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
115 auto_button.set_name ("TrackVisualButton");
116 hide_button.set_name ("TrackRemoveButton");
118 auto_button.unset_flags (Gtk::CAN_FOCUS);
119 hide_button.unset_flags (Gtk::CAN_FOCUS);
121 controls_table.set_no_show_all();
123 ARDOUR_UI::instance()->set_tip(auto_button, _("automation state"));
124 ARDOUR_UI::instance()->set_tip(hide_button, _("hide track"));
126 /* rearrange the name display */
128 /* we never show these for automation tracks, so make
129 life easier and remove them.
132 hide_name_entry();
134 /* move the name label over a bit */
136 string shortpname = _name;
137 bool shortened = false;
139 int ignore_width;
140 shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
142 if (shortpname != _name ){
143 shortened = true;
146 name_label.set_text (shortpname);
147 name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
149 if (nomparent.length()) {
151 /* limit the plug name string */
153 string pname = fit_to_pixels (nomparent, 60, *name_font, ignore_width, true);
154 if (pname != nomparent) {
155 shortened = true;
158 plugname = new Label (pname);
159 plugname->set_name (X_("TrackPlugName"));
160 plugname->show();
161 name_label.set_name (X_("TrackParameterName"));
162 controls_table.remove (name_hbox);
163 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
164 plugname_packed = true;
165 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
166 } else {
167 plugname = 0;
168 plugname_packed = false;
171 if (shortened) {
172 string tipname = nomparent;
173 if (!tipname.empty()) {
174 tipname += ": ";
176 tipname += _name;
177 ARDOUR_UI::instance()->set_tip(controls_ebox, tipname);
180 /* add the buttons */
181 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
183 controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
185 /* add bar controller */
186 controls_table.attach (*_controller.get(), 0, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
188 controls_table.show_all ();
190 hide_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
191 auto_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
193 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
194 controls_base_unselected_name = X_("AutomationTrackControlsBase");
195 controls_ebox.set_name (controls_base_unselected_name);
197 XMLNode* xml_node = get_parent_with_state()->get_automation_child_xml_node (
198 _control->parameter());
200 if (xml_node) {
201 set_state (*xml_node, Stateful::loading_state_version);
204 /* ask for notifications of any new RegionViews */
205 if (show_regions) {
207 assert(_view);
208 _view->attach ();
210 /* no regions, just a single line for the entire track (e.g. bus gain) */
211 } else {
212 boost::shared_ptr<AutomationLine> line(new AutomationLine (
213 ARDOUR::EventTypeMap::instance().to_symbol(_control->parameter()),
214 *this,
215 *_canvas_display,
216 _control->alist()));
218 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
219 line->queue_reset ();
220 add_line (line);
223 /* make sure labels etc. are correct */
225 automation_state_changed ();
226 ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
229 AutomationTimeAxisView::~AutomationTimeAxisView ()
233 void
234 AutomationTimeAxisView::auto_clicked ()
236 using namespace Menu_Helpers;
238 if (automation_menu == 0) {
239 automation_menu = manage (new Menu);
240 automation_menu->set_name ("ArdourContextMenu");
241 MenuList& items (automation_menu->items());
243 items.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
244 &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
245 items.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
246 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
247 items.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
248 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
249 items.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
250 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
253 automation_menu->popup (1, gtk_get_current_event_time());
256 void
257 AutomationTimeAxisView::set_automation_state (AutoState state)
259 if (!ignore_state_request) {
260 if (_automatable) {
261 _automatable->set_parameter_automation_state (_control->parameter(), state);
263 #if 0
264 if (_route == _automatable) { // This is a time axis for route (not region) automation
265 _route->set_parameter_automation_state (_control->parameter(), state);
268 if (_control->list())
269 _control->alist()->set_automation_state(state);
270 #endif
273 if (_view) {
274 _view->set_automation_state (state);
276 /* AutomationStreamViews don't signal when their automation state changes, so handle
277 our updates `manually'.
279 automation_state_changed ();
283 void
284 AutomationTimeAxisView::automation_state_changed ()
286 AutoState state;
288 /* update button label */
290 if (_line) {
291 state = _control->alist()->automation_state ();
292 } else if (_view) {
293 state = _view->automation_state ();
294 } else {
295 state = Off;
298 switch (state & (Off|Play|Touch|Write)) {
299 case Off:
300 auto_button.set_label (_("Manual"));
301 if (auto_off_item) {
302 ignore_state_request = true;
303 auto_off_item->set_active (true);
304 auto_play_item->set_active (false);
305 auto_touch_item->set_active (false);
306 auto_write_item->set_active (false);
307 ignore_state_request = false;
309 break;
310 case Play:
311 auto_button.set_label (_("Play"));
312 if (auto_play_item) {
313 ignore_state_request = true;
314 auto_play_item->set_active (true);
315 auto_off_item->set_active (false);
316 auto_touch_item->set_active (false);
317 auto_write_item->set_active (false);
318 ignore_state_request = false;
320 break;
321 case Write:
322 auto_button.set_label (_("Write"));
323 if (auto_write_item) {
324 ignore_state_request = true;
325 auto_write_item->set_active (true);
326 auto_off_item->set_active (false);
327 auto_play_item->set_active (false);
328 auto_touch_item->set_active (false);
329 ignore_state_request = false;
331 break;
332 case Touch:
333 auto_button.set_label (_("Touch"));
334 if (auto_touch_item) {
335 ignore_state_request = true;
336 auto_touch_item->set_active (true);
337 auto_off_item->set_active (false);
338 auto_play_item->set_active (false);
339 auto_write_item->set_active (false);
340 ignore_state_request = false;
342 break;
343 default:
344 auto_button.set_label (_("???"));
345 break;
349 void
350 AutomationTimeAxisView::interpolation_changed ()
352 AutomationList::InterpolationStyle style = _control->list()->interpolation();
354 if (mode_line_item && mode_discrete_item) {
355 if (style == AutomationList::Discrete) {
356 mode_discrete_item->set_active(true);
357 mode_line_item->set_active(false);
358 } else {
359 mode_line_item->set_active(true);
360 mode_discrete_item->set_active(false);
364 if (_line) {
365 _line->set_interpolation(style);
368 if (_view) {
369 _view->set_interpolation (style);
373 void
374 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
376 _control->list()->set_interpolation(style);
377 if (_line) {
378 _line->set_interpolation(style);
380 if (_view) {
381 _view->set_interpolation (style);
385 void
386 AutomationTimeAxisView::clear_clicked ()
388 _session->begin_reversible_command (_("clear automation"));
389 if (_line) {
390 _line->clear ();
392 _session->commit_reversible_command ();
395 void
396 AutomationTimeAxisView::set_height (uint32_t h)
398 bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
399 uint32_t const normal = preset_height (HeightNormal);
400 bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
402 TimeAxisView* state_parent = get_parent_with_state ();
403 assert(state_parent);
404 XMLNode* xml_node = state_parent->get_automation_child_xml_node (_control->parameter());
406 TimeAxisView::set_height (h);
407 _base_rect->property_y2() = h;
409 if (_line)
410 _line->set_height(h);
412 if (_view) {
413 _view->set_height(h);
414 _view->update_contents_height();
417 char buf[32];
418 snprintf (buf, sizeof (buf), "%u", height);
419 if (xml_node) {
420 xml_node->add_property ("height", buf);
423 if (changed_between_small_and_normal || first_call_to_set_height) {
425 first_call_to_set_height = false;
427 if (h >= preset_height (HeightNormal)) {
428 controls_table.remove (name_hbox);
430 if (plugname) {
431 if (plugname_packed) {
432 controls_table.remove (*plugname);
433 plugname_packed = false;
435 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
436 plugname_packed = true;
437 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
438 } else {
439 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
441 hide_name_entry ();
442 show_name_label ();
443 name_hbox.show_all ();
445 auto_button.show();
446 hide_button.show_all();
448 } else if (h >= preset_height (HeightSmall)) {
449 controls_table.remove (name_hbox);
450 if (plugname) {
451 if (plugname_packed) {
452 controls_table.remove (*plugname);
453 plugname_packed = false;
456 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
457 controls_table.hide_all ();
458 hide_name_entry ();
459 show_name_label ();
460 name_hbox.show_all ();
462 auto_button.hide();
463 hide_button.hide();
465 } else if (h >= preset_height (HeightNormal)) {
466 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
469 if (changed) {
470 if (canvas_item_visible (_canvas_display)) {
471 /* only emit the signal if the height really changed and we were visible */
472 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
477 void
478 AutomationTimeAxisView::set_samples_per_unit (double spu)
480 TimeAxisView::set_samples_per_unit (spu);
482 if (_line)
483 _line->reset ();
485 if (_view)
486 _view->set_samples_per_unit (spu);
489 void
490 AutomationTimeAxisView::hide_clicked ()
492 // LAME fix for refreshing the hide button
493 hide_button.set_sensitive(false);
495 set_marked_for_display (false);
496 hide ();
498 hide_button.set_sensitive(true);
501 void
502 AutomationTimeAxisView::build_display_menu ()
504 using namespace Menu_Helpers;
506 /* prepare it */
508 TimeAxisView::build_display_menu ();
510 /* now fill it with our stuff */
512 MenuList& items = display_menu->items();
514 items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
515 items.push_back (SeparatorElem());
516 items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
517 items.push_back (SeparatorElem());
519 /* state menu */
521 Menu* auto_state_menu = manage (new Menu);
522 auto_state_menu->set_name ("ArdourContextMenu");
523 MenuList& as_items = auto_state_menu->items();
525 as_items.push_back (CheckMenuElem (_("Manual"), sigc::bind (
526 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
527 (AutoState) Off)));
528 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
530 as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
531 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
532 (AutoState) Play)));
533 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
535 as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
536 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
537 (AutoState) Write)));
538 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
540 as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
541 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
542 (AutoState) Touch)));
543 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
545 items.push_back (MenuElem (_("State"), *auto_state_menu));
547 /* mode menu */
549 if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
551 Menu* auto_mode_menu = manage (new Menu);
552 auto_mode_menu->set_name ("ArdourContextMenu");
553 MenuList& am_items = auto_mode_menu->items();
555 RadioMenuItem::Group group;
557 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
558 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
559 AutomationList::Discrete)));
560 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
561 mode_discrete_item->set_active(_control->list()->interpolation() == AutomationList::Discrete);
563 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
564 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
565 AutomationList::Linear)));
566 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
568 // Set default interpolation type to linear if this isn't a (usually) discrete controller
569 if (EventTypeMap::instance().interpolation_of(_control->parameter()) == Evoral::ControlList::Linear) {
570 mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
573 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
576 /* make sure the automation menu state is correct */
578 automation_state_changed ();
579 interpolation_changed ();
582 void
583 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, nframes_t when, double y)
585 if (!_line)
586 return;
588 double x = 0;
590 _canvas_display->w2i (x, y);
592 /* compute vertical fractional position */
594 y = 1.0 - (y / height);
596 /* map using line */
598 _line->view_to_model_coord (x, y);
600 _session->begin_reversible_command (_("add automation event"));
601 XMLNode& before = _control->alist()->get_state();
603 _control->alist()->add (when, y);
605 XMLNode& after = _control->alist()->get_state();
606 _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
608 _session->set_dirty ();
611 bool
612 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
614 return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
617 bool
618 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
620 boost::shared_ptr<Evoral::ControlList> what_we_got;
621 boost::shared_ptr<AutomationList> alist (line.the_list());
622 bool ret = false;
624 XMLNode &before = alist->get_state();
626 switch (op) {
627 case Cut:
628 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
629 _editor.get_cut_buffer().add (what_we_got);
630 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
631 ret = true;
633 break;
634 case Copy:
635 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
636 _editor.get_cut_buffer().add (what_we_got);
638 break;
640 case Clear:
641 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
642 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
643 ret = true;
645 break;
648 if (what_we_got) {
649 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
650 double when = (*x)->when;
651 double val = (*x)->value;
652 line.model_to_view_coord (when, val);
653 (*x)->when = when;
654 (*x)->value = val;
658 return ret;
661 void
662 AutomationTimeAxisView::reset_objects (PointSelection& selection)
664 reset_objects_one (*_line, selection);
667 void
668 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
670 boost::shared_ptr<AutomationList> alist(line.the_list());
672 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
674 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
676 if ((*i).track != this) {
677 continue;
680 alist->reset_range ((*i).start, (*i).end);
684 bool
685 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
687 return cut_copy_clear_objects_one (*_line, selection, op);
690 bool
691 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
693 boost::shared_ptr<Evoral::ControlList> what_we_got;
694 boost::shared_ptr<AutomationList> alist(line.the_list());
695 bool ret = false;
697 XMLNode &before = alist->get_state();
699 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
701 if ((*i).track != this) {
702 continue;
705 switch (op) {
706 case Cut:
707 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
708 _editor.get_cut_buffer().add (what_we_got);
709 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
710 ret = true;
712 break;
713 case Copy:
714 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
715 _editor.get_cut_buffer().add (what_we_got);
717 break;
719 case Clear:
720 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
721 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
722 ret = true;
724 break;
728 delete &before;
730 if (what_we_got) {
731 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
732 double when = (*x)->when;
733 double val = (*x)->value;
734 line.model_to_view_coord (when, val);
735 (*x)->when = when;
736 (*x)->value = val;
740 return ret;
743 bool
744 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
746 return paste_one (*_line, pos, times, selection, nth);
749 bool
750 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
752 AutomationSelection::iterator p;
753 boost::shared_ptr<AutomationList> alist(line.the_list());
755 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
757 if (p == selection.lines.end()) {
758 return false;
761 /* Make a copy of the list because we have to scale the
762 values from view coordinates to model coordinates, and we're
763 not supposed to modify the points in the selection.
766 AutomationList copy (**p);
768 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
769 double when = (*x)->when;
770 double val = (*x)->value;
771 line.view_to_model_coord (when, val);
772 (*x)->when = when;
773 (*x)->value = val;
776 XMLNode &before = alist->get_state();
777 alist->paste (copy, pos, times);
778 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
780 return true;
783 void
784 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
786 if (_line && touched (top, bot)) {
787 double topfrac;
788 double botfrac;
790 /* remember: this is X Window - coordinate space starts in upper left and moves down.
791 _y_position is the "origin" or "top" of the track.
794 double mybot = _y_position + height;
796 if (_y_position >= top && mybot <= bot) {
798 /* _y_position is below top, mybot is above bot, so we're fully
799 covered vertically.
802 topfrac = 1.0;
803 botfrac = 0.0;
805 } else {
807 /* top and bot are within _y_position .. mybot */
809 topfrac = 1.0 - ((top - _y_position) / height);
810 botfrac = 1.0 - ((bot - _y_position) / height);
813 if (_line)
814 _line->get_selectables (start, end, botfrac, topfrac, results);
818 void
819 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
821 if (_line)
822 _line->get_inverted_selectables (sel, result);
825 void
826 AutomationTimeAxisView::set_selected_points (PointSelection& points)
828 if (_line) {
829 _line->set_selected_points (points);
833 void
834 AutomationTimeAxisView::clear_lines ()
836 _line.reset();
837 automation_connection.disconnect ();
840 void
841 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
843 assert(line);
844 assert(!_line);
845 assert(line->the_list() == _control->list());
847 _control->alist()->automation_state_changed.connect (automation_connection, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context());
849 _line = line;
850 //_controller = AutomationController::create(_session, line->the_list(), _control);
852 line->set_height (height);
854 /* pick up the current state */
855 automation_state_changed ();
857 line->show();
860 void
861 AutomationTimeAxisView::entered()
863 if (_line)
864 _line->track_entered();
867 void
868 AutomationTimeAxisView::exited ()
870 if (_line)
871 _line->track_exited();
874 void
875 AutomationTimeAxisView::color_handler ()
877 if (_line) {
878 _line->set_colors();
883 AutomationTimeAxisView::set_state (const XMLNode& node, int version)
885 TimeAxisView::set_state (node, version);
887 XMLProperty const * type = node.property ("automation-id");
888 if (type && type->value () == ARDOUR::EventTypeMap::instance().to_symbol (_control->parameter())) {
889 XMLProperty const * shown = node.property ("shown");
890 if (shown && shown->value () == "yes") {
891 set_marked_for_display (true);
892 _canvas_display->show (); /* FIXME: necessary? show_at? */
896 if (!_marked_for_display) {
897 hide();
900 return 0;
903 XMLNode*
904 AutomationTimeAxisView::get_state_node ()
906 TimeAxisView* state_parent = get_parent_with_state ();
908 if (state_parent) {
909 return state_parent->get_automation_child_xml_node (_control->parameter());
910 } else {
911 return 0;
915 void
916 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
918 XMLNode* xml_node = get_state_node();
919 if (xml_node) {
920 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
924 guint32
925 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
927 update_extra_xml_shown (true);
929 return TimeAxisView::show_at (y, nth, parent);
932 void
933 AutomationTimeAxisView::hide ()
935 update_extra_xml_shown (false);
937 TimeAxisView::hide ();
940 bool
941 AutomationTimeAxisView::set_visibility (bool yn)
943 bool changed = TimeAxisView::set_visibility (yn);
945 if (changed) {
946 get_state_node()->add_property ("shown", yn ? X_("yes") : X_("no"));
949 return changed;
952 /** @return true if this view has any automation data to display */
953 bool
954 AutomationTimeAxisView::has_automation () const
956 return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );