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