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