1 #include <ardour/route.h>
4 #include "automation_time_axis.h"
5 #include "automation_line.h"
6 #include "public_editor.h"
7 #include "canvas-simplerect.h"
8 #include "canvas-waveview.h"
10 #include "ghostregion.h"
11 #include "rgb_macros.h"
12 #include "automation_selectable.h"
13 #include "point_selection.h"
17 using namespace ARDOUR;
19 using namespace Editing;
21 static const gchar * small_x_xpm[] = {
37 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, Route& r, PublicEditor& e, TimeAxisView& rent, Widget* p, std::string nom, std::string state_name, std::string nomparent)
40 TimeAxisView (s, e, &rent, p),
43 _state_name (state_name),
44 height_button (_("h")),
45 clear_button (_("clear")),
46 auto_button (X_("")) /* force addition of a label */
49 in_destructor = false;
54 ignore_state_request = false;
56 base_rect = gtk_canvas_item_new (GTK_CANVAS_GROUP(canvas_display),
57 gtk_canvas_simplerect_get_type(),
61 "outline_color_rgba", color_map[cAutomationTrackOutline],
62 /* outline ends and bottom */
63 "outline_what", (guint32) (0x1|0x2|0x8),
64 "fill_color_rgba", color_map[cAutomationTrackFill],
67 gtk_object_set_data (GTK_OBJECT(base_rect), "trackview", this);
69 gtk_signal_connect (GTK_OBJECT(base_rect), "event",
70 (GtkSignalFunc) PublicEditor::canvas_automation_track_event,
73 hide_button.add (*(manage (new Pixmap (small_x_xpm))));
75 height_button.set_name ("TrackSizeButton");
76 auto_button.set_name ("TrackVisualButton");
77 clear_button.set_name ("TrackVisualButton");
78 hide_button.set_name ("TrackRemoveButton");
80 ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
81 ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
82 ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
83 ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
85 /* rearrange the name display */
87 /* we never show these for automation tracks, so make
88 life easier and remove them.
91 name_hbox.remove (name_entry);
93 /* move the name label over a bit */
95 string shortpname = _name;
96 bool shortened = false;
99 if (shortpname.length() > 18) {
100 shortpname = shortpname.substr (0, 16);
105 name_label.set_text (shortpname);
106 name_label.set_alignment (1.0, 0.5);
108 if (nomparent.length()) {
110 /* limit the plug name string */
112 string pname = nomparent;
114 if (pname.length() > 14) {
115 pname = pname.substr (0, 11);
120 plugname = new Label (pname);
121 plugname->set_name (X_("TrackPlugName"));
122 plugname->set_alignment (1.0, 0.5);
123 name_label.set_name (X_("TrackParameterName"));
124 controls_table.remove (name_hbox);
125 controls_table.attach (*plugname, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
126 plugname_packed = true;
127 controls_table.attach (name_hbox, 1, 6, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
130 plugname_packed = false;
134 string tipname = nomparent;
135 if (!tipname.empty()) {
139 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
142 /* add the buttons */
143 controls_table.attach (hide_button, 0, 1, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
144 controls_table.attach (height_button, 0, 1, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
146 controls_table.attach (auto_button, 7, 9, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
147 controls_table.attach (clear_button, 7, 9, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
149 controls_table.show_all ();
151 height_button.clicked.connect (slot (*this, &AutomationTimeAxisView::height_clicked));
152 clear_button.clicked.connect (slot (*this, &AutomationTimeAxisView::clear_clicked));
153 hide_button.clicked.connect (slot (*this, &AutomationTimeAxisView::hide_clicked));
154 auto_button.clicked.connect (slot (*this, &AutomationTimeAxisView::auto_clicked));
156 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
157 controls_base_unselected_name = X_("AutomationTrackControlsBase");
158 controls_ebox.set_name (controls_base_unselected_name);
160 controls_frame.set_shadow_type (GTK_SHADOW_ETCHED_OUT);
162 XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
163 set_state (*xml_node);
165 /* make sure labels etc. are correct */
167 automation_state_changed ();
170 AutomationTimeAxisView::~AutomationTimeAxisView ()
172 in_destructor = true;
174 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
180 AutomationTimeAxisView::auto_clicked ()
182 using namespace Menu_Helpers;
184 if (automation_menu == 0) {
185 automation_menu = manage (new Menu);
186 automation_menu->set_name ("ArdourContextMenu");
187 MenuList& items (automation_menu->items());
189 items.push_back (MenuElem (_("off"),
190 bind (slot (*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
191 items.push_back (MenuElem (_("play"),
192 bind (slot (*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
193 items.push_back (MenuElem (_("write"),
194 bind (slot (*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
195 items.push_back (MenuElem (_("touch"),
196 bind (slot (*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
199 automation_menu->popup (1, 0);
204 AutomationTimeAxisView::automation_state_changed ()
208 /* update button label */
213 state = lines.front()->the_list().automation_state ();
216 switch (state & (Off|Play|Touch|Write)) {
218 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("off"));
220 ignore_state_request = true;
221 auto_off_item->set_active (true);
222 auto_play_item->set_active (false);
223 auto_touch_item->set_active (false);
224 auto_write_item->set_active (false);
225 ignore_state_request = false;
229 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("play"));
230 if (auto_play_item) {
231 ignore_state_request = true;
232 auto_play_item->set_active (true);
233 auto_off_item->set_active (false);
234 auto_touch_item->set_active (false);
235 auto_write_item->set_active (false);
236 ignore_state_request = false;
240 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("write"));
241 if (auto_write_item) {
242 ignore_state_request = true;
243 auto_write_item->set_active (true);
244 auto_off_item->set_active (false);
245 auto_play_item->set_active (false);
246 auto_touch_item->set_active (false);
247 ignore_state_request = false;
251 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("touch"));
252 if (auto_touch_item) {
253 ignore_state_request = true;
254 auto_touch_item->set_active (true);
255 auto_off_item->set_active (false);
256 auto_play_item->set_active (false);
257 auto_write_item->set_active (false);
258 ignore_state_request = false;
262 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("???"));
268 AutomationTimeAxisView::height_clicked ()
274 AutomationTimeAxisView::clear_clicked ()
276 _session.begin_reversible_command (_("clear automation"));
277 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
280 _session.commit_reversible_command ();
284 AutomationTimeAxisView::set_height (TrackHeight h)
286 bool changed = (height != (uint32_t) h);
288 TimeAxisView* state_parent = get_parent_with_state ();
289 XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
291 controls_table.show_all ();
293 TimeAxisView::set_height (h);
294 gtk_object_set (GTK_OBJECT(base_rect), "y2", (double) h, NULL);
296 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
297 (*i)->set_height (h);
300 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
306 xml_node->add_property ("track_height", "largest");
307 controls_table.remove (name_hbox);
309 if (plugname_packed) {
310 controls_table.remove (*plugname);
311 plugname_packed = false;
313 controls_table.attach (*plugname, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
314 plugname_packed = true;
315 controls_table.attach (name_hbox, 1, 6, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
317 controls_table.attach (name_hbox, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
319 controls_table.show_all ();
324 xml_node->add_property ("track_height", "large");
325 controls_table.remove (name_hbox);
327 if (plugname_packed) {
328 controls_table.remove (*plugname);
329 plugname_packed = false;
331 controls_table.attach (*plugname, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
332 plugname_packed = true;
334 controls_table.attach (name_hbox, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
336 controls_table.show_all ();
341 xml_node->add_property ("track_height", "larger");
342 controls_table.remove (name_hbox);
344 if (plugname_packed) {
345 controls_table.remove (*plugname);
346 plugname_packed = false;
348 controls_table.attach (*plugname, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
349 plugname_packed = true;
351 controls_table.attach (name_hbox, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
353 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, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
366 plugname_packed = true;
367 controls_table.attach (name_hbox, 1, 6, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
369 controls_table.attach (name_hbox, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
371 controls_table.show_all ();
376 xml_node->add_property ("track_height", "smaller");
377 controls_table.remove (name_hbox);
379 if (plugname_packed) {
380 controls_table.remove (*plugname);
381 plugname_packed = false;
384 controls_table.attach (name_hbox, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
385 controls_table.hide_all ();
386 name_hbox.show_all ();
387 controls_table.show ();
391 xml_node->add_property ("track_height", "small");
392 controls_table.remove (name_hbox);
394 if (plugname_packed) {
395 controls_table.remove (*plugname);
396 plugname_packed = false;
399 controls_table.attach (name_hbox, 1, 6, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
400 controls_table.hide_all ();
401 name_hbox.show_all ();
402 controls_table.show ();
407 /* only emit the signal if the height really changed */
408 route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
413 AutomationTimeAxisView::set_samples_per_unit (double spu)
415 TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
417 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
423 AutomationTimeAxisView::hide_clicked ()
425 set_marked_for_display (false);
431 AutomationTimeAxisView::build_display_menu ()
433 using namespace Menu_Helpers;
435 /* get the size menu ready */
441 TimeAxisView::build_display_menu ();
443 /* now fill it with our stuff */
445 MenuList& items = display_menu->items();
447 items.push_back (MenuElem (_("Height"), *size_menu));
448 items.push_back (SeparatorElem());
449 items.push_back (MenuElem (_("Hide"), slot (*this, &AutomationTimeAxisView::hide_clicked)));
450 items.push_back (SeparatorElem());
451 items.push_back (MenuElem (_("Clear"), slot (*this, &AutomationTimeAxisView::clear_clicked)));
452 items.push_back (SeparatorElem());
454 Menu* auto_state_menu = manage (new Menu);
455 auto_state_menu->set_name ("ArdourContextMenu");
456 MenuList& as_items = auto_state_menu->items();
458 as_items.push_back (CheckMenuElem (_("off"),
459 bind (slot (*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
460 auto_off_item = dynamic_cast<CheckMenuItem*>(as_items.back());
462 as_items.push_back (CheckMenuElem (_("play"),
463 bind (slot (*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
464 auto_play_item = dynamic_cast<CheckMenuItem*>(as_items.back());
466 as_items.push_back (CheckMenuElem (_("write"),
467 bind (slot (*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
468 auto_write_item = dynamic_cast<CheckMenuItem*>(as_items.back());
470 as_items.push_back (CheckMenuElem (_("touch"),
471 bind (slot (*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
472 auto_touch_item = dynamic_cast<CheckMenuItem*>(as_items.back());
474 items.push_back (MenuElem (_("State"), *auto_state_menu));
476 /* make sure the automation menu state is correct */
478 automation_state_changed ();
482 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
486 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
487 ret = cut_copy_clear_one ((**i), selection, op);
494 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
496 AutomationList* what_we_got = 0;
497 AutomationList& alist (line.the_list());
500 _session.add_undo (alist.get_memento());
504 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
505 editor.get_cut_buffer().add (what_we_got);
506 _session.add_redo_no_execute (alist.get_memento());
511 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
512 editor.get_cut_buffer().add (what_we_got);
517 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
518 _session.add_redo_no_execute (alist.get_memento());
527 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
528 double foo = (*x)->value;
529 line.model_to_view_y (foo);
538 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
542 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
543 ret = cut_copy_clear_objects_one ((**i), selection, op);
550 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
552 AutomationList* what_we_got = 0;
553 AutomationList& alist (line.the_list());
556 _session.add_undo (alist.get_memento());
558 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
560 if (&(*i).track != this) {
566 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
567 editor.get_cut_buffer().add (what_we_got);
568 _session.add_redo_no_execute (alist.get_memento());
573 if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
574 editor.get_cut_buffer().add (what_we_got);
579 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
580 _session.add_redo_no_execute (alist.get_memento());
590 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
591 double foo = (*x)->value;
592 line.model_to_view_y (foo);
601 AutomationTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
605 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
606 ret = paste_one (**i, pos, times, selection, nth);
613 AutomationTimeAxisView::paste_one (AutomationLine& line, jack_nframes_t pos, float times, Selection& selection, size_t nth)
615 AutomationSelection::iterator p;
616 AutomationList& alist (line.the_list());
618 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
620 if (p == selection.lines.end()) {
624 /* Make a copy of the list because we have to scale the
625 values from view coordinates to model coordinates, and we're
626 not supposed to modify the points in the selection.
629 AutomationList copy (**p);
631 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
632 double foo = (*x)->value;
633 line.view_to_model_y (foo);
637 _session.add_undo (alist.get_memento());
638 alist.paste (copy, pos, times);
639 _session.add_redo_no_execute (alist.get_memento());
645 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
647 ghosts.push_back (gr);
648 gr->GoingAway.connect (slot (*this, &AutomationTimeAxisView::remove_ghost));
652 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
658 list<GhostRegion*>::iterator i;
660 for (i = ghosts.begin(); i != ghosts.end(); ++i) {
669 AutomationTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
671 if (!lines.empty() && touched (top, bot)) {
675 /* remember: this is X Window - coordinate space starts in upper left and moves down.
676 y_position is the "origin" or "top" of the track.
679 double mybot = y_position + height; // XXX need to include Editor::track_spacing;
681 if (y_position >= top && mybot <= bot) {
683 /* y_position is below top, mybot is above bot, so we're fully
692 /* top and bot are within y_position .. mybot */
694 topfrac = 1.0 - ((top - y_position) / height);
695 botfrac = 1.0 - ((bot - y_position) / height);
698 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
699 (*i)->get_selectables (start, end, botfrac, topfrac, results);
705 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
707 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
708 (*i)->get_inverted_selectables (sel, result);
713 AutomationTimeAxisView::set_selected_points (PointSelection& points)
715 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
716 (*i)->set_selected_points (points);
721 AutomationTimeAxisView::clear_lines ()
723 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
728 automation_connection.disconnect ();
732 AutomationTimeAxisView::add_line (AutomationLine& line)
737 /* first line is the Model for automation state */
738 automation_connection = line.the_list().automation_state_changed.connect
739 (slot (*this, &AutomationTimeAxisView::automation_state_changed));
743 lines.push_back (&line);
744 line.set_height (height);
747 /* pick up the current state */
748 automation_state_changed ();
753 AutomationTimeAxisView::show_all_control_points ()
755 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
756 (*i)->show_all_control_points ();
761 AutomationTimeAxisView::hide_all_but_selected_control_points ()
763 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
764 (*i)->hide_all_but_selected_control_points ();
769 AutomationTimeAxisView::entered()
771 show_all_control_points ();
775 AutomationTimeAxisView::exited ()
777 hide_all_but_selected_control_points ();
781 AutomationTimeAxisView::set_state (const XMLNode& node)
783 TimeAxisView::set_state (node);
787 AutomationTimeAxisView::get_state_node ()
789 TimeAxisView* state_parent = get_parent_with_state ();
792 return state_parent->get_child_xml_node (_state_name);