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