fixup auto-connection of new MIDI tracks and MIDI tracks with newly-added audio ports
[ardour2.git] / gtk2_ardour / automation_time_axis.cc
bloba92ac3853077f70de8bb4cb3d261ed341557575b
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 "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"
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 (
63 Session* s,
64 boost::shared_ptr<Route> r,
65 boost::shared_ptr<Automatable> a,
66 boost::shared_ptr<AutomationControl> c,
67 Evoral::Parameter p,
68 PublicEditor& e,
69 TimeAxisView& parent,
70 bool show_regions,
71 ArdourCanvas::Canvas& canvas,
72 const string & nom,
73 const string & nomparent
75 : AxisView (s)
76 , TimeAxisView (s, e, &parent, canvas)
77 , _route (r)
78 , _control (c)
79 , _automatable (a)
80 , _parameter (p)
81 , _base_rect (0)
82 , _view (show_regions ? new AutomationStreamView (*this) : 0)
83 , _name (nom)
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);
95 automation_menu = 0;
96 auto_off_item = 0;
97 auto_touch_item = 0;
98 auto_write_item = 0;
99 auto_play_item = 0;
100 mode_discrete_item = 0;
101 mode_line_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),
123 _base_rect, this));
125 if (!a) {
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.
148 hide_name_entry();
150 /* move the name label over a bit */
152 string shortpname = _name;
153 bool shortened = false;
155 int ignore_width;
156 shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
158 if (shortpname != _name ){
159 shortened = true;
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) {
172 shortened = true;
175 plugname = new Label (pname);
176 plugname->set_name (X_("TrackPlugName"));
177 plugname->show();
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);
182 } else {
183 plugname = 0;
184 plugname_packed = false;
187 if (shortened) {
188 string tipname = nomparent;
189 if (!tipname.empty()) {
190 tipname += ": ";
192 tipname += _name;
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);
201 if (_controller) {
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);
217 if (xml_node) {
218 set_state (*xml_node, Stateful::loading_state_version);
221 /* ask for notifications of any new RegionViews */
222 if (show_regions) {
224 assert(_view);
225 _view->attach ();
227 } else {
228 /* no regions, just a single line for the entire track (e.g. bus gain) */
230 boost::shared_ptr<AutomationLine> line (
231 new AutomationLine (
232 ARDOUR::EventTypeMap::instance().to_symbol(_parameter),
233 *this,
234 *_canvas_display,
235 _control->alist()
239 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
240 line->queue_reset ();
241 add_line (line);
244 /* make sure labels etc. are correct */
246 automation_state_changed ();
247 ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
250 AutomationTimeAxisView::~AutomationTimeAxisView ()
254 void
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());
277 void
278 AutomationTimeAxisView::set_automation_state (AutoState state)
280 if (ignore_state_request) {
281 return;
284 if (_automatable) {
285 _automatable->set_parameter_automation_state (_parameter, state);
287 #if 0
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);
295 #endif
296 if (_view) {
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 ();
306 void
307 AutomationTimeAxisView::automation_state_changed ()
309 AutoState state;
311 /* update button label */
313 if (_line) {
314 state = _control->alist()->automation_state ();
315 } else if (_view) {
316 state = _view->automation_state ();
317 } else {
318 state = Off;
321 switch (state & (Off|Play|Touch|Write)) {
322 case Off:
323 auto_button.set_label (_("Manual"));
324 if (auto_off_item) {
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;
332 break;
333 case Play:
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;
343 break;
344 case Write:
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;
354 break;
355 case Touch:
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;
365 break;
366 default:
367 auto_button.set_label (_("???"));
368 break;
372 /** The interpolation style of our AutomationList has changed, so update */
373 void
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);
380 } else {
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 */
388 void
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.
395 if (_view) {
396 _view->set_interpolation (style);
397 } else {
398 _control->list()->set_interpolation (style);
402 void
403 AutomationTimeAxisView::clear_clicked ()
405 assert (_line || _view);
407 _session->begin_reversible_command (_("clear automation"));
409 if (_line) {
410 _line->clear ();
411 } else if (_view) {
412 _view->clear ();
415 _session->commit_reversible_command ();
416 _session->set_dirty ();
419 void
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;
433 if (_line) {
434 _line->set_height(h);
437 if (_view) {
438 _view->set_height(h);
439 _view->update_contents_height();
442 char buf[32];
443 snprintf (buf, sizeof (buf), "%u", height);
444 if (xml_node) {
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);
455 if (plugname) {
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);
463 } else {
464 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
466 hide_name_entry ();
467 show_name_label ();
468 name_hbox.show_all ();
470 auto_button.show();
471 hide_button.show_all();
473 } else if (h >= preset_height (HeightSmall)) {
474 controls_table.remove (name_hbox);
475 if (plugname) {
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 ();
483 hide_name_entry ();
484 show_name_label ();
485 name_hbox.show_all ();
487 auto_button.hide();
488 hide_button.hide();
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;
494 if (changed) {
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 */
502 void
503 AutomationTimeAxisView::set_samples_per_unit (double spu)
505 TimeAxisView::set_samples_per_unit (spu);
507 if (_line) {
508 _line->reset ();
511 if (_view) {
512 _view->set_samples_per_unit (spu);
516 void
517 AutomationTimeAxisView::hide_clicked ()
519 // LAME fix for refreshing the hide button
520 hide_button.set_sensitive(false);
522 set_marked_for_display (false);
523 hide ();
525 hide_button.set_sensitive(true);
528 void
529 AutomationTimeAxisView::build_display_menu ()
531 using namespace Menu_Helpers;
533 /* prepare it */
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());
546 /* state menu */
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),
554 (AutoState) Off)));
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),
559 (AutoState) Play)));
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));
574 /* mode 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);
608 void
609 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, framepos_t when, double y)
611 if (!_line) {
612 return;
615 double x = 0;
617 _canvas_display->w2i (x, y);
619 /* compute vertical fractional position */
621 y = 1.0 - (y / height);
623 /* map using line */
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 ();
638 void
639 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
641 list<boost::shared_ptr<AutomationLine> > lines;
642 if (_line) {
643 lines.push_back (_line);
644 } else if (_view) {
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);
653 void
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 ());
666 switch (op) {
667 case Cut:
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()));
673 break;
674 case Copy:
675 if ((what_we_got = alist->copy (start, end)) != 0) {
676 _editor.get_cut_buffer().add (what_we_got);
678 break;
680 case Clear:
681 if ((what_we_got = alist->cut (start, end)) != 0) {
682 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
684 break;
687 if (what_we_got) {
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);
692 (*x)->when = when;
693 (*x)->value = val;
698 void
699 AutomationTimeAxisView::reset_objects (PointSelection& selection)
701 list<boost::shared_ptr<AutomationLine> > lines;
702 if (_line) {
703 lines.push_back (_line);
704 } else if (_view) {
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);
713 void
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) {
723 continue;
726 alist->reset_range ((*i).start, (*i).end);
730 void
731 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
733 list<boost::shared_ptr<AutomationLine> > lines;
734 if (_line) {
735 lines.push_back (_line);
736 } else if (_view) {
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);
745 void
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) {
756 continue;
759 switch (op) {
760 case Cut:
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()));
765 break;
766 case Copy:
767 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
768 _editor.get_cut_buffer().add (what_we_got);
770 break;
772 case Clear:
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()));
776 break;
780 delete &before;
782 if (what_we_got) {
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);
787 (*x)->when = when;
788 (*x)->value = 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.
799 bool
800 AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
802 boost::shared_ptr<AutomationLine> line;
804 if (_line) {
805 line = _line;
806 } else if (_view) {
807 line = _view->paste_line (pos);
810 if (!line) {
811 return false;
814 return paste_one (*line, pos, times, selection, nth);
817 bool
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()) {
826 return false;
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);
840 (*x)->when = when;
841 (*x)->value = 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()));
850 return true;
853 void
854 AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
856 if (!_line && !_view) {
857 return;
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;
869 double topfrac;
870 double botfrac;
872 if (_y_position >= top && mybot <= bot) {
874 /* _y_position is below top, mybot is above bot, so we're fully
875 covered vertically.
878 topfrac = 1.0;
879 botfrac = 0.0;
881 } else {
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);
890 if (_line) {
891 _line->get_selectables (start, end, botfrac, topfrac, results);
892 } else if (_view) {
893 _view->get_selectables (start, end, botfrac, topfrac, results);
898 void
899 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
901 if (_line) {
902 _line->get_inverted_selectables (sel, result);
906 void
907 AutomationTimeAxisView::set_selected_points (PointSelection& points)
909 if (_line) {
910 _line->set_selected_points (points);
911 } else if (_view) {
912 _view->set_selected_points (points);
916 void
917 AutomationTimeAxisView::clear_lines ()
919 _line.reset();
920 _list_connections.drop_connections ();
923 void
924 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
926 assert(line);
927 assert(!_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()
938 _line = line;
939 //_controller = AutomationController::create(_session, line->the_list(), _control);
941 line->set_height (height);
943 /* pick up the current state */
944 automation_state_changed ();
946 line->show();
949 void
950 AutomationTimeAxisView::entered()
952 if (_line) {
953 _line->track_entered();
957 void
958 AutomationTimeAxisView::exited ()
960 if (_line) {
961 _line->track_exited();
965 void
966 AutomationTimeAxisView::color_handler ()
968 if (_line) {
969 _line->set_colors();
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) {
992 hide();
995 return 0;
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) {
1011 hide ();
1014 return 0;
1017 XMLNode*
1018 AutomationTimeAxisView::get_state_node ()
1020 TimeAxisView* state_parent = get_parent_with_state ();
1022 if (state_parent) {
1023 return state_parent->get_automation_child_xml_node (_parameter);
1024 } else {
1025 return 0;
1029 void
1030 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
1032 XMLNode* xml_node = get_state_node();
1033 if (xml_node) {
1034 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
1038 guint32
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);
1046 void
1047 AutomationTimeAxisView::hide ()
1049 update_extra_xml_shown (false);
1051 TimeAxisView::hide ();
1054 bool
1055 AutomationTimeAxisView::set_visibility (bool yn)
1057 bool changed = TimeAxisView::set_visibility (yn);
1059 if (changed) {
1060 get_state_node()->add_property ("shown", yn ? X_("yes") : X_("no"));
1063 return changed;
1066 /** @return true if this view has any automation data to display */
1067 bool
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;
1078 if (_line) {
1079 lines.push_back (_line);
1080 } else if (_view) {
1081 lines = _view->get_lines ();
1084 return lines;