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