* renamed canvas-midi-event.* into canvas-note-event.*
authorHans Baier <hansfbaier@googlemail.com>
Tue, 29 Apr 2008 21:58:05 +0000 (21:58 +0000)
committerHans Baier <hansfbaier@googlemail.com>
Tue, 29 Apr 2008 21:58:05 +0000 (21:58 +0000)
* make MidiModel keep track of its CanvasProgramChange instances

git-svn-id: svn://localhost/ardour2/branches/3.0@3297 d708f5d6-7413-0410-9779-e7cbd77b26cf

13 files changed:
gtk2_ardour/SConscript
gtk2_ardour/canvas-midi-event.cc [deleted file]
gtk2_ardour/canvas-midi-event.h [deleted file]
gtk2_ardour/canvas-note-event.cc [new file with mode: 0644]
gtk2_ardour/canvas-note-event.h [new file with mode: 0644]
gtk2_ardour/canvas-note.h
gtk2_ardour/canvas-program-change.cc
gtk2_ardour/canvas-program-change.h
gtk2_ardour/diamond.h
gtk2_ardour/editor_canvas_events.cc
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
libs/ardour/midi_model.cc

index aceb587998baa26bc5dab357c6193c6e7186978a..2d39c0b77265bc87c468744d334322be03b530b4 100644 (file)
@@ -132,7 +132,7 @@ automation_time_axis.cc
 axis_view.cc
 bundle_manager.cc
 canvas-program-change.cc
-canvas-midi-event.cc
+canvas-note-event.cc
 canvas-note.cc
 canvas-simpleline.c
 canvas-simplerect.c
