ardour
automation_time_axis.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000-2007 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include <utility>
22 #include <gtkmm2ext/utils.h>
23 #include <boost/algorithm/string.hpp>
24 #include <boost/lexical_cast.hpp>
25 
26 #include "pbd/error.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/stacktrace.h"
29 
31 #include "ardour/event_type_map.h"
32 #include "ardour/parameter_types.h"
33 #include "ardour/profile.h"
34 #include "ardour/route.h"
35 #include "ardour/session.h"
36 
37 #include "canvas/debug.h"
38 
39 #include "ardour_ui.h"
40 #include "automation_time_axis.h"
41 #include "automation_streamview.h"
42 #include "global_signals.h"
43 #include "gui_thread.h"
44 #include "route_time_axis.h"
45 #include "automation_line.h"
46 #include "paste_context.h"
47 #include "public_editor.h"
48 #include "selection.h"
49 #include "rgb_macros.h"
50 #include "point_selection.h"
51 #include "control_point.h"
52 #include "utils.h"
53 #include "item_counts.h"
54 
55 #include "i18n.h"
56 
57 using namespace std;
58 using namespace ARDOUR;
59 using namespace ARDOUR_UI_UTILS;
60 using namespace PBD;
61 using namespace Gtk;
62 using namespace Gtkmm2ext;
63 using namespace Editing;
64 
65 Pango::FontDescription AutomationTimeAxisView::name_font;
67 
68 
75  Session* s,
80  PublicEditor& e,
81  TimeAxisView& parent,
82  bool show_regions,
83  ArdourCanvas::Canvas& canvas,
84  const string & nom,
85  const string & nomparent
86  )
87  : AxisView (s)
88  , TimeAxisView (s, e, &parent, canvas)
89  , _route (r)
90  , _control (c)
91  , _automatable (a)
92  , _parameter (p)
93  , _base_rect (new ArdourCanvas::Rectangle (_canvas_display))
94  , _view (show_regions ? new AutomationStreamView (*this) : 0)
95  , auto_button (X_("")) /* force addition of a label */
96  , _show_regions (show_regions)
97 {
98  //concatenate plugin name and param name into the tooltip
99  string tipname = nomparent;
100  if (!tipname.empty()) {
101  tipname += ": ";
102  }
103  tipname += nom;
105 
106  //plugin name and param name appear on 2 separate lines in the track header
107  tipname = nomparent;
108  if (!tipname.empty()) {
109  tipname += "\n";
110  }
111  tipname += nom;
112  _name = tipname;
113 
114  CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for auto %2/%1", _name, r->name()));
115  CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for auto %2/%1", _name, r->name()));
116  CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for auto %2/%1", _name, r->name()));
117 
118  if (!have_name_font) {
119  name_font = get_font_for_style (X_("AutomationTrackName"));
120  have_name_font = true;
121  }
122 
123  if (_automatable && _control) {
125  }
126 
127  const std::string fill_color_name = (dynamic_cast<MidiTimeAxisView*>(&parent)
128  ? "midi automation track fill"
129  : "audio automation track fill");
130 
131  automation_menu = 0;
132  auto_off_item = 0;
133  auto_touch_item = 0;
134  auto_write_item = 0;
135  auto_play_item = 0;
136  mode_discrete_item = 0;
137  mode_line_item = 0;
138 
139  ignore_state_request = false;
141 
142  CANVAS_DEBUG_NAME (_base_rect, string_compose ("base rect for %1", _name));
143  _base_rect->set_x1 (ArdourCanvas::COORD_MAX);
144  _base_rect->set_outline (false);
145  _base_rect->set_fill_color (ARDOUR_UI::config()->color_mod (fill_color_name, "automation track fill"));
146  _base_rect->set_data ("trackview", this);
147  _base_rect->Event.connect (sigc::bind (sigc::mem_fun (_editor, &PublicEditor::canvas_automation_track_event), _base_rect, this));
148  if (!a) {
149  _base_rect->lower_to_bottom();
150  }
151 
155 
156  auto_button.set_name ("route button");
157  hide_button.set_name ("route button");
158 
159  auto_button.unset_flags (Gtk::CAN_FOCUS);
160  hide_button.unset_flags (Gtk::CAN_FOCUS);
161 
162  controls_table.set_no_show_all();
163 
164  ARDOUR_UI::instance()->set_tip(auto_button, _("automation state"));
165  ARDOUR_UI::instance()->set_tip(hide_button, _("hide track"));
166 
167  const string str = gui_property ("height");
168  if (!str.empty()) {
169  set_height (atoi (str));
170  } else {
172  }
173 
174  //name label isn't editable on an automation track; remove the tooltip
176 
177  /* repack the name label */
178 
179  if (name_label.get_parent()) {
180  name_label.get_parent()->remove (name_label);
181  }
182 
183  name_label.set_text (_name);
184  name_label.set_alignment (Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER);
185  name_label.set_name (X_("TrackParameterName"));
186  name_label.set_ellipsize (Pango::ELLIPSIZE_END);
187 
188  /* add the buttons */
189  controls_table.set_border_width (1);
190  controls_table.remove (name_hbox);
191  controls_table.attach (hide_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
192  controls_table.attach (name_label, 2, 3, 1, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 2, 0);
193  controls_table.attach (auto_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
194 
195  Gtk::DrawingArea *blank0 = manage (new Gtk::DrawingArea());
196  Gtk::DrawingArea *blank1 = manage (new Gtk::DrawingArea());
197 
198  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&parent);
199  // TODO use rtv->controls_base_unselected_name
200  // subscribe to route_active_changed, ...
201  if (rtv && rtv->is_audio_track()) {
202  blank0->set_name ("AudioTrackControlsBaseUnselected");
203  }
204  else if (rtv && rtv->is_midi_track()) {
205  blank0->set_name ("MidiTrackControlsBaseUnselected");
206  }
207  else {
208  blank0->set_name ("AudioBusControlsBaseUnselected");
209  }
210  blank0->set_size_request (-1, -1);
211  blank1->set_size_request (1, 0);
212  VSeparator* separator = manage (new VSeparator());
213  separator->set_name("TrackSeparator");
214  separator->set_size_request (1, -1);
215 
217  controls_button_size_group->add_widget(*blank0);
218 
219  time_axis_hbox.pack_start (*blank0, false, false);
220  time_axis_hbox.pack_start (*separator, false, false);
221  time_axis_hbox.reorder_child (*blank0, 0);
222  time_axis_hbox.reorder_child (*separator, 1);
223  time_axis_hbox.reorder_child (time_axis_vbox, 2);
224 
225  if (!ARDOUR::Profile->get_mixbus() ) {
226  time_axis_hbox.pack_start (*blank1, false, false);
227  }
228 
229  blank0->show();
230  separator->show();
231  name_label.show ();
232  hide_button.show ();
233 
234  if (_controller) {
236  controls_table.attach (*_controller.get(), 2, 4, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
237  }
238 
239  controls_table.show_all ();
240 
241  hide_button.signal_clicked.connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
242  auto_button.signal_clicked.connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
243 
244  controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
245  controls_base_unselected_name = X_("AutomationTrackControlsBase");
246 
249 
250  /* ask for notifications of any new RegionViews */
251  if (show_regions) {
252 
253  if (_view) {
254  _view->attach ();
255  }
256 
257  } else {
258  /* no regions, just a single line for the entire track (e.g. bus gain) */
259 
260  assert (_control);
261 
263  new AutomationLine (
265  *this,
267  _control->alist(),
268  _control->desc()
269  )
270  );
271 
272  line->set_line_color (ARDOUR_UI::config()->color ("processor automation line"));
273  line->queue_reset ();
274  add_line (line);
275  }
276 
277  /* make sure labels etc. are correct */
278 
280  ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
281 
282  _route->DropReferences.connect (
284  );
285 }
286 
288 {
290  delete _view;
291 }
292 
293 void
295 {
296  _route.reset ();
297 }
298 
299 void
301 {
302  using namespace Menu_Helpers;
303 
304  if (automation_menu == 0) {
305  automation_menu = manage (new Menu);
306  automation_menu->set_name ("ArdourContextMenu");
307  MenuList& items (automation_menu->items());
308 
309  items.push_back (MenuElem (S_("Automation|Manual"), sigc::bind (sigc::mem_fun(*this,
311  items.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
313  items.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
315  items.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
317  }
318 
319  automation_menu->popup (1, gtk_get_current_event_time());
320 }
321 
322 void
324 {
325  if (ignore_state_request) {
326  return;
327  }
328 
329  if (_automatable) {
331  }
332 
333  if (_view) {
334  _view->set_automation_state (state);
335 
336  /* AutomationStreamViews don't signal when their automation state changes, so handle
337  our updates `manually'.
338  */
340  }
341 }
342 
343 void
345 {
346  AutoState state;
347 
348  /* update button label */
349 
350  if (_view) {
351  state = _view->automation_state ();
352  } else if (_line) {
353  assert (_control);
354  state = _control->alist()->automation_state ();
355  } else {
356  state = ARDOUR::Off;
357  }
358 
359  switch (state & (ARDOUR::Off|Play|Touch|Write)) {
360  case ARDOUR::Off:
361  auto_button.set_text (S_("Automation|Manual"));
362  if (auto_off_item) {
363  ignore_state_request = true;
364  auto_off_item->set_active (true);
365  auto_play_item->set_active (false);
366  auto_touch_item->set_active (false);
367  auto_write_item->set_active (false);
368  ignore_state_request = false;
369  }
370  break;
371  case Play:
372  auto_button.set_text (_("Play"));
373  if (auto_play_item) {
374  ignore_state_request = true;
375  auto_play_item->set_active (true);
376  auto_off_item->set_active (false);
377  auto_touch_item->set_active (false);
378  auto_write_item->set_active (false);
379  ignore_state_request = false;
380  }
381  break;
382  case Write:
383  auto_button.set_text (_("Write"));
384  if (auto_write_item) {
385  ignore_state_request = true;
386  auto_write_item->set_active (true);
387  auto_off_item->set_active (false);
388  auto_play_item->set_active (false);
389  auto_touch_item->set_active (false);
390  ignore_state_request = false;
391  }
392  break;
393  case Touch:
394  auto_button.set_text (_("Touch"));
395  if (auto_touch_item) {
396  ignore_state_request = true;
397  auto_touch_item->set_active (true);
398  auto_off_item->set_active (false);
399  auto_play_item->set_active (false);
400  auto_write_item->set_active (false);
401  ignore_state_request = false;
402  }
403  break;
404  default:
405  auto_button.set_text (_("???"));
406  break;
407  }
408 }
409 
411 void
412 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
413 {
415  if (s == AutomationList::Discrete) {
416  mode_discrete_item->set_active(true);
417  mode_line_item->set_active(false);
418  } else {
419  mode_line_item->set_active(true);
420  mode_discrete_item->set_active(false);
421  }
422  }
423 }
424 
426 void
427 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
428 {
429  /* Tell our view's list, if we have one, otherwise tell our own.
430  * Everything else will be signalled back from that.
431  */
432 
433  if (_view) {
434  _view->set_interpolation (style);
435  } else {
436  assert (_control);
437  _control->list()->set_interpolation (style);
438  }
439 }
440 
441 void
443 {
444  assert (_line || _view);
445 
446  _editor.begin_reversible_command (_("clear automation"));
447 
448  if (_line) {
449  _line->clear ();
450  } else if (_view) {
451  _view->clear ();
452  }
453 
455  _session->set_dirty ();
456 }
457 
458 void
460 {
461  bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
462  uint32_t const normal = preset_height (HeightNormal);
463  bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
464 
466 
467  _base_rect->set_y1 (h);
468 
469  if (_line) {
470  _line->set_height(h - 2.5);
471  }
472 
473  if (_view) {
474  _view->set_height(h);
476  }
477 
478  if (changed_between_small_and_normal || first_call_to_set_height) {
479 
480  first_call_to_set_height = false;
481 
482  if (h >= preset_height (HeightNormal)) {
483  if (!(_parameter.type() >= MidiCCAutomation &&
485  auto_button.show();
486  }
487  name_label.show();
488  hide_button.show();
489 
490  } else if (h >= preset_height (HeightSmall)) {
491  controls_table.hide_all ();
492  auto_button.hide();
493  name_label.hide();
494  }
495  }
496 
497  if (changed) {
498  if (_canvas_display->visible() && _route) {
499  /* only emit the signal if the height really changed and we were visible */
500  _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
501  }
502  }
503 }
504 
505 void
507 {
509 
510  if (_line) {
511  _line->reset ();
512  }
513 
514  if (_view) {
516  }
517 }
518 
519 void
521 {
522  hide_button.set_sensitive(false);
523  set_marked_for_display (false);
524  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(parent);
525  if (rtv) {
526  rtv->request_redraw ();
527  }
528  hide_button.set_sensitive(true);
529 }
530 
531 void
533 {
534  using namespace Menu_Helpers;
535 
536  /* prepare it */
537 
539 
540  /* now fill it with our stuff */
541 
542  MenuList& items = display_menu->items();
543 
544  items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
545  items.push_back (SeparatorElem());
546  items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
547  items.push_back (SeparatorElem());
548 
549  /* state menu */
550 
551  Menu* auto_state_menu = manage (new Menu);
552  auto_state_menu->set_name ("ArdourContextMenu");
553  MenuList& as_items = auto_state_menu->items();
554 
555  as_items.push_back (CheckMenuElem (S_("Automation|Manual"), sigc::bind (
556  sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
557  (AutoState) ARDOUR::Off)));
558  auto_off_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
559 
560  as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
561  sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
562  (AutoState) Play)));
563  auto_play_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
564 
565  as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
566  sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
567  (AutoState) Write)));
568  auto_write_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
569 
570  as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
571  sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
572  (AutoState) Touch)));
573  auto_touch_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
574 
575  if (!(_parameter.type() >= MidiCCAutomation &&
577  items.push_back (MenuElem (_("State"), *auto_state_menu));
578  }
579 
580  /* mode menu */
581 
582  /* current interpolation state */
583  AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
584 
586 
587  Menu* auto_mode_menu = manage (new Menu);
588  auto_mode_menu->set_name ("ArdourContextMenu");
589  MenuList& am_items = auto_mode_menu->items();
590 
591  RadioMenuItem::Group group;
592 
593  am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
594  sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
595  AutomationList::Discrete)));
596  mode_discrete_item = dynamic_cast<Gtk::CheckMenuItem*>(&am_items.back());
597  mode_discrete_item->set_active (s == AutomationList::Discrete);
598 
599  am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
600  sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
602  mode_line_item = dynamic_cast<Gtk::CheckMenuItem*>(&am_items.back());
603  mode_line_item->set_active (s == AutomationList::Linear);
604 
605  items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
606  }
607 
608  /* make sure the automation menu state is correct */
609 
612 }
613 
614 void
615 AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when, double y, bool with_guard_points)
616 {
617  if (!_line) {
618  return;
619  }
620 
622 
623  if (list->in_write_pass()) {
624  /* do not allow the GUI to add automation events during an
625  automation write pass.
626  */
627  return;
628  }
629 
630  double x = 0;
631 
632  _canvas_display->canvas_to_item (x, y);
633 
634  /* compute vertical fractional position */
635 
636  y = 1.0 - (y / height);
637 
638  /* map using line */
639 
640  _line->view_to_model_coord (x, y);
641 
642 
643  _editor.snap_to_with_modifier (when, event);
644 
645  _editor.begin_reversible_command (_("add automation event"));
646  XMLNode& before = list->get_state();
647 
648  list->add (when, y, with_guard_points);
649 
650  XMLNode& after = list->get_state();
651  _session->add_command (new MementoCommand<ARDOUR::AutomationList> (*list, &before, &after));
653  _session->set_dirty ();
654 }
655 
656 bool
658 {
659  if (_line) {
660  return paste_one (pos, ctx.count, ctx.times, selection, ctx.counts, ctx.greedy);
661  } else if (_view) {
662  AutomationSelection::const_iterator l = selection.lines.get_nth(_parameter, ctx.counts.n_lines(_parameter));
663  if (l == selection.lines.end()) {
664  if (ctx.greedy && selection.lines.size() == 1) {
665  l = selection.lines.begin();
666  }
667  }
668  if (l != selection.lines.end() && _view->paste (pos, ctx.count, ctx.times, *l)) {
670  return true;
671  }
672  }
673 
674  return false;
675 }
676 
677 bool
678 AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float times, const Selection& selection, ItemCounts& counts, bool greedy)
679 {
681 
682  if (_session->transport_rolling() && alist->automation_write()) {
683  /* do not paste if this control is in write mode and we're rolling */
684  return false;
685  }
686 
687  /* Get appropriate list from selection. */
688  AutomationSelection::const_iterator p = selection.lines.get_nth(_parameter, counts.n_lines(_parameter));
689  if (p == selection.lines.end()) {
690  if (greedy && selection.lines.size() == 1) {
691  p = selection.lines.begin();
692  } else {
693  return false;
694  }
695  }
697 
698  /* add multi-paste offset if applicable */
699  pos += _editor.get_paste_offset(pos, paste_count, (*p)->length());
700 
701  double const model_pos = _line->time_converter().from (pos - _line->time_converter().origin_b ());
702 
703  XMLNode &before = alist->get_state();
704  alist->paste (**p, model_pos, times);
705  _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
706 
707  return true;
708 }
709 
710 void
711 AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool /*within*/)
712 {
713  if (!_line && !_view) {
714  return;
715  }
716 
717  if (touched (top, bot)) {
718 
719  /* remember: this is X Window - coordinate space starts in upper left and moves down.
720  _y_position is the "origin" or "top" of the track.
721  */
722 
723  /* bottom of our track */
724  double const mybot = _y_position + height;
725 
726  double topfrac;
727  double botfrac;
728 
729  if (_y_position >= top && mybot <= bot) {
730 
731  /* _y_position is below top, mybot is above bot, so we're fully
732  covered vertically.
733  */
734 
735  topfrac = 1.0;
736  botfrac = 0.0;
737 
738  } else {
739 
740  /* top and bot are within _y_position .. mybot */
741 
742  topfrac = 1.0 - ((top - _y_position) / height);
743  botfrac = 1.0 - ((bot - _y_position) / height);
744 
745  }
746 
747  if (_line) {
748  _line->get_selectables (start, end, botfrac, topfrac, results);
749  } else if (_view) {
750  _view->get_selectables (start, end, botfrac, topfrac, results);
751  }
752  }
753 }
754 
755 void
757 {
758  if (_line) {
759  _line->get_inverted_selectables (sel, result);
760  }
761 }
762 
763 void
765 {
766  if (_line) {
767  _line->set_selected_points (points);
768  } else if (_view) {
769  _view->set_selected_points (points);
770  }
771 }
772 
773 void
775 {
776  _line.reset();
778 }
779 
780 void
782 {
783  if (_control && line) {
784  assert(line->the_list() == _control->list());
785 
788  );
789 
790  _control->alist()->InterpolationChanged.connect (
792  );
793  }
794 
795  _line = line;
796 
797  line->set_height (height);
798 
799  /* pick up the current state */
801 
803 }
804 
805 void
807 {
808  if (_line) {
809  _line->track_entered();
810  }
811 }
812 
813 void
815 {
816  if (_line) {
817  _line->track_exited();
818  }
819 }
820 
821 void
823 {
824  if (_line) {
825  _line->set_colors();
826  }
827 }
828 
829 int
830 AutomationTimeAxisView::set_state_2X (const XMLNode& node, int /*version*/)
831 {
832  if (node.name() == X_("gain") && _parameter == Evoral::Parameter (GainAutomation)) {
833  XMLProperty const * shown = node.property (X_("shown"));
834  if (shown) {
835  bool yn = string_is_affirmative (shown->value ());
836  if (yn) {
837  _canvas_display->show (); /* FIXME: necessary? show_at? */
838  }
839  set_gui_property ("visible", yn);
840  } else {
841  set_gui_property ("visible", false);
842  }
843  }
844 
845  return 0;
846 }
847 
848 int
850 {
851  return 0;
852 }
853 
854 void
855 AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr<Automatable>& automatable, set<Evoral::Parameter>& visible)
856 {
857  /* this keeps "knowledge" of how we store visibility information
858  in XML private to this class.
859  */
860 
861  assert (automatable);
862 
863  Automatable::Controls& controls (automatable->controls());
864 
865  for (Automatable::Controls::iterator i = controls.begin(); i != controls.end(); ++i) {
866 
868 
869  if (ac && ac->alist()) {
870 
871  const XMLNode* gui_node = ac->extra_xml ("GUI");
872 
873  if (gui_node) {
874  const XMLProperty* prop = gui_node->property ("shown");
875  if (prop) {
876  if (string_is_affirmative (prop->value())) {
877  visible.insert (i->first);
878  }
879  }
880  }
881  }
882  }
883 }
884 
885 
887 bool
889 {
890  return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
891 }
892 
893 list<boost::shared_ptr<AutomationLine> >
895 {
896  list<boost::shared_ptr<AutomationLine> > lines;
897 
898  if (_line) {
899  lines.push_back (_line);
900  } else if (_view) {
901  lines = _view->get_lines ();
902  }
903 
904  return lines;
905 }
906 
907 string
909 {
910  if (_automatable != _route && _control) {
911  return string_compose ("automation %1", _control->id().to_s());
912  } else if (_parameter) {
913  return string_compose ("automation %1 %2/%3/%4",
914  _route->id(),
915  _parameter.type(),
916  _parameter.id(),
917  (int) _parameter.channel());
918  } else {
919  error << "Automation time axis has no state ID" << endmsg;
920  return "";
921  }
922 }
923 
933 bool
935  string const & state_id,
936  PBD::ID & route_id,
937  bool & has_parameter,
939 {
940  stringstream s;
941  s << state_id;
942 
943  string a, b, c;
944  s >> a >> b >> c;
945 
946  if (a != X_("automation")) {
947  return false;
948  }
949 
950  route_id = PBD::ID (b);
951 
952  if (c.empty ()) {
953  has_parameter = false;
954  return true;
955  }
956 
957  has_parameter = true;
958 
959  vector<string> p;
960  boost::split (p, c, boost::is_any_of ("/"));
961 
962  assert (p.size() == 3);
963 
964  parameter = Evoral::Parameter (
965  boost::lexical_cast<int> (p[0]),
966  boost::lexical_cast<int> (p[2]),
967  boost::lexical_cast<int> (p[1])
968  );
969 
970  return true;
971 }
972 
973 void
975 {
976  list<boost::shared_ptr<AutomationLine> > lines;
977  if (_line) {
978  lines.push_back (_line);
979  } else if (_view) {
980  lines = _view->get_lines ();
981  }
982 
983  for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
984  cut_copy_clear_one (**i, selection, op);
985  }
986 }
987 
988 void
990 {
993 
994  XMLNode &before = alist->get_state();
995 
996  /* convert time selection to automation list model coordinates */
998  double const start = tc.from (selection.time.front().start - tc.origin_b ());
999  double const end = tc.from (selection.time.front().end - tc.origin_b ());
1000 
1001  switch (op) {
1002  case Delete:
1003  if (alist->cut (start, end) != 0) {
1004  _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
1005  }
1006  break;
1007 
1008  case Cut:
1009 
1010  if ((what_we_got = alist->cut (start, end)) != 0) {
1011  _editor.get_cut_buffer().add (what_we_got);
1012  _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
1013  }
1014  break;
1015  case Copy:
1016  if ((what_we_got = alist->copy (start, end)) != 0) {
1017  _editor.get_cut_buffer().add (what_we_got);
1018  }
1019  break;
1020 
1021  case Clear:
1022  if ((what_we_got = alist->cut (start, end)) != 0) {
1023  _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
1024  }
1025  break;
1026  }
1027 
1028  if (what_we_got) {
1029  for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
1030  double when = (*x)->when;
1031  double val = (*x)->value;
1032  line.model_to_view_coord (when, val);
1033  (*x)->when = when;
1034  (*x)->value = val;
1035  }
1036  }
1037 }
Gtk::Menu * display_menu
bool transport_rolling() const
Definition: session.h:592
void cut_copy_clear_one(AutomationLine &, Selection &, Editing::CutCopyOp)
int atoi(const string &s)
Definition: convert.cc:140
virtual void snap_to_with_modifier(framepos_t &first, GdkEvent const *ev, ARDOUR::RoundMode direction=ARDOUR::RoundNearest, bool for_mark=false)=0
Gtk::CheckMenuItem * mode_line_item
Gtk::CheckMenuItem * auto_touch_item
virtual A from(B b) const =0
virtual void update_contents_height()
Definition: streamview.cc:629
AutomationSelection lines
Definition: selection.h:84
void set_selected_points(PointSelection &)
PBD::Signal0< void > DropReferences
Definition: destructible.h:34
sigc::signal< void > signal_clicked
const std::string & value() const
Definition: xml++.h:159
void set_interpolation(ARDOUR::AutomationList::InterpolationStyle)
bool is_midi_track() const
Definition: route_ui.cc:1756
Gtk::CheckMenuItem * auto_play_item
std::string gui_property(const std::string &property_name) const
Definition: axis_view.cc:67
boost::shared_ptr< ARDOUR::Route > _route
bool is_audio_track() const
Definition: route_ui.cc:1744
bool touched(double top, double bot)
void add_visibility(VisibleAspects)
std::string state_id() const
bool in_write_pass() const
boost::shared_ptr< AutomationLine > _line
LIBPBD_API void split(std::string, std::vector< std::string > &, char)
virtual void set_height(uint32_t h, TrackHeightMode m=OnlySelf)
Definition: ardour_ui.h:130
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
static ARDOUR_UI * instance()
Definition: ardour_ui.h:187
void view_to_model_coord(double &x, double &y) const
boost::shared_ptr< ARDOUR::AutomationList > the_list() const
virtual int set_height(double)
Definition: streamview.cc:115
Pango::FontDescription get_font_for_style(std::string widgetname)
const std::string & name() const
Definition: xml++.h:104
Lists of selected things.
Definition: selection.h:66
void add_command(Command *const cmd)
Definition: session.h:787
Representation of the interface of the Editor class.
Definition: Beats.hpp:239
std::list< boost::shared_ptr< AutomationLine > > lines() const
Gtk::Table controls_table
LIBPBD_API Transmitter error
uint32_t npoints() const
int set_state(const XMLNode &, int version)
void increase_n_lines(Evoral::Parameter t, size_t delta=1)
Definition: item_counts.h:53
AutomationStreamView * _view
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
static Pango::FontDescription name_font
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
virtual void set_parameter_automation_state(Evoral::Parameter param, AutoState)
Definition: automatable.cc:272
Gtk::Frame time_axis_frame
boost::shared_ptr< ARDOUR::AutomationControl > _control
virtual void set_height(uint32_t, TrackHeightMode m=OnlySelf)
void set_tweaks(Tweaks)
virtual bool set_marked_for_display(bool)
Definition: axis_view.cc:87
#define invalidator(x)
Definition: gui_thread.h:40
void set_elements(Element)
Gtk::HBox name_hbox
virtual void build_display_menu()
Definition: id.h:32
ARDOUR::AutoState automation_state() const
Gtk::CheckMenuItem * auto_off_item
AutomationTimeAxisView(ARDOUR::Session *, boost::shared_ptr< ARDOUR::Route >, boost::shared_ptr< ARDOUR::Automatable >, boost::shared_ptr< ARDOUR::AutomationControl >, Evoral::Parameter, PublicEditor &, TimeAxisView &parent, bool show_regions, ArdourCanvas::Canvas &canvas, const std::string &name, const std::string &plug_name="")
#define _(Text)
Definition: i18n.h:11
void interpolation_changed(ARDOUR::AutomationList::InterpolationStyle)
void get_selectables(ARDOUR::framepos_t start, ARDOUR::framepos_t end, double top, double bot, std::list< Selectable * > &, bool within=false)
void add(std::list< Selectable * > const &)
Definition: selection.cc:1045
std::string controls_base_selected_name
ArdourCanvas::Container * _canvas_display
static bool parse_state_id(std::string const &, PBD::ID &, bool &, Evoral::Parameter &)
bool paste(framepos_t pos, unsigned paste_count, float times, boost::shared_ptr< ARDOUR::AutomationList > list)
#define X_(Text)
Definition: i18n.h:13
ArdourCanvas::Container * _ghost_group
PBD::Signal2< void, std::string, void * > gui_changed
Definition: route.h:324
XMLProperty * property(const char *)
Definition: xml++.cc:413
void set_icon(Icon)
static boost::shared_ptr< AutomationController > create(boost::shared_ptr< ARDOUR::Automatable > parent, const Evoral::Parameter &param, const ARDOUR::ParameterDescriptor &desc, boost::shared_ptr< ARDOUR::AutomationControl > ac)
boost::shared_ptr< ARDOUR::Automatable > _automatable
void attach()
Definition: streamview.cc:100
Evoral::Parameter parameter() const
const ParameterDescriptor & desc() const
bool string_is_affirmative(const std::string &str)
Definition: convert.cc:282
AutoState automation_state() const
Definition: amp.h:29
void request_redraw()
Definition: route_ui.cc:2100
const PBD::ID & id() const
Definition: stateful.h:68
std::string to_s() const
Definition: id.cc:78
void get_selectables(ARDOUR::framepos_t, ARDOUR::framepos_t, double, double, std::list< Selectable * > &, bool within=false)
virtual void begin_reversible_command(std::string cmd_name)=0
#define gui_context()
Definition: gui_thread.h:36
Gtk::CheckMenuItem * mode_discrete_item
void set_selected_points(PointSelection &)
virtual void commit_reversible_command()=0
void cut_copy_clear(Selection &, Editing::CutCopyOp)
void add_line(boost::shared_ptr< AutomationLine >)
virtual Selection & get_cut_buffer() const =0
AutomationType
Definition: types.h:121
int64_t framepos_t
Definition: types.h:66
static EventTypeMap & instance()
boost::shared_ptr< ControlList > cut(double, double)
virtual int set_samples_per_pixel(double)
Definition: streamview.cc:135
PBD::Signal1< void, InterpolationStyle > InterpolationChanged
T * get() const
Definition: shared_ptr.hpp:268
LIBARDOUR_API RuntimeProfile * Profile
Definition: globals.cc:120
CutCopyOp
Definition: editing.h:198
TimeAxisView * parent
ItemCounts counts
Count of consumed selection items.
Definition: paste_context.h:37
const Parameter & parameter() const
Definition: Control.hpp:69
void set_tip(Gtk::Widget &w, const gchar *tip)
PBD::ScopedConnectionList _route_connections
PBD::ScopedConnectionList _list_connections
void set_interpolation(ARDOUR::AutomationList::InterpolationStyle)
ArdourCanvas::Rectangle * _base_rect
std::list< boost::shared_ptr< AutomationLine > > get_lines() const
Glib::RefPtr< Gtk::SizeGroup > controls_button_size_group
ARDOUR::framepos_t start()
ArdourCanvas::Container * selection_group
boost::shared_ptr< ControlList > copy(double, double)
virtual void set_samples_per_pixel(double)
Gtk::CheckMenuItem * auto_write_item
virtual bool canvas_automation_track_event(GdkEvent *event, ArdourCanvas::Item *, AutomationTimeAxisView *)=0
uint32_t id() const
Definition: Parameter.hpp:49
static void what_has_visible_automation(const boost::shared_ptr< ARDOUR::Automatable > &automatable, std::set< Evoral::Parameter > &visible)
void model_to_view_coord(double &x, double &y) const
virtual framecnt_t get_paste_offset(framepos_t pos, unsigned paste_count, framecnt_t duration)=0
void get_selectables(ARDOUR::framepos_t, ARDOUR::framepos_t, double, double, std::list< Selectable * > &)
bool parameter_is_midi(AutomationType type)
const Evoral::TimeConverter< double, ARDOUR::framepos_t > & time_converter() const
Definition: xml++.h:95
ARDOUR::AutomationList::InterpolationStyle interpolation() const
boost::shared_ptr< ControlList > list()
Definition: Control.hpp:66
std::string name() const
TimeSelection time
Definition: selection.h:83
Controls & controls()
Definition: ControlSet.hpp:58
size_t n_lines(Evoral::Parameter t) const
Definition: item_counts.h:42
static UIConfiguration * config()
Definition: ardour_ui.h:188
void set_text(const std::string &)
bool paste_one(ARDOUR::framepos_t, unsigned, float times, const Selection &, ItemCounts &counts, bool greedy=false)
int set_state_2X(const XMLNode &, int)
Evoral::Parameter _parameter
uint32_t type() const
Definition: Parameter.hpp:47
XMLNode * extra_xml(const std::string &str, bool add_if_missing=false)
Definition: stateful.cc:77
sigc::signal< void > ColorsChanged
Definition: debug.h:30
Gtk::HBox time_axis_hbox
float times
Number of times to paste.
Definition: paste_context.h:36
void get_inverted_selectables(Selection &, std::list< Selectable * > &results)
void add_automation_event(GdkEvent *, framepos_t, double, bool with_guard_points)
#define S_(Text)
Definition: i18n.h:18
boost::shared_ptr< AutomationList > alist() const
std::string controls_base_unselected_name
void set_gui_property(const std::string &property_name, const T &value)
Definition: axis_view.h:66
void set_line_color(uint32_t)
const_iterator get_nth(const Evoral::Parameter &param, size_t nth) const
boost::shared_ptr< AutomationController > _controller
Gdk::Color color() const
Definition: axis_view.h:50
void set_height(guint32)
static uint32_t preset_height(Height)
uint32_t height
Gtk::VBox time_axis_vbox
PublicEditor & _editor
bool greedy
If true, greedily steal items that don't match.
Definition: paste_context.h:38
virtual void add(double when, double value, bool with_guards=true, bool with_default=true)
unsigned count
Number of previous pastes to the same position.
Definition: paste_context.h:35
uint8_t channel() const
Definition: Parameter.hpp:48
void get_inverted_selectables(Selection &, std::list< Selectable * > &results)
void set_automation_state(ARDOUR::AutoState)
Gtk::Label name_label
void set_automation_state(ARDOUR::AutoState state)
Gtk::EventBox controls_ebox
double _y_position
boost::shared_ptr< AutomationLine > line()
ARDOUR::Session * _session
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
bool paste(ARDOUR::framepos_t, const Selection &, PasteContext &)
void cleanup_gui_properties()
Definition: axis_view.h:74
void set_selected_points(PointSelection const &)
PBD::Signal1< void, AutoState > automation_state_changed
AutoState
Definition: types.h:145