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