fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / automation_time_axis.cc
blob9163b10dc1bea5e952d791c03069b596dbc91767
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 if (_line) {
389 _session->begin_reversible_command (_("clear automation"));
390 _line->clear ();
394 void
395 AutomationTimeAxisView::set_height (uint32_t h)
397 bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
398 uint32_t const normal = preset_height (HeightNormal);
399 bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
401 TimeAxisView* state_parent = get_parent_with_state ();
402 assert(state_parent);
403 XMLNode* xml_node = state_parent->get_automation_child_xml_node (_control->parameter());
405 TimeAxisView::set_height (h);
406 _base_rect->property_y2() = h;
408 if (_line)
409 _line->set_height(h);
411 if (_view) {
412 _view->set_height(h);
413 _view->update_contents_height();
416 char buf[32];
417 snprintf (buf, sizeof (buf), "%u", height);
418 if (xml_node) {
419 xml_node->add_property ("height", buf);
422 if (changed_between_small_and_normal || first_call_to_set_height) {
424 first_call_to_set_height = false;
426 if (h >= preset_height (HeightNormal)) {
427 controls_table.remove (name_hbox);
429 if (plugname) {
430 if (plugname_packed) {
431 controls_table.remove (*plugname);
432 plugname_packed = false;
434 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
435 plugname_packed = true;
436 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
437 } else {
438 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
440 hide_name_entry ();
441 show_name_label ();
442 name_hbox.show_all ();
444 auto_button.show();
445 hide_button.show_all();
447 } else if (h >= preset_height (HeightSmall)) {
448 controls_table.remove (name_hbox);
449 if (plugname) {
450 if (plugname_packed) {
451 controls_table.remove (*plugname);
452 plugname_packed = false;
455 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
456 controls_table.hide_all ();
457 hide_name_entry ();
458 show_name_label ();
459 name_hbox.show_all ();
461 auto_button.hide();
462 hide_button.hide();
464 } else if (h >= preset_height (HeightNormal)) {
465 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
468 if (changed) {
469 if (canvas_item_visible (_canvas_display)) {
470 /* only emit the signal if the height really changed and we were visible */
471 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
476 void
477 AutomationTimeAxisView::set_samples_per_unit (double spu)
479 TimeAxisView::set_samples_per_unit (spu);
481 if (_line)
482 _line->reset ();
484 if (_view)
485 _view->set_samples_per_unit (spu);
488 void
489 AutomationTimeAxisView::hide_clicked ()
491 // LAME fix for refreshing the hide button
492 hide_button.set_sensitive(false);
494 set_marked_for_display (false);
495 hide ();
497 hide_button.set_sensitive(true);
500 void
501 AutomationTimeAxisView::build_display_menu ()
503 using namespace Menu_Helpers;
505 /* prepare it */
507 TimeAxisView::build_display_menu ();
509 /* now fill it with our stuff */
511 MenuList& items = display_menu->items();
513 items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
514 items.push_back (SeparatorElem());
515 items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
516 items.push_back (SeparatorElem());
518 /* state menu */
520 Menu* auto_state_menu = manage (new Menu);
521 auto_state_menu->set_name ("ArdourContextMenu");
522 MenuList& as_items = auto_state_menu->items();
524 as_items.push_back (CheckMenuElem (_("Manual"), sigc::bind (
525 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
526 (AutoState) Off)));
527 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
529 as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
530 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
531 (AutoState) Play)));
532 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
534 as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
535 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
536 (AutoState) Write)));
537 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
539 as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
540 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
541 (AutoState) Touch)));
542 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
544 items.push_back (MenuElem (_("State"), *auto_state_menu));
546 /* mode menu */
548 if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
550 Menu* auto_mode_menu = manage (new Menu);
551 auto_mode_menu->set_name ("ArdourContextMenu");
552 MenuList& am_items = auto_mode_menu->items();
554 RadioMenuItem::Group group;
556 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
557 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
558 AutomationList::Discrete)));
559 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
560 mode_discrete_item->set_active(_control->list()->interpolation() == AutomationList::Discrete);
562 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
563 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
564 AutomationList::Linear)));
565 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
567 // Set default interpolation type to linear if this isn't a (usually) discrete controller
568 if (EventTypeMap::instance().interpolation_of(_control->parameter()) == Evoral::ControlList::Linear) {
569 mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
572 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
575 /* make sure the automation menu state is correct */
577 automation_state_changed ();
578 interpolation_changed ();
581 void
582 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, nframes_t when, double y)
584 if (!_line)
585 return;
587 double x = 0;
589 _canvas_display->w2i (x, y);
591 /* compute vertical fractional position */
593 y = 1.0 - (y / height);
595 /* map using line */
597 _line->view_to_model_coord (x, y);
599 _session->begin_reversible_command (_("add automation event"));
600 XMLNode& before = _control->alist()->get_state();
602 _control->alist()->add (when, y);
604 XMLNode& after = _control->alist()->get_state();
605 _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
607 _session->set_dirty ();
610 bool
611 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
613 return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
616 bool
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());
621 bool ret = false;
623 XMLNode &before = alist->get_state();
625 switch (op) {
626 case Cut:
627 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
628 _editor.get_cut_buffer().add (what_we_got);
629 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
630 ret = true;
632 break;
633 case Copy:
634 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
635 _editor.get_cut_buffer().add (what_we_got);
637 break;
639 case Clear:
640 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
641 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
642 ret = true;
644 break;
647 if (what_we_got) {
648 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
649 double when = (*x)->when;
650 double val = (*x)->value;
651 line.model_to_view_coord (when, val);
652 (*x)->when = when;
653 (*x)->value = val;
657 return ret;
660 void
661 AutomationTimeAxisView::reset_objects (PointSelection& selection)
663 reset_objects_one (*_line, selection);
666 void
667 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
669 boost::shared_ptr<AutomationList> alist(line.the_list());
671 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
673 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
675 if ((*i).track != this) {
676 continue;
679 alist->reset_range ((*i).start, (*i).end);
683 bool
684 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
686 return cut_copy_clear_objects_one (*_line, selection, op);
689 bool
690 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
692 boost::shared_ptr<Evoral::ControlList> what_we_got;
693 boost::shared_ptr<AutomationList> alist(line.the_list());
694 bool ret = false;
696 XMLNode &before = alist->get_state();
698 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
700 if ((*i).track != this) {
701 continue;
704 switch (op) {
705 case Cut:
706 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
707 _editor.get_cut_buffer().add (what_we_got);
708 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
709 ret = true;
711 break;
712 case Copy:
713 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
714 _editor.get_cut_buffer().add (what_we_got);
716 break;
718 case Clear:
719 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
720 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
721 ret = true;
723 break;
727 delete &before;
729 if (what_we_got) {
730 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
731 double when = (*x)->when;
732 double val = (*x)->value;
733 line.model_to_view_coord (when, val);
734 (*x)->when = when;
735 (*x)->value = val;
739 return ret;
742 bool
743 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
745 return paste_one (*_line, pos, times, selection, nth);
748 bool
749 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
751 AutomationSelection::iterator p;
752 boost::shared_ptr<AutomationList> alist(line.the_list());
754 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
756 if (p == selection.lines.end()) {
757 return false;
760 /* Make a copy of the list because we have to scale the
761 values from view coordinates to model coordinates, and we're
762 not supposed to modify the points in the selection.
765 AutomationList copy (**p);
767 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
768 double when = (*x)->when;
769 double val = (*x)->value;
770 line.view_to_model_coord (when, val);
771 (*x)->when = when;
772 (*x)->value = val;
775 XMLNode &before = alist->get_state();
776 alist->paste (copy, pos, times);
777 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
779 return true;
782 void
783 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
785 if (_line && touched (top, bot)) {
786 double topfrac;
787 double botfrac;
789 /* remember: this is X Window - coordinate space starts in upper left and moves down.
790 _y_position is the "origin" or "top" of the track.
793 double mybot = _y_position + height;
795 if (_y_position >= top && mybot <= bot) {
797 /* _y_position is below top, mybot is above bot, so we're fully
798 covered vertically.
801 topfrac = 1.0;
802 botfrac = 0.0;
804 } else {
806 /* top and bot are within _y_position .. mybot */
808 topfrac = 1.0 - ((top - _y_position) / height);
809 botfrac = 1.0 - ((bot - _y_position) / height);
812 if (_line)
813 _line->get_selectables (start, end, botfrac, topfrac, results);
817 void
818 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
820 if (_line)
821 _line->get_inverted_selectables (sel, result);
824 void
825 AutomationTimeAxisView::set_selected_points (PointSelection& points)
827 if (_line) {
828 _line->set_selected_points (points);
832 void
833 AutomationTimeAxisView::clear_lines ()
835 _line.reset();
836 automation_connection.disconnect ();
839 void
840 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
842 assert(line);
843 assert(!_line);
844 assert(line->the_list() == _control->list());
846 _control->alist()->automation_state_changed.connect (automation_connection, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context());
848 _line = line;
849 //_controller = AutomationController::create(_session, line->the_list(), _control);
851 line->set_height (height);
853 /* pick up the current state */
854 automation_state_changed ();
856 line->show();
859 void
860 AutomationTimeAxisView::entered()
862 if (_line)
863 _line->track_entered();
866 void
867 AutomationTimeAxisView::exited ()
869 if (_line)
870 _line->track_exited();
873 void
874 AutomationTimeAxisView::color_handler ()
876 if (_line) {
877 _line->set_colors();
882 AutomationTimeAxisView::set_state (const XMLNode& node, int version)
884 TimeAxisView::set_state (node, version);
886 XMLProperty const * type = node.property ("automation-id");
887 if (type && type->value () == ARDOUR::EventTypeMap::instance().to_symbol (_control->parameter())) {
888 XMLProperty const * shown = node.property ("shown");
889 if (shown && shown->value () == "yes") {
890 set_marked_for_display (true);
891 _canvas_display->show (); /* FIXME: necessary? show_at? */
895 if (!_marked_for_display) {
896 hide();
899 return 0;
902 XMLNode*
903 AutomationTimeAxisView::get_state_node ()
905 TimeAxisView* state_parent = get_parent_with_state ();
907 if (state_parent) {
908 return state_parent->get_automation_child_xml_node (_control->parameter());
909 } else {
910 return 0;
914 void
915 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
917 XMLNode* xml_node = get_state_node();
918 if (xml_node) {
919 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
923 guint32
924 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
926 update_extra_xml_shown (true);
928 return TimeAxisView::show_at (y, nth, parent);
931 void
932 AutomationTimeAxisView::hide ()
934 update_extra_xml_shown (false);
936 TimeAxisView::hide ();
939 bool
940 AutomationTimeAxisView::set_visibility (bool yn)
942 bool changed = TimeAxisView::set_visibility (yn);
944 if (changed) {
945 get_state_node()->add_property ("shown", yn ? X_("yes") : X_("no"));
948 return changed;
951 /** @return true if this view has any automation data to display */
952 bool
953 AutomationTimeAxisView::has_automation () const
955 return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );