d40f5e3405c7ca90ea92ee6891b709c8b7fdb8e2
[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 #include "ardour/session.h"
27
28 #include "ardour_ui.h"
29 #include "automation_time_axis.h"
30 #include "automation_streamview.h"
31 #include "gui_thread.h"
32 #include "route_time_axis.h"
33 #include "automation_line.h"
34 #include "public_editor.h"
35 #include "simplerect.h"
36 #include "selection.h"
37 #include "rgb_macros.h"
38 #include "automation_selectable.h"
39 #include "point_selection.h"
40 #include "canvas_impl.h"
41 #include "utils.h"
42
43 #include "i18n.h"
44
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
48 using namespace Gtk;
49 using namespace Gtkmm2ext;
50 using namespace Editing;
51
52 Pango::FontDescription* AutomationTimeAxisView::name_font = 0;
53 bool AutomationTimeAxisView::have_name_font = false;
54 const string AutomationTimeAxisView::state_node_name = "AutomationChild";
55
56
57 /** \a a the automatable object this time axis is to display data for.
58  * For route/track automation (e.g. gain) pass the route for both \r and \a.
59  * For route child (e.g. plugin) automation, pass the child for \a.
60  * For region automation (e.g. MIDI CC), pass null for \a.
61  */
62 AutomationTimeAxisView::AutomationTimeAxisView (Session* s, boost::shared_ptr<Route> r,
63                 boost::shared_ptr<Automatable> a, boost::shared_ptr<AutomationControl> c,
64                 PublicEditor& e, TimeAxisView& parent, bool show_regions,
65                 ArdourCanvas::Canvas& canvas, const string & nom, const string & nomparent)
66         : AxisView (s),
67           TimeAxisView (s, e, &parent, canvas),
68           _route (r),
69           _control (c),
70           _automatable (a),
71           _controller(AutomationController::create(a, c->parameter(), c)),
72           _base_rect (0),
73           _view (show_regions ? new AutomationStreamView(*this) : NULL),
74           _name (nom),
75           auto_button (X_("")) /* force addition of a label */
76 {
77         if (!have_name_font) {
78                 name_font = get_font_for_style (X_("AutomationTrackName"));
79                 have_name_font = true;
80         }
81
82         automation_menu = 0;
83         auto_off_item = 0;
84         auto_touch_item = 0;
85         auto_write_item = 0;
86         auto_play_item = 0;
87         mode_discrete_item = 0;
88         mode_line_item = 0;
89
90         ignore_state_request = false;
91         first_call_to_set_height = true;
92
93         _base_rect = new SimpleRect(*_canvas_display);
94         _base_rect->property_x1() = 0.0;
95         _base_rect->property_y1() = 0.0;
96         _base_rect->property_x2() = LONG_MAX - 2;
97         _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
98
99         /* outline ends and bottom */
100         _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
101         _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
102
103         _base_rect->set_data ("trackview", this);
104
105         _base_rect->signal_event().connect (sigc::bind (
106                         sigc::mem_fun (_editor, &PublicEditor::canvas_automation_track_event),
107                         _base_rect, this));
108
109         if (!a) {
110                 _base_rect->lower_to_bottom();
111         }
112
113         hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
114
115         auto_button.set_name ("TrackVisualButton");
116         hide_button.set_name ("TrackRemoveButton");
117
118         auto_button.unset_flags (Gtk::CAN_FOCUS);
119         hide_button.unset_flags (Gtk::CAN_FOCUS);
120
121         controls_table.set_no_show_all();
122
123         ARDOUR_UI::instance()->set_tip(auto_button, _("automation state"));
124         ARDOUR_UI::instance()->set_tip(hide_button, _("hide track"));
125
126         /* rearrange the name display */
127
128         /* we never show these for automation tracks, so make
129            life easier and remove them.
130         */
131
132         hide_name_entry();
133
134         /* move the name label over a bit */
135
136         string shortpname = _name;
137         bool shortened = false;
138
139         int ignore_width;
140         shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
141
142         if (shortpname != _name ){
143                 shortened = true;
144         }
145
146         name_label.set_text (shortpname);
147         name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
148
149         if (nomparent.length()) {
150
151                 /* limit the plug name string */
152
153                 string pname = fit_to_pixels (nomparent, 60, *name_font, ignore_width, true);
154                 if (pname != nomparent) {
155                         shortened = true;
156                 }
157
158                 plugname = new Label (pname);
159                 plugname->set_name (X_("TrackPlugName"));
160                 plugname->show();
161                 name_label.set_name (X_("TrackParameterName"));
162                 controls_table.remove (name_hbox);
163                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
164                 plugname_packed = true;
165                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
166         } else {
167                 plugname = 0;
168                 plugname_packed = false;
169         }
170
171         if (shortened) {
172                 string tipname = nomparent;
173                 if (!tipname.empty()) {
174                         tipname += ": ";
175                 }
176                 tipname += _name;
177                 ARDOUR_UI::instance()->set_tip(controls_ebox, tipname);
178         }
179
180         /* add the buttons */
181         controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
182
183         controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
184
185         /* add bar controller */
186         controls_table.attach (*_controller.get(), 0, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
187
188         controls_table.show_all ();
189
190         hide_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
191         auto_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
192
193         controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
194         controls_base_unselected_name = X_("AutomationTrackControlsBase");
195         controls_ebox.set_name (controls_base_unselected_name);
196
197         XMLNode* xml_node = get_parent_with_state()->get_automation_child_xml_node (
198                         _control->parameter());
199
200         if (xml_node) {
201                 set_state (*xml_node, Stateful::loading_state_version);
202         }
203
204         /* ask for notifications of any new RegionViews */
205         if (show_regions) {
206
207                 assert(_view);
208                 _view->attach ();
209
210         /* no regions, just a single line for the entire track (e.g. bus gain) */
211         } else {
212                 boost::shared_ptr<AutomationLine> line(new AutomationLine (
213                                         ARDOUR::EventTypeMap::instance().to_symbol(_control->parameter()),
214                                         *this,
215                                         *_canvas_display,
216                                         _control->alist()));
217
218                 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
219                 line->queue_reset ();
220                 add_line (line);
221         }
222
223         /* make sure labels etc. are correct */
224
225         automation_state_changed ();
226         ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
227 }
228
229 AutomationTimeAxisView::~AutomationTimeAxisView ()
230 {
231 }
232
233 void
234 AutomationTimeAxisView::auto_clicked ()
235 {
236         using namespace Menu_Helpers;
237
238         if (automation_menu == 0) {
239                 automation_menu = manage (new Menu);
240                 automation_menu->set_name ("ArdourContextMenu");
241                 MenuList& items (automation_menu->items());
242
243                 items.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
244                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
245                 items.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
246                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
247                 items.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
248                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
249                 items.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
250                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
251         }
252
253         automation_menu->popup (1, gtk_get_current_event_time());
254 }
255
256 void
257 AutomationTimeAxisView::set_automation_state (AutoState state)
258 {
259         if (!ignore_state_request) {
260                 if (_automatable) {
261                         _automatable->set_parameter_automation_state (_control->parameter(), state);
262                 }
263 #if 0
264                 if (_route == _automatable) { // This is a time axis for route (not region) automation
265                         _route->set_parameter_automation_state (_control->parameter(), state);
266                 }
267
268                 if (_control->list())
269                         _control->alist()->set_automation_state(state);
270 #endif
271         }
272
273         if (_view) {
274                 _view->set_automation_state (state);
275         }
276 }
277
278 void
279 AutomationTimeAxisView::automation_state_changed ()
280 {
281         AutoState state;
282
283         /* update button label */
284
285         if (!_line) {
286                 state = Off;
287         } else {
288                 state = _control->alist()->automation_state ();
289         }
290
291         switch (state & (Off|Play|Touch|Write)) {
292         case Off:
293                 auto_button.set_label (_("Manual"));
294                 if (auto_off_item) {
295                         ignore_state_request = true;
296                         auto_off_item->set_active (true);
297                         auto_play_item->set_active (false);
298                         auto_touch_item->set_active (false);
299                         auto_write_item->set_active (false);
300                         ignore_state_request = false;
301                 }
302                 break;
303         case Play:
304                 auto_button.set_label (_("Play"));
305                 if (auto_play_item) {
306                         ignore_state_request = true;
307                         auto_play_item->set_active (true);
308                         auto_off_item->set_active (false);
309                         auto_touch_item->set_active (false);
310                         auto_write_item->set_active (false);
311                         ignore_state_request = false;
312                 }
313                 break;
314         case Write:
315                 auto_button.set_label (_("Write"));
316                 if (auto_write_item) {
317                         ignore_state_request = true;
318                         auto_write_item->set_active (true);
319                         auto_off_item->set_active (false);
320                         auto_play_item->set_active (false);
321                         auto_touch_item->set_active (false);
322                         ignore_state_request = false;
323                 }
324                 break;
325         case Touch:
326                 auto_button.set_label (_("Touch"));
327                 if (auto_touch_item) {
328                         ignore_state_request = true;
329                         auto_touch_item->set_active (true);
330                         auto_off_item->set_active (false);
331                         auto_play_item->set_active (false);
332                         auto_write_item->set_active (false);
333                         ignore_state_request = false;
334                 }
335                 break;
336         default:
337                 auto_button.set_label (_("???"));
338                 break;
339         }
340 }
341
342 void
343 AutomationTimeAxisView::interpolation_changed ()
344 {
345         AutomationList::InterpolationStyle style = _control->list()->interpolation();
346
347         if (mode_line_item && mode_discrete_item) {
348                 if (style == AutomationList::Discrete) {
349                         mode_discrete_item->set_active(true);
350                         mode_line_item->set_active(false);
351                 } else {
352                         mode_line_item->set_active(true);
353                         mode_discrete_item->set_active(false);
354                 }
355         }
356
357         if (_line) {
358                 _line->set_interpolation(style);
359         }
360 }
361
362 void
363 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
364 {
365         _control->list()->set_interpolation(style);
366         if (_line) {
367                 _line->set_interpolation(style);
368         }
369 }
370
371 void
372 AutomationTimeAxisView::clear_clicked ()
373 {
374         _session->begin_reversible_command (_("clear automation"));
375         if (_line) {
376                 _line->clear ();
377         }
378         _session->commit_reversible_command ();
379 }
380
381 void
382 AutomationTimeAxisView::set_height (uint32_t h)
383 {
384         bool changed = (height != (uint32_t) h) || first_call_to_set_height;
385         bool changed_between_small_and_normal = (
386                    (height < hNormal && h >= hNormal)
387                 || (height >= hNormal || h < hNormal) );
388
389         TimeAxisView* state_parent = get_parent_with_state ();
390         assert(state_parent);
391         XMLNode* xml_node = state_parent->get_automation_child_xml_node (_control->parameter());
392
393         TimeAxisView::set_height (h);
394         _base_rect->property_y2() = h;
395
396         if (_line)
397                 _line->set_height(h);
398
399         if (_view) {
400                 _view->set_height(h);
401                 _view->update_contents_height();
402         }
403
404         char buf[32];
405         snprintf (buf, sizeof (buf), "%u", height);
406         if (xml_node) {
407                 xml_node->add_property ("height", buf);
408         }
409
410         if (changed_between_small_and_normal || first_call_to_set_height) {
411
412                 first_call_to_set_height = false;
413
414                 if (h >= hNormal) {
415                         controls_table.remove (name_hbox);
416
417                         if (plugname) {
418                                 if (plugname_packed) {
419                                         controls_table.remove (*plugname);
420                                         plugname_packed = false;
421                                 }
422                                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
423                                 plugname_packed = true;
424                                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
425                         } else {
426                                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
427                         }
428                         hide_name_entry ();
429                         show_name_label ();
430                         name_hbox.show_all ();
431
432                         auto_button.show();
433                         hide_button.show_all();
434
435                 } else if (h >= hSmall) {
436                         controls_table.remove (name_hbox);
437                         if (plugname) {
438                                 if (plugname_packed) {
439                                         controls_table.remove (*plugname);
440                                         plugname_packed = false;
441                                 }
442                         }
443                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
444                         controls_table.hide_all ();
445                         hide_name_entry ();
446                         show_name_label ();
447                         name_hbox.show_all ();
448
449                         auto_button.hide();
450                         hide_button.hide();
451                 }
452         } else if (h >= hNormal){
453                 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
454         }
455
456         if (changed) {
457                 if (canvas_item_visible (_canvas_display)) {
458                         /* only emit the signal if the height really changed and we were visible */
459                         _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
460                 }
461         }
462 }
463
464 void
465 AutomationTimeAxisView::set_samples_per_unit (double spu)
466 {
467         TimeAxisView::set_samples_per_unit (spu);
468
469         if (_line)
470                 _line->reset ();
471
472         if (_view)
473                 _view->set_samples_per_unit (spu);
474 }
475
476 void
477 AutomationTimeAxisView::hide_clicked ()
478 {
479         // LAME fix for refreshing the hide button
480         hide_button.set_sensitive(false);
481
482         set_marked_for_display (false);
483         hide ();
484
485         hide_button.set_sensitive(true);
486 }
487
488 void
489 AutomationTimeAxisView::build_display_menu ()
490 {
491         using namespace Menu_Helpers;
492
493         /* prepare it */
494
495         TimeAxisView::build_display_menu ();
496
497         /* now fill it with our stuff */
498
499         MenuList& items = display_menu->items();
500
501         items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
502         items.push_back (SeparatorElem());
503         items.push_back (MenuElem (_("Clear"), sigc::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"), sigc::bind (
513                         sigc::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"), sigc::bind (
518                         sigc::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"), sigc::bind (
523                         sigc::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"), sigc::bind (
528                         sigc::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"), sigc::bind (
545                                 sigc::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                 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
551                                 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
552                                 AutomationList::Linear)));
553                 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
554
555                 // Set default interpolation type to linear if this isn't a (usually) discrete controller
556                 if (EventTypeMap::instance().interpolation_of(_control->parameter()) == Evoral::ControlList::Linear) {
557                         mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
558                 }
559
560                 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
561         }
562
563         /* make sure the automation menu state is correct */
564
565         automation_state_changed ();
566         interpolation_changed ();
567 }
568
569 void
570 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, nframes_t when, double y)
571 {
572         if (!_line)
573                 return;
574
575         double x = 0;
576
577         _canvas_display->w2i (x, y);
578
579         /* compute vertical fractional position */
580
581         y = 1.0 - (y / height);
582
583         /* map using line */
584
585         _line->view_to_model_coord (x, y);
586
587         _session->begin_reversible_command (_("add automation event"));
588         XMLNode& before = _control->alist()->get_state();
589
590         _control->alist()->add (when, y);
591
592         XMLNode& after = _control->alist()->get_state();
593         _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
594
595         _session->set_dirty ();
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
820 void
821 AutomationTimeAxisView::clear_lines ()
822 {
823         _line.reset();
824         automation_connection.disconnect ();
825 }
826
827 void
828 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
829 {
830         assert(line);
831         assert(!_line);
832         assert(line->the_list() == _control->list());
833
834         _control->alist()->automation_state_changed.connect (automation_connection, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context());
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, int version)
871 {
872         TimeAxisView::set_state (node, version);
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
927 bool
928 AutomationTimeAxisView::set_visibility (bool yn)
929 {
930         bool changed = TimeAxisView::set_visibility (yn);
931
932         if (changed) {
933                 get_state_node()->add_property ("shown", yn ? X_("yes") : X_("no"));
934         }
935
936         return changed;
937 }