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