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