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