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