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