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 = new SimpleRect(*canvas_display);
47 base_rect->property_x1() = 0.0;
48 base_rect->property_y1() = 0.0;
49 base_rect->property_x2() = max_frames;
50 base_rect->property_outline_color_rgba() = color_map[cAutomationTrackOutline];
51 /* outline ends and bottom */
52 base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
53 base_rect->property_fill_color_rgba() = color_map[cAutomationTrackFill];
54 //base_rect->property_fill_color_rgba() = color_map[cEnteredControlPoint];
56 base_rect->set_data ("trackview", this);
58 base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
61 hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
63 height_button.set_name ("TrackSizeButton");
64 auto_button.set_name ("TrackVisualButton");
65 clear_button.set_name ("TrackVisualButton");
66 hide_button.set_name ("TrackRemoveButton");
68 controls_table.set_no_show_all();
70 ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
71 ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
72 ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
73 ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
75 /* rearrange the name display */
77 /* we never show these for automation tracks, so make
78 life easier and remove them.
83 /* move the name label over a bit */
85 string shortpname = _name;
86 bool shortened = false;
89 if (shortpname.length() > 18) {
90 shortpname = shortpname.substr (0, 16);
95 name_label.set_text (shortpname);
96 name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
98 if (nomparent.length()) {
100 /* limit the plug name string */
102 string pname = nomparent;
104 if (pname.length() > 14) {
105 pname = pname.substr (0, 11);
110 plugname = new Label (pname);
111 plugname->set_name (X_("TrackPlugName"));
113 name_label.set_name (X_("TrackParameterName"));
114 controls_table.remove (name_hbox);
115 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
116 plugname_packed = true;
117 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
120 plugname_packed = false;
124 string tipname = nomparent;
125 if (!tipname.empty()) {
129 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
132 /* add the buttons */
133 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
134 controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
136 controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
137 controls_table.attach (clear_button, 5, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
139 controls_table.show_all ();
141 height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
142 clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
143 hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
144 auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
146 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
147 controls_base_unselected_name = X_("AutomationTrackControlsBase");
148 controls_ebox.set_name (controls_base_unselected_name);
150 controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
152 XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
155 set_state (*xml_node);
157 cerr << _name << ": looked for state named \"" << _state_name << "\" in parent ("
158 << get_parent_with_state()->name() << ") but found nothing\n";
161 /* make sure labels etc. are correct */
163 automation_state_changed ();
166 AutomationTimeAxisView::~AutomationTimeAxisView ()
168 in_destructor = true;
170 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
176 AutomationTimeAxisView::auto_clicked ()
178 using namespace Menu_Helpers;
180 if (automation_menu == 0) {
181 automation_menu = manage (new Menu);
182 automation_menu->set_name ("ArdourContextMenu");
183 MenuList& items (automation_menu->items());
185 items.push_back (MenuElem (_("Manual"),
186 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
187 items.push_back (MenuElem (_("Play"),
188 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
189 items.push_back (MenuElem (_("Write"),
190 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
191 items.push_back (MenuElem (_("Touch"),
192 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
195 automation_menu->popup (1, gtk_get_current_event_time());
200 AutomationTimeAxisView::automation_state_changed ()
204 /* update button label */
209 state = lines.front()->the_list().automation_state ();
212 switch (state & (Off|Play|Touch|Write)) {
214 auto_button.set_label (_("Manual"));
216 ignore_state_request = true;
217 auto_off_item->set_active (true);
218 auto_play_item->set_active (false);
219 auto_touch_item->set_active (false);
220 auto_write_item->set_active (false);
221 ignore_state_request = false;
225 auto_button.set_label (_("Play"));
226 if (auto_play_item) {
227 ignore_state_request = true;
228 auto_play_item->set_active (true);
229 auto_off_item->set_active (false);
230 auto_touch_item->set_active (false);
231 auto_write_item->set_active (false);
232 ignore_state_request = false;
236 auto_button.set_label (_("Write"));
237 if (auto_write_item) {
238 ignore_state_request = true;
239 auto_write_item->set_active (true);
240 auto_off_item->set_active (false);
241 auto_play_item->set_active (false);
242 auto_touch_item->set_active (false);
243 ignore_state_request = false;
247 auto_button.set_label (_("Touch"));
248 if (auto_touch_item) {
249 ignore_state_request = true;
250 auto_touch_item->set_active (true);
251 auto_off_item->set_active (false);
252 auto_play_item->set_active (false);
253 auto_write_item->set_active (false);
254 ignore_state_request = false;
258 auto_button.set_label (_("???"));
264 AutomationTimeAxisView::height_clicked ()
270 AutomationTimeAxisView::clear_clicked ()
272 _session.begin_reversible_command (_("clear automation"));
273 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
276 _session.commit_reversible_command ();
280 AutomationTimeAxisView::set_height (TrackHeight ht)
282 uint32_t h = height_to_pixels (ht);
283 bool changed = (height != (uint32_t) h);
285 bool changed_between_small_and_normal = ( (ht == Small || ht == Smaller) ^ (height_style == Small || height_style == Smaller) );
287 TimeAxisView* state_parent = get_parent_with_state ();
288 XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
290 TimeAxisView::set_height (ht);
291 base_rect->property_y2() = h;
293 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
294 (*i)->set_height (h);
297 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
304 xml_node->add_property ("track_height", "largest");
308 xml_node->add_property ("track_height", "large");
312 xml_node->add_property ("track_height", "larger");
316 xml_node->add_property ("track_height", "normal");
320 xml_node->add_property ("track_height", "smaller");
324 xml_node->add_property ("track_height", "small");
328 if (changed_between_small_and_normal || first_call_to_set_height) {
329 first_call_to_set_height = false;
336 controls_table.remove (name_hbox);
339 if (plugname_packed) {
340 controls_table.remove (*plugname);
341 plugname_packed = false;
343 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
344 plugname_packed = true;
345 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
347 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
351 name_hbox.show_all ();
354 height_button.show();
356 hide_button.show_all();
362 controls_table.remove (name_hbox);
364 if (plugname_packed) {
365 controls_table.remove (*plugname);
366 plugname_packed = false;
369 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
370 controls_table.hide_all ();
373 name_hbox.show_all ();
376 height_button.hide();
384 /* only emit the signal if the height really changed */
385 route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
390 AutomationTimeAxisView::set_samples_per_unit (double spu)
392 TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
394 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
400 AutomationTimeAxisView::hide_clicked ()
402 // LAME fix for refreshing the hide button
403 hide_button.set_sensitive(false);
405 set_marked_for_display (false);
408 hide_button.set_sensitive(true);
412 AutomationTimeAxisView::build_display_menu ()
414 using namespace Menu_Helpers;
416 /* get the size menu ready */
422 TimeAxisView::build_display_menu ();
424 /* now fill it with our stuff */
426 MenuList& items = display_menu->items();
428 items.push_back (MenuElem (_("Height"), *size_menu));
429 items.push_back (SeparatorElem());
430 items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
431 items.push_back (SeparatorElem());
432 items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
433 items.push_back (SeparatorElem());
435 Menu* auto_state_menu = manage (new Menu);
436 auto_state_menu->set_name ("ArdourContextMenu");
437 MenuList& as_items = auto_state_menu->items();
439 as_items.push_back (CheckMenuElem (_("Manual"),
440 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
441 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
443 as_items.push_back (CheckMenuElem (_("Play"),
444 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
445 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
447 as_items.push_back (CheckMenuElem (_("Write"),
448 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
449 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
451 as_items.push_back (CheckMenuElem (_("Touch"),
452 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
453 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
455 items.push_back (MenuElem (_("State"), *auto_state_menu));
457 /* make sure the automation menu state is correct */
459 automation_state_changed ();
463 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
467 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
468 ret = cut_copy_clear_one ((**i), selection, op);
475 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
477 AutomationList* what_we_got = 0;
478 AutomationList& alist (line.the_list());
481 XMLNode &before = alist.get_state();
485 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
486 editor.get_cut_buffer().add (what_we_got);
487 _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
492 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
493 editor.get_cut_buffer().add (what_we_got);
498 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
499 _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
508 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
509 double foo = (*x)->value;
510 line.model_to_view_y (foo);
519 AutomationTimeAxisView::reset_objects (PointSelection& selection)
521 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
522 reset_objects_one ((**i), selection);
527 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
529 AutomationList& alist (line.the_list());
531 _session.add_command (new MementoCommand<AutomationList>(alist, &alist.get_state(), 0));
533 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
535 if (&(*i).track != this) {
539 alist.reset_range ((*i).start, (*i).end);
544 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
548 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
549 ret = cut_copy_clear_objects_one ((**i), selection, op);
556 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
558 AutomationList* what_we_got = 0;
559 AutomationList& alist (line.the_list());
562 XMLNode &before = alist.get_state();
564 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
566 if (&(*i).track != this) {
572 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
573 editor.get_cut_buffer().add (what_we_got);
574 _session.add_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
579 if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
580 editor.get_cut_buffer().add (what_we_got);
585 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
586 _session.add_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
596 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
597 double foo = (*x)->value;
598 line.model_to_view_y (foo);
607 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
611 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
612 ret = paste_one (**i, pos, times, selection, nth);
619 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
621 AutomationSelection::iterator p;
622 AutomationList& alist (line.the_list());
624 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
626 if (p == selection.lines.end()) {
630 /* Make a copy of the list because we have to scale the
631 values from view coordinates to model coordinates, and we're
632 not supposed to modify the points in the selection.
635 AutomationList copy (**p);
637 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
638 double foo = (*x)->value;
639 line.view_to_model_y (foo);
643 XMLNode &before = alist.get_state();
644 alist.paste (copy, pos, times);
645 _session.add_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
651 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
653 ghosts.push_back (gr);
654 gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
658 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
664 list<GhostRegion*>::iterator i;
666 for (i = ghosts.begin(); i != ghosts.end(); ++i) {
675 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
677 if (!lines.empty() && touched (top, bot)) {
681 /* remember: this is X Window - coordinate space starts in upper left and moves down.
682 y_position is the "origin" or "top" of the track.
685 double mybot = y_position + height; // XXX need to include Editor::track_spacing;
687 if (y_position >= top && mybot <= bot) {
689 /* y_position is below top, mybot is above bot, so we're fully
698 /* top and bot are within y_position .. mybot */
700 topfrac = 1.0 - ((top - y_position) / height);
701 botfrac = 1.0 - ((bot - y_position) / height);
704 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
705 (*i)->get_selectables (start, end, botfrac, topfrac, results);
711 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
713 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
714 (*i)->get_inverted_selectables (sel, result);
719 AutomationTimeAxisView::set_selected_points (PointSelection& points)
721 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
722 (*i)->set_selected_points (points);
727 AutomationTimeAxisView::clear_lines ()
729 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
734 automation_connection.disconnect ();
738 AutomationTimeAxisView::add_line (AutomationLine& line)
743 /* first line is the Model for automation state */
744 automation_connection = line.the_list().automation_state_changed.connect
745 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
749 lines.push_back (&line);
750 line.set_height (height);
753 /* pick up the current state */
754 automation_state_changed ();
759 AutomationTimeAxisView::show_all_control_points ()
761 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
762 (*i)->show_all_control_points ();
767 AutomationTimeAxisView::hide_all_but_selected_control_points ()
769 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
770 (*i)->hide_all_but_selected_control_points ();
775 AutomationTimeAxisView::entered()
777 show_all_control_points ();
781 AutomationTimeAxisView::exited ()
783 hide_all_but_selected_control_points ();
787 AutomationTimeAxisView::set_state (const XMLNode& node)
789 TimeAxisView::set_state (node);
793 AutomationTimeAxisView::get_state_node ()
795 TimeAxisView* state_parent = get_parent_with_state ();
798 return state_parent->get_child_xml_node (_state_name);