summaryrefslogtreecommitdiff
path: root/gtk2_ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2009-08-27 03:09:30 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2009-08-27 03:09:30 +0000
commit3845af6ce92ef15637ffb09410f442e7b4a104c3 (patch)
tree218a29f23c83c3ac57c857d3b1f599f1e6d97a14 /gtk2_ardour
parentc6be9b688802198e04a07dc902c49d1d6b66340e (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.in47
-rw-r--r--gtk2_ardour/ardour3_ui_default.conf2
-rw-r--r--gtk2_ardour/ardour_ui2.cc1
-rw-r--r--gtk2_ardour/ardour_ui_ed.cc7
-rw-r--r--gtk2_ardour/editor.cc111
-rw-r--r--gtk2_ardour/editor.h9
-rw-r--r--gtk2_ardour/editor_ops.cc72
-rw-r--r--gtk2_ardour/keyboard.cc2
-rw-r--r--gtk2_ardour/midi_list_editor.cc99
-rw-r--r--gtk2_ardour/midi_list_editor.h73
-rw-r--r--gtk2_ardour/midi_region_view.cc360
-rw-r--r--gtk2_ardour/midi_region_view.h16
-rw-r--r--gtk2_ardour/plugin_ui.cc6
-rw-r--r--gtk2_ardour/public_editor.h8
-rw-r--r--gtk2_ardour/selection.h1
-rw-r--r--gtk2_ardour/selection_templates.h11
-rw-r--r--gtk2_ardour/utils.cc11
-rw-r--r--gtk2_ardour/utils.h1
-rw-r--r--gtk2_ardour/wscript2
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',