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