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"
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,
38 ArdourCanvas::Canvas& canvas, std::string nom,
39 std::string state_name, std::string nomparent)
42 TimeAxisView (s, e, &rent, canvas),
45 _state_name (state_name),
46 height_button (_("h")),
47 clear_button (_("clear")),
48 auto_button (X_("")) /* force addition of a label */
51 in_destructor = false;
56 ignore_state_request = false;
58 // base_rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP(canvas_display),
59 // gnome_canvas_simplerect_get_type(),
63 // "outline_color_rgba", color_map[cAutomationTrackOutline],
64 // /* outline ends and bottom */
65 // "outline_what", (guint32) (0x1|0x2|0x8),
66 // "fill_color_rgba", color_map[cAutomationTrackFill],
68 base_rect = new SimpleRect(*canvas_display);
69 base_rect->property_x1() = 0.0;
70 base_rect->property_y1() = 0.0;
71 base_rect->property_x2() = 1000000.0;
72 base_rect->property_outline_color_rgba() = color_map[cAutomationTrackOutline];
73 /* outline ends and bottom */
74 base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
75 base_rect->property_fill_color_rgba() = color_map[cAutomationTrackFill];
77 base_rect->set_data ("trackview", this);
79 base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
82 hide_button.add (*(manage (new Gtk::Image (Gdk::Pixbuf::create_from_xpm_data(small_x_xpm)))));
84 height_button.set_name ("TrackSizeButton");
85 auto_button.set_name ("TrackVisualButton");
86 clear_button.set_name ("TrackVisualButton");
87 hide_button.set_name ("TrackRemoveButton");
89 ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
90 ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
91 ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
92 ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
94 /* rearrange the name display */
96 /* we never show these for automation tracks, so make
97 life easier and remove them.
100 name_hbox.remove (name_entry);
102 /* move the name label over a bit */
104 string shortpname = _name;
105 bool shortened = false;
107 if (_name.length()) {
108 if (shortpname.length() > 18) {
109 shortpname = shortpname.substr (0, 16);
114 name_label.set_text (shortpname);
115 name_label.set_alignment (1.0, 0.5);
117 if (nomparent.length()) {
119 /* limit the plug name string */
121 string pname = nomparent;
123 if (pname.length() > 14) {
124 pname = pname.substr (0, 11);
129 plugname = new Label (pname);
130 plugname->set_name (X_("TrackPlugName"));
131 plugname->set_alignment (1.0, 0.5);
132 name_label.set_name (X_("TrackParameterName"));
133 controls_table.remove (name_hbox);
134 controls_table.attach (*plugname, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
135 plugname_packed = true;
136 controls_table.attach (name_hbox, 1, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
139 plugname_packed = false;
143 string tipname = nomparent;
144 if (!tipname.empty()) {
148 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
151 /* add the buttons */
152 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
153 controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
155 controls_table.attach (auto_button, 7, 9, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
156 controls_table.attach (clear_button, 7, 9, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
158 controls_table.show_all ();
160 height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
161 clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
162 hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
163 auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
165 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
166 controls_base_unselected_name = X_("AutomationTrackControlsBase");
167 controls_ebox.set_name (controls_base_unselected_name);
169 controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
171 XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
172 set_state (*xml_node);
174 /* make sure labels etc. are correct */
176 automation_state_changed ();
179 AutomationTimeAxisView::~AutomationTimeAxisView ()
181 in_destructor = true;
183 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
189 AutomationTimeAxisView::auto_clicked ()
191 using namespace Menu_Helpers;
193 if (automation_menu == 0) {
194 automation_menu = manage (new Menu);
195 automation_menu->set_name ("ArdourContextMenu");
196 MenuList& items (automation_menu->items());
198 items.push_back (MenuElem (_("off"),
199 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
200 items.push_back (MenuElem (_("play"),
201 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
202 items.push_back (MenuElem (_("write"),
203 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
204 items.push_back (MenuElem (_("touch"),
205 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
208 automation_menu->popup (1, 0);
213 AutomationTimeAxisView::automation_state_changed ()
217 /* update button label */
222 state = lines.front()->the_list().automation_state ();
225 switch (state & (Off|Play|Touch|Write)) {
227 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("off"));
229 ignore_state_request = true;
230 auto_off_item->set_active (true);
231 auto_play_item->set_active (false);
232 auto_touch_item->set_active (false);
233 auto_write_item->set_active (false);
234 ignore_state_request = false;
238 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("play"));
239 if (auto_play_item) {
240 ignore_state_request = true;
241 auto_play_item->set_active (true);
242 auto_off_item->set_active (false);
243 auto_touch_item->set_active (false);
244 auto_write_item->set_active (false);
245 ignore_state_request = false;
249 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("write"));
250 if (auto_write_item) {
251 ignore_state_request = true;
252 auto_write_item->set_active (true);
253 auto_off_item->set_active (false);
254 auto_play_item->set_active (false);
255 auto_touch_item->set_active (false);
256 ignore_state_request = false;
260 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("touch"));
261 if (auto_touch_item) {
262 ignore_state_request = true;
263 auto_touch_item->set_active (true);
264 auto_off_item->set_active (false);
265 auto_play_item->set_active (false);
266 auto_write_item->set_active (false);
267 ignore_state_request = false;
271 static_cast<Gtk::Label*>(auto_button.get_child())->set_text (_("???"));
277 AutomationTimeAxisView::height_clicked ()
283 AutomationTimeAxisView::clear_clicked ()
285 _session.begin_reversible_command (_("clear automation"));
286 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
289 _session.commit_reversible_command ();
293 AutomationTimeAxisView::set_height (TrackHeight h)
295 bool changed = (height != (uint32_t) h);
297 TimeAxisView* state_parent = get_parent_with_state ();
298 XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
300 controls_table.show_all ();
302 TimeAxisView::set_height (h);
303 gtk_object_set (GTK_OBJECT(base_rect), "y2", (double) h, NULL);
305 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
306 (*i)->set_height (h);
309 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
315 xml_node->add_property ("track_height", "largest");
316 controls_table.remove (name_hbox);
318 if (plugname_packed) {
319 controls_table.remove (*plugname);
320 plugname_packed = false;
322 controls_table.attach (*plugname, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
323 plugname_packed = true;
324 controls_table.attach (name_hbox, 1, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
326 controls_table.attach (name_hbox, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
328 controls_table.show_all ();
333 xml_node->add_property ("track_height", "large");
334 controls_table.remove (name_hbox);
336 if (plugname_packed) {
337 controls_table.remove (*plugname);
338 plugname_packed = false;
340 controls_table.attach (*plugname, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
341 plugname_packed = true;
343 controls_table.attach (name_hbox, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
345 controls_table.show_all ();
350 xml_node->add_property ("track_height", "larger");
351 controls_table.remove (name_hbox);
353 if (plugname_packed) {
354 controls_table.remove (*plugname);
355 plugname_packed = false;
357 controls_table.attach (*plugname, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
358 plugname_packed = true;
360 controls_table.attach (name_hbox, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
362 controls_table.show_all ();
367 xml_node->add_property ("track_height", "normal");
368 controls_table.remove (name_hbox);
370 if (plugname_packed) {
371 controls_table.remove (*plugname);
372 plugname_packed = false;
374 controls_table.attach (*plugname, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
375 plugname_packed = true;
376 controls_table.attach (name_hbox, 1, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
378 controls_table.attach (name_hbox, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
380 controls_table.show_all ();
385 xml_node->add_property ("track_height", "smaller");
386 controls_table.remove (name_hbox);
388 if (plugname_packed) {
389 controls_table.remove (*plugname);
390 plugname_packed = false;
393 controls_table.attach (name_hbox, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
394 controls_table.hide_all ();
395 name_hbox.show_all ();
396 controls_table.show ();
400 xml_node->add_property ("track_height", "small");
401 controls_table.remove (name_hbox);
403 if (plugname_packed) {
404 controls_table.remove (*plugname);
405 plugname_packed = false;
408 controls_table.attach (name_hbox, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
409 controls_table.hide_all ();
410 name_hbox.show_all ();
411 controls_table.show ();
416 /* only emit the signal if the height really changed */
417 route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
422 AutomationTimeAxisView::set_samples_per_unit (double spu)
424 TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
426 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
432 AutomationTimeAxisView::hide_clicked ()
434 set_marked_for_display (false);
440 AutomationTimeAxisView::build_display_menu ()
442 using namespace Menu_Helpers;
444 /* get the size menu ready */
450 TimeAxisView::build_display_menu ();
452 /* now fill it with our stuff */
454 MenuList& items = display_menu->items();
456 items.push_back (MenuElem (_("Height"), *size_menu));
457 items.push_back (SeparatorElem());
458 items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
459 items.push_back (SeparatorElem());
460 items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
461 items.push_back (SeparatorElem());
463 Menu* auto_state_menu = manage (new Menu);
464 auto_state_menu->set_name ("ArdourContextMenu");
465 MenuList& as_items = auto_state_menu->items();
467 as_items.push_back (CheckMenuElem (_("off"),
468 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
469 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
471 as_items.push_back (CheckMenuElem (_("play"),
472 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
473 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
475 as_items.push_back (CheckMenuElem (_("write"),
476 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
477 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
479 as_items.push_back (CheckMenuElem (_("touch"),
480 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
481 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
483 items.push_back (MenuElem (_("State"), *auto_state_menu));
485 /* make sure the automation menu state is correct */
487 automation_state_changed ();
491 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
495 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
496 ret = cut_copy_clear_one ((**i), selection, op);
503 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
505 AutomationList* what_we_got = 0;
506 AutomationList& alist (line.the_list());
509 _session.add_undo (alist.get_memento());
513 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
514 editor.get_cut_buffer().add (what_we_got);
515 _session.add_redo_no_execute (alist.get_memento());
520 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
521 editor.get_cut_buffer().add (what_we_got);
526 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
527 _session.add_redo_no_execute (alist.get_memento());
536 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
537 double foo = (*x)->value;
538 line.model_to_view_y (foo);
547 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
551 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
552 ret = cut_copy_clear_objects_one ((**i), selection, op);
559 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
561 AutomationList* what_we_got = 0;
562 AutomationList& alist (line.the_list());
565 _session.add_undo (alist.get_memento());
567 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
569 if (&(*i).track != this) {
575 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
576 editor.get_cut_buffer().add (what_we_got);
577 _session.add_redo_no_execute (alist.get_memento());
582 if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
583 editor.get_cut_buffer().add (what_we_got);
588 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
589 _session.add_redo_no_execute (alist.get_memento());
599 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
600 double foo = (*x)->value;
601 line.model_to_view_y (foo);
610 AutomationTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
614 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
615 ret = paste_one (**i, pos, times, selection, nth);
622 AutomationTimeAxisView::paste_one (AutomationLine& line, jack_nframes_t pos, float times, Selection& selection, size_t nth)
624 AutomationSelection::iterator p;
625 AutomationList& alist (line.the_list());
627 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
629 if (p == selection.lines.end()) {
633 /* Make a copy of the list because we have to scale the
634 values from view coordinates to model coordinates, and we're
635 not supposed to modify the points in the selection.
638 AutomationList copy (**p);
640 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
641 double foo = (*x)->value;
642 line.view_to_model_y (foo);
646 _session.add_undo (alist.get_memento());
647 alist.paste (copy, pos, times);
648 _session.add_redo_no_execute (alist.get_memento());
654 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
656 ghosts.push_back (gr);
657 gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
661 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
667 list<GhostRegion*>::iterator i;
669 for (i = ghosts.begin(); i != ghosts.end(); ++i) {
678 AutomationTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
680 if (!lines.empty() && touched (top, bot)) {
684 /* remember: this is X Window - coordinate space starts in upper left and moves down.
685 y_position is the "origin" or "top" of the track.
688 double mybot = y_position + height; // XXX need to include Editor::track_spacing;
690 if (y_position >= top && mybot <= bot) {
692 /* y_position is below top, mybot is above bot, so we're fully
701 /* top and bot are within y_position .. mybot */
703 topfrac = 1.0 - ((top - y_position) / height);
704 botfrac = 1.0 - ((bot - y_position) / height);
707 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
708 (*i)->get_selectables (start, end, botfrac, topfrac, results);
714 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
716 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
717 (*i)->get_inverted_selectables (sel, result);
722 AutomationTimeAxisView::set_selected_points (PointSelection& points)
724 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
725 (*i)->set_selected_points (points);
730 AutomationTimeAxisView::clear_lines ()
732 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
737 automation_connection.disconnect ();
741 AutomationTimeAxisView::add_line (AutomationLine& line)
746 /* first line is the Model for automation state */
747 automation_connection = line.the_list().automation_state_changed.connect
748 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
752 lines.push_back (&line);
753 line.set_height (height);
756 /* pick up the current state */
757 automation_state_changed ();
762 AutomationTimeAxisView::show_all_control_points ()
764 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
765 (*i)->show_all_control_points ();
770 AutomationTimeAxisView::hide_all_but_selected_control_points ()
772 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
773 (*i)->hide_all_but_selected_control_points ();
778 AutomationTimeAxisView::entered()
780 show_all_control_points ();
784 AutomationTimeAxisView::exited ()
786 hide_all_but_selected_control_points ();
790 AutomationTimeAxisView::set_state (const XMLNode& node)
792 TimeAxisView::set_state (node);
796 AutomationTimeAxisView::get_state_node ()
798 TimeAxisView* state_parent = get_parent_with_state ();
801 return state_parent->get_child_xml_node (_state_name);