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