92c35cf18579374d61e0f3b6c0d8e9d2bc26ce54
[ardour.git] / gtk2_ardour / automation_time_axis.cc
1 #include <ardour/route.h>
2
3 #include "ardour_ui.h"
4 #include "automation_time_axis.h"
5 #include "automation_line.h"
6 #include "public_editor.h"
7 #include "simplerect.h"
8 #include "selection.h"
9 #include "ghostregion.h"
10 #include "rgb_macros.h"
11 #include "automation_selectable.h"
12 #include "point_selection.h"
13 #include "canvas_impl.h"
14 #include "utils.h"
15
16 #include "i18n.h"
17
18 using namespace ARDOUR;
19 using namespace PBD;
20 using namespace Gtk;
21 using namespace Editing;
22
23 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, Route& r, PublicEditor& e, TimeAxisView& rent, 
24                                                 ArdourCanvas::Canvas& canvas, const string & nom, 
25                                                 const string & state_name, const string & nomparent)
26
27         : AxisView (s), 
28           TimeAxisView (s, e, &rent, canvas),
29           route (r),
30           _name (nom),
31           _state_name (state_name),
32           height_button (_("h")),
33           clear_button (_("clear")),
34           auto_button (X_("")) /* force addition of a label */
35 {
36         automation_menu = 0;
37         in_destructor = false;
38         auto_off_item = 0;
39         auto_touch_item = 0;
40         auto_write_item = 0;
41         auto_play_item = 0;
42         ignore_state_request = false;
43
44         //      base_rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP(canvas_display),
45         //                       gnome_canvas_simplerect_get_type(),
46         //                       "x1", 0.0,
47         //                       "y1", 0.0,
48         //                       "x2", 1000000.0,
49         //                       "outline_color_rgba", color_map[cAutomationTrackOutline],
50         //                       /* outline ends and bottom */
51         //                       "outline_what", (guint32) (0x1|0x2|0x8),
52         //                       "fill_color_rgba", color_map[cAutomationTrackFill],
53         //                       NULL);
54         base_rect = new SimpleRect(*canvas_display);
55         base_rect->property_x1() = 0.0;
56         base_rect->property_y1() = 0.0;
57         base_rect->property_x2() = 1000000.0;
58         base_rect->property_outline_color_rgba() = color_map[cAutomationTrackOutline];
59         /* outline ends and bottom */
60         base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
61         base_rect->property_fill_color_rgba() = color_map[cAutomationTrackFill];
62         
63         base_rect->set_data ("trackview", this);
64
65         base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
66                                                  base_rect, this));
67
68         hide_button.add (*(manage (new Gtk::Image (get_xpm("small_x.xpm")))));
69
70         height_button.set_name ("TrackSizeButton");
71         auto_button.set_name ("TrackVisualButton");
72         clear_button.set_name ("TrackVisualButton");
73         hide_button.set_name ("TrackRemoveButton");
74
75         ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
76         ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
77         ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
78         ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
79
80         /* rearrange the name display */
81
82         /* we never show these for automation tracks, so make
83            life easier and remove them.
84         */
85
86         hide_name_entry();
87
88         /* move the name label over a bit */
89
90         string shortpname = _name;
91         bool shortened = false;
92         
93         if (_name.length()) {
94                 if (shortpname.length() > 18) {
95                         shortpname = shortpname.substr (0, 16);
96                         shortpname += "...";
97                         shortened = true;
98                 }
99         }
100         name_label.set_text (shortpname);
101         name_label.set_alignment (1.0, 0.5);
102
103         if (nomparent.length()) {
104
105                 /* limit the plug name string */
106
107                 string pname = nomparent;
108
109                 if (pname.length() > 14) {
110                         pname = pname.substr (0, 11);
111                         pname += "...";
112                         shortened = true;
113                 }
114
115                 plugname = new Label (pname);
116                 plugname->set_name (X_("TrackPlugName"));
117                 plugname->set_alignment (1.0, 0.5);
118                 name_label.set_name (X_("TrackParameterName"));
119                 controls_table.remove (name_hbox);
120                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
121                 plugname_packed = true;
122                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
123         } else {
124                 plugname = 0;
125                 plugname_packed = false;
126         }
127
128         if (shortened) {
129                 string tipname = nomparent;
130                 if (!tipname.empty()) {
131                         tipname += ": ";
132                 }
133                 tipname += _name;
134                 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
135         }
136         
137         /* add the buttons */
138         controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
139         controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
140
141         controls_table.attach (auto_button, 6, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
142         controls_table.attach (clear_button, 6, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
143         
144         controls_table.show_all ();
145
146         height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
147         clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
148         hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
149         auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
150
151         controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
152         controls_base_unselected_name = X_("AutomationTrackControlsBase");
153         controls_ebox.set_name (controls_base_unselected_name);
154
155         controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
156
157         XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
158         set_state (*xml_node);
159
160         /* make sure labels etc. are correct */
161
162         automation_state_changed ();
163 }
164
165 AutomationTimeAxisView::~AutomationTimeAxisView ()
166 {
167         in_destructor = true;
168
169         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
170                 delete *i;
171         }
172 }
173
174 void
175 AutomationTimeAxisView::auto_clicked ()
176 {
177         using namespace Menu_Helpers;
178
179         if (automation_menu == 0) {
180                 automation_menu = manage (new Menu);
181                 automation_menu->set_name ("ArdourContextMenu");
182                 MenuList& items (automation_menu->items());
183
184                 items.push_back (MenuElem (_("Manual"), 
185                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
186                 items.push_back (MenuElem (_("Play"),
187                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
188                 items.push_back (MenuElem (_("Write"),
189                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
190                 items.push_back (MenuElem (_("Touch"),
191                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
192         }
193
194         automation_menu->popup (1, 0);
195 }
196
197
198 void
199 AutomationTimeAxisView::automation_state_changed ()
200 {
201         AutoState state;
202
203         /* update button label */
204
205         if (lines.empty()) {
206                 state = Off;
207         } else {
208                 state = lines.front()->the_list().automation_state ();
209         }
210
211         switch (state & (Off|Play|Touch|Write)) {
212         case Off:
213                 auto_button.set_label (_("Manual"));
214                 if (auto_off_item) {
215                         ignore_state_request = true;
216                         auto_off_item->set_active (true);
217                         auto_play_item->set_active (false);
218                         auto_touch_item->set_active (false);
219                         auto_write_item->set_active (false);
220                         ignore_state_request = false;
221                 }
222                 break;
223         case Play:
224                 auto_button.set_label (_("Play"));
225                 if (auto_play_item) {
226                         ignore_state_request = true;
227                         auto_play_item->set_active (true);
228                         auto_off_item->set_active (false);
229                         auto_touch_item->set_active (false);
230                         auto_write_item->set_active (false);
231                         ignore_state_request = false;
232                 }
233                 break;
234         case Write:
235                 auto_button.set_label (_("Write"));
236                 if (auto_write_item) {
237                         ignore_state_request = true;
238                         auto_write_item->set_active (true);
239                         auto_off_item->set_active (false);
240                         auto_play_item->set_active (false);
241                         auto_touch_item->set_active (false);
242                         ignore_state_request = false;
243                 }
244                 break;
245         case Touch:
246                 auto_button.set_label (_("Touch"));
247                 if (auto_touch_item) {
248                         ignore_state_request = true;
249                         auto_touch_item->set_active (true);
250                         auto_off_item->set_active (false);
251                         auto_play_item->set_active (false);
252                         auto_write_item->set_active (false);
253                         ignore_state_request = false;
254                 }
255                 break;
256         default:
257                 auto_button.set_label (_("???"));
258                 break;
259         }
260 }
261
262 void
263 AutomationTimeAxisView::height_clicked ()
264 {
265         popup_size_menu (0);
266 }
267
268 void
269 AutomationTimeAxisView::clear_clicked ()
270 {
271         _session.begin_reversible_command (_("clear automation"));
272         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
273                 (*i)->clear ();
274         }
275         _session.commit_reversible_command ();
276 }
277
278 void
279 AutomationTimeAxisView::set_height (TrackHeight ht)
280 {
281         uint32_t h = height_to_pixels (ht);
282         bool changed = (height != (uint32_t) h);
283
284         TimeAxisView* state_parent = get_parent_with_state ();
285         XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
286
287         controls_table.show_all ();
288
289         TimeAxisView::set_height (ht);
290         base_rect->property_y2() = h;
291
292         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
293                 (*i)->set_height (h);
294         }
295
296         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
297                 (*i)->set_height ();
298         }
299
300         switch (height) {
301         case Largest:
302                 xml_node->add_property ("track_height", "largest");
303                 controls_table.remove (name_hbox);
304                 if (plugname) {
305                         if (plugname_packed) {
306                                 controls_table.remove (*plugname);
307                                 plugname_packed = false;
308                         }
309                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
310                         plugname_packed = true;
311                         controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
312                 } else {
313                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
314                 }
315                 controls_table.show_all ();
316                 hide_name_entry ();
317                 show_name_label ();
318                 break;
319
320         case Large:
321                 xml_node->add_property ("track_height", "large");
322                 controls_table.remove (name_hbox);
323                 if (plugname) {
324                         if (plugname_packed) {
325                                 controls_table.remove (*plugname);
326                                 plugname_packed = false;
327                         }
328                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
329                         plugname_packed = true;
330                 } else {
331                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
332                 }
333                 controls_table.show_all ();
334                 hide_name_entry ();
335                 show_name_label ();
336                 break;
337
338         case Larger:
339                 xml_node->add_property ("track_height", "larger");
340                 controls_table.remove (name_hbox);
341                 if (plugname) {
342                         if (plugname_packed) {
343                                 controls_table.remove (*plugname);
344                                 plugname_packed = false;
345                         }
346                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
347                         plugname_packed = true;
348                 } else {
349                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
350                 }
351                 controls_table.show_all ();
352                 hide_name_entry ();
353                 show_name_label ();
354                 break;
355
356         case Normal:
357                 xml_node->add_property ("track_height", "normal");
358                 controls_table.remove (name_hbox);
359                 if (plugname) {
360                         if (plugname_packed) {
361                                 controls_table.remove (*plugname);
362                                 plugname_packed = false;
363                         }
364                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
365                         plugname_packed = true;
366                         controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
367                 } else {
368                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
369                 }
370                 controls_table.show_all ();
371                 hide_name_entry ();
372                 show_name_label ();
373                 break;
374
375         case Smaller:
376                 xml_node->add_property ("track_height", "smaller");
377                 controls_table.remove (name_hbox);
378                 if (plugname) {
379                         if (plugname_packed) {
380                                 controls_table.remove (*plugname);
381                                 plugname_packed = false;
382                         }
383                 }
384                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
385                 controls_table.hide_all ();
386                 hide_name_entry ();
387                 show_name_label ();
388                 name_hbox.show_all ();
389                 controls_table.show ();
390                 break;
391
392         case Small:
393                 xml_node->add_property ("track_height", "small");
394                 controls_table.remove (name_hbox);
395                 if (plugname) {
396                         if (plugname_packed) {
397                                 controls_table.remove (*plugname);
398                                 plugname_packed = false;
399                         }
400                 } 
401                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
402                 controls_table.hide_all ();
403                 hide_name_entry ();
404                 show_name_label ();
405                 name_hbox.show_all ();
406                 controls_table.show ();
407                 break;
408         }
409
410         if (changed) {
411                 /* only emit the signal if the height really changed */
412                  route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
413         }
414 }
415
416 void
417 AutomationTimeAxisView::set_samples_per_unit (double spu)
418 {
419         TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
420
421         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
422                 (*i)->reset ();
423         }
424 }
425  
426 void
427 AutomationTimeAxisView::hide_clicked ()
428 {
429         set_marked_for_display (false);
430         hide ();
431 }
432
433 void
434 AutomationTimeAxisView::build_display_menu ()
435 {
436         using namespace Menu_Helpers;
437
438         /* get the size menu ready */
439
440         build_size_menu ();
441
442         /* prepare it */
443
444         TimeAxisView::build_display_menu ();
445
446         /* now fill it with our stuff */
447
448         MenuList& items = display_menu->items();
449
450         items.push_back (MenuElem (_("Height"), *size_menu));
451         items.push_back (SeparatorElem());
452         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
453         items.push_back (SeparatorElem());
454         items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
455         items.push_back (SeparatorElem());
456
457         Menu* auto_state_menu = manage (new Menu);
458         auto_state_menu->set_name ("ArdourContextMenu");
459         MenuList& as_items = auto_state_menu->items();
460         
461         as_items.push_back (CheckMenuElem (_("Manual"), 
462                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
463         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
464
465         as_items.push_back (CheckMenuElem (_("Play"),
466                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
467         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
468
469         as_items.push_back (CheckMenuElem (_("Write"),
470                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
471         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
472
473         as_items.push_back (CheckMenuElem (_("Touch"),
474                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
475         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
476
477         items.push_back (MenuElem (_("State"), *auto_state_menu));
478
479         /* make sure the automation menu state is correct */
480
481         automation_state_changed ();
482 }
483
484 bool
485 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
486 {
487         bool ret = false;
488
489         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
490                 ret = cut_copy_clear_one ((**i), selection, op);
491         }
492
493         return ret;
494 }
495
496 bool
497 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
498 {
499         AutomationList* what_we_got = 0;
500         AutomationList& alist (line.the_list());
501         bool ret = false;
502
503         XMLNode &before, &after;
504         before = alist.get_state();
505
506         switch (op) {
507         case Cut:
508                 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
509                         editor.get_cut_buffer().add (what_we_got);
510                         _session.add_command(MementoCommand<AutomationList>(alist, before, alist.get_state()));
511                         ret = true;
512                 }
513                 break;
514         case Copy:
515                 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
516                         editor.get_cut_buffer().add (what_we_got);
517                 }
518                 break;
519
520         case Clear:
521                 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
522                         _session.add_command(MementoCommand<AutomationList>(alist, before, alist.get_state()));
523                         delete what_we_got;
524                         what_we_got = 0;
525                         ret = true;
526                 }
527                 break;
528         }
529
530         if (what_we_got) {
531                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
532                         double foo = (*x)->value;
533                         line.model_to_view_y (foo);
534                         (*x)->value = foo;
535                 }
536         }
537
538         return ret;
539 }
540
541 void
542 AutomationTimeAxisView::reset_objects (PointSelection& selection)
543 {
544         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
545                 reset_objects_one ((**i), selection);
546         }
547 }
548
549 void
550 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
551 {
552         AutomationList& alist (line.the_list());
553
554         _session.add_command (MementoUndoCommand<AutomationList>(alist, alist.get_state()));
555
556         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
557
558                 if (&(*i).track != this) {
559                         continue;
560                 }
561                 
562                 alist.reset_range ((*i).start, (*i).end);
563         }
564 }
565
566 bool
567 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
568 {
569         bool ret = false;
570
571         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
572                 ret = cut_copy_clear_objects_one ((**i), selection, op);
573         }
574
575         return ret;
576 }
577
578 bool
579 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
580 {
581         AutomationList* what_we_got = 0;
582         AutomationList& alist (line.the_list());
583         bool ret = false;
584         XMLNode &before, &after;
585
586         before = alist.get_state();
587
588         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
589
590                 if (&(*i).track != this) {
591                         continue;
592                 }
593
594                 switch (op) {
595                 case Cut:
596                         if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
597                                 editor.get_cut_buffer().add (what_we_got);
598                                 _session.add_command (MementoCommand<AutomationList>(alist, before, alist.get_state()));
599                                 ret = true;
600                         }
601                         break;
602                 case Copy:
603                         if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
604                                 editor.get_cut_buffer().add (what_we_got);
605                         }
606                         break;
607                         
608                 case Clear:
609                         if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
610                                 _session.add_command (MementoCommand<AutomationList>(alist, before, alist.get_state()));
611                                 delete what_we_got;
612                                 what_we_got = 0;
613                                 ret = true;
614                         }
615                         break;
616                 }
617         }
618                 
619         if (what_we_got) {
620                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
621                         double foo = (*x)->value;
622                         line.model_to_view_y (foo);
623                         (*x)->value = foo;
624                 }
625         }
626
627         return ret;
628 }
629
630 bool
631 AutomationTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
632 {
633         bool ret = true;
634
635         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
636                 ret = paste_one (**i, pos, times, selection, nth);
637         }
638
639         return ret;
640 }
641
642 bool
643 AutomationTimeAxisView::paste_one (AutomationLine& line, jack_nframes_t pos, float times, Selection& selection, size_t nth)
644 {
645         AutomationSelection::iterator p;
646         AutomationList& alist (line.the_list());
647         
648         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
649
650         if (p == selection.lines.end()) {
651                 return false;
652         }
653
654         /* Make a copy of the list because we have to scale the
655            values from view coordinates to model coordinates, and we're
656            not supposed to modify the points in the selection.
657         */
658            
659         AutomationList copy (**p);
660
661         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
662                 double foo = (*x)->value;
663                 line.view_to_model_y (foo);
664                 (*x)->value = foo;
665         }
666
667         XMLNode &before = alist.get_state();
668         alist.paste (copy, pos, times);
669         _session.add_command (MementoCommand<AutomationList>(alist, before, alist.get_state()));
670
671         return true;
672 }
673
674 void
675 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
676 {
677         ghosts.push_back (gr);
678         gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
679 }
680
681 void
682 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
683 {
684         if (in_destructor) {
685                 return;
686         }
687
688         list<GhostRegion*>::iterator i;
689
690         for (i = ghosts.begin(); i != ghosts.end(); ++i) {
691                 if ((*i) == gr) {
692                         ghosts.erase (i);
693                         break;
694                 }
695         }
696 }
697
698 void
699 AutomationTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
700 {
701         if (!lines.empty() && touched (top, bot)) {
702                 double topfrac;
703                 double botfrac;
704
705                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
706                    y_position is the "origin" or "top" of the track.
707                 */
708
709                 double mybot = y_position + height; // XXX need to include Editor::track_spacing; 
710
711                 if (y_position >= top && mybot <= bot) {
712
713                         /* y_position is below top, mybot is above bot, so we're fully
714                            covered vertically.
715                         */
716
717                         topfrac = 1.0;
718                         botfrac = 0.0;
719
720                 } else {
721
722                         /* top and bot are within y_position .. mybot */
723
724                         topfrac = 1.0 - ((top - y_position) / height);
725                         botfrac = 1.0 - ((bot - y_position) / height);
726                 }
727
728                 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
729                         (*i)->get_selectables (start, end, botfrac, topfrac, results);
730                 }
731         }
732 }
733
734 void
735 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
736 {
737         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
738                 (*i)->get_inverted_selectables (sel, result);
739         }
740 }
741
742 void
743 AutomationTimeAxisView::set_selected_points (PointSelection& points)
744 {
745         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
746                 (*i)->set_selected_points (points);
747         }
748 }
749
750 void
751 AutomationTimeAxisView::clear_lines ()
752 {
753         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
754                 delete *i;
755         }
756
757         lines.clear ();
758         automation_connection.disconnect ();
759 }
760
761 void
762 AutomationTimeAxisView::add_line (AutomationLine& line)
763 {
764         bool get = false;
765
766         if (lines.empty()) {
767                 /* first line is the Model for automation state */
768                 automation_connection = line.the_list().automation_state_changed.connect
769                         (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
770                 get = true;
771         }
772
773         lines.push_back (&line);
774         line.set_height (height);
775
776         if (get) {
777                 /* pick up the current state */
778                 automation_state_changed ();
779         }
780 }
781
782 void
783 AutomationTimeAxisView::show_all_control_points ()
784 {
785         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
786                 (*i)->show_all_control_points ();
787         }
788 }
789
790 void
791 AutomationTimeAxisView::hide_all_but_selected_control_points ()
792 {
793         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
794                 (*i)->hide_all_but_selected_control_points ();
795         }
796 }
797
798 void
799 AutomationTimeAxisView::entered()
800 {
801         show_all_control_points ();
802 }
803
804 void
805 AutomationTimeAxisView::exited ()
806 {
807         hide_all_but_selected_control_points ();
808 }
809
810 void
811 AutomationTimeAxisView::set_state (const XMLNode& node)
812 {
813         TimeAxisView::set_state (node);
814 }
815
816 XMLNode*
817 AutomationTimeAxisView::get_state_node ()
818 {
819         TimeAxisView* state_parent = get_parent_with_state ();
820
821         if (state_parent) {
822                 return state_parent->get_child_xml_node (_state_name);
823         } else {
824                 return 0;
825         }
826 }