first pass of dynamically resizable track heights
[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         }
371
372         if (changed) {
373                 /* only emit the signal if the height really changed */
374                 route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
375         }
376 }
377
378 void
379 AutomationTimeAxisView::set_samples_per_unit (double spu)
380 {
381         TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
382
383         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
384                 (*i)->reset ();
385         }
386 }
387  
388 void
389 AutomationTimeAxisView::hide_clicked ()
390 {
391         // LAME fix for refreshing the hide button
392         hide_button.set_sensitive(false);
393         
394         set_marked_for_display (false);
395         hide ();
396         
397         hide_button.set_sensitive(true);
398 }
399
400 void
401 AutomationTimeAxisView::build_display_menu ()
402 {
403         using namespace Menu_Helpers;
404
405         /* get the size menu ready */
406
407         build_size_menu ();
408
409         /* prepare it */
410
411         TimeAxisView::build_display_menu ();
412
413         /* now fill it with our stuff */
414
415         MenuList& items = display_menu->items();
416
417         items.push_back (MenuElem (_("Height"), *size_menu));
418         items.push_back (SeparatorElem());
419         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
420         items.push_back (SeparatorElem());
421         items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
422         items.push_back (SeparatorElem());
423
424         Menu* auto_state_menu = manage (new Menu);
425         auto_state_menu->set_name ("ArdourContextMenu");
426         MenuList& as_items = auto_state_menu->items();
427         
428         as_items.push_back (CheckMenuElem (_("Manual"), 
429                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
430         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
431
432         as_items.push_back (CheckMenuElem (_("Play"),
433                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
434         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
435
436         as_items.push_back (CheckMenuElem (_("Write"),
437                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
438         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
439
440         as_items.push_back (CheckMenuElem (_("Touch"),
441                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
442         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
443
444         items.push_back (MenuElem (_("State"), *auto_state_menu));
445
446         /* make sure the automation menu state is correct */
447
448         automation_state_changed ();
449 }
450
451 bool
452 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
453 {
454         bool ret = false;
455
456         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
457                 ret = cut_copy_clear_one ((**i), selection, op);
458         }
459
460         return ret;
461 }
462
463 bool
464 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
465 {
466         AutomationList* what_we_got = 0;
467         AutomationList& alist (line.the_list());
468         bool ret = false;
469
470         XMLNode &before = alist.get_state();
471
472         switch (op) {
473         case Cut:
474                 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
475                         editor.get_cut_buffer().add (what_we_got);
476                         _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
477                         ret = true;
478                 }
479                 break;
480         case Copy:
481                 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
482                         editor.get_cut_buffer().add (what_we_got);
483                 }
484                 break;
485
486         case Clear:
487                 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
488                         _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
489                         delete what_we_got;
490                         what_we_got = 0;
491                         ret = true;
492                 }
493                 break;
494         }
495
496         if (what_we_got) {
497                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
498                         double foo = (*x)->value;
499                         line.model_to_view_y (foo);
500                         (*x)->value = foo;
501                 }
502         }
503
504         return ret;
505 }
506
507 void
508 AutomationTimeAxisView::reset_objects (PointSelection& selection)
509 {
510         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
511                 reset_objects_one ((**i), selection);
512         }
513 }
514
515 void
516 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
517 {
518         AutomationList& alist (line.the_list());
519
520         _session.add_command (new MementoCommand<AutomationList>(alist, &alist.get_state(), 0));
521
522         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
523
524                 if (&(*i).track != this) {
525                         continue;
526                 }
527                 
528                 alist.reset_range ((*i).start, (*i).end);
529         }
530 }
531
532 bool
533 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
534 {
535         bool ret = false;
536
537         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
538                 ret = cut_copy_clear_objects_one ((**i), selection, op);
539         }
540
541         return ret;
542 }
543
544 bool
545 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
546 {
547         AutomationList* what_we_got = 0;
548         AutomationList& alist (line.the_list());
549         bool ret = false;
550
551         XMLNode &before = alist.get_state();
552
553         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
554
555                 if (&(*i).track != this) {
556                         continue;
557                 }
558
559                 switch (op) {
560                 case Cut:
561                         if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
562                                 editor.get_cut_buffer().add (what_we_got);
563                                 _session.add_command (new MementoCommand<AutomationList>(alist, new XMLNode (before), &alist.get_state()));
564                                 ret = true;
565                         }
566                         break;
567                 case Copy:
568                         if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
569                                 editor.get_cut_buffer().add (what_we_got);
570                         }
571                         break;
572                         
573                 case Clear:
574                         if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
575                                 _session.add_command (new MementoCommand<AutomationList>(alist, new XMLNode (before), &alist.get_state()));
576                                 delete what_we_got;
577                                 what_we_got = 0;
578                                 ret = true;
579                         }
580                         break;
581                 }
582         }
583
584         delete &before;
585
586         if (what_we_got) {
587                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
588                         double foo = (*x)->value;
589                         line.model_to_view_y (foo);
590                         (*x)->value = foo;
591                 }
592         }
593
594         return ret;
595 }
596
597 bool
598 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
599 {
600         bool ret = true;
601
602         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
603                 ret = paste_one (**i, pos, times, selection, nth);
604         }
605
606         return ret;
607 }
608
609 bool
610 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
611 {
612         AutomationSelection::iterator p;
613         AutomationList& alist (line.the_list());
614         
615         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
616
617         if (p == selection.lines.end()) {
618                 return false;
619         }
620
621         /* Make a copy of the list because we have to scale the
622            values from view coordinates to model coordinates, and we're
623            not supposed to modify the points in the selection.
624         */
625            
626         AutomationList copy (**p);
627
628         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
629                 double foo = (*x)->value;
630                 line.view_to_model_y (foo);
631                 (*x)->value = foo;
632         }
633
634         XMLNode &before = alist.get_state();
635         alist.paste (copy, pos, times);
636         _session.add_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
637
638         return true;
639 }
640
641 void
642 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
643 {
644         ghosts.push_back (gr);
645         gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
646 }
647
648 void
649 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
650 {
651         if (in_destructor) {
652                 return;
653         }
654
655         list<GhostRegion*>::iterator i;
656
657         for (i = ghosts.begin(); i != ghosts.end(); ++i) {
658                 if ((*i) == gr) {
659                         ghosts.erase (i);
660                         break;
661                 }
662         }
663 }
664
665 void
666 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
667 {
668         if (!lines.empty() && touched (top, bot)) {
669                 double topfrac;
670                 double botfrac;
671
672                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
673                    y_position is the "origin" or "top" of the track.
674                 */
675
676                 double mybot = y_position + height;
677
678                 if (y_position >= top && mybot <= bot) {
679
680                         /* y_position is below top, mybot is above bot, so we're fully
681                            covered vertically.
682                         */
683
684                         topfrac = 1.0;
685                         botfrac = 0.0;
686
687                 } else {
688
689                         /* top and bot are within y_position .. mybot */
690
691                         topfrac = 1.0 - ((top - y_position) / height);
692                         botfrac = 1.0 - ((bot - y_position) / height);
693                 }
694
695                 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
696                         (*i)->get_selectables (start, end, botfrac, topfrac, results);
697                 }
698         }
699 }
700
701 void
702 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
703 {
704         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
705                 (*i)->get_inverted_selectables (sel, result);
706         }
707 }
708
709 void
710 AutomationTimeAxisView::set_selected_points (PointSelection& points)
711 {
712         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
713                 (*i)->set_selected_points (points);
714         }
715 }
716
717 void
718 AutomationTimeAxisView::clear_lines ()
719 {
720         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
721                 delete *i;
722         }
723
724         lines.clear ();
725         automation_connection.disconnect ();
726 }
727
728 void
729 AutomationTimeAxisView::add_line (AutomationLine& line)
730 {
731         bool get = false;
732
733         if (lines.empty()) {
734                 /* first line is the Model for automation state */
735                 automation_connection = line.the_list().automation_state_changed.connect
736                         (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
737                 get = true;
738         }
739
740         lines.push_back (&line);
741         line.set_height (height);
742
743         if (get) {
744                 /* pick up the current state */
745                 automation_state_changed ();
746         }
747 }
748
749 void
750 AutomationTimeAxisView::show_all_control_points ()
751 {
752         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
753                 (*i)->show_all_control_points ();
754         }
755 }
756
757 void
758 AutomationTimeAxisView::hide_all_but_selected_control_points ()
759 {
760         for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
761                 (*i)->hide_all_but_selected_control_points ();
762         }
763 }
764
765 void
766 AutomationTimeAxisView::entered()
767 {
768         show_all_control_points ();
769 }
770
771 void
772 AutomationTimeAxisView::exited ()
773 {
774         hide_all_but_selected_control_points ();
775 }
776
777 void
778 AutomationTimeAxisView::set_colors () {
779
780     for( list<GhostRegion *>::iterator i=ghosts.begin(); i != ghosts.end(); i++ ) {
781         (*i)->set_colors();
782     }
783     
784     for( vector<AutomationLine *>::iterator i=lines.begin(); i != lines.end(); i++ ) {
785         (*i)->set_colors();
786     }
787
788 }
789
790 void
791 AutomationTimeAxisView::color_handler () 
792 {
793     set_colors();
794 }
795
796
797
798 void
799 AutomationTimeAxisView::set_state (const XMLNode& node)
800 {
801         TimeAxisView::set_state (node);
802 }
803
804 XMLNode*
805 AutomationTimeAxisView::get_state_node ()
806 {
807         TimeAxisView* state_parent = get_parent_with_state ();
808
809         if (state_parent) {
810                 return state_parent->get_child_xml_node (_state_name);
811         } else {
812                 return 0;
813         }
814 }