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