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;
20 using namespace Editing;
22 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, Route& r, PublicEditor& e, TimeAxisView& rent,
23 ArdourCanvas::Canvas& canvas, const string & nom,
24 const string & state_name, const string & nomparent)
27 TimeAxisView (s, e, &rent, canvas),
30 _state_name (state_name),
31 height_button (_("h")),
32 clear_button (_("clear")),
33 auto_button (X_("")) /* force addition of a label */
36 in_destructor = false;
41 ignore_state_request = false;
43 // base_rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP(canvas_display),
44 // gnome_canvas_simplerect_get_type(),
48 // "outline_color_rgba", color_map[cAutomationTrackOutline],
49 // /* outline ends and bottom */
50 // "outline_what", (guint32) (0x1|0x2|0x8),
51 // "fill_color_rgba", color_map[cAutomationTrackFill],
53 base_rect = new SimpleRect(*canvas_display);
54 base_rect->property_x1() = 0.0;
55 base_rect->property_y1() = 0.0;
56 base_rect->property_x2() = 1000000.0;
57 base_rect->property_outline_color_rgba() = color_map[cAutomationTrackOutline];
58 /* outline ends and bottom */
59 base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
60 base_rect->property_fill_color_rgba() = color_map[cAutomationTrackFill];
62 base_rect->set_data ("trackview", this);
64 base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
67 hide_button.add (*(manage (new Gtk::Image (get_xpm("small_x.xpm")))));
69 height_button.set_name ("TrackSizeButton");
70 auto_button.set_name ("TrackVisualButton");
71 clear_button.set_name ("TrackVisualButton");
72 hide_button.set_name ("TrackRemoveButton");
74 ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
75 ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
76 ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
77 ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
79 /* rearrange the name display */
81 /* we never show these for automation tracks, so make
82 life easier and remove them.
87 /* move the name label over a bit */
89 string shortpname = _name;
90 bool shortened = false;
93 if (shortpname.length() > 18) {
94 shortpname = shortpname.substr (0, 16);
99 name_label.set_text (shortpname);
100 name_label.set_alignment (1.0, 0.5);
102 if (nomparent.length()) {
104 /* limit the plug name string */
106 string pname = nomparent;
108 if (pname.length() > 14) {
109 pname = pname.substr (0, 11);
114 plugname = new Label (pname);
115 plugname->set_name (X_("TrackPlugName"));
116 plugname->set_alignment (1.0, 0.5);
117 name_label.set_name (X_("TrackParameterName"));
118 controls_table.remove (name_hbox);
119 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
120 plugname_packed = true;
121 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
124 plugname_packed = false;
128 string tipname = nomparent;
129 if (!tipname.empty()) {
133 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
136 /* add the buttons */
137 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
138 controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
140 controls_table.attach (auto_button, 6, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
141 controls_table.attach (clear_button, 6, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
143 controls_table.show_all ();
145 height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
146 clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
147 hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
148 auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
150 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
151 controls_base_unselected_name = X_("AutomationTrackControlsBase");
152 controls_ebox.set_name (controls_base_unselected_name);
154 controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
156 XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
157 set_state (*xml_node);
159 /* make sure labels etc. are correct */
161 automation_state_changed ();
164 AutomationTimeAxisView::~AutomationTimeAxisView ()
166 in_destructor = true;
168 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
174 AutomationTimeAxisView::auto_clicked ()
176 using namespace Menu_Helpers;
178 if (automation_menu == 0) {
179 automation_menu = manage (new Menu);
180 automation_menu->set_name ("ArdourContextMenu");
181 MenuList& items (automation_menu->items());
183 items.push_back (MenuElem (_("Isolate"),
184 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
185 items.push_back (MenuElem (_("Play"),
186 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
187 items.push_back (MenuElem (_("Write"),
188 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
189 items.push_back (MenuElem (_("Touch"),
190 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
193 automation_menu->popup (1, 0);
198 AutomationTimeAxisView::automation_state_changed ()
202 /* update button label */
207 state = lines.front()->the_list().automation_state ();
210 switch (state & (Off|Play|Touch|Write)) {
212 auto_button.set_label (_("Isolate"));
214 ignore_state_request = true;
215 auto_off_item->set_active (true);
216 auto_play_item->set_active (false);
217 auto_touch_item->set_active (false);
218 auto_write_item->set_active (false);
219 ignore_state_request = false;
223 auto_button.set_label (_("Play"));
224 if (auto_play_item) {
225 ignore_state_request = true;
226 auto_play_item->set_active (true);
227 auto_off_item->set_active (false);
228 auto_touch_item->set_active (false);
229 auto_write_item->set_active (false);
230 ignore_state_request = false;
234 auto_button.set_label (_("Write"));
235 if (auto_write_item) {
236 ignore_state_request = true;
237 auto_write_item->set_active (true);
238 auto_off_item->set_active (false);
239 auto_play_item->set_active (false);
240 auto_touch_item->set_active (false);
241 ignore_state_request = false;
245 auto_button.set_label (_("Touch"));
246 if (auto_touch_item) {
247 ignore_state_request = true;
248 auto_touch_item->set_active (true);
249 auto_off_item->set_active (false);
250 auto_play_item->set_active (false);
251 auto_write_item->set_active (false);
252 ignore_state_request = false;
256 auto_button.set_label (_("???"));
262 AutomationTimeAxisView::height_clicked ()
268 AutomationTimeAxisView::clear_clicked ()
270 _session.begin_reversible_command (_("clear automation"));
271 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
274 _session.commit_reversible_command ();
278 AutomationTimeAxisView::set_height (TrackHeight ht)
280 uint32_t h = height_to_pixels (ht);
281 bool changed = (height != (uint32_t) h);
283 TimeAxisView* state_parent = get_parent_with_state ();
284 XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
286 controls_table.show_all ();
288 TimeAxisView::set_height (ht);
289 base_rect->property_y2() = h;
291 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
292 (*i)->set_height (h);
295 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
301 xml_node->add_property ("track_height", "largest");
302 controls_table.remove (name_hbox);
304 if (plugname_packed) {
305 controls_table.remove (*plugname);
306 plugname_packed = false;
308 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
309 plugname_packed = true;
310 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
312 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
314 controls_table.show_all ();
320 xml_node->add_property ("track_height", "large");
321 controls_table.remove (name_hbox);
323 if (plugname_packed) {
324 controls_table.remove (*plugname);
325 plugname_packed = false;
327 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
328 plugname_packed = true;
330 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
332 controls_table.show_all ();
338 xml_node->add_property ("track_height", "larger");
339 controls_table.remove (name_hbox);
341 if (plugname_packed) {
342 controls_table.remove (*plugname);
343 plugname_packed = false;
345 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
346 plugname_packed = true;
348 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
350 controls_table.show_all ();
356 xml_node->add_property ("track_height", "normal");
357 controls_table.remove (name_hbox);
359 if (plugname_packed) {
360 controls_table.remove (*plugname);
361 plugname_packed = false;
363 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
364 plugname_packed = true;
365 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
367 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
369 controls_table.show_all ();
375 xml_node->add_property ("track_height", "smaller");
376 controls_table.remove (name_hbox);
378 if (plugname_packed) {
379 controls_table.remove (*plugname);
380 plugname_packed = false;
383 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
384 controls_table.hide_all ();
387 name_hbox.show_all ();
388 controls_table.show ();
392 xml_node->add_property ("track_height", "small");
393 controls_table.remove (name_hbox);
395 if (plugname_packed) {
396 controls_table.remove (*plugname);
397 plugname_packed = false;
400 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
401 controls_table.hide_all ();
404 name_hbox.show_all ();
405 controls_table.show ();
410 /* only emit the signal if the height really changed */
411 route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
416 AutomationTimeAxisView::set_samples_per_unit (double spu)
418 TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
420 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
426 AutomationTimeAxisView::hide_clicked ()
428 set_marked_for_display (false);
433 AutomationTimeAxisView::build_display_menu ()
435 using namespace Menu_Helpers;
437 /* get the size menu ready */
443 TimeAxisView::build_display_menu ();
445 /* now fill it with our stuff */
447 MenuList& items = display_menu->items();
449 items.push_back (MenuElem (_("Height"), *size_menu));
450 items.push_back (SeparatorElem());
451 items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
452 items.push_back (SeparatorElem());
453 items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
454 items.push_back (SeparatorElem());
456 Menu* auto_state_menu = manage (new Menu);
457 auto_state_menu->set_name ("ArdourContextMenu");
458 MenuList& as_items = auto_state_menu->items();
460 as_items.push_back (CheckMenuElem (_("Isolate"),
461 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
462 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
464 as_items.push_back (CheckMenuElem (_("Play"),
465 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
466 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
468 as_items.push_back (CheckMenuElem (_("Write"),
469 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
470 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
472 as_items.push_back (CheckMenuElem (_("Touch"),
473 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
474 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
476 items.push_back (MenuElem (_("State"), *auto_state_menu));
478 /* make sure the automation menu state is correct */
480 automation_state_changed ();
484 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
488 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
489 ret = cut_copy_clear_one ((**i), selection, op);
496 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
498 AutomationList* what_we_got = 0;
499 AutomationList& alist (line.the_list());
502 _session.add_undo (alist.get_memento());
506 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
507 editor.get_cut_buffer().add (what_we_got);
508 _session.add_redo_no_execute (alist.get_memento());
513 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
514 editor.get_cut_buffer().add (what_we_got);
519 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
520 _session.add_redo_no_execute (alist.get_memento());
529 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
530 double foo = (*x)->value;
531 line.model_to_view_y (foo);
540 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
544 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
545 ret = cut_copy_clear_objects_one ((**i), selection, op);
552 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
554 AutomationList* what_we_got = 0;
555 AutomationList& alist (line.the_list());
558 _session.add_undo (alist.get_memento());
560 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
562 if (&(*i).track != this) {
568 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
569 editor.get_cut_buffer().add (what_we_got);
570 _session.add_redo_no_execute (alist.get_memento());
575 if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
576 editor.get_cut_buffer().add (what_we_got);
581 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
582 _session.add_redo_no_execute (alist.get_memento());
592 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
593 double foo = (*x)->value;
594 line.model_to_view_y (foo);
603 AutomationTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
607 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
608 ret = paste_one (**i, pos, times, selection, nth);
615 AutomationTimeAxisView::paste_one (AutomationLine& line, jack_nframes_t pos, float times, Selection& selection, size_t nth)
617 AutomationSelection::iterator p;
618 AutomationList& alist (line.the_list());
620 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
622 if (p == selection.lines.end()) {
626 /* Make a copy of the list because we have to scale the
627 values from view coordinates to model coordinates, and we're
628 not supposed to modify the points in the selection.
631 AutomationList copy (**p);
633 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
634 double foo = (*x)->value;
635 line.view_to_model_y (foo);
639 _session.add_undo (alist.get_memento());
640 alist.paste (copy, pos, times);
641 _session.add_redo_no_execute (alist.get_memento());
647 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
649 ghosts.push_back (gr);
650 gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
654 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
660 list<GhostRegion*>::iterator i;
662 for (i = ghosts.begin(); i != ghosts.end(); ++i) {
671 AutomationTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
673 if (!lines.empty() && touched (top, bot)) {
677 /* remember: this is X Window - coordinate space starts in upper left and moves down.
678 y_position is the "origin" or "top" of the track.
681 double mybot = y_position + height; // XXX need to include Editor::track_spacing;
683 if (y_position >= top && mybot <= bot) {
685 /* y_position is below top, mybot is above bot, so we're fully
694 /* top and bot are within y_position .. mybot */
696 topfrac = 1.0 - ((top - y_position) / height);
697 botfrac = 1.0 - ((bot - y_position) / height);
700 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
701 (*i)->get_selectables (start, end, botfrac, topfrac, results);
707 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
709 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
710 (*i)->get_inverted_selectables (sel, result);
715 AutomationTimeAxisView::set_selected_points (PointSelection& points)
717 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
718 (*i)->set_selected_points (points);
723 AutomationTimeAxisView::clear_lines ()
725 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
730 automation_connection.disconnect ();
734 AutomationTimeAxisView::add_line (AutomationLine& line)
739 /* first line is the Model for automation state */
740 automation_connection = line.the_list().automation_state_changed.connect
741 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
745 lines.push_back (&line);
746 line.set_height (height);
749 /* pick up the current state */
750 automation_state_changed ();
755 AutomationTimeAxisView::show_all_control_points ()
757 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
758 (*i)->show_all_control_points ();
763 AutomationTimeAxisView::hide_all_but_selected_control_points ()
765 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
766 (*i)->hide_all_but_selected_control_points ();
771 AutomationTimeAxisView::entered()
773 show_all_control_points ();
777 AutomationTimeAxisView::exited ()
779 hide_all_but_selected_control_points ();
783 AutomationTimeAxisView::set_state (const XMLNode& node)
785 TimeAxisView::set_state (node);
789 AutomationTimeAxisView::get_state_node ()
791 TimeAxisView* state_parent = get_parent_with_state ();
794 return state_parent->get_child_xml_node (_state_name);