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