Normalize XML property name style, preserving old session loading (on load _ will...
[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           height_button (_("h")),
71           clear_button (_("clear")),
72           auto_button (X_("")) /* force addition of a label */
73 {
74         if (!have_name_font) {
75                 name_font = get_font_for_style (X_("AutomationTrackName"));
76                 have_name_font = true;
77         }
78
79         automation_menu = 0;
80         auto_off_item = 0;
81         auto_touch_item = 0;
82         auto_write_item = 0;
83         auto_play_item = 0;
84         mode_discrete_item = 0;
85         mode_line_item = 0;
86
87         ignore_state_request = false;
88         first_call_to_set_height = true;
89         
90         _base_rect = new SimpleRect(*canvas_display);
91         _base_rect->property_x1() = 0.0;
92         _base_rect->property_y1() = 0.0;
93         _base_rect->property_x2() = editor.frame_to_pixel (max_frames);
94         _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
95         
96         /* outline ends and bottom */
97         _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
98         _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
99         
100         _base_rect->set_data ("trackview", this);
101
102         _base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
103                                                  _base_rect, this));
104
105         _base_rect->lower_to_bottom();
106
107         hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
108
109         height_button.set_name ("TrackSizeButton");
110         auto_button.set_name ("TrackVisualButton");
111         clear_button.set_name ("TrackVisualButton");
112         hide_button.set_name ("TrackRemoveButton");
113
114         controls_table.set_no_show_all();
115
116         ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
117         ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
118         ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
119         ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
120
121         /* rearrange the name display */
122
123         /* we never show these for automation tracks, so make
124            life easier and remove them.
125         */
126
127         hide_name_entry();
128
129         /* move the name label over a bit */
130
131         string shortpname = _name;
132         bool shortened = false;
133
134         int ignore_width;
135         shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
136
137         if (shortpname != _name ){
138                 shortened = true;
139         }
140
141         name_label.set_text (shortpname);
142         name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
143
144         if (nomparent.length()) {
145
146                 /* limit the plug name string */
147
148                 string pname = fit_to_pixels (nomparent, 60, *name_font, ignore_width, true);
149                 if (pname != nomparent) {
150                         shortened = true;
151                 }
152
153                 plugname = new Label (pname);
154                 plugname->set_name (X_("TrackPlugName"));
155                 plugname->show();
156                 name_label.set_name (X_("TrackParameterName"));
157                 controls_table.remove (name_hbox);
158                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
159                 plugname_packed = true;
160                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
161         } else {
162                 plugname = 0;
163                 plugname_packed = false;
164         }
165
166         if (shortened) {
167                 string tipname = nomparent;
168                 if (!tipname.empty()) {
169                         tipname += ": ";
170                 }
171                 tipname += _name;
172                 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
173         }
174         
175         /* add the buttons */
176         controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
177         controls_table.attach (height_button, 0, 1, 1, 2, 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         height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
188         clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
189         hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
190         auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
191
192         controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
193         controls_base_unselected_name = X_("AutomationTrackControlsBase");
194         controls_ebox.set_name (controls_base_unselected_name);
195
196         controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
197
198         XMLNode* xml_node = get_parent_with_state()->get_automation_child_xml_node (
199                         _control->parameter());
200
201         if (xml_node) {
202                 set_state (*xml_node);
203         } 
204                 
205         /* ask for notifications of any new RegionViews */
206         if (show_regions) {
207
208                 assert(_view);
209                 _view->attach ();
210         
211         /* no regions, just a single line for the entire track (e.g. bus gain) */
212         } else {
213         
214                 boost::shared_ptr<AutomationLine> line(new AutomationLine (
215                                         ARDOUR::EventTypeMap::instance().to_symbol(_control->parameter()),
216                                         *this,
217                                         *canvas_display,
218                                         _control->alist()));
219
220                 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
221                 line->queue_reset ();
222                 add_line (line);
223         }
224
225         /* make sure labels etc. are correct */
226
227         automation_state_changed ();
228         ColorsChanged.connect (mem_fun (*this, &AutomationTimeAxisView::color_handler));
229 }
230
231 AutomationTimeAxisView::~AutomationTimeAxisView ()
232 {
233 }
234
235 void
236 AutomationTimeAxisView::auto_clicked ()
237 {
238         using namespace Menu_Helpers;
239
240         if (automation_menu == 0) {
241                 automation_menu = manage (new Menu);
242                 automation_menu->set_name ("ArdourContextMenu");
243                 MenuList& items (automation_menu->items());
244
245                 items.push_back (MenuElem (_("Manual"), bind (mem_fun(*this,
246                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
247                 items.push_back (MenuElem (_("Play"), bind (mem_fun(*this,
248                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
249                 items.push_back (MenuElem (_("Write"), bind (mem_fun(*this,
250                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
251                 items.push_back (MenuElem (_("Touch"), bind (mem_fun(*this,
252                                 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
253         }
254
255         automation_menu->popup (1, gtk_get_current_event_time());
256 }
257
258 void
259 AutomationTimeAxisView::set_automation_state (AutoState state)
260 {
261         if (!ignore_state_request) {
262                 if (_route == _automatable) { // This is a time axis for route (not region) automation
263                         _route->set_parameter_automation_state (_control->parameter(), state);
264                 }
265
266                 if (_control->list())
267                         _control->alist()->set_automation_state(state);
268         }
269         if (_view)
270                 _view->set_automation_state (state);
271 }
272
273 void
274 AutomationTimeAxisView::automation_state_changed ()
275 {
276         AutoState state;
277
278         /* update button label */
279
280         if (!_line) {
281                 state = Off;
282         } else {
283                 state = _control->alist()->automation_state ();
284         }
285         
286         switch (state & (Off|Play|Touch|Write)) {
287         case Off:
288                 auto_button.set_label (_("Manual"));
289                 if (auto_off_item) {
290                         ignore_state_request = true;
291                         auto_off_item->set_active (true);
292                         auto_play_item->set_active (false);
293                         auto_touch_item->set_active (false);
294                         auto_write_item->set_active (false);
295                         ignore_state_request = false;
296                 }
297                 break;
298         case Play:
299                 auto_button.set_label (_("Play"));
300                 if (auto_play_item) {
301                         ignore_state_request = true;
302                         auto_play_item->set_active (true);
303                         auto_off_item->set_active (false);
304                         auto_touch_item->set_active (false);
305                         auto_write_item->set_active (false);
306                         ignore_state_request = false;
307                 }
308                 break;
309         case Write:
310                 auto_button.set_label (_("Write"));
311                 if (auto_write_item) {
312                         ignore_state_request = true;
313                         auto_write_item->set_active (true);
314                         auto_off_item->set_active (false);
315                         auto_play_item->set_active (false);
316                         auto_touch_item->set_active (false);
317                         ignore_state_request = false;
318                 }
319                 break;
320         case Touch:
321                 auto_button.set_label (_("Touch"));
322                 if (auto_touch_item) {
323                         ignore_state_request = true;
324                         auto_touch_item->set_active (true);
325                         auto_off_item->set_active (false);
326                         auto_play_item->set_active (false);
327                         auto_write_item->set_active (false);
328                         ignore_state_request = false;
329                 }
330                 break;
331         default:
332                 auto_button.set_label (_("???"));
333                 break;
334         }
335 }
336
337 void
338 AutomationTimeAxisView::interpolation_changed ()
339 {       
340         AutomationList::InterpolationStyle style = _control->list()->interpolation();
341         
342         if (mode_line_item && mode_discrete_item) {
343                 if (style == AutomationList::Discrete) {
344                         mode_discrete_item->set_active(true);
345                         mode_line_item->set_active(false);
346                 } else {
347                         mode_line_item->set_active(true);
348                         mode_discrete_item->set_active(false);
349                 }
350         }
351         
352         if (_line)
353                 _line->set_interpolation(style);
354 }
355
356 void
357 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
358 {
359         _control->list()->set_interpolation(style);
360         if (_line)
361                 _line->set_interpolation(style);
362 }
363
364 void
365 AutomationTimeAxisView::height_clicked ()
366 {
367         popup_size_menu (0);
368 }
369
370 void
371 AutomationTimeAxisView::clear_clicked ()
372 {
373         _session.begin_reversible_command (_("clear automation"));
374         if (_line)
375                 _line->clear ();
376         _session.commit_reversible_command ();
377 }
378
379 void
380 AutomationTimeAxisView::set_height (uint32_t h)
381 {
382         bool changed = (height != (uint32_t) h) || first_call_to_set_height;
383         bool changed_between_small_and_normal = ( (h == hSmall || h == hSmaller) ^ (height == hSmall || height == hSmaller) );
384
385         TimeAxisView* state_parent = get_parent_with_state ();
386
387         assert(state_parent);
388         XMLNode* xml_node = state_parent->get_automation_child_xml_node (_control->parameter());
389
390         TimeAxisView::set_height (h);
391         _base_rect->property_y2() = h;
392         
393         if (_line)
394                 _line->set_height(h);
395         
396         if (_view) {
397                 _view->set_height(h);
398                 _view->update_contents_height();
399         }
400
401         char buf[32];
402         snprintf (buf, sizeof (buf), "%u", height);
403         xml_node->add_property ("height", buf);
404
405         if (changed_between_small_and_normal || first_call_to_set_height) {
406
407                 first_call_to_set_height = false;
408
409                 if (h >= hNormal) {
410                         controls_table.remove (name_hbox);
411                         
412                         if (plugname) {
413                                 if (plugname_packed) {
414                                         controls_table.remove (*plugname);
415                                         plugname_packed = false;
416                                 }
417                                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
418                                 plugname_packed = true;
419                                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
420                         } else {
421                                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
422                         }
423                         hide_name_entry ();
424                         show_name_label ();
425                         name_hbox.show_all ();
426                         
427                         auto_button.show();
428                         height_button.show();
429                         clear_button.show();
430                         hide_button.show_all();
431
432                 } else if (h >= hSmall) {
433                         controls_table.remove (name_hbox);
434                         if (plugname) {
435                                 if (plugname_packed) {
436                                         controls_table.remove (*plugname);
437                                         plugname_packed = false;
438                                 }
439                         }
440                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
441                         controls_table.hide_all ();
442                         hide_name_entry ();
443                         show_name_label ();
444                         name_hbox.show_all ();
445                         
446                         auto_button.hide();
447                         height_button.hide();
448                         clear_button.hide();
449                         hide_button.hide();
450                 }
451         } else  if (h >= hNormal){
452                         auto_button.show();
453                         height_button.show();
454                         clear_button.show();
455                         hide_button.show_all();
456         }
457
458         if (changed) {
459                 /* only emit the signal if the height really changed */
460                 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
461         }
462 }
463
464 void
465 AutomationTimeAxisView::set_samples_per_unit (double spu)
466 {
467         TimeAxisView::set_samples_per_unit (spu);
468
469         if (_line)
470                 _line->reset ();
471         
472         if (_view)
473                 _view->set_samples_per_unit (spu);
474 }
475  
476 void
477 AutomationTimeAxisView::hide_clicked ()
478 {
479         // LAME fix for refreshing the hide button
480         hide_button.set_sensitive(false);
481         
482         set_marked_for_display (false);
483         hide ();
484         
485         hide_button.set_sensitive(true);
486 }
487
488 void
489 AutomationTimeAxisView::build_display_menu ()
490 {
491         using namespace Menu_Helpers;
492
493         /* get the size menu ready */
494
495         build_size_menu ();
496
497         /* prepare it */
498
499         TimeAxisView::build_display_menu ();
500
501         /* now fill it with our stuff */
502
503         MenuList& items = display_menu->items();
504
505         items.push_back (MenuElem (_("Height"), *size_menu));
506         items.push_back (SeparatorElem());
507         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
508         items.push_back (SeparatorElem());
509         items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
510         items.push_back (SeparatorElem());
511
512         /* state menu */
513
514         Menu* auto_state_menu = manage (new Menu);
515         auto_state_menu->set_name ("ArdourContextMenu");
516         MenuList& as_items = auto_state_menu->items();
517         
518         as_items.push_back (CheckMenuElem (_("Manual"), 
519                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
520         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
521
522         as_items.push_back (CheckMenuElem (_("Play"),
523                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
524         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
525
526         as_items.push_back (CheckMenuElem (_("Write"),
527                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
528         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
529
530         as_items.push_back (CheckMenuElem (_("Touch"),
531                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
532         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
533
534         items.push_back (MenuElem (_("State"), *auto_state_menu));
535         
536         /* mode menu */
537         
538         if (_control->parameter().type() == MidiCCAutomation) {
539                 Menu* auto_mode_menu = manage (new Menu);
540                 auto_mode_menu->set_name ("ArdourContextMenu");
541                 MenuList& am_items = auto_mode_menu->items();
542         
543                 RadioMenuItem::Group group;
544
545                 am_items.push_back (RadioMenuElem (group, _("Discrete"), bind (
546                                                 mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
547                                                 AutomationList::Discrete)));
548                 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
549                 //mode_discrete_item->set_active(_control->list()->interpolation() == AutomationList::Discrete);
550
551                 am_items.push_back (RadioMenuElem (group, _("Line"), bind (
552                                                 mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
553                                                 AutomationList::Linear)));
554                 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
555                 //mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
556
557                 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
558         }
559
560         /* make sure the automation menu state is correct */
561
562         automation_state_changed ();
563         interpolation_changed ();
564 }
565
566 void
567 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkEvent* event, nframes_t when, double y)
568 {
569         if (!_line)
570                 return;
571
572         double x = 0;
573
574         canvas_display->w2i (x, y);
575
576         /* compute vertical fractional position */
577
578         y = 1.0 - (y / height);
579
580         /* map using line */
581
582         _line->view_to_model_y (y);
583
584         _session.begin_reversible_command (_("add automation event"));
585         XMLNode& before = _control->alist()->get_state();
586
587         _control->alist()->add (when, y);
588
589         XMLNode& after = _control->alist()->get_state();
590         _session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
591
592         _session.set_dirty ();
593 }
594
595
596 bool
597 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
598 {
599         return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
600 }
601
602 bool
603 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
604 {
605         boost::shared_ptr<Evoral::ControlList> what_we_got;
606         boost::shared_ptr<AutomationList> alist (line.the_list());
607         bool ret = false;
608
609         XMLNode &before = alist->get_state();
610
611         switch (op) {
612         case Cut:
613                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
614                         editor.get_cut_buffer().add (what_we_got);
615                         _session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
616                         ret = true;
617                 }
618                 break;
619         case Copy:
620                 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
621                         editor.get_cut_buffer().add (what_we_got);
622                 }
623                 break;
624
625         case Clear:
626                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
627                         _session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
628                         ret = true;
629                 }
630                 break;
631         }
632
633         if (what_we_got) {
634                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
635                         double foo = (*x)->value;
636                         line.model_to_view_y (foo);
637                         (*x)->value = foo;
638                 }
639         }
640
641         return ret;
642 }
643
644 void
645 AutomationTimeAxisView::reset_objects (PointSelection& selection)
646 {
647         reset_objects_one (*_line, selection);
648 }
649
650 void
651 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
652 {
653         boost::shared_ptr<AutomationList> alist(line.the_list());
654
655         _session.add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
656
657         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
658
659                 if (&(*i).track != this) {
660                         continue;
661                 }
662                 
663                 alist->reset_range ((*i).start, (*i).end);
664         }
665 }
666
667 bool
668 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
669 {
670         return cut_copy_clear_objects_one (*_line, selection, op);
671 }
672
673 bool
674 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
675 {
676         boost::shared_ptr<Evoral::ControlList> what_we_got;
677         boost::shared_ptr<AutomationList> alist(line.the_list());
678         bool ret = false;
679
680         XMLNode &before = alist->get_state();
681
682         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
683
684                 if (&(*i).track != this) {
685                         continue;
686                 }
687
688                 switch (op) {
689                 case Cut:
690                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
691                                 editor.get_cut_buffer().add (what_we_got);
692                                 _session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
693                                 ret = true;
694                         }
695                         break;
696                 case Copy:
697                         if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
698                                 editor.get_cut_buffer().add (what_we_got);
699                         }
700                         break;
701                         
702                 case Clear:
703                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
704                                 _session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
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->alist()->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_height (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         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() == ARDOUR::EventTypeMap::instance().to_symbol(_control->parameter())) {
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         return 0;
903 }
904
905 XMLNode*
906 AutomationTimeAxisView::get_state_node ()
907 {
908         TimeAxisView* state_parent = get_parent_with_state ();
909
910         if (state_parent) {
911                 return state_parent->get_automation_child_xml_node (_control->parameter());
912         } else {
913                 return 0;
914         }
915 }
916
917 void
918 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
919 {
920         XMLNode* xml_node = get_state_node();
921         if (xml_node) {
922                 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
923         }
924 }
925
926 guint32
927 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
928 {
929         update_extra_xml_shown (true);
930         
931         return TimeAxisView::show_at (y, nth, parent);
932 }
933
934 void
935 AutomationTimeAxisView::hide ()
936 {
937         update_extra_xml_shown (false);
938
939         TimeAxisView::hide ();
940 }
941