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 Pango::FontDescription AutomationTimeAxisView::name_font;
25 bool AutomationTimeAxisView::have_name_font = false;
27 AutomationTimeAxisView::AutomationTimeAxisView (Session& s, boost::shared_ptr<Route> r, PublicEditor& e, TimeAxisView& rent,
28 ArdourCanvas::Canvas& canvas, const string & nom,
29 const string & state_name, const string & nomparent)
32 TimeAxisView (s, e, &rent, canvas),
35 _state_name (state_name),
36 height_button (_("h")),
37 clear_button (_("clear")),
38 auto_button (X_("")) /* force addition of a label */
40 if (!have_name_font) {
41 name_font = get_font_for_style (X_("AutomationTrackName"));
42 have_name_font = true;
46 in_destructor = false;
51 ignore_state_request = false;
52 first_call_to_set_height = true;
54 base_rect = new SimpleRect(*canvas_display);
55 base_rect->property_x1() = 0.0;
56 base_rect->property_y1() = 0.0;
57 base_rect->property_x2() = max_frames;
58 base_rect->property_outline_color_rgba() = color_map[cAutomationTrackOutline];
59 /* outline ends and bottom */
60 base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
61 base_rect->property_fill_color_rgba() = color_map[cAutomationTrackFill];
62 //base_rect->property_fill_color_rgba() = color_map[cEnteredControlPoint];
64 base_rect->set_data ("trackview", this);
66 base_rect->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_automation_track_event),
69 hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
71 height_button.set_name ("TrackSizeButton");
72 auto_button.set_name ("TrackVisualButton");
73 clear_button.set_name ("TrackVisualButton");
74 hide_button.set_name ("TrackRemoveButton");
76 controls_table.set_no_show_all();
78 ARDOUR_UI::instance()->tooltips().set_tip(height_button, _("track height"));
79 ARDOUR_UI::instance()->tooltips().set_tip(auto_button, _("automation state"));
80 ARDOUR_UI::instance()->tooltips().set_tip(clear_button, _("clear track"));
81 ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("hide track"));
83 /* rearrange the name display */
85 /* we never show these for automation tracks, so make
86 life easier and remove them.
91 /* move the name label over a bit */
93 string shortpname = _name;
94 bool shortened = false;
97 shortpname = fit_to_pixels (_name, 60, name_font, ignore_width, true);
99 if (shortpname != _name ){
103 name_label.set_text (shortpname);
104 name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
106 if (nomparent.length()) {
108 /* limit the plug name string */
110 string pname = fit_to_pixels (nomparent, 60, name_font, ignore_width, true);
111 if (pname != nomparent) {
115 plugname = new Label (pname);
116 plugname->set_name (X_("TrackPlugName"));
118 name_label.set_name (X_("TrackParameterName"));
119 controls_table.remove (name_hbox);
120 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
121 plugname_packed = true;
122 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
125 plugname_packed = false;
129 string tipname = nomparent;
130 if (!tipname.empty()) {
134 ARDOUR_UI::instance()->tooltips().set_tip(controls_ebox, tipname);
137 /* add the buttons */
138 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
139 controls_table.attach (height_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
141 controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
142 controls_table.attach (clear_button, 5, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
144 controls_table.show_all ();
146 height_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::height_clicked));
147 clear_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::clear_clicked));
148 hide_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
149 auto_button.signal_clicked().connect (mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
151 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
152 controls_base_unselected_name = X_("AutomationTrackControlsBase");
153 controls_ebox.set_name (controls_base_unselected_name);
155 controls_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
157 XMLNode* xml_node = get_parent_with_state()->get_child_xml_node (_state_name);
160 set_state (*xml_node);
163 /* make sure labels etc. are correct */
165 automation_state_changed ();
168 AutomationTimeAxisView::~AutomationTimeAxisView ()
170 in_destructor = true;
172 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
178 AutomationTimeAxisView::auto_clicked ()
180 using namespace Menu_Helpers;
182 if (automation_menu == 0) {
183 automation_menu = manage (new Menu);
184 automation_menu->set_name ("ArdourContextMenu");
185 MenuList& items (automation_menu->items());
187 items.push_back (MenuElem (_("Manual"),
188 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
189 items.push_back (MenuElem (_("Play"),
190 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
191 items.push_back (MenuElem (_("Write"),
192 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
193 items.push_back (MenuElem (_("Touch"),
194 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
197 automation_menu->popup (1, gtk_get_current_event_time());
202 AutomationTimeAxisView::automation_state_changed ()
206 /* update button label */
211 state = lines.front()->the_list().automation_state ();
214 switch (state & (Off|Play|Touch|Write)) {
216 auto_button.set_label (_("Manual"));
218 ignore_state_request = true;
219 auto_off_item->set_active (true);
220 auto_play_item->set_active (false);
221 auto_touch_item->set_active (false);
222 auto_write_item->set_active (false);
223 ignore_state_request = false;
227 auto_button.set_label (_("Play"));
228 if (auto_play_item) {
229 ignore_state_request = true;
230 auto_play_item->set_active (true);
231 auto_off_item->set_active (false);
232 auto_touch_item->set_active (false);
233 auto_write_item->set_active (false);
234 ignore_state_request = false;
238 auto_button.set_label (_("Write"));
239 if (auto_write_item) {
240 ignore_state_request = true;
241 auto_write_item->set_active (true);
242 auto_off_item->set_active (false);
243 auto_play_item->set_active (false);
244 auto_touch_item->set_active (false);
245 ignore_state_request = false;
249 auto_button.set_label (_("Touch"));
250 if (auto_touch_item) {
251 ignore_state_request = true;
252 auto_touch_item->set_active (true);
253 auto_off_item->set_active (false);
254 auto_play_item->set_active (false);
255 auto_write_item->set_active (false);
256 ignore_state_request = false;
260 auto_button.set_label (_("???"));
266 AutomationTimeAxisView::height_clicked ()
272 AutomationTimeAxisView::clear_clicked ()
274 _session.begin_reversible_command (_("clear automation"));
275 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
278 _session.commit_reversible_command ();
282 AutomationTimeAxisView::set_height (TrackHeight ht)
284 uint32_t h = height_to_pixels (ht);
285 bool changed = (height != (uint32_t) h);
287 bool changed_between_small_and_normal = ( (ht == Small || ht == Smaller) ^ (height_style == Small || height_style == Smaller) );
289 TimeAxisView* state_parent = get_parent_with_state ();
290 XMLNode* xml_node = state_parent->get_child_xml_node (_state_name);
292 TimeAxisView::set_height (ht);
293 base_rect->property_y2() = h;
295 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
296 (*i)->set_height (h);
299 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
306 xml_node->add_property ("track_height", "largest");
310 xml_node->add_property ("track_height", "large");
314 xml_node->add_property ("track_height", "larger");
318 xml_node->add_property ("track_height", "normal");
322 xml_node->add_property ("track_height", "smaller");
326 xml_node->add_property ("track_height", "small");
330 if (changed_between_small_and_normal || first_call_to_set_height) {
331 first_call_to_set_height = false;
338 controls_table.remove (name_hbox);
341 if (plugname_packed) {
342 controls_table.remove (*plugname);
343 plugname_packed = false;
345 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
346 plugname_packed = true;
347 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
349 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
353 name_hbox.show_all ();
356 height_button.show();
358 hide_button.show_all();
364 controls_table.remove (name_hbox);
366 if (plugname_packed) {
367 controls_table.remove (*plugname);
368 plugname_packed = false;
371 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
372 controls_table.hide_all ();
375 name_hbox.show_all ();
378 height_button.hide();
386 /* only emit the signal if the height really changed */
387 route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
392 AutomationTimeAxisView::set_samples_per_unit (double spu)
394 TimeAxisView::set_samples_per_unit (editor.get_current_zoom());
396 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
402 AutomationTimeAxisView::hide_clicked ()
404 // LAME fix for refreshing the hide button
405 hide_button.set_sensitive(false);
407 set_marked_for_display (false);
410 hide_button.set_sensitive(true);
414 AutomationTimeAxisView::build_display_menu ()
416 using namespace Menu_Helpers;
418 /* get the size menu ready */
424 TimeAxisView::build_display_menu ();
426 /* now fill it with our stuff */
428 MenuList& items = display_menu->items();
430 items.push_back (MenuElem (_("Height"), *size_menu));
431 items.push_back (SeparatorElem());
432 items.push_back (MenuElem (_("Hide"), mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
433 items.push_back (SeparatorElem());
434 items.push_back (MenuElem (_("Clear"), mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
435 items.push_back (SeparatorElem());
437 Menu* auto_state_menu = manage (new Menu);
438 auto_state_menu->set_name ("ArdourContextMenu");
439 MenuList& as_items = auto_state_menu->items();
441 as_items.push_back (CheckMenuElem (_("Manual"),
442 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
443 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
445 as_items.push_back (CheckMenuElem (_("Play"),
446 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
447 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
449 as_items.push_back (CheckMenuElem (_("Write"),
450 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
451 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
453 as_items.push_back (CheckMenuElem (_("Touch"),
454 bind (mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
455 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
457 items.push_back (MenuElem (_("State"), *auto_state_menu));
459 /* make sure the automation menu state is correct */
461 automation_state_changed ();
465 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
469 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
470 ret = cut_copy_clear_one ((**i), selection, op);
477 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
479 AutomationList* what_we_got = 0;
480 AutomationList& alist (line.the_list());
483 XMLNode &before = alist.get_state();
487 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
488 editor.get_cut_buffer().add (what_we_got);
489 _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
494 if ((what_we_got = alist.copy (selection.time.front().start, selection.time.front().end)) != 0) {
495 editor.get_cut_buffer().add (what_we_got);
500 if ((what_we_got = alist.cut (selection.time.front().start, selection.time.front().end)) != 0) {
501 _session.add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
510 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
511 double foo = (*x)->value;
512 line.model_to_view_y (foo);
521 AutomationTimeAxisView::reset_objects (PointSelection& selection)
523 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
524 reset_objects_one ((**i), selection);
529 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
531 AutomationList& alist (line.the_list());
533 _session.add_command (new MementoCommand<AutomationList>(alist, &alist.get_state(), 0));
535 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
537 if (&(*i).track != this) {
541 alist.reset_range ((*i).start, (*i).end);
546 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
550 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
551 ret = cut_copy_clear_objects_one ((**i), selection, op);
558 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
560 AutomationList* what_we_got = 0;
561 AutomationList& alist (line.the_list());
564 XMLNode &before = alist.get_state();
566 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
568 if (&(*i).track != this) {
574 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
575 editor.get_cut_buffer().add (what_we_got);
576 _session.add_command (new MementoCommand<AutomationList>(alist, new XMLNode (before), &alist.get_state()));
581 if ((what_we_got = alist.copy ((*i).start, (*i).end)) != 0) {
582 editor.get_cut_buffer().add (what_we_got);
587 if ((what_we_got = alist.cut ((*i).start, (*i).end)) != 0) {
588 _session.add_command (new MementoCommand<AutomationList>(alist, new XMLNode (before), &alist.get_state()));
600 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
601 double foo = (*x)->value;
602 line.model_to_view_y (foo);
611 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
615 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
616 ret = paste_one (**i, pos, times, selection, nth);
623 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
625 AutomationSelection::iterator p;
626 AutomationList& alist (line.the_list());
628 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth);
630 if (p == selection.lines.end()) {
634 /* Make a copy of the list because we have to scale the
635 values from view coordinates to model coordinates, and we're
636 not supposed to modify the points in the selection.
639 AutomationList copy (**p);
641 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
642 double foo = (*x)->value;
643 line.view_to_model_y (foo);
647 XMLNode &before = alist.get_state();
648 alist.paste (copy, pos, times);
649 _session.add_command (new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
655 AutomationTimeAxisView::add_ghost (GhostRegion* gr)
657 ghosts.push_back (gr);
658 gr->GoingAway.connect (mem_fun(*this, &AutomationTimeAxisView::remove_ghost));
662 AutomationTimeAxisView::remove_ghost (GhostRegion* gr)
668 list<GhostRegion*>::iterator i;
670 for (i = ghosts.begin(); i != ghosts.end(); ++i) {
679 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
681 if (!lines.empty() && touched (top, bot)) {
685 /* remember: this is X Window - coordinate space starts in upper left and moves down.
686 y_position is the "origin" or "top" of the track.
689 double mybot = y_position + height; // XXX need to include Editor::track_spacing;
691 if (y_position >= top && mybot <= bot) {
693 /* y_position is below top, mybot is above bot, so we're fully
702 /* top and bot are within y_position .. mybot */
704 topfrac = 1.0 - ((top - y_position) / height);
705 botfrac = 1.0 - ((bot - y_position) / height);
708 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
709 (*i)->get_selectables (start, end, botfrac, topfrac, results);
715 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
717 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
718 (*i)->get_inverted_selectables (sel, result);
723 AutomationTimeAxisView::set_selected_points (PointSelection& points)
725 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
726 (*i)->set_selected_points (points);
731 AutomationTimeAxisView::clear_lines ()
733 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
738 automation_connection.disconnect ();
742 AutomationTimeAxisView::add_line (AutomationLine& line)
747 /* first line is the Model for automation state */
748 automation_connection = line.the_list().automation_state_changed.connect
749 (mem_fun(*this, &AutomationTimeAxisView::automation_state_changed));
753 lines.push_back (&line);
754 line.set_height (height);
757 /* pick up the current state */
758 automation_state_changed ();
763 AutomationTimeAxisView::show_all_control_points ()
765 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
766 (*i)->show_all_control_points ();
771 AutomationTimeAxisView::hide_all_but_selected_control_points ()
773 for (vector<AutomationLine*>::iterator i = lines.begin(); i != lines.end(); ++i) {
774 (*i)->hide_all_but_selected_control_points ();
779 AutomationTimeAxisView::entered()
781 show_all_control_points ();
785 AutomationTimeAxisView::exited ()
787 hide_all_but_selected_control_points ();
791 AutomationTimeAxisView::set_state (const XMLNode& node)
793 TimeAxisView::set_state (node);
797 AutomationTimeAxisView::get_state_node ()
799 TimeAxisView* state_parent = get_parent_with_state ();
802 return state_parent->get_child_xml_node (_state_name);