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 (_("off"),
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 (_("off"));
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 h)
280 bool changed = (height != (uint32_t) h);
282 TimeAxisView* state_parent = get_parent_with_state ();
283 XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
285 controls_table.show_all ();
287 TimeAxisView::set_height (h);
288 base_rect->property_y2() = h;
290 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
291 (*i)->set_height (h);
294 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
300 xml_node->add_property ("track_height", "largest");
301 controls_table.remove (name_hbox);
303 if (plugname_packed) {
304 controls_table.remove (*plugname);
305 plugname_packed = false;
307 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
308 plugname_packed = true;
309 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
311 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
313 controls_table.show_all ();
319 xml_node->add_property ("track_height", "large");
320 controls_table.remove (name_hbox);
322 if (plugname_packed) {
323 controls_table.remove (*plugname);
324 plugname_packed = false;
326 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
327 plugname_packed = true;
329 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
331 controls_table.show_all ();
337 xml_node->add_property ("track_height", "larger");
338 controls_table.remove (name_hbox);
340 if (plugname_packed) {
341 controls_table.remove (*plugname);
342 plugname_packed = false;
344 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
345 plugname_packed = true;
347 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
349 controls_table.show_all ();
355 xml_node->add_property ("track_height", "normal");
356 controls_table.remove (name_hbox);
358 if (plugname_packed) {
359 controls_table.remove (*plugname);
360 plugname_packed = false;
362 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
363 plugname_packed = true;
364 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
366 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
368 controls_table.show_all ();
374 xml_node->add_property ("track_height", "smaller");
375 controls_table.remove (name_hbox);
377 if (plugname_packed) {
378 controls_table.remove (*plugname);
379 plugname_packed = false;
382 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
383 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, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
400 controls_table.hide_all ();
403 name_hbox.show_all ();
404 controls_table.show ();
409 /* only emit the signal if the height really changed */
410 route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
415 AutomationTimeAxisView::set_samples_per_unit (double spu)
417 TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
419 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
425 AutomationTimeAxisView::hide_clicked ()
427 set_marked_for_display (false);
432 AutomationTimeAxisView::build_display_menu ()
434 using namespace Menu_Helpers;
436 /* get the size menu ready */
442 TimeAxisView::build_display_menu ();
444 /* now fill it with our stuff */
446 MenuList& items = display_menu->items();
448 items.push_back (MenuElem (_("Height"), *size_menu));
449 items.push_back (SeparatorElem());
450 items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
451 items.push_back (SeparatorElem());
452 items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
453 items.push_back (SeparatorElem());
455 Menu* auto_state_menu = manage (new Menu);
456 auto_state_menu->set_name ("ArdourContextMenu");
457 MenuList& as_items = auto_state_menu->items();
459 as_items.push_back (CheckMenuElem (_("off"),
460 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
461 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
463 as_items.push_back (CheckMenuElem (_("play"),
464 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
465 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
467 as_items.push_back (CheckMenuElem (_("write"),
468 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
469 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
471 as_items.push_back (CheckMenuElem (_("touch"),
472 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
473 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
475 items.push_back (MenuElem (_("State"), *auto_state_menu));
477 /* make sure the automation menu state is correct */
479 automation_state_changed ();
483 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
487 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
488 ret = cut_copy_clear_one ((**i), selection, op);
495 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
497 AutomationList* what_we_got = 0;
498 AutomationList& alist (line.the_list());
501 _session.add_undo (alist.get_memento());
505 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
506 editor.get_cut_buffer().add (what_we_got);
507 _session.add_redo_no_execute (alist.get_memento());
512 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
513 editor.get_cut_buffer().add (what_we_got);
518 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
519 _session.add_redo_no_execute (alist.get_memento());
528 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
529 double foo = (*x)->value;
530 line.model_to_view_y (foo);
539 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
543 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
544 ret = cut_copy_clear_objects_one ((**i), selection, op);
551 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
553 AutomationList* what_we_got = 0;
554 AutomationList& alist (line.the_list());
557 _session.add_undo (alist.get_memento());
559 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
561 if (&(*i).track != this) {
567 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
568 editor.get_cut_buffer().add (what_we_got);
569 _session.add_redo_no_execute (alist.get_memento());
574 if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
575 editor.get_cut_buffer().add (what_we_got);
580 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
581 _session.add_redo_no_execute (alist.get_memento());
591 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
592 double foo = (*x)->value;
593 line.model_to_view_y (foo);
602 AutomationTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
606 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
607 ret = paste_one (**i, pos, times, selection, nth);
614 AutomationTimeAxisView::paste_one (AutomationLine& line, jack_nframes_t pos, float times, Selection& selection, size_t nth)
616 AutomationSelection::iterator p;
617 AutomationList& alist (line.the_list());
619 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
621 if (p == selection.lines.end()) {
625 /* Make a copy of the list because we have to scale the
626 values from view coordinates to model coordinates, and we're
627 not supposed to modify the points in the selection.
630 AutomationList copy (**p);
632 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
633 double foo = (*x)->value;
634 line.view_to_model_y (foo);
638 _session.add_undo (alist.get_memento());
639 alist.paste (copy, pos, times);
640 _session.add_redo_no_execute (alist.get_memento());
646 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
648 ghosts.push_back (gr);
649 gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
653 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
659 list<GhostRegion*>::iterator i;
661 for (i = ghosts.begin(); i != ghosts.end(); ++i) {
670 AutomationTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
672 if (!lines.empty() && touched (top, bot)) {
676 /* remember: this is X Window - coordinate space starts in upper left and moves down.
677 y_position is the "origin" or "top" of the track.
680 double mybot = y_position + height; // XXX need to include Editor::track_spacing;
682 if (y_position >= top && mybot <= bot) {
684 /* y_position is below top, mybot is above bot, so we're fully
693 /* top and bot are within y_position .. mybot */
695 topfrac = 1.0 - ((top - y_position) / height);
696 botfrac = 1.0 - ((bot - y_position) / height);
699 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
700 (*i)->get_selectables (start, end, botfrac, topfrac, results);
706 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
708 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
709 (*i)->get_inverted_selectables (sel, result);
714 AutomationTimeAxisView::set_selected_points (PointSelection& points)
716 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
717 (*i)->set_selected_points (points);
722 AutomationTimeAxisView::clear_lines ()
724 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
729 automation_connection.disconnect ();
733 AutomationTimeAxisView::add_line (AutomationLine& line)
738 /* first line is the Model for automation state */
739 automation_connection = line.the_list().automation_state_changed.connect
740 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
744 lines.push_back (&line);
745 line.set_height (height);
748 /* pick up the current state */
749 automation_state_changed ();
754 AutomationTimeAxisView::show_all_control_points ()
756 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
757 (*i)->show_all_control_points ();
762 AutomationTimeAxisView::hide_all_but_selected_control_points ()
764 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
765 (*i)->hide_all_but_selected_control_points ();
770 AutomationTimeAxisView::entered()
772 show_all_control_points ();
776 AutomationTimeAxisView::exited ()
778 hide_all_but_selected_control_points ();
782 AutomationTimeAxisView::set_state (const XMLNode& node)
784 TimeAxisView::set_state (node);
788 AutomationTimeAxisView::get_state_node ()
790 TimeAxisView* state_parent = get_parent_with_state ();
793 return state_parent->get_child_xml_node (_state_name);