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