diff --git a/gtk2_ardour/canvas-midi-event.cc b/gtk2_ardour/canvas-midi-event.cc
deleted file mode 100644 (file)
index d9b5713..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
-    Copyright (C) 2007 Paul Davis
-    Author: Dave Robillard
-
-    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 <iostream>
-#include "canvas-midi-event.h"
-#include "midi_region_view.h"
-#include "public_editor.h"
-#include "editing_syms.h"
-#include "keyboard.h"
-
-using namespace std;
-using ARDOUR::MidiModel;
-
-namespace Gnome {
-namespace Canvas {
-
-
-CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item,
-               const boost::shared_ptr<ARDOUR::Note> note)
-       : _region(region)
-       , _item(item)
-       , _text(0)
-       , _channel_selector_widget()
-       , _state(None)
-       , _note(note)
-       , _selected(false)
-{
-       _text = new Text(*(item->property_parent()));
-}
-
-CanvasNoteEvent::~CanvasNoteEvent() 
-{ 
-       if(_text) delete _text;
-       if(_channel_selector_widget) delete _channel_selector_widget;
-}
-
-void 
-CanvasNoteEvent::move_event(double dx, double dy)
-{
-       _item->move(dx, dy);
-       _text->move(dx, dy);
-}
-
-void
-CanvasNoteEvent::show_velocity(void)
-{
-       _text->property_x() = (x1() + x2()) /2;
-       _text->property_y() = (y1() + y2()) /2;
-       ostringstream velo(ios::ate);
-       velo << int(_note->velocity());
-       _text->property_text() = velo.str();
-       _text->property_justification() = Gtk::JUSTIFY_CENTER;
-       _text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get();
-       _text->show();
-       _text->lower_to_bottom();
-       _text->raise(2);
-}
-
-void
-CanvasNoteEvent::hide_velocity(void)
-{
-       _text->hide();
-}
-
-void 
-CanvasNoteEvent::on_channel_selection_change(uint16_t selection)
-{
-       // make note change its color if its channel is not marked active
-       if( (selection & (1 << _note->channel())) == 0 ) {
-               set_fill_color(ARDOUR_UI::config()->canvasvar_MidiNoteFillInactiveChannel.get());
-               set_outline_color(ARDOUR_UI::config()->canvasvar_MidiNoteOutlineInactiveChannel.get());
-       } else {
-               set_fill_color(note_fill_color(_note->velocity()));
-               set_outline_color(note_outline_color(_note->velocity()));
-       }
-       // this forces the item to update..... maybe slow...
-       _item->hide();
-       _item->show();
-}
-
-void 
-CanvasNoteEvent::on_channel_change(uint8_t channel)
-{
-       _region.note_selected(this, true);
-       hide_channel_selector();
-       _region.change_channel(channel);
-}
-
-void
-CanvasNoteEvent::show_channel_selector(void)
-{
-       if(_channel_selector_widget == 0) {
-               cerr << "Note has channel: " << int(_note->channel()) << endl;
-               SingleMidiChannelSelector* _channel_selector = new SingleMidiChannelSelector(_note->channel());
-               _channel_selector->show_all();
-               _channel_selector->channel_selected.connect(
-                       sigc::mem_fun(this, &CanvasNoteEvent::on_channel_change));
-
-               _channel_selector_widget = 
-                       new Widget(*(_item->property_parent()), 
-                                  x1(), 
-                                  y2() + 2, 
-                                  (Gtk::Widget &) *_channel_selector);
-               
-               _channel_selector_widget->hide();
-               _channel_selector_widget->property_height() = 100;
-               _channel_selector_widget->property_width() = 100;
-               _channel_selector_widget->raise_to_top();
-               _channel_selector_widget->show();
-       } else {
-               hide_channel_selector();
-       }
-}
-
-void
-CanvasNoteEvent::hide_channel_selector(void)
-{
-       if(_channel_selector_widget) {
-               _channel_selector_widget->hide();
-               delete _channel_selector_widget;
-               _channel_selector_widget = 0;
-       }
-}
-
-void
-CanvasNoteEvent::selected(bool yn)
-{
-       if (!_note) {
-               return;
-       } else if (yn) {
-               set_fill_color(UINT_INTERPOLATE(note_fill_color(_note->velocity()),
-                                       ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(), 0.1));
-               set_outline_color(ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get());
-               show_velocity();
-       } else {
-               set_fill_color(note_fill_color(_note->velocity()));
-               set_outline_color(note_outline_color(_note->velocity()));
-               hide_velocity();
-       }
-
-       _selected = yn;
-}
-
-
-bool
-CanvasNoteEvent::on_event(GdkEvent* ev)
-{
-       MidiStreamView *streamview = _region.midi_stream_view();
-       static uint8_t drag_delta_note = 0;
-       static double  drag_delta_x = 0;
-       static double last_x, last_y;
-       double event_x, event_y, dx, dy;
-       bool select_mod;
-       uint8_t d_velocity = 10;
-
-       if (_region.get_time_axis_view().editor.current_mouse_mode() != Editing::MouseNote)
-               return false;
-
-       switch (ev->type) {
-       case GDK_SCROLL:
-               if (Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier)) {
-                       d_velocity = 1;
-               }
-
-               if(ev->scroll.direction == GDK_SCROLL_UP) {
-                       _region.note_selected(this, true);
-                       if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
-                               // TODO: absolute velocity
-                       } else {
-                               _region.change_velocity(d_velocity, true);
-                       }
-                       return true;
-               } else if(ev->scroll.direction == GDK_SCROLL_DOWN) {
-                       _region.note_selected(this, true);
-                       if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
-                               // TODO: absolute velocity
-                       } else {
-                               _region.change_velocity(-d_velocity, true);
-                       }
-                       return true;
-               } else {
-                       return false;
-               }
-               
-       case GDK_KEY_PRESS:
-               if (_note && ev->key.keyval == GDK_Delete) {
-                       selected(true);
-                       _region.start_delta_command();
-                       _region.command_remove_note(this);
-               }
-               break;
-
-       case GDK_KEY_RELEASE:
-               if (ev->key.keyval == GDK_Delete) {
-                       _region.apply_command();
-               }
-               break;
-
-       case GDK_ENTER_NOTIFY:
-               _region.note_entered(this);
-               _item->grab_focus();
-               show_velocity();
-               Keyboard::magic_widget_grab_focus();
-               break;
-
-       case GDK_LEAVE_NOTIFY:
-               Keyboard::magic_widget_drop_focus();
-               if(! selected()) {
-                       hide_velocity();
-               }
-               _region.get_canvas_group()->grab_focus();
-               break;
-
-       case GDK_BUTTON_PRESS:
-               if (ev->button.button == 1) {
-                       _state = Pressed;
-               } else if (ev->button.button == 3) {
-                       show_channel_selector();
-               }
-               return true;
-
-       case GDK_MOTION_NOTIFY:
-               event_x = ev->motion.x;
-               event_y = ev->motion.y;
-
-               switch (_state) {
-               case Pressed: // Drag begin
-                       if (_region.mouse_state() != MidiRegionView::SelectTouchDragging) {
-                               _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
-                                               Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
-                               _state = Dragging;
-                               _item->property_parent().get_value()->w2i(event_x, event_y);
-                               event_x = _region.snap_to_pixel(event_x); 
-                               last_x = event_x;
-                               last_y = event_y;
-                               drag_delta_x = 0;
-                               drag_delta_note = 0;
-                               _region.note_selected(this, true);
-                       }
-                       return true;
-
-               case Dragging: // Drag motion
-                       if (ev->motion.is_hint) {
-                               int t_x;
-                               int t_y;
-                               GdkModifierType state;
-                               gdk_window_get_pointer(ev->motion.window, &t_x, &t_y, &state);
-                               event_x = t_x;
-                               event_y = t_y;
-                       }
-                       _item->property_parent().get_value()->w2i(event_x, event_y);
-                       
-                       // Snap
-                       event_x = _region.snap_to_pixel(event_x); 
-
-                       dx = event_x - last_x;
-                       dy = event_y - last_y;
-
-                       last_x = event_x;
-
-                       drag_delta_x += dx;
-
-                       // Snap to note rows
-                       if (abs(dy) < streamview->note_height()) {
-                               dy = 0.0;
-                       } else {
-                               int8_t this_delta_note;
-                               if (dy > 0) {
-                                       this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
-                               } else {
-                                       this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
-                               }
-                               drag_delta_note -= this_delta_note;
-                               dy = streamview->note_height() * this_delta_note;
-                               last_y = last_y + dy;
-                       }
-
-                       _region.move_selection(dx, dy);
-
-                       return true;
-               default:
-                       break;
-               }
-               break;
-
-       case GDK_BUTTON_RELEASE:
-               select_mod = (ev->motion.state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
-               event_x = ev->button.x;
-               event_y = ev->button.y;
-               _item->property_parent().get_value()->w2i(event_x, event_y);
-               
-               if(ev->button.button == 3) {
-                       return true;
-               }
-               
-               switch (_state) {
-               case Pressed: // Clicked
-                       if (_region.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditSelect) {
-                               _state = None;
-
-                               if (_selected && !select_mod && _region.selection_size() > 1)
-                                       _region.unique_select(this);
-                               else if (_selected)
-                                       _region.note_deselected(this, select_mod);
-                               else
-                                       _region.note_selected(this, select_mod);
-                       } else if (_region.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditErase) {
-                               _region.start_delta_command();
-                               _region.command_remove_note(this);
-                               _region.apply_command();
-                       }
-
-                       return true;
-               case Dragging: // Dropped
-                       _item->ungrab(ev->button.time);
-                       _state = None;
-
-                       if (_note)
-                               _region.note_dropped(this,
-                                               _region.midi_view()->editor.pixel_to_frame(abs(drag_delta_x))
-                                                               * ((drag_delta_x < 0.0) ? -1 : 1),
-                                               drag_delta_note);
-                       return true;
-               default:
-                       break;
-               }
-
-       default:
-               break;
-       }
-
-       return false;
-}
-
-} // namespace Canvas
-} // namespace Gnome
-
diff --git a/gtk2_ardour/canvas-midi-event.h b/gtk2_ardour/canvas-midi-event.h
deleted file mode 100644 (file)
index f575808..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
-    Copyright (C) 2007 Paul Davis 
-    Author: Dave Robillard
-
-    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 __gtk_ardour_canvas_midi_event_h__
-#define __gtk_ardour_canvas_midi_event_h__
-
-#include "simplerect.h"
-#include "midi_channel_selector.h"
-#include <libgnomecanvasmm/text.h>
-#include <libgnomecanvasmm/widget.h>
-#include <ardour/midi_model.h>
-
-class Editor;
-class MidiRegionView;
-
-namespace Gnome {
-namespace Canvas {
-
-
-/** This manages all the event handling for any MIDI event on the canvas.
- *
- * This is not actually a canvas item itself to avoid the dreaded diamond,
- * since various types of canvas items (Note (rect), Hit (diamond), etc)
- * need to share this functionality but can't share an ancestor.
- *
- * Note: Because of this, derived classes need to manually bounce events to
- * on_event, it won't happen automatically.
- *
- * A newer, better canvas should remove the need for all the ugly here.
- */
-class CanvasNoteEvent : public sigc::trackable {
-public:
-       CanvasNoteEvent(
-                       MidiRegionView&                       region,
-                       Item*                                 item,
-                       const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>());
-
-       virtual ~CanvasNoteEvent();
-
-       bool on_event(GdkEvent* ev);
-
-       bool selected() const { return _selected; }
-       void selected(bool yn);
-
-       void move_event(double dx, double dy);
-       
-       void show_velocity();
-       void hide_velocity();
-       
-       /**
-        * This slot is called, when a new channel is selected for the single event
-        * */
-       void on_channel_change(uint8_t channel);
-       void on_channel_selection_change(uint16_t selection);
-       
-       void show_channel_selector();
-       
-       void hide_channel_selector();
-
-       virtual void set_outline_color(uint32_t c) = 0;
-       virtual void set_fill_color(uint32_t c) = 0;
-       
-       virtual double x1() = 0;
-       virtual double y1() = 0;
-       virtual double x2() = 0;
-       virtual double y2() = 0;
-
-       const boost::shared_ptr<ARDOUR::Note> note() { return _note; }
-
-protected:
-       enum State { None, Pressed, Dragging };
-
-       MidiRegionView&                       _region;
-       Item* const                           _item;
-       Text*                                 _text;
-       Widget*                               _channel_selector_widget;
-       State                                 _state;
-       const boost::shared_ptr<ARDOUR::Note> _note;
-       bool                                  _own_note;
-       bool                                  _selected;
-};
-
-} // namespace Gnome
-} // namespace Canvas
-
-#endif /* __gtk_ardour_canvas_midi_event_h__ */
diff --git a/gtk2_ardour/canvas-note-event.cc b/gtk2_ardour/canvas-note-event.cc
new file mode 100644 (file)
index 0000000..d775514
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+    Copyright (C) 2007 Paul Davis
+    Author: Dave Robillard
+
+    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 <iostream>
+#include "canvas-note-event.h"
+#include "midi_region_view.h"
+#include "public_editor.h"
+#include "editing_syms.h"
+#include "keyboard.h"
+
+using namespace std;
+using ARDOUR::MidiModel;
+
+namespace Gnome {
+namespace Canvas {
+
+
+CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item,
+               const boost::shared_ptr<ARDOUR::Note> note)
+       : _region(region)
+       , _item(item)
+       , _text(0)
+       , _channel_selector_widget()
+       , _state(None)
+       , _note(note)
+       , _selected(false)
+{
+       _text = new Text(*(item->property_parent()));
+}
+
+CanvasNoteEvent::~CanvasNoteEvent() 
+{ 
+       if(_text) delete _text;
+       if(_channel_selector_widget) delete _channel_selector_widget;
+}
+
+void 
+CanvasNoteEvent::move_event(double dx, double dy)
+{
+       _item->move(dx, dy);
+       _text->move(dx, dy);
+}
+
+void
+CanvasNoteEvent::show_velocity(void)
+{
+       _text->property_x() = (x1() + x2()) /2;
+       _text->property_y() = (y1() + y2()) /2;
+       ostringstream velo(ios::ate);
+       velo << int(_note->velocity());
+       _text->property_text() = velo.str();
+       _text->property_justification() = Gtk::JUSTIFY_CENTER;
+       _text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get();
+       _text->show();
+       _text->lower_to_bottom();
+       _text->raise(2);
+}
+
+void
+CanvasNoteEvent::hide_velocity(void)
+{
+       _text->hide();
+}
+
+void 
+CanvasNoteEvent::on_channel_selection_change(uint16_t selection)
+{
+       // make note change its color if its channel is not marked active
+       if( (selection & (1 << _note->channel())) == 0 ) {
+               set_fill_color(ARDOUR_UI::config()->canvasvar_MidiNoteFillInactiveChannel.get());
+               set_outline_color(ARDOUR_UI::config()->canvasvar_MidiNoteOutlineInactiveChannel.get());
+       } else {
+               set_fill_color(note_fill_color(_note->velocity()));
+               set_outline_color(note_outline_color(_note->velocity()));
+       }
+       // this forces the item to update..... maybe slow...
+       _item->hide();
+       _item->show();
+}
+
+void 
+CanvasNoteEvent::on_channel_change(uint8_t channel)
+{
+       _region.note_selected(this, true);
+       hide_channel_selector();
+       _region.change_channel(channel);
+}
+
+void
+CanvasNoteEvent::show_channel_selector(void)
+{
+       if(_channel_selector_widget == 0) {
+               cerr << "Note has channel: " << int(_note->channel()) << endl;
+               SingleMidiChannelSelector* _channel_selector = new SingleMidiChannelSelector(_note->channel());
+               _channel_selector->show_all();
+               _channel_selector->channel_selected.connect(
+                       sigc::mem_fun(this, &CanvasNoteEvent::on_channel_change));
+
+               _channel_selector_widget = 
+                       new Widget(*(_item->property_parent()), 
+                                  x1(), 
+                                  y2() + 2, 
+                                  (Gtk::Widget &) *_channel_selector);
+               
+               _channel_selector_widget->hide();
+               _channel_selector_widget->property_height() = 100;
+               _channel_selector_widget->property_width() = 100;
+               _channel_selector_widget->raise_to_top();
+               _channel_selector_widget->show();
+       } else {
+               hide_channel_selector();
+       }
+}
+
+void
+CanvasNoteEvent::hide_channel_selector(void)
+{
+       if(_channel_selector_widget) {
+               _channel_selector_widget->hide();
+               delete _channel_selector_widget;
+               _channel_selector_widget = 0;
+       }
+}
+
+void
+CanvasNoteEvent::selected(bool yn)
+{
+       if (!_note) {
+               return;
+       } else if (yn) {
+               set_fill_color(UINT_INTERPOLATE(note_fill_color(_note->velocity()),
+                                       ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(), 0.1));
+               set_outline_color(ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get());
+               show_velocity();
+       } else {
+               set_fill_color(note_fill_color(_note->velocity()));
+               set_outline_color(note_outline_color(_note->velocity()));
+               hide_velocity();
+       }
+
+       _selected = yn;
+}
+
+
+bool
+CanvasNoteEvent::on_event(GdkEvent* ev)
+{
+       MidiStreamView *streamview = _region.midi_stream_view();
+       static uint8_t drag_delta_note = 0;
+       static double  drag_delta_x = 0;
+       static double last_x, last_y;
+       double event_x, event_y, dx, dy;
+       bool select_mod;
+       uint8_t d_velocity = 10;
+
+       if (_region.get_time_axis_view().editor.current_mouse_mode() != Editing::MouseNote)
+               return false;
+
+       switch (ev->type) {
+       case GDK_SCROLL:
+               if (Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier)) {
+                       d_velocity = 1;
+               }
+
+               if(ev->scroll.direction == GDK_SCROLL_UP) {
+                       _region.note_selected(this, true);
+                       if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
+                               // TODO: absolute velocity
+                       } else {
+                               _region.change_velocity(d_velocity, true);
+                       }
+                       return true;
+               } else if(ev->scroll.direction == GDK_SCROLL_DOWN) {
+                       _region.note_selected(this, true);
+                       if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
+                               // TODO: absolute velocity
+                       } else {
+                               _region.change_velocity(-d_velocity, true);
+                       }
+                       return true;
+               } else {
+                       return false;
+               }
+               
+       case GDK_KEY_PRESS:
+               if (_note && ev->key.keyval == GDK_Delete) {
+                       selected(true);
+                       _region.start_delta_command();
+                       _region.command_remove_note(this);
+               }
+               break;
+
+       case GDK_KEY_RELEASE:
+               if (ev->key.keyval == GDK_Delete) {
+                       _region.apply_command();
+               }
+               break;
+
+       case GDK_ENTER_NOTIFY:
+               _region.note_entered(this);
+               _item->grab_focus();
+               show_velocity();
+               Keyboard::magic_widget_grab_focus();
+               break;
+
+       case GDK_LEAVE_NOTIFY:
+               Keyboard::magic_widget_drop_focus();
+               if(! selected()) {
+                       hide_velocity();
+               }
+               _region.get_canvas_group()->grab_focus();
+               break;
+
+       case GDK_BUTTON_PRESS:
+               if (ev->button.button == 1) {
+                       _state = Pressed;
+               } else if (ev->button.button == 3) {
+                       show_channel_selector();
+               }
+               return true;
+
+       case GDK_MOTION_NOTIFY:
+               event_x = ev->motion.x;
+               event_y = ev->motion.y;
+
+               switch (_state) {
+               case Pressed: // Drag begin
+                       if (_region.mouse_state() != MidiRegionView::SelectTouchDragging) {
+                               _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                                               Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
+                               _state = Dragging;
+                               _item->property_parent().get_value()->w2i(event_x, event_y);
+                               event_x = _region.snap_to_pixel(event_x); 
+                               last_x = event_x;
+                               last_y = event_y;
+                               drag_delta_x = 0;
+                               drag_delta_note = 0;
+                               _region.note_selected(this, true);
+                       }
+                       return true;
+
+               case Dragging: // Drag motion
+                       if (ev->motion.is_hint) {
+                               int t_x;
+                               int t_y;
+                               GdkModifierType state;
+                               gdk_window_get_pointer(ev->motion.window, &t_x, &t_y, &state);
+                               event_x = t_x;
+                               event_y = t_y;
+                       }
+                       _item->property_parent().get_value()->w2i(event_x, event_y);
+                       
+                       // Snap
+                       event_x = _region.snap_to_pixel(event_x); 
+
+                       dx = event_x - last_x;
+                       dy = event_y - last_y;
+
+                       last_x = event_x;
+
+                       drag_delta_x += dx;
+
+                       // Snap to note rows
+                       if (abs(dy) < streamview->note_height()) {
+                               dy = 0.0;
+                       } else {
+                               int8_t this_delta_note;
+                               if (dy > 0) {
+                                       this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
+                               } else {
+                                       this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
+                               }
+                               drag_delta_note -= this_delta_note;
+                               dy = streamview->note_height() * this_delta_note;
+                               last_y = last_y + dy;
+                       }
+
+                       _region.move_selection(dx, dy);
+
+                       return true;
+               default:
+                       break;
+               }
+               break;
+
+       case GDK_BUTTON_RELEASE:
+               select_mod = (ev->motion.state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
+               event_x = ev->button.x;
+               event_y = ev->button.y;
+               _item->property_parent().get_value()->w2i(event_x, event_y);
+               
+               if(ev->button.button == 3) {
+                       return true;
+               }
+               
+               switch (_state) {
+               case Pressed: // Clicked
+                       if (_region.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditSelect) {
+                               _state = None;
+
+                               if (_selected && !select_mod && _region.selection_size() > 1)
+                                       _region.unique_select(this);
+                               else if (_selected)
+                                       _region.note_deselected(this, select_mod);
+                               else
+                                       _region.note_selected(this, select_mod);
+                       } else if (_region.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditErase) {
+                               _region.start_delta_command();
+                               _region.command_remove_note(this);
+                               _region.apply_command();
+                       }
+
+                       return true;
+               case Dragging: // Dropped
+                       _item->ungrab(ev->button.time);
+                       _state = None;
+
+                       if (_note)
+                               _region.note_dropped(this,
+                                               _region.midi_view()->editor.pixel_to_frame(abs(drag_delta_x))
+                                                               * ((drag_delta_x < 0.0) ? -1 : 1),
+                                               drag_delta_note);
+                       return true;
+               default:
+                       break;
+               }
+
+       default:
+               break;
+       }
+
+       return false;
+}
+
+} // namespace Canvas
+} // namespace Gnome
+
diff --git a/gtk2_ardour/canvas-note-event.h b/gtk2_ardour/canvas-note-event.h
new file mode 100644 (file)
index 0000000..f575808
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+    Copyright (C) 2007 Paul Davis 
+    Author: Dave Robillard
+
+    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 __gtk_ardour_canvas_midi_event_h__
+#define __gtk_ardour_canvas_midi_event_h__
+
+#include "simplerect.h"
+#include "midi_channel_selector.h"
+#include <libgnomecanvasmm/text.h>
+#include <libgnomecanvasmm/widget.h>
+#include <ardour/midi_model.h>
+
+class Editor;
+class MidiRegionView;
+
+namespace Gnome {
+namespace Canvas {
+
+
+/** This manages all the event handling for any MIDI event on the canvas.
+ *
+ * This is not actually a canvas item itself to avoid the dreaded diamond,
+ * since various types of canvas items (Note (rect), Hit (diamond), etc)
+ * need to share this functionality but can't share an ancestor.
+ *
+ * Note: Because of this, derived classes need to manually bounce events to
+ * on_event, it won't happen automatically.
+ *
+ * A newer, better canvas should remove the need for all the ugly here.
+ */
+class CanvasNoteEvent : public sigc::trackable {
+public:
+       CanvasNoteEvent(
+                       MidiRegionView&                       region,
+                       Item*                                 item,
+                       const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>());
+
+       virtual ~CanvasNoteEvent();
+
+       bool on_event(GdkEvent* ev);
+
+       bool selected() const { return _selected; }
+       void selected(bool yn);
+
+       void move_event(double dx, double dy);
+       
+       void show_velocity();
+       void hide_velocity();
+       
+       /**
+        * This slot is called, when a new channel is selected for the single event
+        * */
+       void on_channel_change(uint8_t channel);
+       void on_channel_selection_change(uint16_t selection);
+       
+       void show_channel_selector();
+       
+       void hide_channel_selector();
+
+       virtual void set_outline_color(uint32_t c) = 0;
+       virtual void set_fill_color(uint32_t c) = 0;
+       
+       virtual double x1() = 0;
+       virtual double y1() = 0;
+       virtual double x2() = 0;
+       virtual double y2() = 0;
+
+       const boost::shared_ptr<ARDOUR::Note> note() { return _note; }
+
+protected:
+       enum State { None, Pressed, Dragging };
+
+       MidiRegionView&                       _region;
+       Item* const                           _item;
+       Text*                                 _text;
+       Widget*                               _channel_selector_widget;
+       State                                 _state;
+       const boost::shared_ptr<ARDOUR::Note> _note;
+       bool                                  _own_note;
+       bool                                  _selected;
+};
+
+} // namespace Gnome
+} // namespace Canvas
+
+#endif /* __gtk_ardour_canvas_midi_event_h__ */
index 8f745bcbc8bf2110ec397575dd02effc4aae18e8..de2d0ecb9d12792b7a13657362746eeac481d4c7 100644 (file)
@@ -23,7 +23,7 @@
 
 #include <iostream>
 #include "simplerect.h"
