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