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