02a88b578bb56d7c11c0e6542ee78fdd772b9d8b
[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, boost::shared_ptr<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 (ht) {
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
317                 hide_name_entry ();
318                 show_name_label ();
319                 break;
320
321         case Large:
322                 xml_node->add_property ("track_height", "large");
323                 controls_table.remove (name_hbox);
324                 if (plugname) {
325                         if (plugname_packed) {
326                                 controls_table.remove (*plugname);
327                                 plugname_packed = false;
328                         }
329                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
330                         plugname_packed = true;
331                 } else {
332                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
333                 }
334                 controls_table.show_all ();
335                 hide_name_entry ();
336                 show_name_label ();
337                 break;
338
339         case Larger:
340                 xml_node->add_property ("track_height", "larger");
341                 controls_table.remove (name_hbox);
342                 if (plugname) {
343                         if (plugname_packed) {
344                                 controls_table.remove (*plugname);
345                                 plugname_packed = false;
346                         }
347                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
348                         plugname_packed = true;
349                 } else {
350                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
351                 }
352                 controls_table.show_all ();
353                 hide_name_entry ();
354                 show_name_label ();
355                 break;
356
357         case Normal:
358                 xml_node->add_property ("track_height", "normal");
359                 controls_table.remove (name_hbox);
360                 if (plugname) {
361                         if (plugname_packed) {
362                                 controls_table.remove (*plugname);
363                                 plugname_packed = false;
364                         }
365                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
366                         plugname_packed = true;
367                         controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
368                 } else {
369                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
370                 }
371                 controls_table.show_all ();
372                 hide_name_entry ();
373                 show_name_label ();
374                 break;
375
376         case Smaller:
377                 xml_node->add_property ("track_height", "smaller");
378                 controls_table.remove (name_hbox);
379                 if (plugname) {
380                         if (plugname_packed) {
381                                 controls_table.remove (*plugname);
382                                 plugname_packed = false;
383                         }
384                 }
385                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
386                 controls_table.hide_all ();
387                 hide_name_entry ();
388                 show_name_label ();
389                 name_hbox.show_all ();
390                 controls_table.show ();
391                 break;
392
393         case Small:
394                 xml_node->add_property ("track_height", "small");
395                 controls_table.remove (name_hbox);
396                 if (plugname) {
397                         if (plugname_packed) {
398                                 controls_table.remove (*plugname);
399                                 plugname_packed = false;
400                         }
401                 } 
402                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
403                 controls_table.hide_all ();
404                 hide_name_entry ();
405                 show_name_label ();
406                 name_hbox.show_all ();
407                 controls_table.show ();
408                 break;
409         }
410
411         if (changed) {
412                 /* only emit the signal if the height really changed */
413                 route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
414         }
415 }
416
417 void
418 AutomationTimeAxisView::set_samples_per_unit (double spu)
419 {
420         TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
421
422         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
423                 (*i)->reset ();
424         }
425 }
426  
427 void
428 AutomationTimeAxisView::hide_clicked ()
429 {
430         set_marked_for_display (false);
431         hide ();
432 }
433
434 void
435 AutomationTimeAxisView::build_display_menu ()
436 {
437         using namespace Menu_Helpers;
438
439         /* get the size menu ready */
440
441         build_size_menu ();
442
443         /* prepare it */
444
445         TimeAxisView::build_display_menu ();
446
447         /* now fill it with our stuff */
448
449         MenuList& items = display_menu->items();
450
451         items.push_back (MenuElem (_("Height"), *size_menu));
452         items.push_back (SeparatorElem());
453         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
454         items.push_back (SeparatorElem());
455         items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
456         items.push_back (SeparatorElem());
457
458         Menu* auto_state_menu = manage (new Menu);
459         auto_state_menu->set_name ("ArdourContextMenu");
460         MenuList& as_items = auto_state_menu->items();
461         
462         as_items.push_back (CheckMenuElem (_("Manual"), 
463                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
464         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
465
466         as_items.push_back (CheckMenuElem (_("Play"),
467                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
468         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
469
470         as_items.push_back (CheckMenuElem (_("Write"),
471                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
472         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
473
474         as_items.push_back (CheckMenuElem (_("Touch"),
475                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
476         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
477
478         items.push_back (MenuElem (_("State"), *auto_state_menu));
479
480         /* make sure the automation menu state is correct */
481
482         automation_state_changed ();
483 }
484
485 bool
486 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
487 {
488         bool ret = false;
489
490         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
491                 ret = cut_copy_clear_one ((**i), selection, op);
492         }
493
494         return ret;
495 }
496
497 bool
498 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
499 {
500         AutomationList* what_we_got = 0;
501         AutomationList& alist (line.the_list());
502         bool ret = false;
503
504         _session.add_undo (alist.get_memento());
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_redo_no_execute (alist.get_memento());
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_redo_no_execute (alist.get_memento());
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_undo (alist.get_memento());
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
585         _session.add_undo (alist.get_memento());
586
587         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
588
589                 if (&(*i).track != this) {
590                         continue;
591                 }
592
593                 switch (op) {
594                 case Cut:
595                         if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
596                                 editor.get_cut_buffer().add (what_we_got);
597                                 _session.add_redo_no_execute (alist.get_memento());
598                                 ret = true;
599                         }
600                         break;
601                 case Copy:
602                         if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
603                                 editor.get_cut_buffer().add (what_we_got);
604                         }
605                         break;
606                         
607                 case Clear:
608                         if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
609                                 _session.add_redo_no_execute (alist.get_memento());
610                                 delete what_we_got;
611                                 what_we_got = 0;
612                                 ret = true;
613                         }
614                         break;
615                 }
616         }
617                 
618         if (what_we_got) {
619                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
620                         double foo = (*x)->value;
621                         line.model_to_view_y (foo);
622                         (*x)->value = foo;
623                 }
624         }
625
626         return ret;
627 }
628
629 bool
630 AutomationTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
631 {
632         bool ret = true;
633
634         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
635                 ret = paste_one (**i, pos, times, selection, nth);
636         }
637
638         return ret;
639 }
640
641 bool
642 AutomationTimeAxisView::paste_one (AutomationLine& line, jack_nframes_t pos, float times, Selection& selection, size_t nth)
643 {
644         AutomationSelection::iterator p;
645         AutomationList& alist (line.the_list());
646         
647         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
648
649         if (p == selection.lines.end()) {
650                 return false;
651         }
652
653         /* Make a copy of the list because we have to scale the
654            values from view coordinates to model coordinates, and we're
655            not supposed to modify the points in the selection.
656         */
657            
658         AutomationList copy (**p);
659
660         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
661                 double foo = (*x)->value;
662                 line.view_to_model_y (foo);
663                 (*x)->value = foo;
664         }
665
666         _session.add_undo (alist.get_memento());
667         alist.paste (copy, pos, times);
668         _session.add_redo_no_execute (alist.get_memento());
669
670         return true;
671 }
672
673 void
674 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
675 {
676         ghosts.push_back (gr);
677         gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
678 }
679
680 void
681 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
682 {
683         if (in_destructor) {
684                 return;
685         }
686
687         list<GhostRegion*>::iterator i;
688
689         for (i = ghosts.begin(); i != ghosts.end(); ++i) {
690                 if ((*i) == gr) {
691                         ghosts.erase (i);
692                         break;
693                 }
694         }
695 }
696
697 void
698 AutomationTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
699 {
700         if (!lines.empty() && touched (top, bot)) {
701                 double topfrac;
702                 double botfrac;
703
704                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
705                    y_position is the "origin" or "top" of the track.
706                 */
707
708                 double mybot = y_position + height; // XXX need to include Editor::track_spacing; 
709
710                 if (y_position >= top && mybot <= bot) {
711
712                         /* y_position is below top, mybot is above bot, so we're fully
713                            covered vertically.
714                         */
715
716                         topfrac = 1.0;
717                         botfrac = 0.0;
718
719                 } else {
720
721                         /* top and bot are within y_position .. mybot */
722
723                         topfrac = 1.0 - ((top - y_position) / height);
724                         botfrac = 1.0 - ((bot - y_position) / height);
725                 }
726
727                 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
728                         (*i)->get_selectables (start, end, botfrac, topfrac, results);
729                 }
730         }
731 }
732
733 void
734 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
735 {
736         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
737                 (*i)->get_inverted_selectables (sel, result);
738         }
739 }
740
741 void
742 AutomationTimeAxisView::set_selected_points (PointSelection& points)
743 {
744         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
745                 (*i)->set_selected_points (points);
746         }
747 }
748
749 void
750 AutomationTimeAxisView::clear_lines ()
751 {
752         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
753                 delete *i;
754         }
755
756         lines.clear ();
757         automation_connection.disconnect ();
758 }
759
760 void
761 AutomationTimeAxisView::add_line (AutomationLine& line)
762 {
763         bool get = false;
764
765         if (lines.empty()) {
766                 /* first line is the Model for automation state */
767                 automation_connection = line.the_list().automation_state_changed.connect
768                         (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
769                 get = true;
770         }
771
772         lines.push_back (&line);
773         line.set_height (height);
774
775         if (get) {
776                 /* pick up the current state */
777                 automation_state_changed ();
778         }
779 }
780
781 void
782 AutomationTimeAxisView::show_all_control_points ()
783 {
784         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
785                 (*i)->show_all_control_points ();
786         }
787 }
788
789 void
790 AutomationTimeAxisView::hide_all_but_selected_control_points ()
791 {
792         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
793                 (*i)->hide_all_but_selected_control_points ();
794         }
795 }
796
797 void
798 AutomationTimeAxisView::entered()
799 {
800         show_all_control_points ();
801 }
802
803 void
804 AutomationTimeAxisView::exited ()
805 {
806         hide_all_but_selected_control_points ();
807 }
808
809 void
810 AutomationTimeAxisView::set_state (const XMLNode& node)
811 {
812         TimeAxisView::set_state (node);
813 }
814
815 XMLNode*
816 AutomationTimeAxisView::get_state_node ()
817 {
818         TimeAxisView* state_parent = get_parent_with_state ();
819
820         if (state_parent) {
821                 return state_parent->get_child_xml_node (_state_name);
822         } else {
823                 return 0;
824         }
825 }