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