* MIDI control lanes: Set Interpolationtype according to Parameter
[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 <ardour/route.h>
22 #include <ardour/automation_control.h>
23 #include <pbd/memento_command.h>
24 #include <gtkmm2ext/barcontroller.h>
25
26 #include "ardour_ui.h"
27 #include "automation_time_axis.h"
28 #include "automation_streamview.h"
29 #include "route_time_axis.h"
30 #include "automation_line.h"
31 #include "public_editor.h"
32 #include "simplerect.h"
33 #include "selection.h"
34 #include "rgb_macros.h"
35 #include "automation_selectable.h"
36 #include "point_selection.h"
37 #include "canvas_impl.h"
38 #include "utils.h"
39
40 #include "i18n.h"
41
42 using namespace ARDOUR;
43 using namespace PBD;
44 using namespace Gtk;
45 using namespace Gtkmm2ext;
46 using namespace Editing;
47
48 Pango::FontDescription* AutomationTimeAxisView::name_font = 0;
49 bool AutomationTimeAxisView::have_name_font = false;
50 const string AutomationTimeAxisView::state_node_name = "AutomationChild";
51
52
53 /** \a a the automatable object this time axis is to display data for.
54  * For route/track automation (e.g. gain) pass the route for both \r and \a.
55  * For route child (e.g. plugin) automation, pass the child for \a.
56  * For region automation (e.g. MIDI CC), pass null for \a.
57  */
58 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Route> r,
59                 boost::shared_ptr<Automatable> a, boost::shared_ptr<AutomationControl> c,
60                 PublicEditor& e, TimeAxisView& parent, bool show_regions,
61                 ArdourCanvas::Canvas& canvas, const string & nom, const string & nomparent)
62         : AxisView (s), 
63           TimeAxisView (s, e, &parent, canvas),
64           _route (r),
65           _control (c),
66           _automatable (a),
67           _controller(AutomationController::create(a, c->parameter(), c)),
68           _base_rect (0),
69           _view (show_regions ? new AutomationStreamView(*this) : NULL),
70           _name (nom),
71           clear_button (_("clear")),
72           auto_button (X_("")) /* force addition of a label */
73 {
74         if (!have_name_font) {
75                 name_font = get_font_for_style (X_("AutomationTrackName"));
76                 have_name_font = true;
77         }
78
79         automation_menu = 0;
80         auto_off_item = 0;
81         auto_touch_item = 0;
82         auto_write_item = 0;
83         auto_play_item = 0;
84         mode_discrete_item = 0;
85         mode_line_item = 0;
86
87         ignore_state_request = false;
88         first_call_to_set_height = true;
89         
90         _base_rect = new SimpleRect(*_canvas_display);
91         _base_rect->property_x1() = 0.0;
92         _base_rect->property_y1() = 0.0;
93         _base_rect->property_x2() = LONG_MAX - 2;
94         _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
95         
96         /* outline ends and bottom */
97         _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
98         _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
99         
100         _base_rect->set_data ("trackview", this);
101
102         _base_rect->signal_event().connect (bind (mem_fun (_editor, &PublicEditor::canvas_automation_track_event),
103                                                  _base_rect, this));
104
105         _base_rect->lower_to_bottom();
106
107         hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
108
109         auto_button.set_name ("TrackVisualButton");
110         clear_button.set_name ("TrackVisualButton");
111         hide_button.set_name ("TrackRemoveButton");
112
113         auto_button.unset_flags (Gtk::CAN_FOCUS);
114         clear_button.unset_flags (Gtk::CAN_FOCUS);
115         hide_button.unset_flags (Gtk::CAN_FOCUS);
116
117         controls_table.set_no_show_all();
118
119         ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
120         ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
121         ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
122
123         /* rearrange the name display */
124
125         /* we never show these for automation tracks, so make
126            life easier and remove them.
127         */
128
129         hide_name_entry();
130
131         /* move the name label over a bit */
132
133         string shortpname = _name;
134         bool shortened = false;
135
136         int ignore_width;
137         shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
138
139         if (shortpname != _name ){
140                 shortened = true;
141         }
142
143         name_label.set_text (shortpname);
144         name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
145
146         if (nomparent.length()) {
147
148                 /* limit the plug name string */
149
150                 string pname = fit_to_pixels (nomparent, 60, *name_font, ignore_width, true);
151                 if (pname != nomparent) {
152                         shortened = true;
153                 }
154
155                 plugname = new Label (pname);
156                 plugname->set_name (X_("TrackPlugName"));
157                 plugname->show();
158                 name_label.set_name (X_("TrackParameterName"));
159                 controls_table.remove (name_hbox);
160                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
161                 plugname_packed = true;
162                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
163         } else {
164                 plugname = 0;
165                 plugname_packed = false;
166         }
167
168         if (shortened) {
169                 string tipname = nomparent;
170                 if (!tipname.empty()) {
171                         tipname += ": ";
172                 }
173                 tipname += _name;
174                 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
175         }
176         
177         /* add the buttons */
178         controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
179
180         controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
181         controls_table.attach (clear_button, 5, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
182         
183         /* add bar controller */
184         controls_table.attach (*_controller.get(), 0, 8, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
185
186         controls_table.show_all ();
187
188         clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
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         controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
197
198         XMLNode* xml_node = get_parent_with_state()->get_automation_child_xml_node (
199                         _control->parameter());
200
201         if (xml_node) {
202                 set_state (*xml_node);
203         } 
204                 
205         /* ask for notifications of any new RegionViews */
206         if (show_regions) {
207
208                 assert(_view);
209                 _view->attach ();
210         
211         /* no regions, just a single line for the entire track (e.g. bus gain) */
212         } else {
213         
214                 boost::shared_ptr<AutomationLine> line(new AutomationLine (
215                                         ARDOUR::EventTypeMap::instance().to_symbol(_control->parameter()),
216                                         *this,
217                                         *_canvas_display,
218                                         _control->alist()));
219
220                 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
221                 line->queue_reset ();
222                 add_line (line);
223         }
224
225         /* make sure labels etc. are correct */
226
227         automation_state_changed ();
228         ColorsChanged.connect (mem_fun (*this, &AutomationTimeAxisView::color_handler));
229 }
230
231 AutomationTimeAxisView::~AutomationTimeAxisView ()
232 {
233 }
234
235 void
236 AutomationTimeAxisView::auto_clicked ()
237 {
238         using namespace Menu_Helpers;
239
240         if (automation_menu == 0) {
241                 automation_menu = manage (new Menu);
242                 automation_menu->set_name ("ArdourContextMenu");
243                 MenuList& items (automation_menu->items());
244
245                 items.push_back (MenuElem (_("Manual"), bind (mem_fun(*this,
246                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
247                 items.push_back (MenuElem (_("Play"), bind (mem_fun(*this,
248                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
249                 items.push_back (MenuElem (_("Write"), bind (mem_fun(*this,
250                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
251                 items.push_back (MenuElem (_("Touch"), bind (mem_fun(*this,
252                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
253         }
254
255         automation_menu->popup (1, gtk_get_current_event_time());
256 }
257
258 void
259 AutomationTimeAxisView::set_automation_state (AutoState state)
260 {
261         if (!ignore_state_request) {
262                 if (_route == _automatable) { // This is a time axis for route (not region) automation
263                         _route->set_parameter_automation_state (_control->parameter(), state);
264                 }
265
266                 if (_control->list())
267                         _control->alist()->set_automation_state(state);
268         }
269         if (_view)
270                 _view->set_automation_state (state);
271 }
272
273 void
274 AutomationTimeAxisView::automation_state_changed ()
275 {
276         AutoState state;
277
278         /* update button label */
279
280         if (!_line) {
281                 state = Off;
282         } else {
283                 state = _control->alist()->automation_state ();
284         }
285         
286         switch (state & (Off|Play|Touch|Write)) {
287         case Off:
288                 auto_button.set_label (_("Manual"));
289                 if (auto_off_item) {
290                         ignore_state_request = true;
291                         auto_off_item->set_active (true);
292                         auto_play_item->set_active (false);
293                         auto_touch_item->set_active (false);
294                         auto_write_item->set_active (false);
295                         ignore_state_request = false;
296                 }
297                 break;
298         case Play:
299                 auto_button.set_label (_("Play"));
300                 if (auto_play_item) {
301                         ignore_state_request = true;
302                         auto_play_item->set_active (true);
303                         auto_off_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 Write:
310                 auto_button.set_label (_("Write"));
311                 if (auto_write_item) {
312                         ignore_state_request = true;
313                         auto_write_item->set_active (true);
314                         auto_off_item->set_active (false);
315                         auto_play_item->set_active (false);
316                         auto_touch_item->set_active (false);
317                         ignore_state_request = false;
318                 }
319                 break;
320         case Touch:
321                 auto_button.set_label (_("Touch"));
322                 if (auto_touch_item) {
323                         ignore_state_request = true;
324                         auto_touch_item->set_active (true);
325                         auto_off_item->set_active (false);
326                         auto_play_item->set_active (false);
327                         auto_write_item->set_active (false);
328                         ignore_state_request = false;
329                 }
330                 break;
331         default:
332                 auto_button.set_label (_("???"));
333                 break;
334         }
335 }
336
337 void
338 AutomationTimeAxisView::interpolation_changed ()
339 {       
340         AutomationList::InterpolationStyle style = _control->list()->interpolation();
341         
342         if (mode_line_item && mode_discrete_item) {
343                 if (style == AutomationList::Discrete) {
344                         mode_discrete_item->set_active(true);
345                         mode_line_item->set_active(false);
346                 } else {
347                         mode_line_item->set_active(true);
348                         mode_discrete_item->set_active(false);
349                 }
350         }
351         
352         if (_line) {
353                 _line->set_interpolation(style);
354         }
355 }
356
357 void
358 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
359 {
360         _control->list()->set_interpolation(style);
361         if (_line) {
362                 _line->set_interpolation(style);
363         }
364 }
365
366 void
367 AutomationTimeAxisView::clear_clicked ()
368 {
369         _session.begin_reversible_command (_("clear automation"));
370         if (_line) {
371                 _line->clear ();
372         }
373         _session.commit_reversible_command ();
374 }
375
376 void
377 AutomationTimeAxisView::set_height (uint32_t h)
378 {
379         bool changed = (height != (uint32_t) h) || first_call_to_set_height;
380         bool changed_between_small_and_normal = ( 
381                 (height < hNormal && h >= hNormal) || 
382                 (height >= hNormal || 
383                 h < hNormal)
384         );
385
386         TimeAxisView* state_parent = get_parent_with_state ();
387         assert(state_parent);
388         XMLNode* xml_node = state_parent->get_automation_child_xml_node (_control->parameter());
389
390         TimeAxisView::set_height (h);
391         _base_rect->property_y2() = h;
392         
393         if (_line)
394                 _line->set_height(h);
395         
396         if (_view) {
397                 _view->set_height(h);
398                 _view->update_contents_height();
399         }
400
401         char buf[32];
402         snprintf (buf, sizeof (buf), "%u", height);
403         if (xml_node) {
404                 xml_node->add_property ("height", buf);
405         }
406
407         if (changed_between_small_and_normal || first_call_to_set_height) {
408
409                 first_call_to_set_height = false;
410
411                 if (h >= hNormal) {
412                         controls_table.remove (name_hbox);
413                         
414                         if (plugname) {
415                                 if (plugname_packed) {
416                                         controls_table.remove (*plugname);
417                                         plugname_packed = false;
418                                 }
419                                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
420                                 plugname_packed = true;
421                                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
422                         } else {
423                                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
424                         }
425                         hide_name_entry ();
426                         show_name_label ();
427                         name_hbox.show_all ();
428                         
429                         auto_button.show();
430                         clear_button.show();
431                         hide_button.show_all();
432
433                 } else if (h >= hSmall) {
434                         controls_table.remove (name_hbox);
435                         if (plugname) {
436                                 if (plugname_packed) {
437                                         controls_table.remove (*plugname);
438                                         plugname_packed = false;
439                                 }
440                         }
441                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
442                         controls_table.hide_all ();
443                         hide_name_entry ();
444                         show_name_label ();
445                         name_hbox.show_all ();
446                         
447                         auto_button.hide();
448                         clear_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"), 
516                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
517         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
518
519         as_items.push_back (CheckMenuElem (_("Play"),
520                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
521         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
522
523         as_items.push_back (CheckMenuElem (_("Write"),
524                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
525         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
526
527         as_items.push_back (CheckMenuElem (_("Touch"),
528                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
529         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
530
531         items.push_back (MenuElem (_("State"), *auto_state_menu));
532         
533         /* mode menu */
534
535         // To be deleted: MIDI events should always be discrete, so no choice for the user here.
536         /*
537         if ( EventTypeMap::instance().is_midi_parameter(_control->parameter()) ) {
538                 
539                 Menu* auto_mode_menu = manage (new Menu);
540                 auto_mode_menu->set_name ("ArdourContextMenu");
541                 MenuList& am_items = auto_mode_menu->items();
542         
543                 RadioMenuItem::Group group;
544
545                 am_items.push_back (RadioMenuElem (group, _("Discrete"), bind (
546                                                 mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
547                                                 AutomationList::Discrete)));
548                 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
549                 mode_discrete_item->set_active(_control->list()->interpolation() == AutomationList::Discrete);
550
551                 am_items.push_back (RadioMenuElem (group, _("Line"), bind (
552                                                 mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
553                                                 AutomationList::Linear)));
554                 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
555                 mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
556
557                 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
558         }
559         */
560
561         /* make sure the automation menu state is correct */
562
563         automation_state_changed ();
564         interpolation_changed ();
565 }
566
567 void
568 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkEvent* event, nframes_t when, double y)
569 {
570         if (!_line)
571                 return;
572
573         double x = 0;
574
575         _canvas_display->w2i (x, y);
576
577         /* compute vertical fractional position */
578
579         y = 1.0 - (y / height);
580
581         /* map using line */
582
583         _line->view_to_model_y (y);
584
585         _session.begin_reversible_command (_("add automation event"));
586         XMLNode& before = _control->alist()->get_state();
587
588         _control->alist()->add (when, y);
589
590         XMLNode& after = _control->alist()->get_state();
591         _session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
592
593         _session.set_dirty ();
594 }
595
596
597 bool
598 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
599 {
600         return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
601 }
602
603 bool
604 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
605 {
606         boost::shared_ptr<Evoral::ControlList> what_we_got;
607         boost::shared_ptr<AutomationList> alist (line.the_list());
608         bool ret = false;
609
610         XMLNode &before = alist->get_state();
611
612         switch (op) {
613         case Cut:
614                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
615                         _editor.get_cut_buffer().add (what_we_got);
616                         _session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
617                         ret = true;
618                 }
619                 break;
620         case Copy:
621                 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
622                         _editor.get_cut_buffer().add (what_we_got);
623                 }
624                 break;
625
626         case Clear:
627                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
628                         _session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
629                         ret = true;
630                 }
631                 break;
632         }
633
634         if (what_we_got) {
635                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
636                         double foo = (*x)->value;
637                         line.model_to_view_y (foo);
638                         (*x)->value = foo;
639                 }
640         }
641
642         return ret;
643 }
644
645 void
646 AutomationTimeAxisView::reset_objects (PointSelection& selection)
647 {
648         reset_objects_one (*_line, selection);
649 }
650
651 void
652 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
653 {
654         boost::shared_ptr<AutomationList> alist(line.the_list());
655
656         _session.add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
657
658         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
659
660                 if (&(*i).track != this) {
661                         continue;
662                 }
663                 
664                 alist->reset_range ((*i).start, (*i).end);
665         }
666 }
667
668 bool
669 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
670 {
671         return cut_copy_clear_objects_one (*_line, selection, op);
672 }
673
674 bool
675 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
676 {
677         boost::shared_ptr<Evoral::ControlList> what_we_got;
678         boost::shared_ptr<AutomationList> alist(line.the_list());
679         bool ret = false;
680
681         XMLNode &before = alist->get_state();
682
683         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
684
685                 if (&(*i).track != this) {
686                         continue;
687                 }
688
689                 switch (op) {
690                 case Cut:
691                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
692                                 _editor.get_cut_buffer().add (what_we_got);
693                                 _session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
694                                 ret = true;
695                         }
696                         break;
697                 case Copy:
698                         if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
699                                 _editor.get_cut_buffer().add (what_we_got);
700                         }
701                         break;
702                         
703                 case Clear:
704                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
705                                 _session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
706                                 ret = true;
707                         }
708                         break;
709                 }
710         }
711
712         delete &before;
713
714         if (what_we_got) {
715                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
716                         double foo = (*x)->value;
717                         line.model_to_view_y (foo);
718                         (*x)->value = foo;
719                 }
720         }
721
722         return ret;
723 }
724
725 bool
726 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
727 {
728         return paste_one (*_line, pos, times, selection, nth);
729 }
730
731 bool
732 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
733 {
734         AutomationSelection::iterator p;
735         boost::shared_ptr<AutomationList> alist(line.the_list());
736         
737         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
738
739         if (p == selection.lines.end()) {
740                 return false;
741         }
742
743         /* Make a copy of the list because we have to scale the
744            values from view coordinates to model coordinates, and we're
745            not supposed to modify the points in the selection.
746         */
747            
748         AutomationList copy (**p);
749
750         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
751                 double foo = (*x)->value;
752                 line.view_to_model_y (foo);
753                 (*x)->value = foo;
754         }
755
756         XMLNode &before = alist->get_state();
757         alist->paste (copy, pos, times);
758         _session.add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
759
760         return true;
761 }
762
763 void
764 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
765 {
766         if (_line && touched (top, bot)) {
767                 double topfrac;
768                 double botfrac;
769
770                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
771                    _y_position is the "origin" or "top" of the track.
772                 */
773
774                 double mybot = _y_position + height;
775
776                 if (_y_position >= top && mybot <= bot) {
777
778                         /* _y_position is below top, mybot is above bot, so we're fully
779                            covered vertically.
780                         */
781
782                         topfrac = 1.0;
783                         botfrac = 0.0;
784
785                 } else {
786
787                         /* top and bot are within _y_position .. mybot */
788
789                         topfrac = 1.0 - ((top - _y_position) / height);
790                         botfrac = 1.0 - ((bot - _y_position) / height);
791                 }
792
793                 if (_line)
794                         _line->get_selectables (start, end, botfrac, topfrac, results);
795         }
796 }
797
798 void
799 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
800 {
801         if (_line)
802                 _line->get_inverted_selectables (sel, result);
803 }
804
805 void
806 AutomationTimeAxisView::set_selected_points (PointSelection& points)
807 {
808         if (_line)
809                 _line->set_selected_points (points);
810 }
811
812 void
813 AutomationTimeAxisView::clear_lines ()
814 {
815         _line.reset();
816         automation_connection.disconnect ();
817 }
818
819 void
820 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
821 {
822         assert(line);
823         assert(!_line);
824         assert(line->the_list() == _control->list());
825
826         automation_connection = _control->alist()->automation_state_changed.connect
827                 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
828
829         _line = line;
830         //_controller = AutomationController::create(_session, line->the_list(), _control);
831
832         line->set_height (height);
833
834         /* pick up the current state */
835         automation_state_changed ();
836
837         line->show();
838 }
839
840 void
841 AutomationTimeAxisView::entered()
842 {
843         if (_line)
844                 _line->track_entered();
845 }
846
847 void
848 AutomationTimeAxisView::exited ()
849 {
850         if (_line)
851                 _line->track_exited();
852 }
853
854 /*void
855 AutomationTimeAxisView::set_colors ()
856 {
857     for (list<GhostRegion*>::iterator i=ghosts.begin(); i != ghosts.end(); i++ ) {
858                 (*i)->set_colors();
859     }
860     
861         if (_line)
862                 _line->set_colors();
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)
875 {
876         TimeAxisView::set_state (node);
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