From bde476873498b97480419d00e918a64c49e10234 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 30 Jan 2024 12:40:30 -0700 Subject: [PATCH] move a bunch of MIDI editing into EditingContext --- gtk2_ardour/editing_context.cc | 280 +++++++++++++++++++++++++++++++- gtk2_ardour/editing_context.h | 30 +++- gtk2_ardour/editor.cc | 81 +-------- gtk2_ardour/editor.h | 25 +-- gtk2_ardour/editor_mouse.cc | 22 --- gtk2_ardour/editor_ops.cc | 254 +++++------------------------ gtk2_ardour/editor_selection.cc | 6 + gtk2_ardour/midi_cue_editor.cc | 107 +++++++++++- gtk2_ardour/midi_cue_editor.h | 9 + gtk2_ardour/public_editor.h | 6 - 10 files changed, 462 insertions(+), 358 deletions(-) diff --git a/gtk2_ardour/editing_context.cc b/gtk2_ardour/editing_context.cc index ba9c8b966d..b795cbde43 100644 --- a/gtk2_ardour/editing_context.cc +++ b/gtk2_ardour/editing_context.cc @@ -21,19 +21,28 @@ #include "pbd/error.h" #include "pbd/stacktrace.h" +#include "ardour/legatize.h" +#include "ardour/midi_region.h" +#include "ardour/midi_source.h" #include "ardour/rc_configuration.h" +#include "ardour/transpose.h" #include "ardour/quantize.h" #include "gtkmm2ext/bindings.h" #include "actions.h" +#include "ardour_ui.h" +#include "edit_note_dialog.h" #include "editing_context.h" #include "editor_drag.h" #include "keyboard.h" #include "midi_region_view.h" +#include "note_base.h" #include "quantize_dialog.h" #include "selection.h" #include "selection_memento.h" +#include "transform_dialog.h" +#include "transpose_dialog.h" #include "verbose_cursor.h" #include "pbd/i18n.h" @@ -48,6 +57,7 @@ using namespace Temporal; using std::string; sigc::signal EditingContext::DropDownKeys; +Gtkmm2ext::Bindings* EditingContext::button_bindings = nullptr; static const gchar *_grid_type_strings[] = { N_("No Grid"), @@ -104,6 +114,17 @@ EditingContext::EditingContext () , _visible_canvas_height (0) , quantize_dialog (nullptr) { + if (!button_bindings) { + button_bindings = new Bindings ("editor-mouse"); + + XMLNode* node = button_settings(); + if (node) { + for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { + button_bindings->load_operation (**i); + } + } + } + grid_type_strings = I18N (_grid_type_strings); } @@ -260,7 +281,7 @@ EditingContext::register_midi_actions (Bindings* midi_bindings) } void -EditingContext::midi_action (void (MidiRegionView::*method)()) +EditingContext::midi_action (void (MidiView::*method)()) { MidiRegionSelection ms = get_selection().midi_regions(); @@ -1638,3 +1659,260 @@ EditingContext::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType return ret; } +void +EditingContext::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event) +{ + using namespace Menu_Helpers; + + NoteBase* note = reinterpret_cast(item->get_data("notebase")); + if (!note) { + return; + } + + /* We need to get the selection here and pass it to the operations, since + popping up the menu will cause a region leave event which clears + entered_regionview. */ + + MidiView& mrv = note->region_view(); + const RegionSelection rs = region_selection (); + const uint32_t sel_size = mrv.selection_size (); + + MenuList& items = _note_context_menu.items(); + items.clear(); + + if (sel_size > 0) { + items.push_back (MenuElem(_("Delete"), sigc::mem_fun(mrv, &MidiView::delete_selection))); + } + + items.push_back(MenuElem(_("Edit..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::edit_notes), &mrv))); + items.push_back(MenuElem(_("Transpose..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::transpose_regions), rs))); + items.push_back(MenuElem(_("Legatize"), sigc::bind(sigc::mem_fun(*this, &EditingContext::legatize_regions), rs, false))); + if (sel_size < 2) { + items.back().set_sensitive (false); + } + items.push_back(MenuElem(_("Quantize..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::quantize_regions), rs))); + items.push_back(MenuElem(_("Remove Overlap"), sigc::bind(sigc::mem_fun(*this, &EditingContext::legatize_regions), rs, true))); + if (sel_size < 2) { + items.back().set_sensitive (false); + } + items.push_back(MenuElem(_("Transform..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::transform_regions), rs))); + + _note_context_menu.popup (event->button.button, event->button.time); +} + +XMLNode* +EditingContext::button_settings () const +{ + XMLNode* settings = ARDOUR_UI::instance()->editor_settings(); + XMLNode* node = find_named_node (*settings, X_("Buttons")); + + if (!node) { + node = new XMLNode (X_("Buttons")); + } + + return node; +} + +std::vector +EditingContext::filter_to_unique_midi_region_views (RegionSelection const & rs) const +{ + typedef std::pair,timepos_t> MapEntry; + std::set single_region_set; + + std::vector views; + + /* build a list of regions that are unique with respect to their source + * and start position. Note: this is non-exhaustive... if someone has a + * non-forked copy of a MIDI region and then suitably modifies it, this + * will still put both regions into the list of things to be acted + * upon. + * + * Solution: user should not select both regions, or should fork one of them. + */ + + for (auto const & rv : rs) { + + MidiView* mrv = dynamic_cast (rv); + + if (!mrv) { + continue; + } + + MapEntry entry = make_pair (mrv->midi_region()->midi_source(), mrv->midi_region()->start()); + + if (single_region_set.insert (entry).second) { + views.push_back (mrv); + } + } + + return views; +} + +void +EditingContext::quantize_region () +{ + if (_session) { + quantize_regions(region_selection ()); + } +} + +void +EditingContext::quantize_regions (const RegionSelection& rs) +{ + if (rs.n_midi_regions() == 0) { + return; + } + + Quantize* quant = get_quantize_op (); + + if (!quant) { + return; + } + + if (!quant->empty()) { + apply_midi_note_edit_op (*quant, rs); + } + + delete quant; +} + +void +EditingContext::legatize_region (bool shrink_only) +{ + if (_session) { + legatize_regions(region_selection (), shrink_only); + } +} + +void +EditingContext::legatize_regions (const RegionSelection& rs, bool shrink_only) +{ + if (rs.n_midi_regions() == 0) { + return; + } + + Legatize legatize(shrink_only); + apply_midi_note_edit_op (legatize, rs); +} + +void +EditingContext::transform_region () +{ + if (_session) { + transform_regions(region_selection ()); + } +} + +void +EditingContext::transform_regions (const RegionSelection& rs) +{ + if (rs.n_midi_regions() == 0) { + return; + } + + TransformDialog td; + + td.present(); + const int r = td.run(); + td.hide(); + + if (r == Gtk::RESPONSE_OK) { + Transform transform(td.get()); + apply_midi_note_edit_op(transform, rs); + } +} + +void +EditingContext::transpose_region () +{ + if (_session) { + transpose_regions(region_selection ()); + } +} + +void +EditingContext::transpose_regions (const RegionSelection& rs) +{ + if (rs.n_midi_regions() == 0) { + return; + } + + TransposeDialog d; + int const r = d.run (); + + if (r == RESPONSE_ACCEPT) { + Transpose transpose(d.semitones ()); + apply_midi_note_edit_op (transpose, rs); + } +} + +void +EditingContext::edit_notes (MidiView* mrv) +{ + MidiView::Selection const & s = mrv->selection(); + + if (s.empty ()) { + return; + } + + EditNoteDialog* d = new EditNoteDialog (mrv, s); + d->show_all (); + + d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &EditingContext::note_edit_done), d)); +} + +void +EditingContext::note_edit_done (int r, EditNoteDialog* d) +{ + d->done (r); + delete d; +} + +PBD::Command* +EditingContext::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiView& mrv) +{ + Evoral::Sequence::Notes selected; + mrv.selection_as_notelist (selected, true); + + if (selected.empty()) { + return 0; + } + + std::vector::Notes> v; + v.push_back (selected); + + timepos_t pos = mrv.midi_region()->source_position(); + + return op (mrv.midi_region()->model(), pos.beats(), v); +} + +void +EditingContext::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs) +{ + if (rs.empty()) { + return; + } + + bool in_command = false; + + std::vector views = filter_to_unique_midi_region_views (rs); + + for (auto & mv : views) { + + Command* cmd = apply_midi_note_edit_op_to_region (op, *mv); + if (cmd) { + if (!in_command) { + begin_reversible_command (op.name ()); + in_command = true; + } + (*cmd)(); + _session->add_command (cmd); + } + } + + if (in_command) { + commit_reversible_command (); + _session->set_dirty (); + } +} + diff --git a/gtk2_ardour/editing_context.h b/gtk2_ardour/editing_context.h index d0bd4a5a1a..f3cd8248fe 100644 --- a/gtk2_ardour/editing_context.h +++ b/gtk2_ardour/editing_context.h @@ -60,7 +60,9 @@ class XMLNode; class CursorContext; class DragManager; class EditorCursor; +class EditNoteDialog; class MidiRegionView; +class MidiView; class MouseCursors; class VerboseCursor; class TrackViewList; @@ -308,9 +310,14 @@ public: /* MIDI actions, proxied to selected MidiRegionView(s) */ ARDOUR::Quantize* get_quantize_op (); - virtual void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs) = 0; - void midi_action (void (MidiRegionView::*method)()); - virtual std::vector filter_to_unique_midi_region_views (RegionSelection const & ms) const = 0; + void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs); + void midi_action (void (MidiView::*method)()); + std::vector filter_to_unique_midi_region_views (RegionSelection const & ms) const; + + void quantize_region (); + void transform_region (); + void legatize_region (bool shrink_only); + void transpose_region (); void register_midi_actions (Gtkmm2ext::Bindings*); @@ -470,6 +477,23 @@ public: virtual bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) = 0; virtual bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) = 0; + void popup_note_context_menu (ArdourCanvas::Item*, GdkEvent*); + Gtk::Menu _note_context_menu; + + static Gtkmm2ext::Bindings* button_bindings; + XMLNode* button_settings () const; + + virtual RegionSelection region_selection() = 0; + + void edit_notes (MidiView*); + void note_edit_done (int, EditNoteDialog*); + + PBD::Command* apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiView& mrv); + + void quantize_regions (const RegionSelection& rs); + void legatize_regions (const RegionSelection& rs, bool shrink_only); + void transform_regions (const RegionSelection& rs); + void transpose_regions (const RegionSelection& rs); }; diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index b501199d6c..1ad8a28e94 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -320,7 +320,6 @@ Editor::Editor () , _err_screen_engine (0) , cut_buffer_start (0) , cut_buffer_length (0) - , button_bindings (0) , last_paste_pos (timepos_t::max (Temporal::AudioTime)) /* XXX NUTEMPO how to choose time domain */ , paste_count (0) , sfbrowser (0) @@ -794,17 +793,6 @@ Editor::Editor () _show_marker_lines = false; - /* Button bindings */ - - button_bindings = new Bindings ("editor-mouse"); - - XMLNode* node = button_settings(); - if (node) { - for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { - button_bindings->load_operation (**i); - } - } - constructed = true; /* grab current parameter state */ @@ -858,19 +846,6 @@ Editor::~Editor() } } -XMLNode* -Editor::button_settings () const -{ - XMLNode* settings = ARDOUR_UI::instance()->editor_settings(); - XMLNode* node = find_named_node (*settings, X_("Buttons")); - - if (!node) { - node = new XMLNode (X_("Buttons")); - } - - return node; -} - bool Editor::get_smart_mode () const { @@ -6268,60 +6243,6 @@ Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* ev _control_point_context_menu.popup (event->button.button, event->button.time); } -void -Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event) -{ - using namespace Menu_Helpers; - - NoteBase* note = reinterpret_cast(item->get_data("notebase")); - if (!note) { - return; - } - - /* We need to get the selection here and pass it to the operations, since - popping up the menu will cause a region leave event which clears - entered_regionview. */ - - MidiView& mrv = note->region_view(); - const RegionSelection rs = get_regions_from_selection_and_entered (); - const uint32_t sel_size = mrv.selection_size (); - - MenuList& items = _note_context_menu.items(); - items.clear(); - - if (sel_size > 0) { - items.push_back(MenuElem(_("Delete"), - sigc::mem_fun(mrv, &MidiView::delete_selection))); - } - - items.push_back(MenuElem(_("Edit..."), - sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv))); - - items.push_back(MenuElem(_("Transpose..."), - sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs))); - - - items.push_back(MenuElem(_("Legatize"), - sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false))); - if (sel_size < 2) { - items.back().set_sensitive (false); - } - - items.push_back(MenuElem(_("Quantize..."), - sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs))); - - items.push_back(MenuElem(_("Remove Overlap"), - sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true))); - if (sel_size < 2) { - items.back().set_sensitive (false); - } - - items.push_back(MenuElem(_("Transform..."), - sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs))); - - _note_context_menu.popup (event->button.button, event->button.time); -} - void Editor::zoom_vertical_modifier_released() { @@ -6595,5 +6516,5 @@ Editor::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapP ArdourCanvas::Duple Editor::upper_left() const { - get_trackview_group ()->canvas_origin ().y; + return get_trackview_group ()->canvas_origin (); } diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 1b59675301..136b659309 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -533,6 +533,8 @@ protected: void suspend_route_redisplay (); void resume_route_redisplay (); + RegionSelection region_selection(); + private: void color_handler (); @@ -769,9 +771,6 @@ private: void popup_control_point_context_menu (ArdourCanvas::Item*, GdkEvent*); Gtk::Menu _control_point_context_menu; - void popup_note_context_menu (ArdourCanvas::Item*, GdkEvent*); - Gtk::Menu _note_context_menu; - void initial_display (); void add_stripables (ARDOUR::StripableList&); void add_routes (ARDOUR::RouteList&); @@ -1162,9 +1161,6 @@ private: bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); - Gtkmm2ext::Bindings* button_bindings; - XMLNode* button_settings () const; - /* KEYMAP HANDLING */ void register_actions (); @@ -1245,18 +1241,9 @@ private: void normalize_region (); void adjust_region_gain (bool up); void reset_region_gain (); - void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs); - void set_tempo_curve_range (double& max, double& min) const; - void quantize_region (); - void quantize_regions (const RegionSelection& rs); - void legatize_region (bool shrink_only); - void legatize_regions (const RegionSelection& rs, bool shrink_only); void deinterlace_midi_regions (const RegionSelection& rs); void deinterlace_selected_midi_regions (); - void transform_region (); - void transform_regions (const RegionSelection& rs); - void transpose_region (); - void transpose_regions (const RegionSelection& rs); + void set_tempo_curve_range (double& max, double& min) const; void insert_patch_change (bool from_context); void fork_selected_regions (); void fork_regions_from_unselected (); @@ -1726,7 +1713,6 @@ private: void edit_meter_marker (MeterMarker&); void edit_bbt_marker (BBTMarker&); void edit_control_point (ArdourCanvas::Item*); - void edit_notes (MidiView*); void edit_region (RegionView*); void edit_current_meter (); @@ -2139,8 +2125,6 @@ private: void apply_filter (ARDOUR::Filter&, std::string cmd, ProgressReporter* progress = 0); - PBD::Command* apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiRegionView& mrv); - /* plugin setup */ int plugin_setup (std::shared_ptr, std::shared_ptr, ARDOUR::Route::PluginSetupOptions); @@ -2327,7 +2311,6 @@ private: bool _show_touched_automation; int time_fx (ARDOUR::RegionList&, Temporal::ratio_t ratio, bool pitching, bool fixed_end); - void note_edit_done (int, EditNoteDialog*); void toggle_sound_midi_notes (); /** Flag for a bit of a hack wrt control point selection; see set_selected_control_point_from_click */ @@ -2351,8 +2334,6 @@ private: MainMenuDisabler* _main_menu_disabler; - std::vector filter_to_unique_midi_region_views (RegionSelection const & ms) const; - /* private helper functions to help with registering region actions */ Glib::RefPtr register_region_action (Glib::RefPtr group, Editing::RegionActionTarget, char const* name, char const* label, sigc::slot slot); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 730c7333c4..ac93dce464 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -2323,28 +2323,6 @@ Editor::edit_control_point (ArdourCanvas::Item* item) } } -void -Editor::edit_notes (MidiView* mrv) -{ - MidiView::Selection const & s = mrv->selection(); - - if (s.empty ()) { - return; - } - - EditNoteDialog* d = new EditNoteDialog (mrv, s); - d->show_all (); - - d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d)); -} - -void -Editor::note_edit_done (int r, EditNoteDialog* d) -{ - d->done (r); - delete d; -} - void Editor::edit_region (RegionView* rv) { diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 68951769b0..c74e9aa61c 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -6130,54 +6130,6 @@ Editor::strip_region_silence () } } -PBD::Command* -Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv) -{ - Evoral::Sequence::Notes selected; - mrv.selection_as_notelist (selected, true); - - if (selected.empty()) { - return 0; - } - - vector::Notes> v; - v.push_back (selected); - - timepos_t pos = mrv.midi_region()->source_position(); - - return op (mrv.midi_region()->model(), pos.beats(), v); -} - -void -Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs) -{ - if (rs.empty()) { - return; - } - - bool in_command = false; - - vector views = filter_to_unique_midi_region_views (rs); - - for (vector::iterator mrv = views.begin(); mrv != views.end(); ++mrv) { - - Command* cmd = apply_midi_note_edit_op_to_region (op, **mrv); - if (cmd) { - if (!in_command) { - begin_reversible_command (op.name ()); - in_command = true; - } - (*cmd)(); - _session->add_command (cmd); - } - } - - if (in_command) { - commit_reversible_command (); - _session->set_dirty (); - } -} - #include "ardour/midi_source.h" // MidiSource::name() void @@ -6307,140 +6259,6 @@ Editor::fork_selected_regions () } } -void -Editor::quantize_region () -{ - if (_session) { - quantize_regions(get_regions_from_selection_and_entered ()); - } -} - -void -Editor::quantize_regions (const RegionSelection& rs) -{ - if (rs.n_midi_regions() == 0) { - return; - } - - Quantize* quant = get_quantize_op (); - - if (!quant) { - return; - } - - if (!quant->empty()) { - apply_midi_note_edit_op (*quant, rs); - } - - delete quant; -} - -void -Editor::legatize_region (bool shrink_only) -{ - if (_session) { - legatize_regions(get_regions_from_selection_and_entered (), shrink_only); - } -} - -void -Editor::deinterlace_midi_regions (const RegionSelection& rs) -{ - begin_reversible_command (_("de-interlace midi")); - - RegionSelection rcopy = rs; - if (_session) { - - for (RegionSelection::iterator i = rcopy.begin (); i != rcopy.end(); i++) { - MidiRegionView* const mrv = dynamic_cast (*i); - if (mrv) { - XMLNode& before (mrv->region()->playlist()->get_state()); - - /* pass the regions to deinterlace_midi_region*/ - _session->deinterlace_midi_region(mrv->midi_region()); - - XMLNode& after (mrv->region()->playlist()->get_state()); - _session->add_command (new MementoCommand(*(mrv->region()->playlist()), &before, &after)); - } - } - } - - /* Remove the original region(s) safely, without rippling, as part of this command */ - remove_regions(rs, false /*can_ripple*/, true /*as_part_of_other_command*/); - - commit_reversible_command (); -} - -void -Editor::deinterlace_selected_midi_regions () -{ - if (_session) { - RegionSelection rs = get_regions_from_selection_and_entered (); - deinterlace_midi_regions(rs); - } -} - -void -Editor::legatize_regions (const RegionSelection& rs, bool shrink_only) -{ - if (rs.n_midi_regions() == 0) { - return; - } - - Legatize legatize(shrink_only); - apply_midi_note_edit_op (legatize, rs); -} - -void -Editor::transform_region () -{ - if (_session) { - transform_regions(get_regions_from_selection_and_entered ()); - } -} - -void -Editor::transform_regions (const RegionSelection& rs) -{ - if (rs.n_midi_regions() == 0) { - return; - } - - TransformDialog td; - - td.present(); - const int r = td.run(); - td.hide(); - - if (r == Gtk::RESPONSE_OK) { - Transform transform(td.get()); - apply_midi_note_edit_op(transform, rs); - } -} - -void -Editor::transpose_region () -{ - if (_session) { - transpose_regions(get_regions_from_selection_and_entered ()); - } -} - -void -Editor::transpose_regions (const RegionSelection& rs) -{ - if (rs.n_midi_regions() == 0) { - return; - } - - TransposeDialog d; - int const r = d.run (); - - if (r == RESPONSE_ACCEPT) { - Transpose transpose(d.semitones ()); - apply_midi_note_edit_op (transpose, rs); - } -} void Editor::insert_patch_change (bool from_context) @@ -6563,6 +6381,43 @@ Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress } } +void +Editor::deinterlace_midi_regions (const RegionSelection& rs) +{ + begin_reversible_command (_("de-interlace midi")); + + RegionSelection rcopy = rs; + if (_session) { + + for (RegionSelection::iterator i = rcopy.begin (); i != rcopy.end(); i++) { + MidiRegionView* const mrv = dynamic_cast (*i); + if (mrv) { + XMLNode& before (mrv->region()->playlist()->get_state()); + + /* pass the regions to deinterlace_midi_region*/ + _session->deinterlace_midi_region(mrv->midi_region()); + + XMLNode& after (mrv->region()->playlist()->get_state()); + _session->add_command (new MementoCommand(*(mrv->region()->playlist()), &before, &after)); + } + } + } + + /* Remove the original region(s) safely, without rippling, as part of this command */ + remove_regions(rs, false /*can_ripple*/, true /*as_part_of_other_command*/); + + commit_reversible_command (); +} + +void +Editor::deinterlace_selected_midi_regions () +{ + if (_session) { + RegionSelection rs = region_selection (); + deinterlace_midi_regions(rs); + } +} + void Editor::external_edit_region () { @@ -9361,41 +9216,6 @@ Editor::launch_playlist_selector () } } -vector -Editor::filter_to_unique_midi_region_views (RegionSelection const & ms) const -{ - typedef std::pair,timepos_t> MapEntry; - std::set single_region_set; - - vector views; - - /* build a list of regions that are unique with respect to their source - * and start position. Note: this is non-exhaustive... if someone has a - * non-forked copy of a MIDI region and then suitably modifies it, this - * will still put both regions into the list of things to be acted - * upon. - * - * Solution: user should not select both regions, or should fork one of them. - */ - - for (MidiRegionSelection::const_iterator i = ms.begin(); i != ms.end(); ++i) { - - MidiRegionView* mrv = dynamic_cast (*i); - - if (!mrv) { - continue; - } - - MapEntry entry = make_pair (mrv->midi_region()->midi_source(), mrv->region()->start()); - - if (single_region_set.insert (entry).second) { - views.push_back (mrv); - } - } - - return views; -} - void Editor::add_region_marker () { diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 347c43e0de..afb731c2c1 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -2475,3 +2475,9 @@ Editor::move_selected_tracks (bool up) ensure_time_axis_view_is_visible (*scroll_to, false); } } + +RegionSelection +Editor::region_selection() +{ + return get_regions_from_selection_and_entered (); +} diff --git a/gtk2_ardour/midi_cue_editor.cc b/gtk2_ardour/midi_cue_editor.cc index a039c74659..3d91cf5f25 100644 --- a/gtk2_ardour/midi_cue_editor.cc +++ b/gtk2_ardour/midi_cue_editor.cc @@ -26,9 +26,12 @@ #include "canvas/rectangle.h" #include "editor_cursors.h" +#include "editor_drag.h" +#include "keyboard.h" #include "midi_cue_background.h" #include "midi_cue_editor.h" #include "midi_cue_view.h" +#include "note_base.h" #include "ui_config.h" #include "verbose_cursor.h" @@ -36,12 +39,14 @@ using namespace ARDOUR; using namespace ArdourCanvas; +using namespace Gtkmm2ext; using namespace Temporal; MidiCueEditor::MidiCueEditor() : vertical_adjustment (0.0, 0.0, 10.0, 400.0) , horizontal_adjustment (0.0, 0.0, 1e16) , view (nullptr) + , mouse_mode (Editing::MouseDraw) { build_canvas (); @@ -250,14 +255,51 @@ MidiCueEditor::set_region (std::shared_ptr t, std::shared_ptr } bool -MidiCueEditor::button_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) +MidiCueEditor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { + switch (event->button.button) { + case 1: + return button_press_handler_1 (item, event, item_type); + break; + + case 2: + return button_press_handler_2 (item, event, item_type); + break; + + case 3: + break; + + default: + return button_press_dispatch (&event->button); + break; + + } + return true; } bool -MidiCueEditor::button_press_handler_1 (ArdourCanvas::Item*, GdkEvent*, ItemType) +MidiCueEditor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { + NoteBase* note = nullptr; + + if (mouse_mode == Editing::MouseContent) { + switch (item_type) { + case NoteItem: + /* Existing note: allow trimming/motion */ + if ((note = reinterpret_cast (item->get_data ("notebase")))) { + if (note->big_enough_to_trim() && note->mouse_near_ends()) { + _drags->set (new NoteResizeDrag (*this, item), event, get_canvas_cursor()); + } else { + _drags->set (new NoteDrag (*this, item), event); + } + } + return true; + default: + break; + } + } + return true; } @@ -268,21 +310,39 @@ MidiCueEditor::button_press_handler_2 (ArdourCanvas::Item*, GdkEvent*, ItemType) } bool -MidiCueEditor::button_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) +MidiCueEditor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { + if (Keyboard::is_context_menu_event (&event->button)) { + switch (item_type) { + case NoteItem: + if (internal_editing()) { + popup_note_context_menu (item, event); + } + break; + default: + break; + } + } + return true; } bool -MidiCueEditor::button_press_dispatch (GdkEventButton*) +MidiCueEditor::button_press_dispatch (GdkEventButton* ev) { - return true; + /* this function is intended only for buttons 4 and above. */ + + Gtkmm2ext::MouseButton b (ev->state, ev->button); + return button_bindings->activate (b, Gtkmm2ext::Bindings::Press); } bool -MidiCueEditor::button_release_dispatch (GdkEventButton*) +MidiCueEditor::button_release_dispatch (GdkEventButton* ev) { - return true; + /* this function is intended only for buttons 4 and above. */ + + Gtkmm2ext::MouseButton b (ev->state, ev->button); + return button_bindings->activate (b, Gtkmm2ext::Bindings::Release); } bool @@ -315,3 +375,36 @@ MidiCueEditor::key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) return true; } +void +MidiCueEditor::set_mouse_mode (Editing::MouseMode m, bool force) +{ + if (m != Editing::MouseDraw && m != Editing::MouseContent) { + return; + } + + mouse_mode = m; +} + +void +MidiCueEditor::step_mouse_mode (bool next) +{ +} + +Editing::MouseMode +MidiCueEditor::current_mouse_mode () const +{ + return mouse_mode; +} + +bool +MidiCueEditor::internal_editing() const +{ + return true; +} + +RegionSelection +MidiCueEditor::region_selection() +{ + RegionSelection rs; + return rs; +} diff --git a/gtk2_ardour/midi_cue_editor.h b/gtk2_ardour/midi_cue_editor.h index f30bfb3574..dedb689e58 100644 --- a/gtk2_ardour/midi_cue_editor.h +++ b/gtk2_ardour/midi_cue_editor.h @@ -69,6 +69,11 @@ class MidiCueEditor : public CueEditor void reset_zoom (samplecnt_t); + void set_mouse_mode (Editing::MouseMode, bool force = false); + void step_mouse_mode (bool next); + Editing::MouseMode current_mouse_mode () const; + bool internal_editing() const; + protected: Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start, Temporal::RoundMode direction, @@ -125,6 +130,10 @@ class MidiCueEditor : public CueEditor void build_canvas (); void canvas_allocate (Gtk::Allocation); + + Editing::MouseMode mouse_mode; + + RegionSelection region_selection(); }; diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index 2b659a6409..d2086274b2 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -175,10 +175,6 @@ public: virtual void reverse_region () = 0; virtual void normalize_region () = 0; - virtual void quantize_region () = 0; - virtual void legatize_region (bool shrink_only) = 0; - virtual void transform_region () = 0; - virtual void transpose_region () = 0; virtual void pitch_shift_region () = 0; virtual void transition_to_rolling (bool fwd) = 0; @@ -330,8 +326,6 @@ public: virtual void add_to_idle_resize (TimeAxisView*, int32_t) = 0; virtual Temporal::timecnt_t get_paste_offset (Temporal::timepos_t const & pos, unsigned paste_count, Temporal::timecnt_t const & duration) = 0; - virtual void edit_notes (MidiView*) = 0; - virtual void queue_visual_videotimeline_update () = 0; virtual void set_close_video_sensitive (bool) = 0; virtual void toggle_ruler_video (bool) = 0;