63476b051bea5cbdd836b23513b36e228a17dd18
[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                 // For discrete types we dont allow the linear option since it makes no sense for those Controls
553                 if (EventTypeMap::instance().interpolation_of(_control->parameter()) == Evoral::ControlList::Linear) {
554                         am_items.push_back (RadioMenuElem (group, _("Line"), bind (
555                                                         mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
556                                                         AutomationList::Linear)));
557                         mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
558                         mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
559                 }
560
561                 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
562         }
563
564         /* make sure the automation menu state is correct */
565
566         automation_state_changed ();
567         interpolation_changed ();
568 }
569
570 void
571 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, nframes_t when, double y)
572 {
573         if (!_line)
574                 return;
575
576         double x = 0;
577
578         _canvas_display->w2i (x, y);
579
580         /* compute vertical fractional position */
581
582         y = 1.0 - (y / height);
583
584         /* map using line */
585
586         _line->view_to_model_coord (x, y);
587
588         _session.begin_reversible_command (_("add automation event"));
589         XMLNode& before = _control->alist()->get_state();
590
591         _control->alist()->add (when, y);
592
593         XMLNode& after = _control->alist()->get_state();
594         _session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
595
596         _session.set_dirty ();
597 }
598
599 bool
600 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
601 {
602         return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
603 }
604
605 bool
606 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
607 {
608         boost::shared_ptr<Evoral::ControlList> what_we_got;
609         boost::shared_ptr<AutomationList> alist (line.the_list());
610         bool ret = false;
611
612         XMLNode &before = alist->get_state();
613
614         switch (op) {
615         case Cut:
616                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
617                         _editor.get_cut_buffer().add (what_we_got);
618                         _session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
619                         ret = true;
620                 }
621                 break;
622         case Copy:
623                 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
624                         _editor.get_cut_buffer().add (what_we_got);
625                 }
626                 break;
627
628         case Clear:
629                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
630                         _session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
631                         ret = true;
632                 }
633                 break;
634         }
635
636         if (what_we_got) {
637                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
638                         double when = (*x)->when;
639                         double val  = (*x)->value;
640                         line.model_to_view_coord (when, val);
641                         (*x)->when = when;
642                         (*x)->value = val;
643                 }
644         }
645
646         return ret;
647 }
648
649 void
650 AutomationTimeAxisView::reset_objects (PointSelection& selection)
651 {
652         reset_objects_one (*_line, selection);
653 }
654
655 void
656 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
657 {
658         boost::shared_ptr<AutomationList> alist(line.the_list());
659
660         _session.add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
661
662         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
663
664                 if (&(*i).track != this) {
665                         continue;
666                 }
667
668                 alist->reset_range ((*i).start, (*i).end);
669         }
670 }
671
672 bool
673 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
674 {
675         return cut_copy_clear_objects_one (*_line, selection, op);
676 }
677
678 bool
679 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
680 {
681         boost::shared_ptr<Evoral::ControlList> what_we_got;
682         boost::shared_ptr<AutomationList> alist(line.the_list());
683         bool ret = false;
684
685         XMLNode &before = alist->get_state();
686
687         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
688
689                 if (&(*i).track != this) {
690                         continue;
691                 }
692
693                 switch (op) {
694                 case Cut:
695                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
696                                 _editor.get_cut_buffer().add (what_we_got);
697                                 _session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
698                                 ret = true;
699                         }
700                         break;
701                 case Copy:
702                         if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
703                                 _editor.get_cut_buffer().add (what_we_got);
704                         }
705                         break;
706
707                 case Clear:
708                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
709                                 _session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
710                                 ret = true;
711                         }
712                         break;
713                 }
714         }
715
716         delete &before;
717
718         if (what_we_got) {
719                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
720                         double when = (*x)->when;
721                         double val  = (*x)->value;
722                         line.model_to_view_coord (when, val);
723                         (*x)->when = when;
724                         (*x)->value = val;
725                 }
726         }
727
728         return ret;
729 }
730
731 bool
732 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
733 {
734         return paste_one (*_line, pos, times, selection, nth);
735 }
736
737 bool
738 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
739 {
740         AutomationSelection::iterator p;
741         boost::shared_ptr<AutomationList> alist(line.the_list());
742
743         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
744
745         if (p == selection.lines.end()) {
746                 return false;
747         }
748
749         /* Make a copy of the list because we have to scale the
750            values from view coordinates to model coordinates, and we're
751            not supposed to modify the points in the selection.
752         */
753
754         AutomationList copy (**p);
755
756         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
757                 double when = (*x)->when;
758                 double val  = (*x)->value;
759                 line.view_to_model_coord (when, val);
760                 (*x)->when = when;
761                 (*x)->value = val;
762         }
763
764         XMLNode &before = alist->get_state();
765         alist->paste (copy, pos, times);
766         _session.add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
767
768         return true;
769 }
770
771 void
772 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
773 {
774         if (_line && touched (top, bot)) {
775                 double topfrac;
776                 double botfrac;
777
778                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
779                    _y_position is the "origin" or "top" of the track.
780                 */
781
782                 double mybot = _y_position + height;
783
784                 if (_y_position >= top && mybot <= bot) {
785
786                         /* _y_position is below top, mybot is above bot, so we're fully
787                            covered vertically.
788                         */
789
790                         topfrac = 1.0;
791                         botfrac = 0.0;
792
793                 } else {
794
795                         /* top and bot are within _y_position .. mybot */
796
797                         topfrac = 1.0 - ((top - _y_position) / height);
798                         botfrac = 1.0 - ((bot - _y_position) / height);
799                 }
800
801                 if (_line)
802                         _line->get_selectables (start, end, botfrac, topfrac, results);
803         }
804 }
805
806 void
807 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
808 {
809         if (_line)
810                 _line->get_inverted_selectables (sel, result);
811 }
812
813 void
814 AutomationTimeAxisView::set_selected_points (PointSelection& points)
815 {
816         if (_line)
817                 _line->set_selected_points (points);
818 }
819
820 void
821 AutomationTimeAxisView::clear_lines ()
822 {
823         _line.reset();
824         automation_connection.disconnect ();
825 }
826
827 void
828 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
829 {
830         assert(line);
831         assert(!_line);
832         assert(line->the_list() == _control->list());
833
834         automation_connection = _control->alist()->automation_state_changed.connect
835                 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
836
837         _line = line;
838         //_controller = AutomationController::create(_session, line->the_list(), _control);
839
840         line->set_height (height);
841
842         /* pick up the current state */
843         automation_state_changed ();
844
845         line->show();
846 }
847
848 void
849 AutomationTimeAxisView::entered()
850 {
851         if (_line)
852                 _line->track_entered();
853 }
854
855 void
856 AutomationTimeAxisView::exited ()
857 {
858         if (_line)
859                 _line->track_exited();
860 }
861
862 void
863 AutomationTimeAxisView::color_handler ()
864 {
865         if (_line) {
866                 _line->set_colors();
867         }
868 }
869
870 int
871 AutomationTimeAxisView::set_state (const XMLNode& node, int version)
872 {
873         TimeAxisView::set_state (node, version);
874
875         XMLProperty const * type = node.property ("automation-id");
876         if (type && type->value () == ARDOUR::EventTypeMap::instance().to_symbol (_control->parameter())) {
877                 XMLProperty const * shown = node.property ("shown");
878                 if (shown && shown->value () == "yes") {
879                         set_marked_for_display (true);
880                         _canvas_display->show (); /* FIXME: necessary? show_at? */
881                 }
882         }
883
884         if (!_marked_for_display) {
885                 hide();
886         }
887
888         return 0;
889 }
890
891 XMLNode*
892 AutomationTimeAxisView::get_state_node ()
893 {
894         TimeAxisView* state_parent = get_parent_with_state ();
895
896         if (state_parent) {
897                 return state_parent->get_automation_child_xml_node (_control->parameter());
898         } else {
899                 return 0;
900         }
901 }
902
903 void
904 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
905 {
906         XMLNode* xml_node = get_state_node();
907         if (xml_node) {
908                 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
909         }
910 }
911
912 guint32
913 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
914 {
915         update_extra_xml_shown (true);
916
917         return TimeAxisView::show_at (y, nth, parent);
918 }
919
920 void
921 AutomationTimeAxisView::hide ()
922 {
923         update_extra_xml_shown (false);
924
925         TimeAxisView::hide ();
926 }
927