1 #include <ardour/route.h>
4 #include "automation_time_axis.h"
5 #include "automation_line.h"
6 #include "public_editor.h"
7 #include "simplerect.h"
9 #include "ghostregion.h"
10 #include "rgb_macros.h"
11 #include "automation_selectable.h"
12 #include "point_selection.h"
13 #include "canvas_impl.h"
18 using namespace ARDOUR;
21 using namespace Editing;
23 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, Route& r, PublicEditor& e, TimeAxisView& rent,
24 ArdourCanvas::Canvas& canvas, const string & nom,
25 const string & state_name, const string & nomparent)
28 TimeAxisView (s, e, &rent, canvas),
31 _state_name (state_name),
32 height_button (_("h")),
33 clear_button (_("clear")),
34 auto_button (X_("")) /* force addition of a label */
37 in_destructor = false;
42 ignore_state_request = false;
44 // base_rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP(canvas_display),
45 // gnome_canvas_simplerect_get_type(),
49 // "outline_color_rgba", color_map[cAutomationTrackOutline],
50 // /* outline ends and bottom */
51 // "outline_what", (guint32) (0x1|0x2|0x8),
52 // "fill_color_rgba", color_map[cAutomationTrackFill],
54 base_rect = new SimpleRect(*canvas_display);
55 base_rect->property_x1() = 0.0;
56 base_rect->property_y1() = 0.0;
57 base_rect->property_x2() = 1000000.0;
58 base_rect->property_outline_color_rgba() = color_map[cAutomationTrackOutline];
59 /* outline ends and bottom */
60 base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
61 base_rect->property_fill_color_rgba() = color_map[cAutomationTrackFill];
63 base_rect->set_data ("trackview", this);
65 base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
68 hide_button.add (*(manage (new Gtk::Image (get_xpm("small_x.xpm")))));
70 height_button.set_name ("TrackSizeButton");
71 auto_button.set_name ("TrackVisualButton");
72 clear_button.set_name ("TrackVisualButton");
73 hide_button.set_name ("TrackRemoveButton");
75 ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
76 ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
77 ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
78 ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
80 /* rearrange the name display */
82 /* we never show these for automation tracks, so make
83 life easier and remove them.
88 /* move the name label over a bit */
90 string shortpname = _name;
91 bool shortened = false;
94 if (shortpname.length() > 18) {
95 shortpname = shortpname.substr (0, 16);
100 name_label.set_text (shortpname);
101 name_label.set_alignment (1.0, 0.5);
103 if (nomparent.length()) {
105 /* limit the plug name string */
107 string pname = nomparent;
109 if (pname.length() > 14) {
110 pname = pname.substr (0, 11);
115 plugname = new Label (pname);
116 plugname->set_name (X_("TrackPlugName"));
117 plugname->set_alignment (1.0, 0.5);
118 name_label.set_name (X_("TrackParameterName"));
119 controls_table.remove (name_hbox);
120 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
121 plugname_packed = true;
122 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
125 plugname_packed = false;
129 string tipname = nomparent;
130 if (!tipname.empty()) {
134 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
137 /* add the buttons */
138 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
139 controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
141 controls_table.attach (auto_button, 6, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
142 controls_table.attach (clear_button, 6, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
144 controls_table.show_all ();
146 height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
147 clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
148 hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
149 auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
151 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
152 controls_base_unselected_name = X_("AutomationTrackControlsBase");
153 controls_ebox.set_name (controls_base_unselected_name);
155 controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
157 XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
158 set_state (*xml_node);
160 /* make sure labels etc. are correct */
162 automation_state_changed ();
165 AutomationTimeAxisView::~AutomationTimeAxisView ()
167 in_destructor = true;
169 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
175 AutomationTimeAxisView::auto_clicked ()
177 using namespace Menu_Helpers;
179 if (automation_menu == 0) {
180 automation_menu = manage (new Menu);
181 automation_menu->set_name ("ArdourContextMenu");
182 MenuList& items (automation_menu->items());
184 items.push_back (MenuElem (_("Manual"),
185 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
186 items.push_back (MenuElem (_("Play"),
187 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
188 items.push_back (MenuElem (_("Write"),
189 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
190 items.push_back (MenuElem (_("Touch"),
191 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
194 automation_menu->popup (1, 0);
199 AutomationTimeAxisView::automation_state_changed ()
203 /* update button label */
208 state = lines.front()->the_list().automation_state ();
211 switch (state & (Off|Play|Touch|Write)) {
213 auto_button.set_label (_("Manual"));
215 ignore_state_request = true;
216 auto_off_item->set_active (true);
217 auto_play_item->set_active (false);
218 auto_touch_item->set_active (false);
219 auto_write_item->set_active (false);
220 ignore_state_request = false;
224 auto_button.set_label (_("Play"));
225 if (auto_play_item) {
226 ignore_state_request = true;
227 auto_play_item->set_active (true);
228 auto_off_item->set_active (false);
229 auto_touch_item->set_active (false);
230 auto_write_item->set_active (false);
231 ignore_state_request = false;
235 auto_button.set_label (_("Write"));
236 if (auto_write_item) {
237 ignore_state_request = true;
238 auto_write_item->set_active (true);
239 auto_off_item->set_active (false);
240 auto_play_item->set_active (false);
241 auto_touch_item->set_active (false);
242 ignore_state_request = false;
246 auto_button.set_label (_("Touch"));
247 if (auto_touch_item) {
248 ignore_state_request = true;
249 auto_touch_item->set_active (true);
250 auto_off_item->set_active (false);
251 auto_play_item->set_active (false);
252 auto_write_item->set_active (false);
253 ignore_state_request = false;
257 auto_button.set_label (_("???"));
263 AutomationTimeAxisView::height_clicked ()
269 AutomationTimeAxisView::clear_clicked ()
271 _session.begin_reversible_command (_("clear automation"));
272 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
275 _session.commit_reversible_command ();
279 AutomationTimeAxisView::set_height (TrackHeight ht)
281 uint32_t h = height_to_pixels (ht);
282 bool changed = (height != (uint32_t) h);
284 TimeAxisView* state_parent = get_parent_with_state ();
285 XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
287 //controls_table.show_all ();
289 TimeAxisView::set_height (ht);
290 base_rect->property_y2() = h;
292 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
293 (*i)->set_height (h);
296 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
302 xml_node->add_property ("track_height", "largest");
303 controls_table.remove (name_hbox);
305 if (plugname_packed) {
306 controls_table.remove (*plugname);
307 plugname_packed = false;
309 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
310 plugname_packed = true;
311 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
313 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
315 controls_table.show_all ();
322 xml_node->add_property ("track_height", "large");
323 controls_table.remove (name_hbox);
325 if (plugname_packed) {
326 controls_table.remove (*plugname);
327 plugname_packed = false;
329 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
330 plugname_packed = true;
332 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
334 controls_table.show_all ();
340 xml_node->add_property ("track_height", "larger");
341 controls_table.remove (name_hbox);
343 if (plugname_packed) {
344 controls_table.remove (*plugname);
345 plugname_packed = false;
347 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
348 plugname_packed = true;
350 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
352 controls_table.show_all ();
358 xml_node->add_property ("track_height", "normal");
359 controls_table.remove (name_hbox);
361 if (plugname_packed) {
362 controls_table.remove (*plugname);
363 plugname_packed = false;
365 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
366 plugname_packed = true;
367 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
369 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
371 controls_table.show_all ();
377 xml_node->add_property ("track_height", "smaller");
378 controls_table.remove (name_hbox);
380 if (plugname_packed) {
381 controls_table.remove (*plugname);
382 plugname_packed = false;
385 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
386 controls_table.hide_all ();
389 name_hbox.show_all ();
390 controls_table.show ();
394 xml_node->add_property ("track_height", "small");
395 controls_table.remove (name_hbox);
397 if (plugname_packed) {
398 controls_table.remove (*plugname);
399 plugname_packed = false;
402 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
403 controls_table.hide_all ();
406 name_hbox.show_all ();
407 controls_table.show ();
412 /* only emit the signal if the height really changed */
413 route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
418 AutomationTimeAxisView::set_samples_per_unit (double spu)
420 TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
422 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
428 AutomationTimeAxisView::hide_clicked ()
430 set_marked_for_display (false);
435 AutomationTimeAxisView::build_display_menu ()
437 using namespace Menu_Helpers;
439 /* get the size menu ready */
445 TimeAxisView::build_display_menu ();
447 /* now fill it with our stuff */
449 MenuList& items = display_menu->items();
451 items.push_back (MenuElem (_("Height"), *size_menu));
452 items.push_back (SeparatorElem());
453 items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
454 items.push_back (SeparatorElem());
455 items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
456 items.push_back (SeparatorElem());
458 Menu* auto_state_menu = manage (new Menu);
459 auto_state_menu->set_name ("ArdourContextMenu");
460 MenuList& as_items = auto_state_menu->items();
462 as_items.push_back (CheckMenuElem (_("Manual"),
463 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
464 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
466 as_items.push_back (CheckMenuElem (_("Play"),
467 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
468 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
470 as_items.push_back (CheckMenuElem (_("Write"),
471 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
472 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
474 as_items.push_back (CheckMenuElem (_("Touch"),
475 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
476 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
478 items.push_back (MenuElem (_("State"), *auto_state_menu));
480 /* make sure the automation menu state is correct */
482 automation_state_changed ();
486 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
490 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
491 ret = cut_copy_clear_one ((**i), selection, op);
498 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
500 AutomationList* what_we_got = 0;
501 AutomationList& alist (line.the_list());
504 _session.add_undo (alist.get_memento());
508 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
509 editor.get_cut_buffer().add (what_we_got);
510 _session.add_redo_no_execute (alist.get_memento());
515 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
516 editor.get_cut_buffer().add (what_we_got);
521 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
522 _session.add_redo_no_execute (alist.get_memento());
531 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
532 double foo = (*x)->value;
533 line.model_to_view_y (foo);
542 AutomationTimeAxisView::reset_objects (PointSelection& selection)
544 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
545 reset_objects_one ((**i), selection);
550 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
552 AutomationList& alist (line.the_list());
554 _session.add_undo (alist.get_memento());
556 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
558 if (&(*i).track != this) {
562 alist.reset_range ((*i).start, (*i).end);
567 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
571 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
572 ret = cut_copy_clear_objects_one ((**i), selection, op);
579 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
581 AutomationList* what_we_got = 0;
582 AutomationList& alist (line.the_list());
585 _session.add_undo (alist.get_memento());
587 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
589 if (&(*i).track != this) {
595 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
596 editor.get_cut_buffer().add (what_we_got);
597 _session.add_redo_no_execute (alist.get_memento());
602 if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
603 editor.get_cut_buffer().add (what_we_got);
608 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
609 _session.add_redo_no_execute (alist.get_memento());
619 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
620 double foo = (*x)->value;
621 line.model_to_view_y (foo);
630 AutomationTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
634 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
635 ret = paste_one (**i, pos, times, selection, nth);
642 AutomationTimeAxisView::paste_one (AutomationLine& line, jack_nframes_t pos, float times, Selection& selection, size_t nth)
644 AutomationSelection::iterator p;
645 AutomationList& alist (line.the_list());
647 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
649 if (p == selection.lines.end()) {
653 /* Make a copy of the list because we have to scale the
654 values from view coordinates to model coordinates, and we're
655 not supposed to modify the points in the selection.
658 AutomationList copy (**p);
660 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
661 double foo = (*x)->value;
662 line.view_to_model_y (foo);
666 _session.add_undo (alist.get_memento());
667 alist.paste (copy, pos, times);
668 _session.add_redo_no_execute (alist.get_memento());
674 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
676 ghosts.push_back (gr);
677 gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
681 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
687 list<GhostRegion*>::iterator i;
689 for (i = ghosts.begin(); i != ghosts.end(); ++i) {
698 AutomationTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
700 if (!lines.empty() && touched (top, bot)) {
704 /* remember: this is X Window - coordinate space starts in upper left and moves down.
705 y_position is the "origin" or "top" of the track.
708 double mybot = y_position + height; // XXX need to include Editor::track_spacing;
710 if (y_position >= top && mybot <= bot) {
712 /* y_position is below top, mybot is above bot, so we're fully
721 /* top and bot are within y_position .. mybot */
723 topfrac = 1.0 - ((top - y_position) / height);
724 botfrac = 1.0 - ((bot - y_position) / height);
727 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
728 (*i)->get_selectables (start, end, botfrac, topfrac, results);
734 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
736 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
737 (*i)->get_inverted_selectables (sel, result);
742 AutomationTimeAxisView::set_selected_points (PointSelection& points)
744 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
745 (*i)->set_selected_points (points);
750 AutomationTimeAxisView::clear_lines ()
752 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
757 automation_connection.disconnect ();
761 AutomationTimeAxisView::add_line (AutomationLine& line)
766 /* first line is the Model for automation state */
767 automation_connection = line.the_list().automation_state_changed.connect
768 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
772 lines.push_back (&line);
773 line.set_height (height);
776 /* pick up the current state */
777 automation_state_changed ();
782 AutomationTimeAxisView::show_all_control_points ()
784 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
785 (*i)->show_all_control_points ();
790 AutomationTimeAxisView::hide_all_but_selected_control_points ()
792 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
793 (*i)->hide_all_but_selected_control_points ();
798 AutomationTimeAxisView::entered()
800 show_all_control_points ();
804 AutomationTimeAxisView::exited ()
806 hide_all_but_selected_control_points ();
810 AutomationTimeAxisView::set_state (const XMLNode& node)
812 TimeAxisView::set_state (node);
816 AutomationTimeAxisView::get_state_node ()
818 TimeAxisView* state_parent = get_parent_with_state ();
821 return state_parent->get_child_xml_node (_state_name);