diff --git a/gtk2_ardour/automation.bindings b/gtk2_ardour/automation.bindings new file mode 100644 index 0000000000..dd3139a40c --- /dev/null +++ b/gtk2_ardour/automation.bindings @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index 3cdbea638b..d8d74d367d 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -971,6 +971,7 @@ AutomationTimeAxisView::entered() if (_line) { _line->track_entered(); } + _editor.enable_automation_bindings (); } void @@ -979,6 +980,8 @@ AutomationTimeAxisView::exited () if (_line) { _line->track_exited(); } + + _editor.enable_automation_bindings (); } void diff --git a/gtk2_ardour/editing_context.cc b/gtk2_ardour/editing_context.cc index 839f39f758..f7cd000d08 100644 --- a/gtk2_ardour/editing_context.cc +++ b/gtk2_ardour/editing_context.cc @@ -303,6 +303,32 @@ EditingContext::set_selected_midi_region_view (MidiRegionView& mrv) get_selection().set (&mrv); } +void +EditingContext::register_automation_actions (Bindings* automation_bindings, std::string const & prefix) +{ + _automation_actions = ActionManager::create_action_group (automation_bindings, prefix + X_("Automation")); + + reg_sens (_automation_actions, "create-point", _("Create Automation Point"), sigc::mem_fun (*this, &EditingContext::automation_create_point_at_edit_point)); + reg_sens (_automation_actions, "move-points-later", _("Create Automation P (at Playhead)"), sigc::mem_fun (*this, &EditingContext::automation_move_points_later)); + reg_sens (_automation_actions, "move-points-earlier", _("Create Automation Point (at Playhead)"), sigc::mem_fun (*this, &EditingContext::automation_move_points_earlier)); + reg_sens (_automation_actions, "raise-points", _("Create Automation Point (at Playhead)"), sigc::mem_fun (*this, &EditingContext::automation_raise_points)); + reg_sens (_automation_actions, "lower-points", _("Create Automation Point (at Playhead)"), sigc::mem_fun (*this, &EditingContext::automation_lower_points)); + + ActionManager::set_sensitive (_automation_actions, false); +} + +void +EditingContext::enable_automation_bindings () +{ + ActionManager::set_sensitive (_automation_actions, true); +} + +void +EditingContext::disable_automation_bindings () +{ + ActionManager::set_sensitive (_automation_actions, false); +} + void EditingContext::register_common_actions (Bindings* common_bindings, std::string const & prefix) { @@ -3254,10 +3280,12 @@ EditingContext::load_shared_bindings () { Bindings* m = Bindings::get_bindings (X_("MIDI")); Bindings* b = Bindings::get_bindings (X_("Editing")); + Bindings* a = Bindings::get_bindings (X_("Automation")); if (need_shared_actions) { register_midi_actions (m, string()); register_common_actions (b, string()); + register_automation_actions (a, string()); need_shared_actions = false; } @@ -3279,8 +3307,13 @@ EditingContext::load_shared_bindings () register_common_actions (shared_bindings, _name); shared_bindings->associate (); + Bindings* automation_bindings = new Bindings (_name, *a); + register_automation_actions (automation_bindings, _name); + automation_bindings->associate (); + /* Attach bindings to the canvas for this editing context */ + bindings.push_back (automation_bindings); bindings.push_back (midi_bindings); bindings.push_back (shared_bindings); } diff --git a/gtk2_ardour/editing_context.h b/gtk2_ardour/editing_context.h index f9d16a7df0..6a112e1463 100644 --- a/gtk2_ardour/editing_context.h +++ b/gtk2_ardour/editing_context.h @@ -421,6 +421,7 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, static bool need_shared_actions; void register_midi_actions (Gtkmm2ext::Bindings*, std::string const &); void register_common_actions (Gtkmm2ext::Bindings*, std::string const &); + void register_automation_actions (Gtkmm2ext::Bindings*, std::string const &); ArdourCanvas::Rectangle* rubberband_rect; @@ -497,12 +498,16 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, void center_screen (samplepos_t); void reset_x_origin_to_follow_playhead (); + void enable_automation_bindings (); + void disable_automation_bindings (); + protected: std::string _name; bool within_track_canvas; Glib::RefPtr _midi_actions; Glib::RefPtr _common_actions; + Glib::RefPtr _automation_actions; Glib::RefPtr editor_actions; Glib::RefPtr snap_actions; Glib::RefPtr length_actions; @@ -818,4 +823,9 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, void center_screen_internal (samplepos_t, float); + virtual void automation_create_point_at_edit_point() {} + virtual void automation_raise_points () {} + virtual void automation_lower_points () {}; + virtual void automation_move_points_later () {}; + virtual void automation_move_points_earlier () {}; }; diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 639a5b4ace..80c3a205ef 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1603,6 +1603,11 @@ private: protected: void _commit_tempo_map_edit (Temporal::TempoMap::WritableSharedPtr&, bool with_update = false); + void automation_create_point_at_edit_point(); + void automation_raise_points (); + void automation_lower_points (); + void automation_move_points_later (); + void automation_move_points_earlier (); private: friend class DragManager; diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 3b2c68b3d6..98a40bfac2 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -39,6 +39,8 @@ #include "ardour/session.h" #include "ardour/types.h" +#include "temporal/bbt_time.h" + #include "canvas/canvas.h" #include "canvas/pixbuf.h" @@ -46,6 +48,7 @@ #include "actions.h" #include "ardour_ui.h" +#include "control_point.h" #include "editing.h" #include "editor.h" #include "gui_thread.h" @@ -1333,3 +1336,128 @@ Editor::register_region_actions () sensitize_all_region_actions (false); } +void +Editor::automation_create_point_at_edit_point () +{ + AutomationTimeAxisView* atv = dynamic_cast (entered_track); + if (!atv) { + return; + } + + timepos_t where (get_preferred_edit_position());; + GdkEvent event; + + event.type = GDK_KEY_PRESS; + event.button.button = 1; + event.button.state = 0; + + atv->line()->add (atv->control(), &event, where, atv->line()->the_list()->eval (where), false); +} + +void +Editor::automation_lower_points () +{ + PointSelection& points (selection->points); + + if (points.empty()) { + return; + } + + AutomationTimeAxisView* atv = dynamic_cast (entered_track); + + if (!atv) { + return; + } + + begin_reversible_command (_("automation event lower")); + add_command (new MementoCommand (atv->line()->memento_command_binder(), &atv->line()->the_list()->get_state(), 0)); + atv->line()->the_list()->freeze (); + for (auto & p : points) { + atv->line()->the_list()->modify (p->model(), (*p->model())->when, max (0.0, (*p->model())->value - 0.1)); + } + atv->line()->the_list()->thaw (); + add_command (new MementoCommand(atv->line()->memento_command_binder (), 0, &atv->line()->the_list()->get_state())); + commit_reversible_command (); +} + +void +Editor::automation_raise_points () +{ + PointSelection& points (selection->points); + + if (points.empty()) { + return; + } + + AutomationTimeAxisView* atv = dynamic_cast (entered_track); + + if (!atv) { + return; + } + + begin_reversible_command (_("automation event raise")); + add_command (new MementoCommand (atv->line()->memento_command_binder(), &atv->line()->the_list()->get_state(), 0)); + atv->line()->the_list()->freeze (); + for (auto & p : points) { + atv->line()->the_list()->modify (p->model(), (*p->model())->when, min (1.0, (*p->model())->value + 0.1)); + } + atv->line()->the_list()->thaw (); + add_command (new MementoCommand(atv->line()->memento_command_binder (), 0, &atv->line()->the_list()->get_state())); + commit_reversible_command (); +} + +void +Editor::automation_move_points_later () +{ + PointSelection& points (selection->points); + + if (points.empty()) { + return; + } + + AutomationTimeAxisView* atv = dynamic_cast (entered_track); + + if (!atv) { + return; + } + + begin_reversible_command (_("automation points move later")); + add_command (new MementoCommand (atv->line()->memento_command_binder(), &atv->line()->the_list()->get_state(), 0)); + atv->line()->the_list()->freeze (); + for (auto & p : points) { + timepos_t model_time ((*p->model())->when); + model_time += Temporal::BBT_Offset (0, 1, 0); + atv->line()->the_list()->modify (p->model(), model_time, (*p->model())->value); + } + atv->line()->the_list()->thaw (); + add_command (new MementoCommand(atv->line()->memento_command_binder (), 0, &atv->line()->the_list()->get_state())); + commit_reversible_command (); +} + +void +Editor::automation_move_points_earlier () +{ + PointSelection& points (selection->points); + + if (points.empty()) { + return; + } + + AutomationTimeAxisView* atv = dynamic_cast (entered_track); + + if (!atv) { + return; + } + + begin_reversible_command (_("automation points move earlier")); + add_command (new MementoCommand (atv->line()->memento_command_binder(), &atv->line()->the_list()->get_state(), 0)); + atv->line()->the_list()->freeze (); + for (auto & p : points) { + timepos_t model_time ((*p->model())->when); + model_time = model_time.earlier (Temporal::BBT_Offset (0, 1, 0)); + atv->line()->the_list()->modify (p->model(), model_time, (*p->model())->value); + } + atv->line()->the_list()->thaw (); + add_command (new MementoCommand(atv->line()->memento_command_binder (), 0, &atv->line()->the_list()->get_state())); + commit_reversible_command (); +} diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 6e5d319c11..cc71e41da4 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -945,7 +945,7 @@ def build(bld): for b in [ 'ardour' ] : obj = bld( target = b + '.keys', - source = [ b + '.keys.in', 'mixer.bindings', 'processor_box.bindings', 'step_editing.bindings', 'monitor.bindings', 'trigger.bindings', 'regionfx_box.bindings' ], + source = [ b + '.keys.in', 'mixer.bindings', 'processor_box.bindings', 'step_editing.bindings', 'monitor.bindings', 'trigger.bindings', 'regionfx_box.bindings', 'automation.bindings' ], rule = a_rule ) obj.install_path = bld.env['CONFDIR']