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