Fixed overflow issue. Code originally meant to truncate the 64 bit integer did not...
[ardour.git] / gtk2_ardour / automation_time_axis.cc
1 /*
2     Copyright (C) 2000-2007 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <ardour/route.h>
21 #include <pbd/memento_command.h>
22
23 #include "ardour_ui.h"
24 #include "automation_time_axis.h"
25 #include "automation_line.h"
26 #include "public_editor.h"
27 #include "simplerect.h"
28 #include "selection.h"
29 #include "ghostregion.h"
30 #include "rgb_macros.h"
31 #include "automation_selectable.h"
32 #include "point_selection.h"
33 #include "canvas_impl.h"
34 #include "utils.h"
35
36 #include "i18n.h"
37
38 using namespace ARDOUR;
39 using namespace PBD;
40 using namespace Gtk;
41 using namespace Editing;
42
43 Pango::FontDescription* AutomationTimeAxisView::name_font = 0;
44 bool AutomationTimeAxisView::have_name_font = false;
45
46 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Route> r, PublicEditor& e, TimeAxisView& rent, 
47                                                 ArdourCanvas::Canvas& canvas, const string & nom, 
48                                                 const string & state_name, const string & nomparent)
49
50         : AxisView (s), 
51           TimeAxisView (s, e, &rent, canvas),
52           route (r),
53           _name (nom),
54           _state_name (state_name),
55           height_button (_("h")),
56           clear_button (_("clear")),
57           auto_button (X_("")) /* force addition of a label */
58 {
59         if (!have_name_font) {
60                 name_font = get_font_for_style (X_("AutomationTrackName"));
61                 have_name_font = true;
62         }
63
64         automation_menu = 0;
65         in_destructor = false;
66         auto_off_item = 0;
67         auto_touch_item = 0;
68         auto_write_item = 0;
69         auto_play_item = 0;
70         ignore_state_request = false;
71         first_call_to_set_height = true;
72
73         base_rect = new SimpleRect(*canvas_display);
74         base_rect->property_x1() = 0.0;
75         base_rect->property_y1() = 0.0;
76         base_rect->property_x2() = editor.frame_to_pixel (max_frames);
77         base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
78         /* outline ends and bottom */
79         base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
80         base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
81         //base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredControlPoint.get();
82         
83         base_rect->set_data ("trackview", this);
84
85         base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
86                                                  base_rect, this));
87
88         hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
89
90         height_button.set_name ("TrackSizeButton");
91         auto_button.set_name ("TrackVisualButton");
92         clear_button.set_name ("TrackVisualButton");
93         hide_button.set_name ("TrackRemoveButton");
94
95         controls_table.set_no_show_all();
96
97         ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
98         ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
99         ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
100         ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
101
102         /* rearrange the name display */
103
104         /* we never show these for automation tracks, so make
105            life easier and remove them.
106         */
107
108         hide_name_entry();
109
110         /* move the name label over a bit */
111
112         string shortpname = _name;
113         bool shortened = false;
114
115         int ignore_width;
116         shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
117
118         if (shortpname != _name ){
119                 shortened = true;
120         }
121
122         name_label.set_text (shortpname);
123         name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
124
125         if (nomparent.length()) {
126
127                 /* limit the plug name string */
128
129                 string pname = fit_to_pixels (nomparent, 60, *name_font, ignore_width, true);
130                 if (pname != nomparent) {
131                         shortened = true;
132                 }
133
134                 plugname = new Label (pname);
135                 plugname->set_name (X_("TrackPlugName"));
136                 plugname->show();
137                 name_label.set_name (X_("TrackParameterName"));
138                 controls_table.remove (name_hbox);
139                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
140                 plugname_packed = true;
141                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
142         } else {
143                 plugname = 0;
144                 plugname_packed = false;
145         }
146
147         if (shortened) {
148                 string tipname = nomparent;
149                 if (!tipname.empty()) {
150                         tipname += ": ";
151                 }
152                 tipname += _name;
153                 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
154         }
155         
156         /* add the buttons */
157         controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
158         controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
159
160         controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
161         controls_table.attach (clear_button, 5, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
162
163         controls_table.show_all ();
164
165         height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
166         clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
167         hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
168         auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
169
170         controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
171         controls_base_unselected_name = X_("AutomationTrackControlsBase");
172         controls_ebox.set_name (controls_base_unselected_name);
173
174         controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
175
176         XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
177
178         if (xml_node) {
179                 set_state (*xml_node);
180         } 
181
182         /* make sure labels etc. are correct */
183
184         automation_state_changed ();
185         ColorsChanged.connect (mem_fun (*this, &AutomationTimeAxisView::color_handler));
186 }
187
188 AutomationTimeAxisView::~AutomationTimeAxisView ()
189 {
190         in_destructor = true;
191
192         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
193                 delete *i;
194         }
195 }
196
197 void
198 AutomationTimeAxisView::auto_clicked ()
199 {
200         using namespace Menu_Helpers;
201
202         if (automation_menu == 0) {
203                 automation_menu = manage (new Menu);
204                 automation_menu->set_name ("ArdourContextMenu");
205                 MenuList& items (automation_menu->items());
206
207                 items.push_back (MenuElem (_("Manual"), 
208                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
209                 items.push_back (MenuElem (_("Play"),
210                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
211                 items.push_back (MenuElem (_("Write"),
212                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
213                 items.push_back (MenuElem (_("Touch"),
214                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
215         }
216
217         automation_menu->popup (1, gtk_get_current_event_time());
218 }
219
220
221 void
222 AutomationTimeAxisView::automation_state_changed ()
223 {
224         AutoState state;
225
226         /* update button label */
227
228         if (lines.empty()) {
229                 state = Off;
230         } else {
231                 state = lines.front()->the_list().automation_state ();
232         }
233
234         switch (state & (Off|Play|Touch|Write)) {
235         case Off:
236                 auto_button.set_label (_("Manual"));
237                 if (auto_off_item) {
238                         ignore_state_request = true;
239                         auto_off_item->set_active (true);
240                         auto_play_item->set_active (false);
241                         auto_touch_item->set_active (false);
242                         auto_write_item->set_active (false);
243                         ignore_state_request = false;
244                 }
245                 break;
246         case Play:
247                 auto_button.set_label (_("Play"));
248                 if (auto_play_item) {
249                         ignore_state_request = true;
250                         auto_play_item->set_active (true);
251                         auto_off_item->set_active (false);
252                         auto_touch_item->set_active (false);
253                         auto_write_item->set_active (false);
254                         ignore_state_request = false;
255                 }
256                 break;
257         case Write:
258                 auto_button.set_label (_("Write"));
259                 if (auto_write_item) {
260                         ignore_state_request = true;
261                         auto_write_item->set_active (true);
262                         auto_off_item->set_active (false);
263                         auto_play_item->set_active (false);
264                         auto_touch_item->set_active (false);
265                         ignore_state_request = false;
266                 }
267                 break;
268         case Touch:
269                 auto_button.set_label (_("Touch"));
270                 if (auto_touch_item) {
271                         ignore_state_request = true;
272                         auto_touch_item->set_active (true);
273                         auto_off_item->set_active (false);
274                         auto_play_item->set_active (false);
275                         auto_write_item->set_active (false);
276                         ignore_state_request = false;
277                 }
278                 break;
279         default:
280                 auto_button.set_label (_("???"));
281                 break;
282         }
283 }
284
285 void
286 AutomationTimeAxisView::height_clicked ()
287 {
288         popup_size_menu (0);
289 }
290
291 void
292 AutomationTimeAxisView::clear_clicked ()
293 {
294         _session.begin_reversible_command (_("clear automation"));
295         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
296                 (*i)->clear ();
297         }
298         _session.commit_reversible_command ();
299 }
300
301 void
302 AutomationTimeAxisView::set_height (uint32_t h)
303 {
304         bool changed = (height != (uint32_t) h);
305         bool changed_between_small_and_normal = ( (h == hSmall || h == hSmaller) ^ (height == hSmall || height == hSmaller) );
306
307         TimeAxisView* state_parent = get_parent_with_state ();
308         XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
309
310         TimeAxisView::set_height (h);
311         base_rect->property_y2() = h;
312
313         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
314                 (*i)->set_height (h);
315         }
316
317         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
318                 (*i)->set_height ();
319         }
320
321         char buf[32];
322         snprintf (buf, sizeof (buf), "%u", height);
323         xml_node->add_property ("height", buf);
324
325         if (changed_between_small_and_normal || first_call_to_set_height) {
326                 first_call_to_set_height = false;
327
328                 if (h >= hNormal) {
329                         controls_table.remove (name_hbox);
330                         
331                         if (plugname) {
332                                 if (plugname_packed) {
333                                         controls_table.remove (*plugname);
334                                         plugname_packed = false;
335                                 }
336                                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
337                                 plugname_packed = true;
338                                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
339                         } else {
340                                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
341                         }
342                         hide_name_entry ();
343                         show_name_label ();
344                         name_hbox.show_all ();
345                         
346                         auto_button.show();
347                         height_button.show();
348                         clear_button.show();
349                         hide_button.show_all();
350
351                 } else if (h >= hSmall) {
352                         controls_table.remove (name_hbox);
353                         if (plugname) {
354                                 if (plugname_packed) {
355                                         controls_table.remove (*plugname);
356                                         plugname_packed = false;
357                                 }
358                         }
359                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
360                         controls_table.hide_all ();
361                         hide_name_entry ();
362                         show_name_label ();
363                         name_hbox.show_all ();
364                         
365                         auto_button.hide();
366                         height_button.hide();
367                         clear_button.hide();
368                         hide_button.hide();
369                 }
370         } else  if (h >= hNormal){
371                         auto_button.show();
372                         height_button.show();
373                         clear_button.show();
374                         hide_button.show_all();
375         }
376
377         if (changed) {
378                 /* only emit the signal if the height really changed */
379                 route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
380         }
381 }
382
383 void
384 AutomationTimeAxisView::set_samples_per_unit (double spu)
385 {
386         TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
387
388         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
389                 (*i)->reset ();
390         }
391 }
392  
393 void
394 AutomationTimeAxisView::hide_clicked ()
395 {
396         // LAME fix for refreshing the hide button
397         hide_button.set_sensitive(false);
398         
399         set_marked_for_display (false);
400         hide ();
401         
402         hide_button.set_sensitive(true);
403 }
404
405 void
406 AutomationTimeAxisView::build_display_menu ()
407 {
408         using namespace Menu_Helpers;
409
410         /* get the size menu ready */
411
412         build_size_menu ();
413
414         /* prepare it */
415
416         TimeAxisView::build_display_menu ();
417
418         /* now fill it with our stuff */
419
420         MenuList& items = display_menu->items();
421
422         items.push_back (MenuElem (_("Height"), *size_menu));
423         items.push_back (SeparatorElem());
424         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
425         items.push_back (SeparatorElem());
426         items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
427         items.push_back (SeparatorElem());
428
429         Menu* auto_state_menu = manage (new Menu);
430         auto_state_menu->set_name ("ArdourContextMenu");
431         MenuList& as_items = auto_state_menu->items();
432         
433         as_items.push_back (CheckMenuElem (_("Manual"), 
434                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
435         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
436
437         as_items.push_back (CheckMenuElem (_("Play"),
438                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
439         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
440
441         as_items.push_back (CheckMenuElem (_("Write"),
442                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
443         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
444
445         as_items.push_back (CheckMenuElem (_("Touch"),
446                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
447         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
448
449         items.push_back (MenuElem (_("State"), *auto_state_menu));
450
451         /* make sure the automation menu state is correct */
452
453         automation_state_changed ();
454 }
455
456 bool
457 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
458 {
459         bool ret = false;
460
461         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
462                 ret = cut_copy_clear_one ((**i), selection, op);
463         }
464
465         return ret;
466 }
467
468 bool
469 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
470 {
471         AutomationList* what_we_got = 0;
472         AutomationList& alist (line.the_list());
473         bool ret = false;
474
475         XMLNode &before = alist.get_state();
476
477         switch (op) {
478         case Cut:
479                 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
480                         editor.get_cut_buffer().add (what_we_got);
481                         _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
482                         ret = true;
483                 }
484                 break;
485         case Copy:
486                 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
487                         editor.get_cut_buffer().add (what_we_got);
488                 }
489                 break;
490
491         case Clear:
492                 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
493                         _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
494                         delete what_we_got;
495                         what_we_got = 0;
496                         ret = true;
497                 }
498                 break;
499         }
500
501         if (what_we_got) {
502                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
503                         double foo = (*x)->value;
504                         line.model_to_view_y (foo);
505                         (*x)->value = foo;
506                 }
507         }
508
509         return ret;
510 }
511
512 void
513 AutomationTimeAxisView::reset_objects (PointSelection& selection)
514 {
515         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
516                 reset_objects_one ((**i), selection);
517         }
518 }
519
520 void
521 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
522 {
523         AutomationList& alist (line.the_list());
524
525         _session.add_command (new MementoCommand<AutomationList>(alist, &alist.get_state(), 0));
526
527         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
528
529                 if (&(*i).track != this) {
530                         continue;
531                 }
532                 
533                 alist.reset_range ((*i).start, (*i).end);
534         }
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         XMLNode &before = alist.get_state();
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_command (new MementoCommand<AutomationList>(alist, new XMLNode (before), &alist.get_state()));
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_command (new MementoCommand<AutomationList>(alist, new XMLNode (before), &alist.get_state()));
581                                 delete what_we_got;
582                                 what_we_got = 0;
583                                 ret = true;
584                         }
585                         break;
586                 }
587         }
588
589         delete &before;
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 (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, 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         XMLNode &before = alist.get_state();
640         alist.paste (copy, pos, times);
641         _session.add_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
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 (nframes_t start, 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;
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_colors () {
784
785     for( list<GhostRegion *>::iterator i=ghosts.begin(); i != ghosts.end(); i++ ) {
786         (*i)->set_colors();
787     }
788     
789     for( vector<AutomationLine *>::iterator i=lines.begin(); i != lines.end(); i++ ) {
790         (*i)->set_colors();
791     }
792
793 }
794
795 void
796 AutomationTimeAxisView::color_handler () 
797 {
798     set_colors();
799 }
800
801
802
803 int
804 AutomationTimeAxisView::set_state (const XMLNode& node)
805 {
806         return TimeAxisView::set_state (node);
807 }
808
809 XMLNode*
810 AutomationTimeAxisView::get_state_node ()
811 {
812         TimeAxisView* state_parent = get_parent_with_state ();
813
814         if (state_parent) {
815                 return state_parent->get_child_xml_node (_state_name);
816         } else {
817                 return 0;
818         }
819 }