fix for crash when loading a session after running another, caused by not checking...
[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, &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, &before, &alist.get_state()));
589                                 delete what_we_got;
590                                 what_we_got = 0;
591                                 ret = true;
592                         }
593                         break;
594                 }
595         }
596                 
597         if (what_we_got) {
598                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
599                         double foo = (*x)->value;
600                         line.model_to_view_y (foo);
601                         (*x)->value = foo;
602                 }
603         }
604
605         return ret;
606 }
607
608 bool
609 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
610 {
611         bool ret = true;
612
613         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
614                 ret = paste_one (**i, pos, times, selection, nth);
615         }
616
617         return ret;
618 }
619
620 bool
621 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
622 {
623         AutomationSelection::iterator p;
624         AutomationList& alist (line.the_list());
625         
626         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
627
628         if (p == selection.lines.end()) {
629                 return false;
630         }
631
632         /* Make a copy of the list because we have to scale the
633            values from view coordinates to model coordinates, and we're
634            not supposed to modify the points in the selection.
635         */
636            
637         AutomationList copy (**p);
638
639         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
640                 double foo = (*x)->value;
641                 line.view_to_model_y (foo);
642                 (*x)->value = foo;
643         }
644
645         XMLNode &before = alist.get_state();
646         alist.paste (copy, pos, times);
647         _session.add_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
648
649         return true;
650 }
651
652 void
653 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
654 {
655         ghosts.push_back (gr);
656         gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
657 }
658
659 void
660 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
661 {
662         if (in_destructor) {
663                 return;
664         }
665
666         list<GhostRegion*>::iterator i;
667
668         for (i = ghosts.begin(); i != ghosts.end(); ++i) {
669                 if ((*i) == gr) {
670                         ghosts.erase (i);
671                         break;
672                 }
673         }
674 }
675
676 void
677 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
678 {
679         if (!lines.empty() && touched (top, bot)) {
680                 double topfrac;
681                 double botfrac;
682
683                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
684                    y_position is the "origin" or "top" of the track.
685                 */
686
687                 double mybot = y_position + height; // XXX need to include Editor::track_spacing; 
688
689                 if (y_position >= top && mybot <= bot) {
690
691                         /* y_position is below top, mybot is above bot, so we're fully
692                            covered vertically.
693                         */
694
695                         topfrac = 1.0;
696                         botfrac = 0.0;
697
698                 } else {
699
700                         /* top and bot are within y_position .. mybot */
701
702                         topfrac = 1.0 - ((top - y_position) / height);
703                         botfrac = 1.0 - ((bot - y_position) / height);
704                 }
705
706                 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
707                         (*i)->get_selectables (start, end, botfrac, topfrac, results);
708                 }
709         }
710 }
711
712 void
713 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
714 {
715         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
716                 (*i)->get_inverted_selectables (sel, result);
717         }
718 }
719
720 void
721 AutomationTimeAxisView::set_selected_points (PointSelection& points)
722 {
723         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
724                 (*i)->set_selected_points (points);
725         }
726 }
727
728 void
729 AutomationTimeAxisView::clear_lines ()
730 {
731         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
732                 delete *i;
733         }
734
735         lines.clear ();
736         automation_connection.disconnect ();
737 }
738
739 void
740 AutomationTimeAxisView::add_line (AutomationLine& line)
741 {
742         bool get = false;
743
744         if (lines.empty()) {
745                 /* first line is the Model for automation state */
746                 automation_connection = line.the_list().automation_state_changed.connect
747                         (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
748                 get = true;
749         }
750
751         lines.push_back (&line);
752         line.set_height (height);
753
754         if (get) {
755                 /* pick up the current state */
756                 automation_state_changed ();
757         }
758 }
759
760 void
761 AutomationTimeAxisView::show_all_control_points ()
762 {
763         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
764                 (*i)->show_all_control_points ();
765         }
766 }
767
768 void
769 AutomationTimeAxisView::hide_all_but_selected_control_points ()
770 {
771         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
772                 (*i)->hide_all_but_selected_control_points ();
773         }
774 }
775
776 void
777 AutomationTimeAxisView::entered()
778 {
779         show_all_control_points ();
780 }
781
782 void
783 AutomationTimeAxisView::exited ()
784 {
785         hide_all_but_selected_control_points ();
786 }
787
788 void
789 AutomationTimeAxisView::set_state (const XMLNode& node)
790 {
791         TimeAxisView::set_state (node);
792 }
793
794 XMLNode*
795 AutomationTimeAxisView::get_state_node ()
796 {
797         TimeAxisView* state_parent = get_parent_with_state ();
798
799         if (state_parent) {
800                 return state_parent->get_child_xml_node (_state_name);
801         } else {
802                 return 0;
803         }
804 }