1 #include <ardour/route.h>
2 #include <pbd/memento_command.h>
5 #include "automation_time_axis.h"
6 #include "automation_line.h"
7 #include "public_editor.h"
8 #include "simplerect.h"
10 #include "ghostregion.h"
11 #include "rgb_macros.h"
12 #include "automation_selectable.h"
13 #include "point_selection.h"
14 #include "canvas_impl.h"
19 using namespace ARDOUR;
22 using namespace Editing;
24 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Route> r, PublicEditor& e, TimeAxisView& rent,
25 ArdourCanvas::Canvas& canvas, const string & nom,
26 const string & state_name, const string & nomparent)
29 TimeAxisView (s, e, &rent, canvas),
32 _state_name (state_name),
33 height_button (_("h")),
34 clear_button (_("clear")),
35 auto_button (X_("")) /* force addition of a label */
38 in_destructor = false;
43 ignore_state_request = false;
44 first_call_to_set_height = true;
46 // base_rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP(canvas_display),
47 // gnome_canvas_simplerect_get_type(),
51 // "outline_color_rgba", color_map[cAutomationTrackOutline],
52 // /* outline ends and bottom */
53 // "outline_what", (guint32) (0x1|0x2|0x8),
54 // "fill_color_rgba", color_map[cAutomationTrackFill],
56 base_rect = new SimpleRect(*canvas_display);
57 base_rect->property_x1() = 0.0;
58 base_rect->property_y1() = 0.0;
59 base_rect->property_x2() = 1000000.0;
60 base_rect->property_outline_color_rgba() = color_map[cAutomationTrackOutline];
61 /* outline ends and bottom */
62 base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
63 base_rect->property_fill_color_rgba() = color_map[cAutomationTrackFill];
65 base_rect->set_data ("trackview", this);
67 base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
70 hide_button.add (*(manage (new Gtk::Image (get_xpm("small_x.xpm")))));
72 height_button.set_name ("TrackSizeButton");
73 auto_button.set_name ("TrackVisualButton");
74 clear_button.set_name ("TrackVisualButton");
75 hide_button.set_name ("TrackRemoveButton");
77 controls_table.set_no_show_all();
79 ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
80 ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
81 ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
82 ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
84 /* rearrange the name display */
86 /* we never show these for automation tracks, so make
87 life easier and remove them.
92 /* move the name label over a bit */
94 string shortpname = _name;
95 bool shortened = false;
98 if (shortpname.length() > 18) {
99 shortpname = shortpname.substr (0, 16);
104 name_label.set_text (shortpname);
105 name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
107 if (nomparent.length()) {
109 /* limit the plug name string */
111 string pname = nomparent;
113 if (pname.length() > 14) {
114 pname = pname.substr (0, 11);
119 plugname = new Label (pname);
120 plugname->set_name (X_("TrackPlugName"));
122 name_label.set_name (X_("TrackParameterName"));
123 controls_table.remove (name_hbox);
124 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
125 plugname_packed = true;
126 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
129 plugname_packed = false;
133 string tipname = nomparent;
134 if (!tipname.empty()) {
138 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
141 /* add the buttons */
142 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
143 controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
145 controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
146 controls_table.attach (clear_button, 5, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
148 controls_table.show_all ();
150 height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
151 clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
152 hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
153 auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
155 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
156 controls_base_unselected_name = X_("AutomationTrackControlsBase");
157 controls_ebox.set_name (controls_base_unselected_name);
159 controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
161 XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
162 set_state (*xml_node);
164 /* make sure labels etc. are correct */
166 automation_state_changed ();
169 AutomationTimeAxisView::~AutomationTimeAxisView ()
171 in_destructor = true;
173 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
179 AutomationTimeAxisView::auto_clicked ()
181 using namespace Menu_Helpers;
183 if (automation_menu == 0) {
184 automation_menu = manage (new Menu);
185 automation_menu->set_name ("ArdourContextMenu");
186 MenuList& items (automation_menu->items());
188 items.push_back (MenuElem (_("Manual"),
189 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
190 items.push_back (MenuElem (_("Play"),
191 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
192 items.push_back (MenuElem (_("Write"),
193 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
194 items.push_back (MenuElem (_("Touch"),
195 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
198 automation_menu->popup (1, 0);
203 AutomationTimeAxisView::automation_state_changed ()
207 /* update button label */
212 state = lines.front()->the_list().automation_state ();
215 switch (state & (Off|Play|Touch|Write)) {
217 auto_button.set_label (_("Manual"));
219 ignore_state_request = true;
220 auto_off_item->set_active (true);
221 auto_play_item->set_active (false);
222 auto_touch_item->set_active (false);
223 auto_write_item->set_active (false);
224 ignore_state_request = false;
228 auto_button.set_label (_("Play"));
229 if (auto_play_item) {
230 ignore_state_request = true;
231 auto_play_item->set_active (true);
232 auto_off_item->set_active (false);
233 auto_touch_item->set_active (false);
234 auto_write_item->set_active (false);
235 ignore_state_request = false;
239 auto_button.set_label (_("Write"));
240 if (auto_write_item) {
241 ignore_state_request = true;
242 auto_write_item->set_active (true);
243 auto_off_item->set_active (false);
244 auto_play_item->set_active (false);
245 auto_touch_item->set_active (false);
246 ignore_state_request = false;
250 auto_button.set_label (_("Touch"));
251 if (auto_touch_item) {
252 ignore_state_request = true;
253 auto_touch_item->set_active (true);
254 auto_off_item->set_active (false);
255 auto_play_item->set_active (false);
256 auto_write_item->set_active (false);
257 ignore_state_request = false;
261 auto_button.set_label (_("???"));
267 AutomationTimeAxisView::height_clicked ()
273 AutomationTimeAxisView::clear_clicked ()
275 _session.begin_reversible_command (_("clear automation"));
276 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
279 _session.commit_reversible_command ();
283 AutomationTimeAxisView::set_height (TrackHeight ht)
285 uint32_t h = height_to_pixels (ht);
286 bool changed = (height != (uint32_t) h);
288 bool changed_between_small_and_normal = ( (ht == Small || ht == Smaller) ^ (height_style == Small || height_style == Smaller) );
290 TimeAxisView* state_parent = get_parent_with_state ();
291 XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
293 TimeAxisView::set_height (ht);
294 base_rect->property_y2() = h;
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) {
307 xml_node->add_property ("track_height", "largest");
311 xml_node->add_property ("track_height", "large");
315 xml_node->add_property ("track_height", "larger");
319 xml_node->add_property ("track_height", "normal");
323 xml_node->add_property ("track_height", "smaller");
327 xml_node->add_property ("track_height", "small");
331 if (changed_between_small_and_normal || first_call_to_set_height) {
332 first_call_to_set_height = false;
339 controls_table.remove (name_hbox);
342 if (plugname_packed) {
343 controls_table.remove (*plugname);
344 plugname_packed = false;
346 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
347 plugname_packed = true;
348 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
350 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
354 name_hbox.show_all ();
357 height_button.show();
359 hide_button.show_all();
365 controls_table.remove (name_hbox);
367 if (plugname_packed) {
368 controls_table.remove (*plugname);
369 plugname_packed = false;
372 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
373 controls_table.hide_all ();
376 name_hbox.show_all ();
379 height_button.hide();
387 /* only emit the signal if the height really changed */
388 route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
393 AutomationTimeAxisView::set_samples_per_unit (double spu)
395 TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
397 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
403 AutomationTimeAxisView::hide_clicked ()
405 // LAME fix for refreshing the hide button
406 hide_button.set_sensitive(false);
408 set_marked_for_display (false);
411 hide_button.set_sensitive(true);
415 AutomationTimeAxisView::build_display_menu ()
417 using namespace Menu_Helpers;
419 /* get the size menu ready */
425 TimeAxisView::build_display_menu ();
427 /* now fill it with our stuff */
429 MenuList& items = display_menu->items();
431 items.push_back (MenuElem (_("Height"), *size_menu));
432 items.push_back (SeparatorElem());
433 items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
434 items.push_back (SeparatorElem());
435 items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
436 items.push_back (SeparatorElem());
438 Menu* auto_state_menu = manage (new Menu);
439 auto_state_menu->set_name ("ArdourContextMenu");
440 MenuList& as_items = auto_state_menu->items();
442 as_items.push_back (CheckMenuElem (_("Manual"),
443 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
444 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
446 as_items.push_back (CheckMenuElem (_("Play"),
447 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
448 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
450 as_items.push_back (CheckMenuElem (_("Write"),
451 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
452 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
454 as_items.push_back (CheckMenuElem (_("Touch"),
455 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
456 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
458 items.push_back (MenuElem (_("State"), *auto_state_menu));
460 /* make sure the automation menu state is correct */
462 automation_state_changed ();
466 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
470 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
471 ret = cut_copy_clear_one ((**i), selection, op);
478 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
480 AutomationList* what_we_got = 0;
481 AutomationList& alist (line.the_list());
484 XMLNode &before = alist.get_state();
488 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
489 editor.get_cut_buffer().add (what_we_got);
490 _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
495 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
496 editor.get_cut_buffer().add (what_we_got);
501 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
502 _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
511 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
512 double foo = (*x)->value;
513 line.model_to_view_y (foo);
522 AutomationTimeAxisView::reset_objects (PointSelection& selection)
524 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
525 reset_objects_one ((**i), selection);
530 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
532 AutomationList& alist (line.the_list());
534 _session.add_command (new MementoCommand<AutomationList>(alist, &alist.get_state(), 0));
536 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
538 if (&(*i).track != this) {
542 alist.reset_range ((*i).start, (*i).end);
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 XMLNode &before = alist.get_state();
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_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
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_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
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 (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, 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 XMLNode &before = alist.get_state();
647 alist.paste (copy, pos, times);
648 _session.add_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
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 (nframes_t start, 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);