fbe15680115dcfb746856b70c89711046d75be7c
[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 ht)
279 {
280         uint32_t h = height_to_pixels (ht);
281         bool changed = (height != (uint32_t) h);
282
283         TimeAxisView* state_parent = get_parent_with_state ();
284         XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
285
286         controls_table.show_all ();
287
288         TimeAxisView::set_height (ht);
289         base_rect->property_y2() = h;
290
291         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
292                 (*i)->set_height (h);
293         }
294
295         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
296                 (*i)->set_height ();
297         }
298
299         switch (height) {
300         case Largest:
301                 xml_node->add_property ("track_height", "largest");
302                 controls_table.remove (name_hbox);
303                 if (plugname) {
304                         if (plugname_packed) {
305                                 controls_table.remove (*plugname);
306                                 plugname_packed = false;
307                         }
308                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
309                         plugname_packed = true;
310                         controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
311                 } else {
312                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
313                 }
314                 controls_table.show_all ();
315                 hide_name_entry ();
316                 show_name_label ();
317                 break;
318
319         case Large:
320                 xml_node->add_property ("track_height", "large");
321                 controls_table.remove (name_hbox);
322                 if (plugname) {
323                         if (plugname_packed) {
324                                 controls_table.remove (*plugname);
325                                 plugname_packed = false;
326                         }
327                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
328                         plugname_packed = true;
329                 } else {
330                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
331                 }
332                 controls_table.show_all ();
333                 hide_name_entry ();
334                 show_name_label ();
335                 break;
336
337         case Larger:
338                 xml_node->add_property ("track_height", "larger");
339                 controls_table.remove (name_hbox);
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                 } else {
348                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
349                 }
350                 controls_table.show_all ();
351                 hide_name_entry ();
352                 show_name_label ();
353                 break;
354
355         case Normal:
356                 xml_node->add_property ("track_height", "normal");
357                 controls_table.remove (name_hbox);
358                 if (plugname) {
359                         if (plugname_packed) {
360                                 controls_table.remove (*plugname);
361                                 plugname_packed = false;
362                         }
363                         controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
364                         plugname_packed = true;
365                         controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
366                 } else {
367                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
368                 }
369                 controls_table.show_all ();
370                 hide_name_entry ();
371                 show_name_label ();
372                 break;
373
374         case Smaller:
375                 xml_node->add_property ("track_height", "smaller");
376                 controls_table.remove (name_hbox);
377                 if (plugname) {
378                         if (plugname_packed) {
379                                 controls_table.remove (*plugname);
380                                 plugname_packed = false;
381                         }
382                 }
383                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
384                 controls_table.hide_all ();
385                 hide_name_entry ();
386                 show_name_label ();
387                 name_hbox.show_all ();
388                 controls_table.show ();
389                 break;
390
391         case Small:
392                 xml_node->add_property ("track_height", "small");
393                 controls_table.remove (name_hbox);
394                 if (plugname) {
395                         if (plugname_packed) {
396                                 controls_table.remove (*plugname);
397                                 plugname_packed = false;
398                         }
399                 } 
400                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
401                 controls_table.hide_all ();
402                 hide_name_entry ();
403                 show_name_label ();
404                 name_hbox.show_all ();
405                 controls_table.show ();
406                 break;
407         }
408
409         if (changed) {
410                 /* only emit the signal if the height really changed */
411                  route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
412         }
413 }
414
415 void
416 AutomationTimeAxisView::set_samples_per_unit (double spu)
417 {
418         TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
419
420         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
421                 (*i)->reset ();
422         }
423 }
424  
425 void
426 AutomationTimeAxisView::hide_clicked ()
427 {
428         set_marked_for_display (false);
429         hide ();
430 }
431
432 void
433 AutomationTimeAxisView::build_display_menu ()
434 {
435         using namespace Menu_Helpers;
436
437         /* get the size menu ready */
438
439         build_size_menu ();
440
441         /* prepare it */
442
443         TimeAxisView::build_display_menu ();
444
445         /* now fill it with our stuff */
446
447         MenuList& items = display_menu->items();
448
449         items.push_back (MenuElem (_("Height"), *size_menu));
450         items.push_back (SeparatorElem());
451         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
452         items.push_back (SeparatorElem());
453         items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
454         items.push_back (SeparatorElem());
455
456         Menu* auto_state_menu = manage (new Menu);
457         auto_state_menu->set_name ("ArdourContextMenu");
458         MenuList& as_items = auto_state_menu->items();
459         
460         as_items.push_back (CheckMenuElem (_("off"), 
461                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
462         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
463
464         as_items.push_back (CheckMenuElem (_("play"),
465                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
466         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
467
468         as_items.push_back (CheckMenuElem (_("write"),
469                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
470         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
471
472         as_items.push_back (CheckMenuElem (_("touch"),
473                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
474         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
475
476         items.push_back (MenuElem (_("State"), *auto_state_menu));
477
478         /* make sure the automation menu state is correct */
479
480         automation_state_changed ();
481 }
482
483 bool
484 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
485 {
486         bool ret = false;
487
488         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
489                 ret = cut_copy_clear_one ((**i), selection, op);
490         }
491
492         return ret;
493 }
494
495 bool
496 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
497 {
498         AutomationList* what_we_got = 0;
499         AutomationList& alist (line.the_list());
500         bool ret = false;
501
502         _session.add_undo (alist.get_memento());
503
504         switch (op) {
505         case Cut:
506                 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
507                         editor.get_cut_buffer().add (what_we_got);
508                         _session.add_redo_no_execute (alist.get_memento());
509                         ret = true;
510                 }
511                 break;
512         case Copy:
513                 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
514                         editor.get_cut_buffer().add (what_we_got);
515                 }
516                 break;
517
518         case Clear:
519                 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
520                         _session.add_redo_no_execute (alist.get_memento());
521                         delete what_we_got;
522                         what_we_got = 0;
523                         ret = true;
524                 }
525                 break;
526         }
527
528         if (what_we_got) {
529                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
530                         double foo = (*x)->value;
531                         line.model_to_view_y (foo);
532                         (*x)->value = foo;
533                 }
534         }
535
536         return ret;
537 }
538
539 bool
540 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
541 {
542         bool ret = false;
543
544         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
545                 ret = cut_copy_clear_objects_one ((**i), selection, op);
546         }
547
548         return ret;
549 }
550
551 bool
552 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
553 {
554         AutomationList* what_we_got = 0;
555         AutomationList& alist (line.the_list());
556         bool ret = false;
557
558         _session.add_undo (alist.get_memento());
559
560         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
561
562                 if (&(*i).track != this) {
563                         continue;
564                 }
565
566                 switch (op) {
567                 case Cut:
568                         if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
569                                 editor.get_cut_buffer().add (what_we_got);
570                                 _session.add_redo_no_execute (alist.get_memento());
571                                 ret = true;
572                         }
573                         break;
574                 case Copy:
575                         if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
576                                 editor.get_cut_buffer().add (what_we_got);
577                         }
578                         break;
579                         
580                 case Clear:
581                         if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
582                                 _session.add_redo_no_execute (alist.get_memento());
583                                 delete what_we_got;
584                                 what_we_got = 0;
585                                 ret = true;
586                         }
587                         break;
588                 }
589         }
590                 
591         if (what_we_got) {
592                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
593                         double foo = (*x)->value;
594                         line.model_to_view_y (foo);
595                         (*x)->value = foo;
596                 }
597         }
598
599         return ret;
600 }
601
602 bool
603 AutomationTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
604 {
605         bool ret = true;
606
607         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
608                 ret = paste_one (**i, pos, times, selection, nth);
609         }
610
611         return ret;
612 }
613
614 bool
615 AutomationTimeAxisView::paste_one (AutomationLine& line, jack_nframes_t pos, float times, Selection& selection, size_t nth)
616 {
617         AutomationSelection::iterator p;
618         AutomationList& alist (line.the_list());
619         
620         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
621
622         if (p == selection.lines.end()) {
623                 return false;
624         }
625
626         /* Make a copy of the list because we have to scale the
627            values from view coordinates to model coordinates, and we're
628            not supposed to modify the points in the selection.
629         */
630            
631         AutomationList copy (**p);
632
633         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
634                 double foo = (*x)->value;
635                 line.view_to_model_y (foo);
636                 (*x)->value = foo;
637         }
638
639         _session.add_undo (alist.get_memento());
640         alist.paste (copy, pos, times);
641         _session.add_redo_no_execute (alist.get_memento());
642
643         return true;
644 }
645
646 void
647 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
648 {
649         ghosts.push_back (gr);
650         gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
651 }
652
653 void
654 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
655 {
656         if (in_destructor) {
657                 return;
658         }
659
660         list<GhostRegion*>::iterator i;
661
662         for (i = ghosts.begin(); i != ghosts.end(); ++i) {
663                 if ((*i) == gr) {
664                         ghosts.erase (i);
665                         break;
666                 }
667         }
668 }
669
670 void
671 AutomationTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
672 {
673         if (!lines.empty() && touched (top, bot)) {
674                 double topfrac;
675                 double botfrac;
676
677                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
678                    y_position is the "origin" or "top" of the track.
679                 */
680
681                 double mybot = y_position + height; // XXX need to include Editor::track_spacing; 
682
683                 if (y_position >= top && mybot <= bot) {
684
685                         /* y_position is below top, mybot is above bot, so we're fully
686                            covered vertically.
687                         */
688
689                         topfrac = 1.0;
690                         botfrac = 0.0;
691
692                 } else {
693
694                         /* top and bot are within y_position .. mybot */
695
696                         topfrac = 1.0 - ((top - y_position) / height);
697                         botfrac = 1.0 - ((bot - y_position) / height);
698                 }
699
700                 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
701                         (*i)->get_selectables (start, end, botfrac, topfrac, results);
702                 }
703         }
704 }
705
706 void
707 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
708 {
709         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
710                 (*i)->get_inverted_selectables (sel, result);
711         }
712 }
713
714 void
715 AutomationTimeAxisView::set_selected_points (PointSelection& points)
716 {
717         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
718                 (*i)->set_selected_points (points);
719         }
720 }
721
722 void
723 AutomationTimeAxisView::clear_lines ()
724 {
725         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
726                 delete *i;
727         }
728
729         lines.clear ();
730         automation_connection.disconnect ();
731 }
732
733 void
734 AutomationTimeAxisView::add_line (AutomationLine& line)
735 {
736         bool get = false;
737
738         if (lines.empty()) {
739                 /* first line is the Model for automation state */
740                 automation_connection = line.the_list().automation_state_changed.connect
741                         (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
742                 get = true;
743         }
744
745         lines.push_back (&line);
746         line.set_height (height);
747
748         if (get) {
749                 /* pick up the current state */
750                 automation_state_changed ();
751         }
752 }
753
754 void
755 AutomationTimeAxisView::show_all_control_points ()
756 {
757         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
758                 (*i)->show_all_control_points ();
759         }
760 }
761
762 void
763 AutomationTimeAxisView::hide_all_but_selected_control_points ()
764 {
765         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
766                 (*i)->hide_all_but_selected_control_points ();
767         }
768 }
769
770 void
771 AutomationTimeAxisView::entered()
772 {
773         show_all_control_points ();
774 }
775
776 void
777 AutomationTimeAxisView::exited ()
778 {
779         hide_all_but_selected_control_points ();
780 }
781
782 void
783 AutomationTimeAxisView::set_state (const XMLNode& node)
784 {
785         TimeAxisView::set_state (node);
786 }
787
788 XMLNode*
789 AutomationTimeAxisView::get_state_node ()
790 {
791         TimeAxisView* state_parent = get_parent_with_state ();
792
793         if (state_parent) {
794                 return state_parent->get_child_xml_node (_state_name);
795         } else {
796                 return 0;
797         }
798 }