diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2009-08-27 03:09:30 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2009-08-27 03:09:30 +0000 |
commit | 3845af6ce92ef15637ffb09410f442e7b4a104c3 (patch) | |
tree | 218a29f23c83c3ac57c857d3b1f599f1e6d97a14 /gtk2_ardour | |
parent | c6be9b688802198e04a07dc902c49d1d6b66340e (diff) |
lots of MIDI editing stuff. to be explained on the website when its done
git-svn-id: svn://localhost/ardour2/branches/3.0@5596 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'gtk2_ardour')
-rw-r--r-- | gtk2_ardour/ardour3_ui_dark.rc.in | 47 | ||||
-rw-r--r-- | gtk2_ardour/ardour3_ui_default.conf | 2 | ||||
-rw-r--r-- | gtk2_ardour/ardour_ui2.cc | 1 | ||||
-rw-r--r-- | gtk2_ardour/ardour_ui_ed.cc | 7 | ||||
-rw-r--r-- | gtk2_ardour/editor.cc | 111 | ||||
-rw-r--r-- | gtk2_ardour/editor.h | 9 | ||||
-rw-r--r-- | gtk2_ardour/editor_ops.cc | 72 | ||||
-rw-r--r-- | gtk2_ardour/keyboard.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/midi_list_editor.cc | 99 | ||||
-rw-r--r-- | gtk2_ardour/midi_list_editor.h | 73 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.cc | 360 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.h | 16 | ||||
-rw-r--r-- | gtk2_ardour/plugin_ui.cc | 6 | ||||
-rw-r--r-- | gtk2_ardour/public_editor.h | 8 | ||||
-rw-r--r-- | gtk2_ardour/selection.h | 1 | ||||
-rw-r--r-- | gtk2_ardour/selection_templates.h | 11 | ||||
-rw-r--r-- | gtk2_ardour/utils.cc | 11 | ||||
-rw-r--r-- | gtk2_ardour/utils.h | 1 | ||||
-rw-r--r-- | gtk2_ardour/wscript | 2 |
19 files changed, 715 insertions, 124 deletions
diff --git a/gtk2_ardour/ardour3_ui_dark.rc.in b/gtk2_ardour/ardour3_ui_dark.rc.in index bac7baae8c..8046d4d075 100644 --- a/gtk2_ardour/ardour3_ui_dark.rc.in +++ b/gtk2_ardour/ardour3_ui_dark.rc.in @@ -81,9 +81,51 @@ style "time_axis_view_item_name" font_name = "%FONT_SMALLER%" } -style "default_base" = "medium_text" +style "white_tree_view" { + GtkButton::default_border = { 0, 0, 0, 0 } + GtkButton::default_outside_border = { 0, 0, 0, 0 } + GtkButton::button_relief = GTK_RELIEF_NONE + GtkTreeView::vertical-padding = 0 + GtkTreeView::horizontal-padding = 0 + GtkTreeView::even-row-color = { 0.70, 0.70, 0.70 } + GtkTreeView::odd-row-color = { 0.64, 0.64, 0.64 } + + fg[NORMAL] = { 0.30, 0.30, 0.40 } + fg[ACTIVE] = { 0.30, 0.30, 0.40 } + fg[PRELIGHT] = { 1.0, 1.0, 1.0 } + fg[INSENSITIVE] = { 0.30, 0.30, 0.40 } + fg[SELECTED] = { 0.30, 0.30, 0.40 } + + bg[NORMAL] = { 0.80, 0.80, 0.80 } + bg[ACTIVE] = { 0.80, 0.80, 0.80 } + bg[PRELIGHT] = { 0.80, 0.80, 0.80 } + bg[INSENSITIVE] = { 0.80, 0.80, 0.80 } + bg[SELECTED] = { 0.80, 0.80, 0.80 } + + text[NORMAL] = { 0.30, 0.30, 0.40 } + text[ACTIVE] = { 0.30, 0.30, 0.40 } + text[PRELIGHT] = { 0.30, 0.30, 0.40 } + text[INSENSITIVE] = { 0.30, 0.30, 0.40 } + text[SELECTED] = { 0, 0, 0 } + base[ACTIVE] = { 0.80, 0.80, 0.80 } + base[NORMAL] = { 0.80, 0.80, 0.80 } + base[PRELIGHT] = { 0.90, 0.90, 0.90 } + base[INSENSITIVE] = "#4c5159" + base[SELECTED] = { 0.60, 0.60, 0.80 } + + engine "clearlooks" + { + menubarstyle = 0 # 0 = flat, 1 = sunken, 2 = flat gradient + menuitemstyle = 0 # 0 = flat, 1 = 3d-ish (gradient), 2 = 3d-ish (button) + listviewitemstyle = 0 # 0 = flat, 1 = 3d-ish (gradient) + progressbarstyle = 1 # 0 = candy bar, 1 = fancy candy bar, 2 = flat + } +} + +style "default_base" = "medium_text" +{ GtkWidget::cursor_color = {1.0, 1.0, 1.0 } GtkButton::default_border = { 0, 0, 0, 0 } GtkButton::default_outside_border = { 0, 0, 0, 0 } @@ -1594,4 +1636,5 @@ widget "*RegionListWholeFile" style:highest "treeview_parent_node" widget "*EditorHScrollbar" style:highest "editor_hscrollbar" widget "*OddPortGroups" style:highest "odd_port_groups" widget "*EvenPortGroups" style:highest "even_port_groups" - +Widget "*MidiListView" style:highest "white_tree_view" +Widget "*MidiListView*" style:highest "white_tree_view" diff --git a/gtk2_ardour/ardour3_ui_default.conf b/gtk2_ardour/ardour3_ui_default.conf index 222b409c3f..94a2b62830 100644 --- a/gtk2_ardour/ardour3_ui_default.conf +++ b/gtk2_ardour/ardour3_ui_default.conf @@ -62,7 +62,7 @@ <Option name="midi note meter color max" value="ee3333aa"/> <Option name="midi note meter color mid" value="eeee33aa"/> <Option name="midi note meter color min" value="33ee33aa"/> - <Option name="midi note selected" value="8888ffb0"/> + <Option name="midi note selected" value="ff0000ff"/> <Option name="midi note velocity text" value="000000ff"/> <Option name="midi program change fill" value="0000ffa0"/> <Option name="midi program change outline" value="a7a7d4ff"/> diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc index 1b16c6a761..0394d8be8c 100644 --- a/gtk2_ardour/ardour_ui2.cc +++ b/gtk2_ardour/ardour_ui2.cc @@ -191,6 +191,7 @@ ARDOUR_UI::setup_transport () { transport_tearoff = manage (new TearOff (transport_tearoff_hbox)); transport_tearoff->set_name ("TransportBase"); + transport_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &transport_tearoff->tearoff_window()), false); if (Profile->get_sae()) { transport_tearoff->set_can_be_torn_off (false); diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index dcc7a91ff1..b827625db2 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -39,6 +39,7 @@ #include "editor.h" #include "actions.h" #include "mixer_ui.h" +#include "utils.h" #ifdef GTKOSX #include <gtkmm2ext/sync-menu.h> @@ -567,7 +568,6 @@ ARDOUR_UI::use_menubar_as_top_menubar () #endif } - void ARDOUR_UI::setup_clock () { @@ -580,11 +580,12 @@ ARDOUR_UI::setup_clock () big_clock_window->add (big_clock); WindowTitle title(Glib::get_application_name()); - title += _("Clock"); + title += _("Big Clock"); big_clock_window->set_title (title.get_string()); - big_clock_window->set_type_hint (Gdk::WINDOW_TYPE_HINT_MENU); + big_clock_window->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY); big_clock_window->signal_realize().connect (bind (sigc::ptr_fun (set_decoration), big_clock_window, (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH))); big_clock_window->signal_unmap().connect (bind (sigc::ptr_fun(&ActionManager::uncheck_toggleaction), X_("<Actions>/Common/ToggleBigClock"))); + big_clock_window->signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), big_clock_window), false); manage_window (*big_clock_window); } diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index c18d70868b..34f258a348 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -1704,7 +1704,11 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi bind (mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr<Region>(region))); items.push_back (MenuElem (_("Rename"), mem_fun(*this, &Editor::rename_region))); - items.push_back (MenuElem (_("Popup region editor"), mem_fun(*this, &Editor::edit_region))); + if (mr && internal_editing()) { + items.push_back (MenuElem (_("Popup list editor"), mem_fun(*this, &Editor::show_midi_list_editor))); + } else { + items.push_back (MenuElem (_("Popup region editor"), mem_fun(*this, &Editor::edit_region))); + } } items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top))); @@ -2643,23 +2647,23 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) break; case SnapToAThirtysecondBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 32); + start = session->tempo_map().round_to_beat_subdivision (start, 32, direction); break; case SnapToASixteenthBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 16); + start = session->tempo_map().round_to_beat_subdivision (start, 16, direction); break; case SnapToAEighthBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 8); + start = session->tempo_map().round_to_beat_subdivision (start, 8, direction); break; case SnapToAQuarterBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 4); + start = session->tempo_map().round_to_beat_subdivision (start, 4, direction); break; case SnapToAThirdBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 3); + start = session->tempo_map().round_to_beat_subdivision (start, 3, direction); break; case SnapToMark: @@ -2758,45 +2762,6 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) } } -double -Editor::snap_length_beats (nframes64_t start) -{ - if (!session) { - return 1.0; - } - - /* FIXME: This could/should also work with non-tempo based snap settings (ie seconds) */ - - switch (snap_type) { - case SnapToBar: - return session->tempo_map().meter_at(start).beats_per_bar(); - - case SnapToBeat: - return 1.0; - - case SnapToAThirtysecondBeat: - return 1.0 / (double)32.0; - break; - - case SnapToASixteenthBeat: - return 1.0 / (double)16.0; - break; - - case SnapToAEighthBeat: - return 1.0 / (double)8.0; - break; - - case SnapToAQuarterBeat: - return 1.0 / (double)4.0; - break; - - case SnapToAThirdBeat: - return 1.0 / (double)3.0; - - default: - return 1.0; - } -} void Editor::setup_toolbar () @@ -2846,6 +2811,7 @@ Editor::setup_toolbar () mouse_mode_tearoff = manage (new TearOff (*mode_box)); mouse_mode_tearoff->set_name ("MouseModeBase"); + mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &mouse_mode_tearoff->tearoff_window()), false); if (Profile->get_sae()) { mouse_mode_tearoff->set_can_be_torn_off (false); @@ -2985,6 +2951,7 @@ Editor::setup_toolbar () tools_tearoff = manage (new TearOff (*hbox)); tools_tearoff->set_name ("MouseModeBase"); + tools_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &tools_tearoff->tearoff_window()), false); if (Profile->get_sae()) { tools_tearoff->set_can_be_torn_off (false); @@ -3846,6 +3813,60 @@ Editor::playlist_selector () const return *_playlist_selector; } +Evoral::MusicalTime +Editor::get_grid_type_as_beats (bool& success, nframes64_t position) +{ + success = true; + + switch (snap_type) { + case SnapToBeat: + return 1.0; + break; + + case SnapToAThirtysecondBeat: + return 1.0/32.0; + break; + + case SnapToASixteenthBeat: + return 1.0/16.0; + break; + + case SnapToAEighthBeat: + return 1.0/8.0; + break; + + case SnapToAQuarterBeat: + return 1.0/4.0; + break; + + case SnapToAThirdBeat: + return 1.0/3.0; + break; + + case SnapToBar: + if (session) { + return session->tempo_map().meter_at (position).beats_per_bar(); + } + break; + + case SnapToCDFrame: + case SnapToSMPTEFrame: + case SnapToSMPTESeconds: + case SnapToSMPTEMinutes: + case SnapToSeconds: + case SnapToMinutes: + case SnapToRegionStart: + case SnapToRegionEnd: + case SnapToRegionSync: + case SnapToRegionBoundary: + default: + success = false; + break; + } + + return 0.0; +} + nframes64_t Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next) { diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 5f5147a677..66e6f2c5f8 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -82,6 +82,7 @@ namespace ARDOUR { class Filter; class Crossfade; class ChanCount; + class MidiOperator; } namespace LADSPA { @@ -102,6 +103,7 @@ class Drag; class GlobalPortMatrixWindow; class GroupedButtons; class Marker; +class MidiRegionView; class MixerStrip; class PlaylistSelector; class PluginSelector; @@ -333,6 +335,7 @@ class Editor : public PublicEditor /* nudge is initiated by transport controls owned by ARDOUR_UI */ nframes64_t get_nudge_distance (nframes64_t pos, nframes64_t& next); + Evoral::MusicalTime get_grid_type_as_beats (bool& success, nframes64_t position); void nudge_forward (bool next, bool force_playhead); void nudge_backward (bool next, bool force_playhead); @@ -1060,6 +1063,7 @@ class Editor : public PublicEditor void remove_selected_regions (); void remove_clicked_region (); void edit_region (); + void show_midi_list_editor (); void rename_region (); void duplicate_some_regions (RegionSelection&, float times); void duplicate_selection (float times); @@ -1493,8 +1497,6 @@ public: void snap_to_with_modifier (nframes64_t& first, GdkEvent const *, int32_t direction = 0, bool for_mark = false); void snap_to (nframes64_t& first, nframes64_t& last, int32_t direction = 0, bool for_mark = false); - double snap_length_beats (nframes64_t start); - uint32_t bbt_beat_subdivision; /* toolbar */ @@ -1915,6 +1917,9 @@ public: void apply_filter (ARDOUR::Filter&, std::string cmd); + void apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiRegionView& mrv); + void apply_midi_note_edit_op (ARDOUR::MidiOperator& op); + /* handling cleanup */ int playlist_deletion_dialog (boost::shared_ptr<ARDOUR::Playlist>); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index d9491a21b0..feeae881f0 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -79,6 +79,7 @@ #include "strip_silence_dialog.h" #include "editor_routes.h" #include "editor_regions.h" +#include "quantize_dialog.h" #include "i18n.h" @@ -2535,6 +2536,13 @@ Editor::edit_region () selection->foreach_regionview (&RegionView::show_region_editor); } +/** Show the midi list editor for the selected MIDI regions */ +void +Editor::show_midi_list_editor () +{ + selection->foreach_midi_regionview (&MidiRegionView::show_list_editor); +} + void Editor::rename_region() { @@ -4801,6 +4809,48 @@ Editor::strip_region_silence () } } +void +Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv) +{ + vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v; + Evoral::Sequence<Evoral::MusicalTime>::Notes selected; + + v.push_back (selected); + + mrv.selection_as_notelist (v.front()); + op (v); + mrv.replace_selected (v.front()); +} + +void +Editor::apply_midi_note_edit_op (MidiOperator& op) +{ + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + + begin_reversible_command (op.name ()); + + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) { + RegionSelection::iterator tmp = r; + ++tmp; + + MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r); + + if (mrv) { + apply_midi_note_edit_op_to_region (op, *mrv); + } + + r = tmp; + } + + commit_reversible_command (); + rs.clear (); +} void Editor::quantize_region () @@ -4809,9 +4859,18 @@ Editor::quantize_region () return; } - // FIXME: varying meter? - Quantize quant (*session, snap_length_beats(0)); - apply_filter (quant, _("quantize regions")); + QuantizeDialog* qd = new QuantizeDialog (*this); + + qd->present (); + qd->run (); + qd->hide (); + + Quantize quant (*session, Plain, + qd->snap_start(), qd->snap_end(), + qd->start_grid_size(), qd->end_grid_size(), + qd->strength(), qd->swing(), qd->threshold()); + + apply_midi_note_edit_op (quant); } void @@ -4834,13 +4893,6 @@ Editor::apply_filter (Filter& filter, string command) RegionSelection::iterator tmp = r; ++tmp; - MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r); - if (mrv) { - if (mrv->midi_region()->apply(filter) == 0) { - mrv->redisplay_model(); - } - } - AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r); if (arv) { boost::shared_ptr<Playlist> playlist = arv->region()->playlist(); diff --git a/gtk2_ardour/keyboard.cc b/gtk2_ardour/keyboard.cc index f3542054aa..157cfffd01 100644 --- a/gtk2_ardour/keyboard.cc +++ b/gtk2_ardour/keyboard.cc @@ -237,6 +237,7 @@ Keyboard::snooper (GtkWidget *widget, GdkEventKey *event) const AccelKey& ak (k->first); if (keyval == ak.get_key() && (Gdk::ModifierType)(event->state | Gdk::RELEASE_MASK) == ak.get_mod()) { + cerr << "Suppress auto repeat\n"; ret = true; break; } @@ -261,6 +262,7 @@ Keyboard::snooper (GtkWidget *widget, GdkEventKey *event) Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (ts.first.c_str(), ts.second.c_str()); if (act) { act->activate(); + cerr << "use repeat, suppress other\n"; ret = true; } break; diff --git a/gtk2_ardour/midi_list_editor.cc b/gtk2_ardour/midi_list_editor.cc new file mode 100644 index 0000000000..2db6cbd25e --- /dev/null +++ b/gtk2_ardour/midi_list_editor.cc @@ -0,0 +1,99 @@ +/* + Copyright (C) 2009 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "evoral/midi_util.h" +#include "ardour/midi_region.h" + +#include "midi_list_editor.h" + +#include "i18n.h" + +using namespace std; +using namespace Gtk; +using namespace Glib; +using namespace ARDOUR; + +MidiListEditor::MidiListEditor (boost::shared_ptr<MidiRegion> r) + : ArdourDialog (r->name(), false, false) + , region (r) +{ + model = ListStore::create (columns); + view.set_model (model); + + view.append_column (_("Channel"), columns.channel); + view.append_column (_("Note"), columns.note); + view.append_column (_("Name"), columns.note_name); + view.append_column (_("Velocity"), columns.velocity); + view.append_column (_("Start"), columns.start); + view.append_column (_("End"), columns.end); + view.append_column (_("Length"), columns.length); + view.set_headers_visible (true); + view.set_name (X_("MidiListView")); + view.set_rules_hint (true); + + for (int i = 0; i < 6; ++i) { + CellRendererText* renderer = dynamic_cast<CellRendererText*>(view.get_column_cell_renderer (i)); + renderer->property_editable() = true; + renderer->signal_edited().connect (mem_fun (*this, &MidiListEditor::edited)); + } + + scroller.add (view); + scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); + + redisplay_model (); + + view.show (); + scroller.show (); + + get_vbox()->pack_start (scroller); + set_size_request (400, 400); +} + +MidiListEditor::~MidiListEditor () +{ +} + +void +MidiListEditor::edited (const Glib::ustring& /* path */, const Glib::ustring& /* text */) +{ + redisplay_model (); +} + +void +MidiListEditor::redisplay_model () +{ + view.set_model (Glib::RefPtr<Gtk::ListStore>(0)); + model->clear (); + + MidiModel::Notes notes = region->midi_source(0)->model()->notes(); + TreeModel::Row row; + + for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) { + row = *(model->append()); + row[columns.channel] = (*i)->channel(); + row[columns.note_name] = Evoral::midi_note_name ((*i)->note()); + row[columns.note] = (*i)->note(); + row[columns.velocity] = (*i)->velocity(); + row[columns.start] = (*i)->time(); + row[columns.length] = (*i)->length(); + row[columns.end] = (*i)->end_time(); + } + + view.set_model (model); +} diff --git a/gtk2_ardour/midi_list_editor.h b/gtk2_ardour/midi_list_editor.h new file mode 100644 index 0000000000..ac931dcca6 --- /dev/null +++ b/gtk2_ardour/midi_list_editor.h @@ -0,0 +1,73 @@ +/* + Copyright (C) 2009 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_gtk2_midi_list_editor_h_ +#define __ardour_gtk2_midi_list_editor_h_ + +#include <gtkmm/treeview.h> +#include <gtkmm/liststore.h> +#include <gtkmm/scrolledwindow.h> + +#include "evoral/types.hpp" + +#include "ardour_dialog.h" + +namespace ARDOUR { + class MidiRegion; + class MidiModel; +}; + +class MidiListEditor : public ArdourDialog +{ + public: + MidiListEditor(boost::shared_ptr<ARDOUR::MidiRegion>); + ~MidiListEditor(); + + private: + struct MidiListModelColumns : public Gtk::TreeModel::ColumnRecord { + MidiListModelColumns() { + add (channel); + add (note); + add (note_name); + add (velocity); + add (start); + add (length); + add (end); + }; + Gtk::TreeModelColumn<uint8_t> channel; + Gtk::TreeModelColumn<uint8_t> note; + Gtk::TreeModelColumn<std::string> note_name; + Gtk::TreeModelColumn<uint8_t> velocity; + Gtk::TreeModelColumn<Evoral::MusicalTime> start; + Gtk::TreeModelColumn<Evoral::MusicalTime> length; + Gtk::TreeModelColumn<Evoral::MusicalTime> end; + }; + + MidiListModelColumns columns; + Glib::RefPtr<Gtk::ListStore> model; + Gtk::TreeView view; + Gtk::ScrolledWindow scroller; + + boost::shared_ptr<ARDOUR::MidiRegion> region; + + void edited (const Glib::ustring&, const Glib::ustring&); + void redisplay_model (); +}; + +#endif /* __ardour_gtk2_midi_list_editor_h_ */ diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 4efaa2eed2..95b1ffad93 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -50,6 +50,7 @@ #include "gui_thread.h" #include "keyboard.h" #include "midi_cut_buffer.h" +#include "midi_list_editor.h" #include "midi_region_view.h" #include "midi_streamview.h" #include "midi_time_axis.h" @@ -160,9 +161,7 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd) midi_region()->midi_source(0)->load_model(); } - cerr << "Looking up model for midi region\n"; _model = midi_region()->midi_source(0)->model(); - cerr << " model = " << _model << endl; _enable_display = false; RegionView::init (basic_color, false); @@ -236,53 +235,84 @@ MidiRegionView::canvas_event(GdkEvent* ev) break; case GDK_KEY_PRESS: - if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) { + + /* since GTK bindings are generally activated on press, and since + detectable auto-repeat is the name of the game and only sends + repeated presses, carry out key actions at key press, not release. + */ + + if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R){ _mouse_state = SelectTouchDragging; return true; + } else if (ev->key.keyval == GDK_Escape) { clear_selection(); _mouse_state = None; - } - return false; - case GDK_KEY_RELEASE: - if (ev->key.keyval == GDK_Delete) { + } else if (ev->key.keyval == GDK_comma || ev->key.keyval == GDK_period) { + + bool start = (ev->key.keyval == GDK_comma); + bool end = (ev->key.keyval == GDK_period); + bool shorter = Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier); + bool fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier); + + change_note_lengths (fine, shorter, start, end); + + return true; + + } else if (ev->key.keyval == GDK_Delete) { + delete_selection(); apply_command(); return true; - } else if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) { - _mouse_state = None; - return true; + } else if (ev->key.keyval == GDK_Tab) { + if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) { goto_previous_note (); } else { goto_next_note (); } return true; + } else if (ev->key.keyval == GDK_Up) { if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) { change_velocities (1, true); } else { - transpose (true, false); + transpose (true, Keyboard::modifier_state_equals (ev->key.state, Keyboard::SecondaryModifier)); } + return true; } else if (ev->key.keyval == GDK_Down) { - + if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) { change_velocities (-1, true); } else { - transpose (false, false); + transpose (false, Keyboard::modifier_state_equals (ev->key.state, Keyboard::SecondaryModifier)); } - } else if (ev->key.keyval == GDK_Left) { + return true; + } else if (ev->key.keyval == GDK_Left) { + nudge_notes (false); + return true; } else if (ev->key.keyval == GDK_Right) { nudge_notes (true); + return true; + + } else if (ev->key.keyval == GDK_Control_L) { + return true; + } + return false; + + case GDK_KEY_RELEASE: + if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R) { + _mouse_state = None; + return true; } return false; @@ -416,7 +446,9 @@ MidiRegionView::canvas_event(GdkEvent* ev) group->ungrab(ev->button.time); event_frame = trackview.editor().pixel_to_frame(event_x); - if (_pressed_button != 1) { + if (ev->button.button == 3) { + return false; + } else if (_pressed_button != 1) { return false; } @@ -429,7 +461,9 @@ MidiRegionView::canvas_event(GdkEvent* ev) break; case MouseObject: create_note_at(event_x, event_y, _default_note_length); - default: break; + break; + default: + break; } _mouse_state = None; break; @@ -459,6 +493,12 @@ MidiRegionView::canvas_event(GdkEvent* ev) return false; } +void +MidiRegionView::show_list_editor () +{ + MidiListEditor* mle = new MidiListEditor (midi_region()); + mle->show (); +} /** Add a note to the model, and the view, at a canvas (click) coordinate. * \param x horizontal position in pixels @@ -578,7 +618,26 @@ MidiRegionView::apply_command() _marked_for_selection.clear(); _marked_for_velocity.clear(); } + +void +MidiRegionView::apply_command_as_subcommand() +{ + if (!_delta_command) { + return; + } + + // Mark all selected notes for selection when model reloads + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + _marked_for_selection.insert((*i)->note()); + } + _model->apply_command_as_subcommand(trackview.session(), _delta_command); + _delta_command = NULL; + midi_view()->midi_track()->diskstream()->playlist_modified(); + + _marked_for_selection.clear(); + _marked_for_velocity.clear(); +} void MidiRegionView::abort_command() @@ -673,7 +732,7 @@ void MidiRegionView::display_sysexes() { for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) { - ARDOUR::MidiModel::TimeType time = (*i)->time(); + Evoral::MusicalTime time = (*i)->time(); assert(time >= 0); ostringstream str; @@ -1033,8 +1092,12 @@ MidiRegionView::add_note(const boost::shared_ptr<NoteType> note) if (event) { if (_marked_for_selection.find(note) != _marked_for_selection.end()) { + cerr << "Added note " << *note << " was marked for selection\n"; note_selected(event, true); + } else { + cerr << "Added note " << *note << " not selected\n"; } + if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) { event->show_velocity(); } @@ -1190,9 +1253,7 @@ MidiRegionView::delete_selection() return; } - if (!_delta_command) { - _delta_command = _model->new_delta_command("delete selection"); - } + start_delta_command (_("delete selection")); for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { if ((*i)->selected()) { @@ -1201,6 +1262,8 @@ MidiRegionView::delete_selection() } _selection.clear(); + + apply_command (); } void @@ -1252,8 +1315,8 @@ MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool } else { /* find end of latest note selected, select all between that and the start of "ev" */ - MidiModel::TimeType earliest = DBL_MAX; - MidiModel::TimeType latest = 0; + Evoral::MusicalTime earliest = DBL_MAX; + Evoral::MusicalTime latest = 0; for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { if ((*i)->note()->end_time() > latest) { @@ -1265,18 +1328,24 @@ MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool } if (ev->note()->end_time() > latest) { - earliest = latest; latest = ev->note()->end_time(); - } else { + } + + if (ev->note()->time() < earliest) { earliest = ev->note()->time(); } + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - if ((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) { - add_to_selection (*i); - } - if ((*i)->note()->end_time() > latest) { + /* find notes entirely within OR spanning the earliest..latest range */ + + if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) || + ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) { + add_to_selection (*i); + } + + if ((*i)->note()->time() > latest) { break; } } @@ -1431,13 +1500,32 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) const boost::shared_ptr<NoteType> copy(new NoteType(*(*i)->note().get())); nframes64_t start_frames = beats_to_frames((*i)->note()->time()); + + cerr << "starting at " << (*i)->note()->time() + << " (" << start_frames << ") delta on drag = " << dt << endl; + if (dt >= 0) { + cerr << "Motion was " << snap_frame_to_frame(trackview.editor().pixel_to_frame(dt)) << endl; start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt)); } else { + cerr << "rev Motion was " << snap_frame_to_frame(trackview.editor().pixel_to_frame(dt)) << endl; start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt)); } - copy->set_time(frames_to_beats(start_frames)); + cerr << "start frame will be " << start_frames << " vs. region " + << _region->position () + << endl; + + Evoral::MusicalTime new_time = frames_to_beats(start_frames); + + if (new_time < 0) { + i = next; + continue; + } + + copy->set_time (new_time); + + cerr << "copy time = " << copy->time() << endl; uint8_t original_pitch = (*i)->note()->note(); uint8_t new_pitch = original_pitch + dnote - highest_note_difference; @@ -1457,6 +1545,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) copy->set_note(new_pitch); command_remove_note(*i); + cerr << "Adding note: " << *copy << endl; command_add_note(copy, (*i)->selected()); i = next; @@ -1671,7 +1760,63 @@ MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool rela } void -MidiRegionView::change_note_time (CanvasNoteEvent* event, MidiModel::TimeType delta, bool relative) +MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta) +{ + /* NOTE: the semantics of the two delta arguments are slightly subtle: + + front_delta: if positive - move the start of the note later in time (shortening it) + if negative - move the start of the note earlier in time (lengthening it) + + end_delta: if positive - move the end of the note later in time (lengthening it) + if negative - move the end of the note earlier in time (shortening it) + */ + + const boost::shared_ptr<NoteType> copy(new NoteType(*(event->note().get()))); + + cerr << "Trim front by " << front_delta << " end by " << end_delta << endl; + + if (front_delta) { + if (front_delta < 0) { + if (copy->time() < -front_delta) { + copy->set_time (0); + } else { + copy->set_time (copy->time() + front_delta); // moves earlier + } + /* start moved toward zero, so move the end point out to where it used to be. + Note that front_delta is negative, so this increases the length. + */ + copy->set_length (copy->length() - front_delta); + } else { + Evoral::MusicalTime new_pos = copy->time() + front_delta; + + if (new_pos >= copy->end_time()) { + return; + } + + copy->set_time (copy->time() + front_delta); + + /* start moved toward the end, so move the end point back to where it used to be */ + copy->set_length (copy->length() - front_delta); + } + + } + + if (end_delta) { + if (end_delta < 0) { + if (copy->length() < -end_delta) { + return; + } + } + + copy->set_length (copy->length() + end_delta); + } + + command_remove_note(event); + command_add_note(copy, event->selected(), false); +} + +void +MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative) { const boost::shared_ptr<NoteType> copy(new NoteType(*(event->note().get()))); @@ -1745,6 +1890,44 @@ MidiRegionView::transpose (bool up, bool fine) } void +MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end) +{ + Evoral::MusicalTime delta; + + if (fine) { + delta = 1.0/128.0; + } else { + /* grab the current grid distance */ + bool success; + delta = trackview.editor().get_grid_type_as_beats (success, _region->position()); + if (!success) { + /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */ + cerr << "Grid type not available as beats - TO BE FIXED\n"; + return; + } + } + + if (shorter) { + delta = -delta; + } + + start_delta_command (_("change note lengths")); + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { + Selection::iterator next = i; + ++next; + + /* note the negation of the delta for start */ + + trim_note (*i, (start ? -delta : 0), (end ? delta : 0)); + i = next; + } + + apply_command (); + +} + +void MidiRegionView::nudge_notes (bool forward) { if (_selection.empty()) { @@ -1757,23 +1940,36 @@ MidiRegionView::nudge_notes (bool forward) */ nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time()); - nframes64_t next_pos = ref_point; + nframes64_t unused; + nframes64_t distance; - if (forward) { - next_pos += 1; - } else { - if (next_pos == 0) { - return; - } - next_pos -= 1; - } + if ((distance = trackview.editor().get_nudge_distance (ref_point, unused)) == 0) { - trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false); - nframes64_t distance = ref_point - next_pos; + /* no nudge distance set - use grid */ - cerr << "ref was " << ref_point << " next is " << next_pos << endl; + nframes64_t next_pos = ref_point; + + if (forward) { + /* XXX need check on max_frames, but that needs max_frames64 or something */ + next_pos += 1; + } else { + if (next_pos == 0) { + return; + } + next_pos -= 1; + } + + cerr << "ref point was " << ref_point << " next was " << next_pos; + trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false); + distance = ref_point - next_pos; + cerr << " final is " << next_pos << " distance = " << distance << endl; + } + + if (distance == 0) { + return; + } - MidiModel::TimeType delta = frames_to_beats (fabs (distance)); + Evoral::MusicalTime delta = frames_to_beats (fabs (distance)); if (!forward) { delta = -delta; @@ -1911,7 +2107,7 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op) MidiCutBuffer* MidiRegionView::selection_as_cut_buffer () const { - Evoral::Sequence<MidiModel::TimeType>::Notes notes; + NoteList notes; for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { notes.push_back (boost::shared_ptr<NoteType> (new NoteType (*((*i)->note().get())))); @@ -1919,7 +2115,8 @@ MidiRegionView::selection_as_cut_buffer () const /* sort them into time order */ - sort (notes.begin(), notes.end(), Evoral::Sequence<MidiModel::TimeType>::note_time_comparator); + Evoral::Sequence<Evoral::MusicalTime>::LaterNoteComparator cmp; + sort (notes.begin(), notes.end(), cmp); MidiCutBuffer* cb = new MidiCutBuffer (trackview.session()); cb->set (notes); @@ -1936,10 +2133,10 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb) start_delta_command (_("paste")); - MidiModel::TimeType beat_delta; - MidiModel::TimeType paste_pos_beats; - MidiModel::TimeType duration; - MidiModel::TimeType end_point; + Evoral::MusicalTime beat_delta; + Evoral::MusicalTime paste_pos_beats; + Evoral::MusicalTime duration; + Evoral::MusicalTime end_point; duration = mcb.notes().back()->end_time() - mcb.notes().front()->time(); paste_pos_beats = frames_to_beats (pos - _region->position()); @@ -1950,7 +2147,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb) for (int n = 0; n < (int) times; ++n) { - for (Evoral::Sequence<MidiModel::TimeType>::Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) { + for (NoteList::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) { boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get()))); copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta); @@ -2033,3 +2230,66 @@ MidiRegionView::goto_previous_note () unique_select (*(_events.rbegin())); } + +MidiModel::DeltaCommand* +MidiRegionView::apply (Filter& filter, const std::string& name) +{ + MidiModel::Notes before = _model->notes(); + MidiModel::Notes after; + + _region->apply (filter); + + redisplay_model (); + + after = _model->notes(); + + start_delta_command (name); + + for (MidiModel::Notes::iterator i = before.begin(); i != before.end(); ++i) { + _delta_command->add (*i); + } + + for (MidiModel::Notes::iterator i = after.begin(); i != after.end(); ++i) { + _delta_command->remove (*i); + } + + return _delta_command; +} + +void +MidiRegionView::selection_as_notelist (NoteList& selected) +{ + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { + if ((*i)->selected()) { + /* make a copy of the original */ + selected.push_back (boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > + (new Evoral::Note<Evoral::MusicalTime> (*((*i)->note())))); + } + } +} + +void +MidiRegionView::replace_selected (NoteList& replacements) +{ + start_delta_command ("whatever"); + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { + Selection::iterator tmp; + tmp = i; + ++tmp; + + command_remove_note (*i); + remove_from_selection (*i); + + i = tmp; + } + + _selection.clear (); + + for (NoteList::iterator i = replacements.begin(); i != replacements.end(); ++i) { + command_add_note (*i, true, false); + } + + apply_command_as_subcommand (); +} + diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 073b78ad1a..7edc4cd835 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -45,6 +45,7 @@ namespace ARDOUR { class MidiRegion; class MidiModel; + class Filter; }; namespace MIDI { @@ -62,7 +63,8 @@ class MidiCutBuffer; class MidiRegionView : public RegionView { public: - typedef Evoral::Note<ARDOUR::MidiModel::TimeType> NoteType; + typedef Evoral::Note<Evoral::MusicalTime> NoteType; + typedef Evoral::Sequence<Evoral::MusicalTime>::Notes NoteList; MidiRegionView (ArdourCanvas::Group *, RouteTimeAxisView&, @@ -86,6 +88,8 @@ class MidiRegionView : public RegionView inline MidiStreamView* midi_stream_view() const { return midi_view()->midi_view(); } + ARDOUR::MidiModel::DeltaCommand* apply (ARDOUR::Filter&, const std::string& name); + void set_height (double); void apply_note_range(uint8_t lowest, uint8_t highest, bool force=false); @@ -168,6 +172,7 @@ class MidiRegionView : public RegionView void command_remove_note(ArdourCanvas::CanvasNoteEvent* ev); void apply_command(); + void apply_command_as_subcommand(); void abort_command(); void note_entered(ArdourCanvas::CanvasNoteEvent* ev); @@ -264,9 +269,15 @@ class MidiRegionView : public RegionView void goto_previous_note (); void goto_next_note (); void change_velocities (int8_t velocity, bool relative); + void change_note_lengths (bool, bool, bool start, bool end); void transpose (bool up, bool fine); void nudge_notes (bool forward); + void show_list_editor (); + + void selection_as_notelist (NoteList& selected); + void replace_selected (NoteList& replacements); + protected: /** Allows derived types to specify their visibility requirements * to the TimeAxisViewItem parent class. @@ -308,6 +319,8 @@ class MidiRegionView : public RegionView void change_note_velocity(ArdourCanvas::CanvasNoteEvent* ev, int8_t vel, bool relative=false); void change_note_note(ArdourCanvas::CanvasNoteEvent* ev, int8_t note, bool relative=false); void change_note_time(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::TimeType, bool relative=false); + void trim_note(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::TimeType start_delta, + ARDOUR::MidiModel::TimeType end_delta); void clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev); void clear_selection() { clear_selection_except(NULL); } @@ -361,6 +374,7 @@ class MidiRegionView : public RegionView /* connection used to connect to model's ContentChanged signal */ sigc::connection content_connection; + }; #endif /* __gtk_ardour_midi_region_view_h__ */ diff --git a/gtk2_ardour/plugin_ui.cc b/gtk2_ardour/plugin_ui.cc index 1c5a56f531..86dbcd829b 100644 --- a/gtk2_ardour/plugin_ui.cc +++ b/gtk2_ardour/plugin_ui.cc @@ -331,11 +331,7 @@ PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert) bool PluginUIWindow::on_key_press_event (GdkEventKey* event) { - if (!key_press_focus_accelerator_handler (*this, event)) { - return PublicEditor::instance().on_key_press_event(event); - } else { - return true; - } + return relay_key_press (event, this); } bool diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index f2d57a72a2..ee7f303884 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -31,6 +31,7 @@ #include <jack/types.h> #include <sigc++/signal.h> +#include "evoral/types.hpp" #include "ardour/route_group.h" #include "pbd/statefuldestructible.h" @@ -121,9 +122,6 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway /** Snap a value according to the current snap setting. */ virtual void snap_to (nframes64_t& first, int32_t direction = 0, bool for_mark = false) = 0; - /** Get the current snap value in beats */ - virtual double snap_length_beats (nframes64_t start) = 0; - /** Undo some transactions. * @param n Number of transactions to undo. */ @@ -264,7 +262,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway virtual void foreach_time_axis_view (sigc::slot<void,TimeAxisView&>) = 0; virtual void add_to_idle_resize (TimeAxisView*, int32_t) = 0; virtual nframes64_t get_nudge_distance (nframes64_t pos, nframes64_t& next) = 0; - + virtual Evoral::MusicalTime get_grid_type_as_beats (bool& success, nframes64_t position) = 0; #ifdef WITH_CMT virtual void add_imageframe_time_axis(const std::string & track_name, void*) = 0; @@ -350,7 +348,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway static PublicEditor* _instance; - friend class PluginUIWindow; + friend bool relay_key_press (GdkEventKey*, Gtk::Window*); }; #endif // __gtk_ardour_public_editor_h__ diff --git a/gtk2_ardour/selection.h b/gtk2_ardour/selection.h index 288c832178..a898705d23 100644 --- a/gtk2_ardour/selection.h +++ b/gtk2_ardour/selection.h @@ -181,6 +181,7 @@ class Selection : public sigc::trackable void foreach_region (void (ARDOUR::Region::*method)(void)); void foreach_regionview (void (RegionView::*method)(void)); + void foreach_midi_regionview (void (MidiRegionView::*method)(void)); template<class A> void foreach_region (void (ARDOUR::Region::*method)(A), A arg); private: diff --git a/gtk2_ardour/selection_templates.h b/gtk2_ardour/selection_templates.h index cc7933d879..cc2585c17e 100644 --- a/gtk2_ardour/selection_templates.h +++ b/gtk2_ardour/selection_templates.h @@ -29,6 +29,7 @@ #include "selection.h" #include "region_view.h" +#include "midi_region_view.h" inline void Selection::foreach_region (void (ARDOUR::Region::*method)(void)) { @@ -45,6 +46,16 @@ Selection::foreach_regionview (void (RegionView::*method)(void)) { } } +inline void +Selection::foreach_midi_regionview (void (MidiRegionView::*method)(void)) { + for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) { + MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i); + if (mrv) { + (mrv->*(method))(); + } + } +} + template<class A> inline void Selection::foreach_region (void (ARDOUR::Region::*method)(A), A arg) { for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) { diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index 7a1c9c6968..5e5534222e 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -41,6 +41,7 @@ #include "ardour/filesystem_paths.h" #include "ardour_ui.h" +#include "public_editor.h" #include "keyboard.h" #include "utils.h" #include "i18n.h" @@ -509,6 +510,16 @@ extern "C" { #endif bool +relay_key_press (GdkEventKey* ev, Gtk::Window* win) +{ + if (!key_press_focus_accelerator_handler (*win, ev)) { + return PublicEditor::instance().on_key_press_event(ev); + } else { + return true; + } +} + +bool key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) { GtkWindow* win = window.gobj(); diff --git a/gtk2_ardour/utils.h b/gtk2_ardour/utils.h index 4cf54f6c91..5e0c3b53c2 100644 --- a/gtk2_ardour/utils.h +++ b/gtk2_ardour/utils.h @@ -82,6 +82,7 @@ bool canvas_item_visible (ArdourCanvas::Item* item); void set_color (Gdk::Color&, int); +bool relay_key_press (GdkEventKey* ev, Gtk::Window* win); bool key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev); bool possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval); diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 87503ff2d3..17d9096dc9 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -127,6 +127,7 @@ gtk2_ardour_sources = [ 'marker.cc', 'midi_channel_selector.cc', 'midi_cut_buffer.cc', + 'midi_list_editor.cc', 'midi_port_dialog.cc', 'midi_region_view.cc', 'midi_scroomer.cc', @@ -157,6 +158,7 @@ gtk2_ardour_sources = [ 'processor_box.cc', 'prompter.cc', 'public_editor.cc', + 'quantize_dialog.cc', 'rc_option_editor.cc', 'region_gain_line.cc', 'region_selection.cc', |