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