A few fixes to interpolation of MIDI controller data. Don't interpolate
[ardour.git] / gtk2_ardour / automation_time_axis.cc
1 /*
2     Copyright (C) 2000-2007 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
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"
27
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"
42
43 #include "i18n.h"
44
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
48 using namespace Gtk;
49 using namespace Gtkmm2ext;
50 using namespace Editing;
51
52 Pango::FontDescription* AutomationTimeAxisView::name_font = 0;
53 bool AutomationTimeAxisView::have_name_font = false;
54 const string AutomationTimeAxisView::state_node_name = "AutomationChild";
55
56
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.
61  */
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 */
76 {
77         if (!have_name_font) {
78                 name_font = get_font_for_style (X_("AutomationTrackName"));
79                 have_name_font = true;
80         }
81
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;
89
90         ignore_state_request = false;
91         first_call_to_set_height = true;
92
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();
98
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();
102
103         _base_rect->set_data ("trackview", this);
104
105         _base_rect->signal_event().connect (sigc::bind (
106                         sigc::mem_fun (_editor, &PublicEditor::canvas_automation_track_event),
107                         _base_rect, this));
108
109         if (!a) {
110                 _base_rect->lower_to_bottom();
111         }
112
113         hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
114
115         auto_button.set_name ("TrackVisualButton");
116         hide_button.set_name ("TrackRemoveButton");
117
118         auto_button.unset_flags (Gtk::CAN_FOCUS);
119         hide_button.unset_flags (Gtk::CAN_FOCUS);
120
121         controls_table.set_no_show_all();
122
123         ARDOUR_UI::instance()->set_tip(auto_button, _("automation state"));
124         ARDOUR_UI::instance()->set_tip(hide_button, _("hide track"));
125
126         /* rearrange the name display */
127
128         /* we never show these for automation tracks, so make
129            life easier and remove them.
130         */
131
132         hide_name_entry();
133
134         /* move the name label over a bit */
135
136         string shortpname = _name;
137         bool shortened = false;
138
139         int ignore_width;
140         shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
141
142         if (shortpname != _name ){
143                 shortened = true;
144         }
145
146         name_label.set_text (shortpname);
147         name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
148
149         if (nomparent.length()) {
150
151                 /* limit the plug name string */
152
153                 string pname = fit_to_pixels (nomparent, 60, *name_font, ignore_width, true);
154                 if (pname != nomparent) {
155                         shortened = true;
156                 }
157
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;
169         }
170
171         if (shortened) {
172                 string tipname = nomparent;
173                 if (!tipname.empty()) {
174                         tipname += ": ";
175                 }
176                 tipname += _name;
177                 ARDOUR_UI::instance()->set_tip(controls_ebox, tipname);
178         }
179
180         /* add the buttons */
181         controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
182
183         controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
184
185         /* add bar controller */
186         controls_table.attach (*_controller.get(), 0, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
187
188         controls_table.show_all ();
189
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));
192
193         controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
194         controls_base_unselected_name = X_("AutomationTrackControlsBase");
195         controls_ebox.set_name (controls_base_unselected_name);
196
197         XMLNode* xml_node = get_parent_with_state()->get_automation_child_xml_node (
198                         _control->parameter());
199
200         if (xml_node) {
201                 set_state (*xml_node, Stateful::loading_state_version);
202         }
203
204         /* ask for notifications of any new RegionViews */
205         if (show_regions) {
206
207                 assert(_view);
208                 _view->attach ();
209
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()));
217
218                 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
219                 line->queue_reset ();
220                 add_line (line);
221         }
222
223         /* make sure labels etc. are correct */
224
225         automation_state_changed ();
226         ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
227 }
228
229 AutomationTimeAxisView::~AutomationTimeAxisView ()
230 {
231 }
232
233 void
234 AutomationTimeAxisView::auto_clicked ()
235 {
236         using namespace Menu_Helpers;
237
238         if (automation_menu == 0) {
239                 automation_menu = manage (new Menu);
240                 automation_menu->set_name ("ArdourContextMenu");
241                 MenuList& items (automation_menu->items());
242
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)));
251         }
252
253         automation_menu->popup (1, gtk_get_current_event_time());
254 }
255
256 void
257 AutomationTimeAxisView::set_automation_state (AutoState state)
258 {
259         if (!ignore_state_request) {
260                 if (_automatable) {
261                         _automatable->set_parameter_automation_state (_control->parameter(), state);
262                 }
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);
266                 }
267
268                 if (_control->list())
269                         _control->alist()->set_automation_state(state);
270 #endif
271         }
272
273         if (_view) {
274                 _view->set_automation_state (state);
275
276                 /* AutomationStreamViews don't signal when their automation state changes, so handle
277                    our updates `manually'.
278                 */
279                 automation_state_changed ();
280         }
281 }
282
283 void
284 AutomationTimeAxisView::automation_state_changed ()
285 {
286         AutoState state;
287
288         /* update button label */
289
290         if (_line) {
291                 state = _control->alist()->automation_state ();
292         } else if (_view) {
293                 state = _view->automation_state ();
294         } else {
295                 state = Off;
296         }
297
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;
308                 }
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;
319                 }
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;
330                 }
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;
341                 }
342                 break;
343         default:
344                 auto_button.set_label (_("???"));
345                 break;
346         }
347 }
348
349 /** The interpolation style of our AutomationList has changed, so update */
350 void
351 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
352 {
353         if (mode_line_item && mode_discrete_item) {
354                 if (s == AutomationList::Discrete) {
355                         mode_discrete_item->set_active(true);
356                         mode_line_item->set_active(false);
357                 } else {
358                         mode_line_item->set_active(true);
359                         mode_discrete_item->set_active(false);
360                 }
361         }
362 }
363
364 /** A menu item has been selected to change our interpolation mode */
365 void
366 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
367 {
368         /* Tell our view's list, if we have one, otherwise tell our own.
369          * Everything else will be signalled back from that.
370          */
371         
372         if (_view) {
373                 _view->set_interpolation (style);
374         } else {
375                 _control->list()->set_interpolation (style);
376         }
377 }
378
379 void
380 AutomationTimeAxisView::clear_clicked ()
381 {
382         if (_line) {
383                 _session->begin_reversible_command (_("clear automation"));
384                 _line->clear ();
385         }
386 }
387
388 void
389 AutomationTimeAxisView::set_height (uint32_t h)
390 {
391         bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
392         uint32_t const normal = preset_height (HeightNormal);
393         bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
394
395         TimeAxisView* state_parent = get_parent_with_state ();
396         assert(state_parent);
397         XMLNode* xml_node = state_parent->get_automation_child_xml_node (_control->parameter());
398
399         TimeAxisView::set_height (h);
400         _base_rect->property_y2() = h;
401
402         if (_line)
403                 _line->set_height(h);
404
405         if (_view) {
406                 _view->set_height(h);
407                 _view->update_contents_height();
408         }
409
410         char buf[32];
411         snprintf (buf, sizeof (buf), "%u", height);
412         if (xml_node) {
413                 xml_node->add_property ("height", buf);
414         }
415
416         if (changed_between_small_and_normal || first_call_to_set_height) {
417
418                 first_call_to_set_height = false;
419
420                 if (h >= preset_height (HeightNormal)) {
421                         controls_table.remove (name_hbox);
422
423                         if (plugname) {
424                                 if (plugname_packed) {
425                                         controls_table.remove (*plugname);
426                                         plugname_packed = false;
427                                 }
428                                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
429                                 plugname_packed = true;
430                                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
431                         } else {
432                                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
433                         }
434                         hide_name_entry ();
435                         show_name_label ();
436                         name_hbox.show_all ();
437
438                         auto_button.show();
439                         hide_button.show_all();
440
441                 } else if (h >= preset_height (HeightSmall)) {
442                         controls_table.remove (name_hbox);
443                         if (plugname) {
444                                 if (plugname_packed) {
445                                         controls_table.remove (*plugname);
446                                         plugname_packed = false;
447                                 }
448                         }
449                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
450                         controls_table.hide_all ();
451                         hide_name_entry ();
452                         show_name_label ();
453                         name_hbox.show_all ();
454
455                         auto_button.hide();
456                         hide_button.hide();
457                 }
458         } else if (h >= preset_height (HeightNormal)) {
459                 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
460         }
461
462         if (changed) {
463                 if (canvas_item_visible (_canvas_display)) {
464                         /* only emit the signal if the height really changed and we were visible */
465                         _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
466                 }
467         }
468 }
469
470 void
471 AutomationTimeAxisView::set_samples_per_unit (double spu)
472 {
473         TimeAxisView::set_samples_per_unit (spu);
474
475         if (_line)
476                 _line->reset ();
477
478         if (_view)
479                 _view->set_samples_per_unit (spu);
480 }
481
482 void
483 AutomationTimeAxisView::hide_clicked ()
484 {
485         // LAME fix for refreshing the hide button
486         hide_button.set_sensitive(false);
487
488         set_marked_for_display (false);
489         hide ();
490
491         hide_button.set_sensitive(true);
492 }
493
494 void
495 AutomationTimeAxisView::build_display_menu ()
496 {
497         using namespace Menu_Helpers;
498
499         /* prepare it */
500
501         TimeAxisView::build_display_menu ();
502
503         /* now fill it with our stuff */
504
505         MenuList& items = display_menu->items();
506
507         items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
508         items.push_back (SeparatorElem());
509         items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
510         items.push_back (SeparatorElem());
511
512         /* state menu */
513
514         Menu* auto_state_menu = manage (new Menu);
515         auto_state_menu->set_name ("ArdourContextMenu");
516         MenuList& as_items = auto_state_menu->items();
517
518         as_items.push_back (CheckMenuElem (_("Manual"), sigc::bind (
519                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
520                         (AutoState) Off)));
521         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
522
523         as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
524                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
525                         (AutoState) Play)));
526         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
527
528         as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
529                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
530                         (AutoState) Write)));
531         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
532
533         as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
534                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
535                         (AutoState) Touch)));
536         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
537
538         items.push_back (MenuElem (_("State"), *auto_state_menu));
539
540         /* mode menu */
541
542         /* current interpolation state */
543         AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
544
545         if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
546
547                 Menu* auto_mode_menu = manage (new Menu);
548                 auto_mode_menu->set_name ("ArdourContextMenu");
549                 MenuList& am_items = auto_mode_menu->items();
550
551                 RadioMenuItem::Group group;
552
553                 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
554                                 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
555                                 AutomationList::Discrete)));
556                 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
557                 mode_discrete_item->set_active (s == AutomationList::Discrete);
558
559                 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
560                                 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
561                                 AutomationList::Linear)));
562                 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
563                 mode_line_item->set_active (s == AutomationList::Linear);
564
565                 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
566         }
567
568         /* make sure the automation menu state is correct */
569
570         automation_state_changed ();
571         interpolation_changed (s);
572 }
573
574 void
575 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, nframes_t when, double y)
576 {
577         if (!_line)
578                 return;
579
580         double x = 0;
581
582         _canvas_display->w2i (x, y);
583
584         /* compute vertical fractional position */
585
586         y = 1.0 - (y / height);
587
588         /* map using line */
589
590         _line->view_to_model_coord (x, y);
591
592         _session->begin_reversible_command (_("add automation event"));
593         XMLNode& before = _control->alist()->get_state();
594
595         _control->alist()->add (when, y);
596
597         XMLNode& after = _control->alist()->get_state();
598         _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
599
600         _session->set_dirty ();
601 }
602
603 bool
604 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
605 {
606         return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
607 }
608
609 bool
610 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
611 {
612         boost::shared_ptr<Evoral::ControlList> what_we_got;
613         boost::shared_ptr<AutomationList> alist (line.the_list());
614         bool ret = false;
615
616         XMLNode &before = alist->get_state();
617
618         switch (op) {
619         case Cut:
620                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
621                         _editor.get_cut_buffer().add (what_we_got);
622                         _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
623                         ret = true;
624                 }
625                 break;
626         case Copy:
627                 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
628                         _editor.get_cut_buffer().add (what_we_got);
629                 }
630                 break;
631
632         case Clear:
633                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
634                         _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
635                         ret = true;
636                 }
637                 break;
638         }
639
640         if (what_we_got) {
641                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
642                         double when = (*x)->when;
643                         double val  = (*x)->value;
644                         line.model_to_view_coord (when, val);
645                         (*x)->when = when;
646                         (*x)->value = val;
647                 }
648         }
649
650         return ret;
651 }
652
653 void
654 AutomationTimeAxisView::reset_objects (PointSelection& selection)
655 {
656         reset_objects_one (*_line, selection);
657 }
658
659 void
660 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
661 {
662         boost::shared_ptr<AutomationList> alist(line.the_list());
663
664         _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
665
666         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
667
668                 if ((*i).track != this) {
669                         continue;
670                 }
671
672                 alist->reset_range ((*i).start, (*i).end);
673         }
674 }
675
676 bool
677 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
678 {
679         return cut_copy_clear_objects_one (*_line, selection, op);
680 }
681
682 bool
683 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
684 {
685         boost::shared_ptr<Evoral::ControlList> what_we_got;
686         boost::shared_ptr<AutomationList> alist(line.the_list());
687         bool ret = false;
688
689         XMLNode &before = alist->get_state();
690
691         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
692
693                 if ((*i).track != this) {
694                         continue;
695                 }
696
697                 switch (op) {
698                 case Cut:
699                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
700                                 _editor.get_cut_buffer().add (what_we_got);
701                                 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
702                                 ret = true;
703                         }
704                         break;
705                 case Copy:
706                         if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
707                                 _editor.get_cut_buffer().add (what_we_got);
708                         }
709                         break;
710
711                 case Clear:
712                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
713                                 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
714                                 ret = true;
715                         }
716                         break;
717                 }
718         }
719
720         delete &before;
721
722         if (what_we_got) {
723                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
724                         double when = (*x)->when;
725                         double val  = (*x)->value;
726                         line.model_to_view_coord (when, val);
727                         (*x)->when = when;
728                         (*x)->value = val;
729                 }
730         }
731
732         return ret;
733 }
734
735 bool
736 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
737 {
738         return paste_one (*_line, pos, times, selection, nth);
739 }
740
741 bool
742 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
743 {
744         AutomationSelection::iterator p;
745         boost::shared_ptr<AutomationList> alist(line.the_list());
746
747         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
748
749         if (p == selection.lines.end()) {
750                 return false;
751         }
752
753         /* Make a copy of the list because we have to scale the
754            values from view coordinates to model coordinates, and we're
755            not supposed to modify the points in the selection.
756         */
757
758         AutomationList copy (**p);
759
760         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
761                 double when = (*x)->when;
762                 double val  = (*x)->value;
763                 line.view_to_model_coord (when, val);
764                 (*x)->when = when;
765                 (*x)->value = val;
766         }
767
768         XMLNode &before = alist->get_state();
769         alist->paste (copy, pos, times);
770         _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
771
772         return true;
773 }
774
775 void
776 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
777 {
778         if (_line && touched (top, bot)) {
779                 double topfrac;
780                 double botfrac;
781
782                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
783                    _y_position is the "origin" or "top" of the track.
784                 */
785
786                 double mybot = _y_position + height;
787
788                 if (_y_position >= top && mybot <= bot) {
789
790                         /* _y_position is below top, mybot is above bot, so we're fully
791                            covered vertically.
792                         */
793
794                         topfrac = 1.0;
795                         botfrac = 0.0;
796
797                 } else {
798
799                         /* top and bot are within _y_position .. mybot */
800
801                         topfrac = 1.0 - ((top - _y_position) / height);
802                         botfrac = 1.0 - ((bot - _y_position) / height);
803                 }
804
805                 if (_line)
806                         _line->get_selectables (start, end, botfrac, topfrac, results);
807         }
808 }
809
810 void
811 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
812 {
813         if (_line)
814                 _line->get_inverted_selectables (sel, result);
815 }
816
817 void
818 AutomationTimeAxisView::set_selected_points (PointSelection& points)
819 {
820         if (_line) {
821                 _line->set_selected_points (points);
822         }
823 }
824
825 void
826 AutomationTimeAxisView::clear_lines ()
827 {
828         _line.reset();
829         _list_connections.drop_connections ();
830 }
831
832 void
833 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
834 {
835         assert(line);
836         assert(!_line);
837         assert(line->the_list() == _control->list());
838
839         _control->alist()->automation_state_changed.connect (
840                 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
841                 );
842         
843         _control->alist()->InterpolationChanged.connect (
844                 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
845                 );
846
847         _line = line;
848         //_controller = AutomationController::create(_session, line->the_list(), _control);
849
850         line->set_height (height);
851
852         /* pick up the current state */
853         automation_state_changed ();
854
855         line->show();
856 }
857
858 void
859 AutomationTimeAxisView::entered()
860 {
861         if (_line)
862                 _line->track_entered();
863 }
864
865 void
866 AutomationTimeAxisView::exited ()
867 {
868         if (_line)
869                 _line->track_exited();
870 }
871
872 void
873 AutomationTimeAxisView::color_handler ()
874 {
875         if (_line) {
876                 _line->set_colors();
877         }
878 }
879
880 int
881 AutomationTimeAxisView::set_state (const XMLNode& node, int version)
882 {
883         TimeAxisView::set_state (node, version);
884
885         XMLProperty const * type = node.property ("automation-id");
886         if (type && type->value () == ARDOUR::EventTypeMap::instance().to_symbol (_control->parameter())) {
887                 XMLProperty const * shown = node.property ("shown");
888                 if (shown && shown->value () == "yes") {
889                         set_marked_for_display (true);
890                         _canvas_display->show (); /* FIXME: necessary? show_at? */
891                 }
892         }
893
894         if (!_marked_for_display) {
895                 hide();
896         }
897
898         return 0;
899 }
900
901 XMLNode*
902 AutomationTimeAxisView::get_state_node ()
903 {
904         TimeAxisView* state_parent = get_parent_with_state ();
905
906         if (state_parent) {
907                 return state_parent->get_automation_child_xml_node (_control->parameter());
908         } else {
909                 return 0;
910         }
911 }
912
913 void
914 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
915 {
916         XMLNode* xml_node = get_state_node();
917         if (xml_node) {
918                 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
919         }
920 }
921
922 guint32
923 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
924 {
925         update_extra_xml_shown (true);
926
927         return TimeAxisView::show_at (y, nth, parent);
928 }
929
930 void
931 AutomationTimeAxisView::hide ()
932 {
933         update_extra_xml_shown (false);
934
935         TimeAxisView::hide ();
936 }
937
938 bool
939 AutomationTimeAxisView::set_visibility (bool yn)
940 {
941         bool changed = TimeAxisView::set_visibility (yn);
942
943         if (changed) {
944                 get_state_node()->add_property ("shown", yn ? X_("yes") : X_("no"));
945         }
946
947         return changed;
948 }
949
950 /** @return true if this view has any automation data to display */
951 bool
952 AutomationTimeAxisView::has_automation () const
953 {
954         return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
955 }