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