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