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