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