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