Comment out excessive terminal output.
[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 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Route> r,
53                 boost::shared_ptr<Automatable> a, boost::shared_ptr<AutomationControl> c,
54                 PublicEditor& e, TimeAxisView& parent, bool show_regions,
55                 ArdourCanvas::Canvas& canvas, const string & nom, const string & nomparent)
56
57         : AxisView (s), 
58           TimeAxisView (s, e, &parent, canvas),
59           _route (r),
60           _control (c),
61           _automatable (a),
62           _controller(AutomationController::create(a, c->list(), c)),
63           _base_rect (0),
64           _view (show_regions ? new AutomationStreamView(*this) : NULL),
65           _name (nom),
66           height_button (_("h")),
67           clear_button (_("clear")),
68           auto_button (X_("")) /* force addition of a label */
69 {
70         if (!have_name_font) {
71                 name_font = get_font_for_style (X_("AutomationTrackName"));
72                 have_name_font = true;
73         }
74
75         automation_menu = 0;
76         auto_off_item = 0;
77         auto_touch_item = 0;
78         auto_write_item = 0;
79         auto_play_item = 0;
80         mode_discrete_item = 0;
81         mode_line_item = 0;
82
83         ignore_state_request = false;
84         first_call_to_set_height = true;
85         
86         _base_rect = new SimpleRect(*canvas_display);
87         _base_rect->property_x1() = 0.0;
88         _base_rect->property_y1() = 0.0;
89         _base_rect->property_x2() = editor.frame_to_pixel (max_frames);
90         _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
91         
92         /* outline ends and bottom */
93         _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
94         _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
95         
96         _base_rect->set_data ("trackview", this);
97
98         _base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
99                                                  _base_rect, this));
100
101         _base_rect->lower_to_bottom();
102
103         hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
104
105         height_button.set_name ("TrackSizeButton");
106         auto_button.set_name ("TrackVisualButton");
107         clear_button.set_name ("TrackVisualButton");
108         hide_button.set_name ("TrackRemoveButton");
109
110         controls_table.set_no_show_all();
111
112         ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
113         ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
114         ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
115         ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
116
117         /* rearrange the name display */
118
119         /* we never show these for automation tracks, so make
120            life easier and remove them.
121         */
122
123         hide_name_entry();
124
125         /* move the name label over a bit */
126
127         string shortpname = _name;
128         bool shortened = false;
129
130         int ignore_width;
131         shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
132
133         if (shortpname != _name ){
134                 shortened = true;
135         }
136
137         name_label.set_text (shortpname);
138         name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
139
140         if (nomparent.length()) {
141
142                 /* limit the plug name string */
143
144                 string pname = fit_to_pixels (nomparent, 60, *name_font, ignore_width, true);
145                 if (pname != nomparent) {
146                         shortened = true;
147                 }
148
149                 plugname = new Label (pname);
150                 plugname->set_name (X_("TrackPlugName"));
151                 plugname->show();
152                 name_label.set_name (X_("TrackParameterName"));
153                 controls_table.remove (name_hbox);
154                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
155                 plugname_packed = true;
156                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
157         } else {
158                 plugname = 0;
159                 plugname_packed = false;
160         }
161
162         if (shortened) {
163                 string tipname = nomparent;
164                 if (!tipname.empty()) {
165                         tipname += ": ";
166                 }
167                 tipname += _name;
168                 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
169         }
170         
171         /* add the buttons */
172         controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
173         controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
174
175         controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
176         controls_table.attach (clear_button, 5, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
177         
178         /* add bar controller */
179         controls_table.attach (*_controller.get(), 0, 8, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
180
181         controls_table.show_all ();
182
183         height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
184         clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
185         hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
186         auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
187
188         controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
189         controls_base_unselected_name = X_("AutomationTrackControlsBase");
190         controls_ebox.set_name (controls_base_unselected_name);
191
192         controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
193
194         XMLNode* xml_node = get_parent_with_state()->get_automation_child_xml_node (
195                         _control->parameter());
196
197         if (xml_node) {
198                 set_state (*xml_node);
199         } 
200                 
201         /* ask for notifications of any new RegionViews */
202         if (show_regions) {
203
204                 assert(_view);
205                 _view->attach ();
206         
207         /* no regions, just a single line for the entire track (e.g. bus gain) */
208         } else {
209         
210                 boost::shared_ptr<AutomationLine> line(new AutomationLine (
211                                         _control->parameter().to_string(),
212                                         *this,
213                                         *canvas_display,
214                                         _control->list()));
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"), 
242                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
243                 items.push_back (MenuElem (_("Play"),
244                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
245                 items.push_back (MenuElem (_("Write"),
246                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
247                 items.push_back (MenuElem (_("Touch"),
248                                            bind (mem_fun(*this, &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) { // FIXME: ew
259                         _route->set_parameter_automation_state (
260                                         _control->parameter(),
261                                         state);
262                 }
263
264                 _control->list()->set_automation_state(state);
265
266         }
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->list()->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 void
353 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
354 {
355         _control->list()->set_interpolation(style);
356                 if (_line)
357         _line->set_interpolation(style);
358 }
359
360 void
361 AutomationTimeAxisView::height_clicked ()
362 {
363         popup_size_menu (0);
364 }
365
366 void
367 AutomationTimeAxisView::clear_clicked ()
368 {
369         _session.begin_reversible_command (_("clear automation"));
370         if (_line)
371                 _line->clear ();
372         _session.commit_reversible_command ();
373 }
374
375 void
376 AutomationTimeAxisView::set_height (TrackHeight ht)
377 {
378         uint32_t h = height_to_pixels (ht);
379         bool changed = (height != (uint32_t) h) || first_call_to_set_height;
380         
381         if (first_call_to_set_height)
382                 first_call_to_set_height = false;
383
384         TimeAxisView::set_height (ht);
385         _base_rect->property_y2() = h;
386         
387         if (_line)
388                 _line->set_y_position_and_height (0, h);
389         
390         if (_view) {
391                 _view->set_height(h);
392                 _view->update_contents_y_position_and_height();
393         }
394
395         TimeAxisView* state_parent = get_parent_with_state ();
396         assert(state_parent);
397
398         XMLNode* xml_node = state_parent->get_automation_child_xml_node(_control->parameter());
399         assert(xml_node);
400
401         switch (ht) {
402         case Largest:
403                 xml_node->add_property ("track_height", "largest");
404                 break;
405
406         case Large:
407                 xml_node->add_property ("track_height", "large");
408                 break;
409
410         case Larger:
411                 xml_node->add_property ("track_height", "larger");
412                 break;
413
414         case Normal:
415                 xml_node->add_property ("track_height", "normal");
416                 break;
417
418         case Smaller:
419                 xml_node->add_property ("track_height", "smaller");
420                 break;
421
422         case Small:
423                 xml_node->add_property ("track_height", "small");
424                 break;
425         }
426
427         switch (ht) {
428                 case Large:
429                 case Larger:
430                 case Largest:
431                         _controller->show ();
432
433                 case Normal:
434                         if (ht == Normal)
435                                 _controller->hide();
436
437                         controls_table.remove (name_hbox);
438
439                         if (plugname) {
440                                 if (plugname_packed) {
441                                         controls_table.remove (*plugname);
442                                         plugname_packed = false;
443                                 }
444                                 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
445                                 plugname_packed = true;
446                                 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
447                         } else {
448                                 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
449                         }
450                         hide_name_entry ();
451                         show_name_label ();
452                         name_hbox.show_all ();
453
454                         auto_button.show();
455                         height_button.show();
456                         clear_button.show();
457                         hide_button.show_all();
458                         break;
459
460                 case Smaller:
461                         _controller->hide();
462
463                 case Small:
464
465                         controls_table.remove (name_hbox);
466                         if (plugname) {
467                                 if (plugname_packed) {
468                                         controls_table.remove (*plugname);
469                                         plugname_packed = false;
470                                 }
471                         }
472                         controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
473                         controls_table.hide_all ();
474                         hide_name_entry ();
475                         show_name_label ();
476                         name_hbox.show_all ();
477
478                         auto_button.hide();
479                         height_button.hide();
480                         clear_button.hide();
481                         hide_button.hide();
482                         break;
483         }
484
485         if (changed) {
486                 /* only emit the signal if the height really changed */
487                 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
488         }
489 }
490
491 void
492 AutomationTimeAxisView::set_samples_per_unit (double spu)
493 {
494         TimeAxisView::set_samples_per_unit (spu);
495
496         if (_line)
497                 _line->reset ();
498         
499         if (_view)
500                 _view->set_samples_per_unit (spu);
501 }
502  
503 void
504 AutomationTimeAxisView::hide_clicked ()
505 {
506         // LAME fix for refreshing the hide button
507         hide_button.set_sensitive(false);
508         
509         set_marked_for_display (false);
510         hide ();
511         
512         hide_button.set_sensitive(true);
513 }
514
515 void
516 AutomationTimeAxisView::build_display_menu ()
517 {
518         using namespace Menu_Helpers;
519
520         /* get the size menu ready */
521
522         build_size_menu ();
523
524         /* prepare it */
525
526         TimeAxisView::build_display_menu ();
527
528         /* now fill it with our stuff */
529
530         MenuList& items = display_menu->items();
531
532         items.push_back (MenuElem (_("Height"), *size_menu));
533         items.push_back (SeparatorElem());
534         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
535         items.push_back (SeparatorElem());
536         items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
537         items.push_back (SeparatorElem());
538
539         /* state menu */
540
541         Menu* auto_state_menu = manage (new Menu);
542         auto_state_menu->set_name ("ArdourContextMenu");
543         MenuList& as_items = auto_state_menu->items();
544         
545         as_items.push_back (CheckMenuElem (_("Manual"), 
546                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
547         auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
548
549         as_items.push_back (CheckMenuElem (_("Play"),
550                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
551         auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
552
553         as_items.push_back (CheckMenuElem (_("Write"),
554                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
555         auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
556
557         as_items.push_back (CheckMenuElem (_("Touch"),
558                                            bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
559         auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
560
561         items.push_back (MenuElem (_("State"), *auto_state_menu));
562         
563         /* mode menu */
564         
565         if (_control->parameter().type() == MidiCCAutomation) {
566                 Menu* auto_mode_menu = manage (new Menu);
567                 auto_mode_menu->set_name ("ArdourContextMenu");
568                 MenuList& am_items = auto_mode_menu->items();
569         
570                 RadioMenuItem::Group group;
571
572                 am_items.push_back (RadioMenuElem (group, _("Discrete"), bind (
573                                                 mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
574                                                 AutomationList::Discrete)));
575                 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
576                 //mode_discrete_item->set_active(_control->list()->interpolation() == AutomationList::Discrete);
577
578                 am_items.push_back (RadioMenuElem (group, _("Line"), bind (
579                                                 mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
580                                                 AutomationList::Linear)));
581                 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
582                 //mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear);
583
584                 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
585         }
586
587         /* make sure the automation menu state is correct */
588
589         automation_state_changed ();
590         interpolation_changed ();
591 }
592
593 void
594 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkEvent* event, nframes_t when, double y)
595 {
596         if (!_line)
597                 return;
598
599         double x = 0;
600
601         canvas_display->w2i (x, y);
602
603         /* compute vertical fractional position */
604
605         y = 1.0 - (y / height);
606
607         /* map using line */
608
609         _line->view_to_model_y (y);
610
611         _session.begin_reversible_command (_("add automation event"));
612         XMLNode& before = _control->list()->get_state();
613
614         _control->list()->add (when, y);
615
616         XMLNode& after = _control->list()->get_state();
617         _session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->list().get(), &before, &after));
618
619         _session.set_dirty ();
620 }
621
622
623 bool
624 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
625 {
626         return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
627 }
628
629 bool
630 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
631 {
632         AutomationList* what_we_got = 0;
633         boost::shared_ptr<AutomationList> alist (line.the_list());
634         bool ret = false;
635
636         XMLNode &before = alist->get_state();
637
638         switch (op) {
639         case Cut:
640                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
641                         editor.get_cut_buffer().add (what_we_got);
642                         _session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
643                         ret = true;
644                 }
645                 break;
646         case Copy:
647                 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
648                         editor.get_cut_buffer().add (what_we_got);
649                 }
650                 break;
651
652         case Clear:
653                 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
654                         _session.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
655                         delete what_we_got;
656                         what_we_got = 0;
657                         ret = true;
658                 }
659                 break;
660         }
661
662         if (what_we_got) {
663                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
664                         double foo = (*x)->value;
665                         line.model_to_view_y (foo);
666                         (*x)->value = foo;
667                 }
668         }
669
670         return ret;
671 }
672
673 void
674 AutomationTimeAxisView::reset_objects (PointSelection& selection)
675 {
676         reset_objects_one (*_line, selection);
677 }
678
679 void
680 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
681 {
682         boost::shared_ptr<AutomationList> alist(line.the_list());
683
684         _session.add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
685
686         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
687
688                 if (&(*i).track != this) {
689                         continue;
690                 }
691                 
692                 alist->reset_range ((*i).start, (*i).end);
693         }
694 }
695
696 bool
697 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
698 {
699         return cut_copy_clear_objects_one (*_line, selection, op);
700 }
701
702 bool
703 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
704 {
705         AutomationList* what_we_got = 0;
706         boost::shared_ptr<AutomationList> alist(line.the_list());
707         bool ret = false;
708
709         XMLNode &before = alist->get_state();
710
711         for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
712
713                 if (&(*i).track != this) {
714                         continue;
715                 }
716
717                 switch (op) {
718                 case Cut:
719                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
720                                 editor.get_cut_buffer().add (what_we_got);
721                                 _session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
722                                 ret = true;
723                         }
724                         break;
725                 case Copy:
726                         if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
727                                 editor.get_cut_buffer().add (what_we_got);
728                         }
729                         break;
730                         
731                 case Clear:
732                         if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
733                                 _session.add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
734                                 delete what_we_got;
735                                 what_we_got = 0;
736                                 ret = true;
737                         }
738                         break;
739                 }
740         }
741
742         delete &before;
743
744         if (what_we_got) {
745                 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
746                         double foo = (*x)->value;
747                         line.model_to_view_y (foo);
748                         (*x)->value = foo;
749                 }
750         }
751
752         return ret;
753 }
754
755 bool
756 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
757 {
758         return paste_one (*_line, pos, times, selection, nth);
759 }
760
761 bool
762 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
763 {
764         AutomationSelection::iterator p;
765         boost::shared_ptr<AutomationList> alist(line.the_list());
766         
767         for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
768
769         if (p == selection.lines.end()) {
770                 return false;
771         }
772
773         /* Make a copy of the list because we have to scale the
774            values from view coordinates to model coordinates, and we're
775            not supposed to modify the points in the selection.
776         */
777            
778         AutomationList copy (**p);
779
780         for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
781                 double foo = (*x)->value;
782                 line.view_to_model_y (foo);
783                 (*x)->value = foo;
784         }
785
786         XMLNode &before = alist->get_state();
787         alist->paste (copy, pos, times);
788         _session.add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
789
790         return true;
791 }
792
793 void
794 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
795 {
796         if (_line && touched (top, bot)) {
797                 double topfrac;
798                 double botfrac;
799
800                 /* remember: this is X Window - coordinate space starts in upper left and moves down.
801                    y_position is the "origin" or "top" of the track.
802                 */
803
804                 double mybot = y_position + height;
805
806                 if (y_position >= top && mybot <= bot) {
807
808                         /* y_position is below top, mybot is above bot, so we're fully
809                            covered vertically.
810                         */
811
812                         topfrac = 1.0;
813                         botfrac = 0.0;
814
815                 } else {
816
817                         /* top and bot are within y_position .. mybot */
818
819                         topfrac = 1.0 - ((top - y_position) / height);
820                         botfrac = 1.0 - ((bot - y_position) / height);
821                 }
822
823                 if (_line)
824                         _line->get_selectables (start, end, botfrac, topfrac, results);
825         }
826 }
827
828 void
829 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
830 {
831         if (_line)
832                 _line->get_inverted_selectables (sel, result);
833 }
834
835 void
836 AutomationTimeAxisView::set_selected_points (PointSelection& points)
837 {
838         if (_line)
839                 _line->set_selected_points (points);
840 }
841
842 void
843 AutomationTimeAxisView::clear_lines ()
844 {
845         _line.reset();
846         automation_connection.disconnect ();
847 }
848
849 void
850 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
851 {
852         assert(line);
853         assert(!_line);
854         assert(line->the_list() == _control->list());
855
856         automation_connection = _control->list()->automation_state_changed.connect
857                 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
858
859         _line = line;
860         //_controller = AutomationController::create(_session, line->the_list(), _control);
861
862         line->set_y_position_and_height (0, height);
863
864         /* pick up the current state */
865         automation_state_changed ();
866
867         line->show();
868 }
869
870 void
871 AutomationTimeAxisView::entered()
872 {
873         if (_line)
874                 _line->track_entered();
875 }
876
877 void
878 AutomationTimeAxisView::exited ()
879 {
880         if (_line)
881                 _line->track_exited();
882 }
883
884 /*void
885 AutomationTimeAxisView::set_colors ()
886 {
887     for (list<GhostRegion*>::iterator i=ghosts.begin(); i != ghosts.end(); i++ ) {
888                 (*i)->set_colors();
889     }
890     
891         if (_line)
892                 _line->set_colors();
893                 }*/
894
895 void
896 AutomationTimeAxisView::color_handler () 
897 {
898         if (_line) {
899                 _line->set_colors();
900         }
901 }
902
903 void
904 AutomationTimeAxisView::set_state (const XMLNode& node)
905 {
906         TimeAxisView::set_state (node);
907         
908         XMLNodeList kids;
909         XMLNodeConstIterator iter;
910
911         kids = node.children ();
912
913         for (iter = kids.begin(); iter != kids.end(); ++iter) {
914                 
915                 if ((*iter)->name() == state_node_name) {
916                         XMLProperty* type = (*iter)->property("automation-id");
917
918                         if (type && type->value() == _control->parameter().to_string()) {
919                                 XMLProperty *shown = (*iter)->property("shown_editor");
920
921                                 if (shown && shown->value() == "yes") {
922                                         set_marked_for_display(true);
923                                         canvas_display->show(); /* FIXME: necessary? show_at? */
924                                 }
925                                 break;
926                         }
927                 }
928         }
929
930         if (!_marked_for_display)
931                 hide();
932 }
933
934 XMLNode*
935 AutomationTimeAxisView::get_state_node ()
936 {
937         TimeAxisView* state_parent = get_parent_with_state ();
938
939         if (state_parent) {
940                 return state_parent->get_automation_child_xml_node (_control->parameter());
941         } else {
942                 return 0;
943         }
944 }
945
946 void
947 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
948 {
949         XMLNode* xml_node = get_state_node();
950         xml_node->add_property ("shown", editor_shown ? "yes" : "no");
951 }
952
953 guint32
954 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
955 {
956         update_extra_xml_shown (true);
957         
958         return TimeAxisView::show_at (y, nth, parent);
959 }
960
961 void
962 AutomationTimeAxisView::hide ()
963 {
964         update_extra_xml_shown (false);
965
966         TimeAxisView::hide ();
967 }
968