-#include "canvas-midi-event.h"
+#include "canvas-note-event.h"
 #include "midi_util.h"
 
 namespace Gnome {
index 5883dd5b86f97337c6b1ae6efcb864ccb10b59ca..103bd7bb8def95aeec5773aa6efd26bdd2362fbb 100644 (file)
@@ -1,7 +1,8 @@
 #include "canvas-program-change.h"
 #include <iostream>
+#include "ardour_ui.h"
 
-using namespace ArdourCanvas;
+using namespace Gnome::Canvas;
 using namespace std;
 
 CanvasProgramChange::CanvasProgramChange(
index cf99ca07157049ac472d81eb5a3d7b02e303ea31..37dc0cc8dae0227edf88e65525a50908b188f956 100644 (file)
@@ -4,15 +4,14 @@
 #include <libgnomecanvasmm/group.h>
 #include "simplerect.h"
 #include "simpleline.h"
-#include "midi_region_view.h"
 #include <libgnomecanvasmm/text.h>
 #include <libgnomecanvasmm/widget.h>
 #include <ardour/midi_model.h>
 
-namespace Gnome
-{
-namespace Canvas
-{
+class MidiRegionView;
+
+namespace Gnome {
+namespace Canvas {
 
 class CanvasProgramChange : public Group
 {
index 32544f4880e645510d26b99dc33e08ac1320c6d4..da344fed29a535708714c05ff5adf69323894dfd 100644 (file)
@@ -21,7 +21,7 @@
 #define __ardour_diamond_h__
 
 #include <libgnomecanvasmm/polygon.h>
-#include "canvas-midi-event.h"
+#include "canvas-note-event.h"
 
 namespace Gnome {
 namespace Canvas {
index 054be80bc67ebc74748a2fbffa5cabe202368e24..0da0bfc4878290e700c6f3a56531cf4413803917 100644 (file)
@@ -40,7 +40,7 @@
 #include "control_point.h"
 #include "canvas_impl.h"
 #include "simplerect.h"
-#include "canvas-midi-event.h"
+#include "canvas-note-event.h"
 
 #include "i18n.h"
 
index 09165bfcfdb5a6ff02a41a9bf84ae7495b75080a..d1b5f749900d158123f43b9d728736362eec87ee 100644 (file)
@@ -431,6 +431,7 @@ MidiRegionView::clear_events()
                delete *i;
 
        _events.clear();
+       _pgm_changes.clear();
 }
 
 
@@ -476,14 +477,8 @@ MidiRegionView::redisplay_model()
                }
 
                MidiModel::PgmChanges& pgm_changes = _model->pgm_changes();
-               /*
-               for (MidiModel::PgmChanges::const_iterator i = pgm_changes.begin(); 
-                    i != pgm_changes.end(); 
-                    ++i) {
-                       add_pgm_change()
-               }
-               */
-               for_each(pgm_changes.begin(), pgm_changes.end(), sigc::mem_fun(this, &MidiRegionView::add_pgm_change));
+               for_each(pgm_changes.begin(), pgm_changes.end(), 
+                       sigc::mem_fun(this, &MidiRegionView::add_pgm_change));
 
                end_write();
 
@@ -820,8 +815,9 @@ MidiRegionView::add_pgm_change(boost::shared_ptr<MIDI::Event> event)
        const double x = trackview.editor.frame_to_pixel((nframes_t)event->time() - _region->start());
        
        double height = midi_stream_view()->contents_height();
-       new CanvasProgramChange(*this, *group, event, height, x, 1.0);
-       //TODO : keep track of pgm changes
+       _pgm_changes.push_back(
+               boost::shared_ptr<CanvasProgramChange>(
+                       new CanvasProgramChange(*this, *group, event, height, x, 1.0)));
 }
 
 void
index 7c545fa82af5aa22abc187d99cb9805442a7c290..1a37205bef3a5b54d7ef8626a47c76894a5ef783 100644 (file)
@@ -36,7 +36,7 @@
 #include "enums.h"
 #include "canvas.h"
 #include "canvas-note.h"
-#include "canvas-midi-event.h"
+#include "canvas-note-event.h"
 #include "canvas-program-change.h"
 
 namespace ARDOUR {
@@ -254,6 +254,7 @@ class MidiRegionView : public RegionView
 
        boost::shared_ptr<ARDOUR::MidiModel>        _model;
        std::vector<ArdourCanvas::CanvasNoteEvent*> _events;
+       std::vector< boost::shared_ptr<ArdourCanvas::CanvasProgramChange> > _pgm_changes;
        ArdourCanvas::CanvasNote**                  _active_notes;
        ArdourCanvas::Group*                        _note_group;
        ARDOUR::MidiModel::DeltaCommand*            _delta_command;
index 495991853adf5b3d813db2d9671e20a20c655405..23dff7f165b7070384383612eabd2777605c19b1 100644 (file)
@@ -362,11 +362,13 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes
                assert(_read_iter->size() > 0);
                dst.write(_read_iter->time() + stamp_offset - negative_stamp_offset, _read_iter->size(), _read_iter->buffer());
                
+               /*
                cerr << this << " MidiModel::read event @ " << _read_iter->time()  
                     << " type: " << hex << int(_read_iter->type()) << dec 
                     << " note: " << int(_read_iter->note()) 
                     << " velocity: " << int(_read_iter->velocity()) 
                     << endl;
+               */
                
                ++_read_iter;
                ++read_events;
@@ -573,7 +575,7 @@ MidiModel::append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t
 void
 MidiModel::append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number) 
 {
-       cerr << "MidiModel::append_pgm_change_unlocked: channel " << int(chan) << " time: " << time << " program number: " << int(number) <<endl; 
+       //cerr << "MidiModel::append_pgm_change_unlocked: channel " << int(chan) << " time: " << time << " program number: " << int(number) <<endl; 
        assert(chan < 16);
        assert(_writing);
        _edited = true;
@@ -603,6 +605,9 @@ MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note)
        for(Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) {
                Note& _n = *(*n);
                const Note& _note = *note;
+               // TODO: There is still the issue, that after restarting ardour
+               // persisted undo does not work, because of rounding errors in the
+               // event times after saving/restoring to/from MIDI files
                cerr << "======================================= " << endl;
                cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity())  << endl;
                cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity())  << endl;