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