5cb26a643541f2f9ec5216fba7564b7d134772bc
[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()->tooltips().set_tip(auto_button, _("automation state"));
124         ARDOUR_UI::instance()->tooltips().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()->tooltips().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                 /* only emit the signal if the height really changed */
458                 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
459         }
460 }
461
462 void
463 AutomationTimeAxisView::set_samples_per_unit (double spu)
464 {
465         TimeAxisView::set_samples_per_unit (spu);
466
467         if (_line)
468                 _line->reset ();
469
470         if (_view)
471                 _view->set_samples_per_unit (spu);
472 }
473
474 void
475 AutomationTimeAxisView::hide_clicked ()
476 {
477         // LAME fix for refreshing the hide button
478         hide_button.set_sensitive(false);
479
480         set_marked_for_display (false);
481         hide ();
482
483         hide_button.set_sensitive(true);
484 }
485
486 void
487 AutomationTimeAxisView::build_display_menu ()
488 {
489         using namespace Menu_Helpers;
490
491         /* get the size menu ready */
492
493         build_size_menu ();
494
495         /* prepare it */
496
497         TimeAxisView::build_display_menu ();
498
499         /* now fill it with our stuff */
500
501         MenuList& items = display_menu->items();
502
503         items.push_back (MenuElem (_("Height"), *size_menu));
504         items.push_back (SeparatorElem());
505         items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
506         items.push_back (SeparatorElem());
507         items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
508         items.push_back (SeparatorElem());
509
510         /* state menu */
511
512         Menu* auto_state_menu = manage (new Menu);
513         auto_state_menu->set_name ("ArdourContextMenu");
514         MenuList& as_items = auto_state_menu->items();
515
516         as_items.push_back (CheckMenuElem (_("Manual"), sigc::bind (
517                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
518                         (AutoState) Off)));
519         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
520
521         as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
522                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
523                         (AutoState) Play)));
524         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
525
526         as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
527                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
528                         (AutoState) Write)));
529         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
530
531         as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
532                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
533                         (AutoState) Touch)));
534         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
535
536         items.push_back (MenuElem (_("State"), *auto_state_menu));
537
538         /* mode menu */
539
540         if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
541
542                 Menu* auto_mode_menu = manage (new Menu);
543                 auto_mode_menu->set_name ("ArdourContextMenu");
544                 MenuList& am_items = auto_mode_menu->items();
545
546                 RadioMenuItem::Group group;
547
548                 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
549                                 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
550                                 AutomationList::Discrete)));
551                 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
552                 mode_discrete_item->set_active(_control->list()->interpolation() == AutomationList::Discrete);
553
554                 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
555                                 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
556                                 AutomationList::Linear)));
557                 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
558
559                 // Set default interpolation type to linear if this isn't a (usually) discrete controller
560                 if (EventTypeMap::instance().interpolation_of(_control->parameter()) == Evoral::ControlList::Linear) {
561                         mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
562                 }
563
564                 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
565         }
566
567         /* make sure the automation menu state is correct */
568
569         automation_state_changed ();
570         interpolation_changed ();
571 }
572
573 void
574 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, nframes_t when, double y)
575 {
576         if (!_line)
577                 return;
578
579         double x = 0;
580
581         _canvas_display->w2i (x, y);
582
583         /* compute vertical fractional position */
584
585         y = 1.0 - (y / height);
586
587         /* map using line */
588
589         _line->view_to_model_coord (x, y);
590
591         _session->begin_reversible_command (_("add automation event"));
592         XMLNode& before = _control->alist()->get_state();
593
594         _control->alist()->add (when, y);
595
596         XMLNode& after = _control->alist()->get_state();
597         _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
598
599         _session->set_dirty ();
600 }
601
602 bool
603 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
604 {
605         return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
606 }
607
608 bool
609 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
610 {
611         boost::shared_ptr<Evoral::ControlList> what_we_got;
612         boost::shared_ptr<AutomationList> alist (line.the_list());
613         bool ret = false;
614
615         XMLNode &before = alist->get_state();
616
617         switch (op) {
618         case Cut:
619                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
620                         _editor.get_cut_buffer().add (what_we_got);
621                         _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
622                         ret = true;
623                 }
624                 break;
625         case Copy:
626                 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
627                         _editor.get_cut_buffer().add (what_we_got);
628                 }
629                 break;
630
631         case Clear:
632                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
633                         _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
634                         ret = true;
635                 }
636                 break;
637         }
638
639         if (what_we_got) {
640                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
641                         double when = (*x)->when;
642                         double val  = (*x)->value;
643                         line.model_to_view_coord (when, val);
644                         (*x)->when = when;
645                         (*x)->value = val;
646                 }
647         }
648
649         return ret;
650 }
651
652 void
653 AutomationTimeAxisView::reset_objects (PointSelection& selection)
654 {
655         reset_objects_one (*_line, selection);
656 }
657
658 void
659 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
660 {
661         boost::shared_ptr<AutomationList> alist(line.the_list());
662
663         _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
664
665         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
666
667                 if ((*i).track != this) {
668                         continue;
669                 }
670
671                 alist->reset_range ((*i).start, (*i).end);
672         }
673 }
674
675 bool
676 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
677 {
678         return cut_copy_clear_objects_one (*_line, selection, op);
679 }
680
681 bool
682 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
683 {
684         boost::shared_ptr<Evoral::ControlList> what_we_got;
685         boost::shared_ptr<AutomationList> alist(line.the_list());
686         bool ret = false;
687
688         XMLNode &before = alist->get_state();
689
690         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
691
692                 if ((*i).track != this) {
693                         continue;
694                 }
695
696                 switch (op) {
697                 case Cut:
698                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
699                                 _editor.get_cut_buffer().add (what_we_got);
700                                 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
701                                 ret = true;
702                         }
703                         break;
704                 case Copy:
705                         if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
706                                 _editor.get_cut_buffer().add (what_we_got);
707                         }
708                         break;
709
710                 case Clear:
711                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
712                                 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
713                                 ret = true;
714                         }
715                         break;
716                 }
717         }
718
719         delete &before;
720
721         if (what_we_got) {
722                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
723                         double when = (*x)->when;
724                         double val  = (*x)->value;
725                         line.model_to_view_coord (when, val);
726                         (*x)->when = when;
727                         (*x)->value = val;
728                 }
729         }
730
731         return ret;
732 }
733
734 bool
735 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
736 {
737         return paste_one (*_line, pos, times, selection, nth);
738 }
739
740 bool
741 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
742 {
743         AutomationSelection::iterator p;
744         boost::shared_ptr<AutomationList> alist(line.the_list());
745
746         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
747
748         if (p == selection.lines.end()) {
749                 return false;
750         }
751
752         /* Make a copy of the list because we have to scale the
753            values from view coordinates to model coordinates, and we're
754            not supposed to modify the points in the selection.
755         */
756
757         AutomationList copy (**p);
758
759         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
760                 double when = (*x)->when;
761                 double val  = (*x)->value;
762                 line.view_to_model_coord (when, val);
763                 (*x)->when = when;
764                 (*x)->value = val;
765         }
766
767         XMLNode &before = alist->get_state();
768         alist->paste (copy, pos, times);
769         _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
770
771         return true;
772 }
773
774 void
775 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
776 {
777         if (_line && touched (top, bot)) {
778                 double topfrac;
779                 double botfrac;
780
781                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
782                    _y_position is the "origin" or "top" of the track.
783                 */
784
785                 double mybot = _y_position + height;
786
787                 if (_y_position >= top && mybot <= bot) {
788
789                         /* _y_position is below top, mybot is above bot, so we're fully
790                            covered vertically.
791                         */
792
793                         topfrac = 1.0;
794                         botfrac = 0.0;
795
796                 } else {
797
798                         /* top and bot are within _y_position .. mybot */
799
800                         topfrac = 1.0 - ((top - _y_position) / height);
801                         botfrac = 1.0 - ((bot - _y_position) / height);
802                 }
803
804                 if (_line)
805                         _line->get_selectables (start, end, botfrac, topfrac, results);
806         }
807 }
808
809 void
810 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
811 {
812         if (_line)
813                 _line->get_inverted_selectables (sel, result);
814 }
815
816 void
817 AutomationTimeAxisView::set_selected_points (PointSelection& points)
818 {
819         if (_line) {
820                 _line->set_selected_points (points);
821         }
822 }
823
824 void
825 AutomationTimeAxisView::clear_lines ()
826 {
827         _line.reset();
828         automation_connection.disconnect ();
829 }
830
831 void
832 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
833 {
834         assert(line);
835         assert(!_line);
836         assert(line->the_list() == _control->list());
837
838         _control->alist()->automation_state_changed.connect (automation_connection, boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context());
839
840         _line = line;
841         //_controller = AutomationController::create(_session, line->the_list(), _control);
842
843         line->set_height (height);
844
845         /* pick up the current state */
846         automation_state_changed ();
847
848         line->show();
849 }
850
851 void
852 AutomationTimeAxisView::entered()
853 {
854         if (_line)
855                 _line->track_entered();
856 }
857
858 void
859 AutomationTimeAxisView::exited ()
860 {
861         if (_line)
862                 _line->track_exited();
863 }
864
865 void
866 AutomationTimeAxisView::color_handler ()
867 {
868         if (_line) {
869                 _line->set_colors();
870         }
871 }
872
873 int
874 AutomationTimeAxisView::set_state (const XMLNode& node, int version)
875 {
876         TimeAxisView::set_state (node, version);
877
878         XMLProperty const * type = node.property ("automation-id");
879         if (type && type->value () == ARDOUR::EventTypeMap::instance().to_symbol (_control->parameter())) {
880                 XMLProperty const * shown = node.property ("shown");
881                 if (shown && shown->value () == "yes") {
882                         set_marked_for_display (true);
883                         _canvas_display->show (); /* FIXME: necessary? show_at? */
884                 }
885         }
886
887         if (!_marked_for_display) {
888                 hide();
889         }
890
891         return 0;
892 }
893
894 XMLNode*
895 AutomationTimeAxisView::get_state_node ()
896 {
897         TimeAxisView* state_parent = get_parent_with_state ();
898
899         if (state_parent) {
900                 return state_parent->get_automation_child_xml_node (_control->parameter());
901         } else {
902                 return 0;
903         }
904 }
905
906 void
907 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
908 {
909         XMLNode* xml_node = get_state_node();
910         if (xml_node) {
911                 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
912         }
913 }
914
915 guint32
916 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
917 {
918         update_extra_xml_shown (true);
919
920         return TimeAxisView::show_at (y, nth, parent);
921 }
922
923 void
924 AutomationTimeAxisView::hide ()
925 {
926         update_extra_xml_shown (false);
927
928         TimeAxisView::hide ();
929 }
930