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