e9cb98ca34fb24e418139337f90dfcc6c9431f72
[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 /** The interpolation style of our AutomationList has changed, so update */
350 void
351 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
352 {
353         if (mode_line_item && mode_discrete_item) {
354                 if (s == AutomationList::Discrete) {
355                         mode_discrete_item->set_active(true);
356                         mode_line_item->set_active(false);
357                 } else {
358                         mode_line_item->set_active(true);
359                         mode_discrete_item->set_active(false);
360                 }
361         }
362 }
363
364 /** A menu item has been selected to change our interpolation mode */
365 void
366 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
367 {
368         /* Tell our view's list, if we have one, otherwise tell our own.
369          * Everything else will be signalled back from that.
370          */
371         
372         if (_view) {
373                 _view->set_interpolation (style);
374         } else {
375                 _control->list()->set_interpolation (style);
376         }
377 }
378
379 void
380 AutomationTimeAxisView::clear_clicked ()
381 {
382         assert (_line || _view);
383         
384         _session->begin_reversible_command (_("clear automation"));
385         
386         if (_line) {
387                 _line->clear ();
388         } else if (_view) {
389                 _view->clear ();
390         }
391
392         _session->commit_reversible_command ();
393         _session->set_dirty ();
394 }
395
396 void
397 AutomationTimeAxisView::set_height (uint32_t h)
398 {
399         bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
400         uint32_t const normal = preset_height (HeightNormal);
401         bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
402
403         TimeAxisView* state_parent = get_parent_with_state ();
404         assert(state_parent);
405         XMLNode* xml_node = state_parent->get_automation_child_xml_node (_control->parameter());
406
407         TimeAxisView::set_height (h);
408         _base_rect->property_y2() = h;
409
410         if (_line)
411                 _line->set_height(h);
412
413         if (_view) {
414                 _view->set_height(h);
415                 _view->update_contents_height();
416         }
417
418         char buf[32];
419         snprintf (buf, sizeof (buf), "%u", height);
420         if (xml_node) {
421                 xml_node->add_property ("height", buf);
422         }
423
424         if (changed_between_small_and_normal || first_call_to_set_height) {
425
426                 first_call_to_set_height = false;
427
428                 if (h >= preset_height (HeightNormal)) {
429                         controls_table.remove (name_hbox);
430
431                         if (plugname) {
432                                 if (plugname_packed) {
433                                         controls_table.remove (*plugname);
434                                         plugname_packed = false;
435                                 }
436                                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
437                                 plugname_packed = true;
438                                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
439                         } else {
440                                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
441                         }
442                         hide_name_entry ();
443                         show_name_label ();
444                         name_hbox.show_all ();
445
446                         auto_button.show();
447                         hide_button.show_all();
448
449                 } else if (h >= preset_height (HeightSmall)) {
450                         controls_table.remove (name_hbox);
451                         if (plugname) {
452                                 if (plugname_packed) {
453                                         controls_table.remove (*plugname);
454                                         plugname_packed = false;
455                                 }
456                         }
457                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
458                         controls_table.hide_all ();
459                         hide_name_entry ();
460                         show_name_label ();
461                         name_hbox.show_all ();
462
463                         auto_button.hide();
464                         hide_button.hide();
465                 }
466         } else if (h >= preset_height (HeightNormal)) {
467                 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
468         }
469
470         if (changed) {
471                 if (canvas_item_visible (_canvas_display)) {
472                         /* only emit the signal if the height really changed and we were visible */
473                         _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
474                 }
475         }
476 }
477
478 void
479 AutomationTimeAxisView::set_samples_per_unit (double spu)
480 {
481         TimeAxisView::set_samples_per_unit (spu);
482
483         if (_line)
484                 _line->reset ();
485
486         if (_view)
487                 _view->set_samples_per_unit (spu);
488 }
489
490 void
491 AutomationTimeAxisView::hide_clicked ()
492 {
493         // LAME fix for refreshing the hide button
494         hide_button.set_sensitive(false);
495
496         set_marked_for_display (false);
497         hide ();
498
499         hide_button.set_sensitive(true);
500 }
501
502 void
503 AutomationTimeAxisView::build_display_menu ()
504 {
505         using namespace Menu_Helpers;
506
507         /* prepare it */
508
509         TimeAxisView::build_display_menu ();
510
511         /* now fill it with our stuff */
512
513         MenuList& items = display_menu->items();
514
515         items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
516         items.push_back (SeparatorElem());
517         items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
518         items.push_back (SeparatorElem());
519
520         /* state menu */
521
522         Menu* auto_state_menu = manage (new Menu);
523         auto_state_menu->set_name ("ArdourContextMenu");
524         MenuList& as_items = auto_state_menu->items();
525
526         as_items.push_back (CheckMenuElem (_("Manual"), sigc::bind (
527                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
528                         (AutoState) Off)));
529         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
530
531         as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
532                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
533                         (AutoState) Play)));
534         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
535
536         as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
537                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
538                         (AutoState) Write)));
539         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
540
541         as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
542                         sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
543                         (AutoState) Touch)));
544         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
545
546         items.push_back (MenuElem (_("State"), *auto_state_menu));
547
548         /* mode menu */
549
550         /* current interpolation state */
551         AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
552
553         if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
554
555                 Menu* auto_mode_menu = manage (new Menu);
556                 auto_mode_menu->set_name ("ArdourContextMenu");
557                 MenuList& am_items = auto_mode_menu->items();
558
559                 RadioMenuItem::Group group;
560
561                 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
562                                 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
563                                 AutomationList::Discrete)));
564                 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
565                 mode_discrete_item->set_active (s == AutomationList::Discrete);
566
567                 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
568                                 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
569                                 AutomationList::Linear)));
570                 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
571                 mode_line_item->set_active (s == AutomationList::Linear);
572
573                 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
574         }
575
576         /* make sure the automation menu state is correct */
577
578         automation_state_changed ();
579         interpolation_changed (s);
580 }
581
582 void
583 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, nframes_t when, double y)
584 {
585         if (!_line)
586                 return;
587
588         double x = 0;
589
590         _canvas_display->w2i (x, y);
591
592         /* compute vertical fractional position */
593
594         y = 1.0 - (y / height);
595
596         /* map using line */
597
598         _line->view_to_model_coord (x, y);
599
600         _session->begin_reversible_command (_("add automation event"));
601         XMLNode& before = _control->alist()->get_state();
602
603         _control->alist()->add (when, y);
604
605         XMLNode& after = _control->alist()->get_state();
606         _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
607
608         _session->set_dirty ();
609 }
610
611 bool
612 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
613 {
614         return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
615 }
616
617 bool
618 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
619 {
620         boost::shared_ptr<Evoral::ControlList> what_we_got;
621         boost::shared_ptr<AutomationList> alist (line.the_list());
622         bool ret = false;
623
624         XMLNode &before = alist->get_state();
625
626         switch (op) {
627         case Cut:
628                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
629                         _editor.get_cut_buffer().add (what_we_got);
630                         _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
631                         ret = true;
632                 }
633                 break;
634         case Copy:
635                 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
636                         _editor.get_cut_buffer().add (what_we_got);
637                 }
638                 break;
639
640         case Clear:
641                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
642                         _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
643                         ret = true;
644                 }
645                 break;
646         }
647
648         if (what_we_got) {
649                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
650                         double when = (*x)->when;
651                         double val  = (*x)->value;
652                         line.model_to_view_coord (when, val);
653                         (*x)->when = when;
654                         (*x)->value = val;
655                 }
656         }
657
658         return ret;
659 }
660
661 void
662 AutomationTimeAxisView::reset_objects (PointSelection& selection)
663 {
664         reset_objects_one (*_line, selection);
665 }
666
667 void
668 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
669 {
670         boost::shared_ptr<AutomationList> alist(line.the_list());
671
672         _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
673
674         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
675
676                 if ((*i).track != this) {
677                         continue;
678                 }
679
680                 alist->reset_range ((*i).start, (*i).end);
681         }
682 }
683
684 bool
685 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
686 {
687         return cut_copy_clear_objects_one (*_line, selection, op);
688 }
689
690 bool
691 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
692 {
693         boost::shared_ptr<Evoral::ControlList> what_we_got;
694         boost::shared_ptr<AutomationList> alist(line.the_list());
695         bool ret = false;
696
697         XMLNode &before = alist->get_state();
698
699         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
700
701                 if ((*i).track != this) {
702                         continue;
703                 }
704
705                 switch (op) {
706                 case Cut:
707                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
708                                 _editor.get_cut_buffer().add (what_we_got);
709                                 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
710                                 ret = true;
711                         }
712                         break;
713                 case Copy:
714                         if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
715                                 _editor.get_cut_buffer().add (what_we_got);
716                         }
717                         break;
718
719                 case Clear:
720                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
721                                 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
722                                 ret = true;
723                         }
724                         break;
725                 }
726         }
727
728         delete &before;
729
730         if (what_we_got) {
731                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
732                         double when = (*x)->when;
733                         double val  = (*x)->value;
734                         line.model_to_view_coord (when, val);
735                         (*x)->when = when;
736                         (*x)->value = val;
737                 }
738         }
739
740         return ret;
741 }
742
743 bool
744 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
745 {
746         return paste_one (*_line, pos, times, selection, nth);
747 }
748
749 bool
750 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
751 {
752         AutomationSelection::iterator p;
753         boost::shared_ptr<AutomationList> alist(line.the_list());
754
755         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
756
757         if (p == selection.lines.end()) {
758                 return false;
759         }
760
761         /* Make a copy of the list because we have to scale the
762            values from view coordinates to model coordinates, and we're
763            not supposed to modify the points in the selection.
764         */
765
766         AutomationList copy (**p);
767
768         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
769                 double when = (*x)->when;
770                 double val  = (*x)->value;
771                 line.view_to_model_coord (when, val);
772                 (*x)->when = when;
773                 (*x)->value = val;
774         }
775
776         XMLNode &before = alist->get_state();
777         alist->paste (copy, pos, times);
778         _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
779
780         return true;
781 }
782
783 void
784 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
785 {
786         if (_line && touched (top, bot)) {
787                 double topfrac;
788                 double botfrac;
789
790                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
791                    _y_position is the "origin" or "top" of the track.
792                 */
793
794                 double mybot = _y_position + height;
795
796                 if (_y_position >= top && mybot <= bot) {
797
798                         /* _y_position is below top, mybot is above bot, so we're fully
799                            covered vertically.
800                         */
801
802                         topfrac = 1.0;
803                         botfrac = 0.0;
804
805                 } else {
806
807                         /* top and bot are within _y_position .. mybot */
808
809                         topfrac = 1.0 - ((top - _y_position) / height);
810                         botfrac = 1.0 - ((bot - _y_position) / height);
811                 }
812
813                 if (_line)
814                         _line->get_selectables (start, end, botfrac, topfrac, results);
815         }
816 }
817
818 void
819 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
820 {
821         if (_line)
822                 _line->get_inverted_selectables (sel, result);
823 }
824
825 void
826 AutomationTimeAxisView::set_selected_points (PointSelection& points)
827 {
828         if (_line) {
829                 _line->set_selected_points (points);
830         }
831 }
832
833 void
834 AutomationTimeAxisView::clear_lines ()
835 {
836         _line.reset();
837         _list_connections.drop_connections ();
838 }
839
840 void
841 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
842 {
843         assert(line);
844         assert(!_line);
845         assert(line->the_list() == _control->list());
846
847         _control->alist()->automation_state_changed.connect (
848                 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
849                 );
850         
851         _control->alist()->InterpolationChanged.connect (
852                 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
853                 );
854
855         _line = line;
856         //_controller = AutomationController::create(_session, line->the_list(), _control);
857
858         line->set_height (height);
859
860         /* pick up the current state */
861         automation_state_changed ();
862
863         line->show();
864 }
865
866 void
867 AutomationTimeAxisView::entered()
868 {
869         if (_line)
870                 _line->track_entered();
871 }
872
873 void
874 AutomationTimeAxisView::exited ()
875 {
876         if (_line)
877                 _line->track_exited();
878 }
879
880 void
881 AutomationTimeAxisView::color_handler ()
882 {
883         if (_line) {
884                 _line->set_colors();
885         }
886 }
887
888 int
889 AutomationTimeAxisView::set_state (const XMLNode& node, int version)
890 {
891         TimeAxisView::set_state (node, version);
892
893         XMLProperty const * type = node.property ("automation-id");
894         if (type && type->value () == ARDOUR::EventTypeMap::instance().to_symbol (_control->parameter())) {
895                 XMLProperty const * shown = node.property ("shown");
896                 if (shown && shown->value () == "yes") {
897                         set_marked_for_display (true);
898                         _canvas_display->show (); /* FIXME: necessary? show_at? */
899                 }
900         }
901
902         if (!_marked_for_display) {
903                 hide();
904         }
905
906         return 0;
907 }
908
909 XMLNode*
910 AutomationTimeAxisView::get_state_node ()
911 {
912         TimeAxisView* state_parent = get_parent_with_state ();
913
914         if (state_parent) {
915                 return state_parent->get_automation_child_xml_node (_control->parameter());
916         } else {
917                 return 0;
918         }
919 }
920
921 void
922 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
923 {
924         XMLNode* xml_node = get_state_node();
925         if (xml_node) {
926                 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
927         }
928 }
929
930 guint32
931 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
932 {
933         update_extra_xml_shown (true);
934
935         return TimeAxisView::show_at (y, nth, parent);
936 }
937
938 void
939 AutomationTimeAxisView::hide ()
940 {
941         update_extra_xml_shown (false);
942
943         TimeAxisView::hide ();
944 }
945
946 bool
947 AutomationTimeAxisView::set_visibility (bool yn)
948 {
949         bool changed = TimeAxisView::set_visibility (yn);
950
951         if (changed) {
952                 get_state_node()->add_property ("shown", yn ? X_("yes") : X_("no"));
953         }
954
955         return changed;
956 }
957
958 /** @return true if this view has any automation data to display */
959 bool
960 AutomationTimeAxisView::has_automation () const
961 {
962         return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
963 }