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