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