Index: gtk2_ardour/editor_canvas_events.cc
===================================================================
--- gtk2_ardour/editor_canvas_events.cc	(Revision 3197)
+++ gtk2_ardour/editor_canvas_events.cc	(Arbeitskopie)
@@ -40,6 +40,7 @@
 #include "control_point.h"
 #include "canvas_impl.h"
 #include "simplerect.h"
+#include "canvas-midi-event.h"
 
 #include "i18n.h"
 
@@ -48,6 +49,7 @@
 using namespace ARDOUR;
 using namespace PBD;
 using namespace Gtk;
+using namespace ArdourCanvas;
 
 bool
 Editor::track_canvas_scroll (GdkEventScroll* ev)
@@ -56,6 +58,7 @@
 	double wx, wy;
 	nframes_t xdelta;
 	int direction = ev->direction;
+	CanvasMidiEvent *midi_event = dynamic_cast<CanvasMidiEvent *>(track_canvas->get_item_at(ev->x, ev->y));
 
   retry:
 	switch (direction) {
@@ -94,6 +97,9 @@
 			current_stepping_trackview->step_height (true);
 			return true;
 		} else {
+			if(midi_event) {
+				return midi_event->on_event(reinterpret_cast<GdkEvent *>(ev));
+			}
 			scroll_tracks_up_line ();
 			return true;
 		}
@@ -129,6 +135,9 @@
 			current_stepping_trackview->step_height (false);
 			return true;
 		} else {
+			if(midi_event) {
+				return midi_event->on_event(reinterpret_cast<GdkEvent *>(ev));
+			}
 			scroll_tracks_down_line ();
 			return true;
 		}
Index: gtk2_ardour/midi_util.h
===================================================================
--- gtk2_ardour/midi_util.h	(Revision 3197)
+++ gtk2_ardour/midi_util.h	(Arbeitskopie)
@@ -54,5 +54,14 @@
 	}
 }
 
+inline static void clamp_0_to_127(uint8_t &val)
+{
+	if( (127 < val) && (val < 192) ) {
+		val = 127;
+	} else if( (192 <= val) && (val < 255) ) {
+		val = 0;
+	} 
+}
+
 #endif /* __gtk_ardour_midi_util_h__ */
 
Index: gtk2_ardour/canvas-midi-event.cc
===================================================================
--- gtk2_ardour/canvas-midi-event.cc	(Revision 3197)
+++ gtk2_ardour/canvas-midi-event.cc	(Arbeitskopie)
@@ -35,25 +35,56 @@
 		const boost::shared_ptr<ARDOUR::Note> note)
 	: _region(region)
 	, _item(item)
+	, _text(0)
 	, _state(None)
 	, _note(note)
 	, _selected(false)
 {
+	_text = new Text(*(item->property_parent()));
 }
 
+void 
+CanvasMidiEvent::move_event(double dx, double dy)
+{
+	_item->move(dx, dy);
+	_text->move(dx, dy);
+}
 
 void
+CanvasMidiEvent::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
+CanvasMidiEvent::hide_velocity(void)
+{
+	_text->hide();
+}
+
+void
 CanvasMidiEvent::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.85));
+					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;
@@ -70,11 +101,38 @@
 	double event_x, event_y, dx, dy;
 	nframes_t event_frame;
 	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);
@@ -92,11 +150,15 @@
 	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;
 
Index: gtk2_ardour/midi_region_view.h
===================================================================
--- gtk2_ardour/midi_region_view.h	(Revision 3197)
+++ gtk2_ardour/midi_region_view.h	(Arbeitskopie)
@@ -103,6 +103,7 @@
 
 	void command_remove_note(ArdourCanvas::CanvasMidiEvent* ev) {
 		if (_delta_command && ev->note()) {
+			_selection.erase(ev);
 			_delta_command->remove(ev->note());
 			ev->selected(true);
 		}
@@ -162,6 +163,14 @@
 	 */
 	void commit_resizing(ArdourCanvas::CanvasNote::NoteEnd note_end, double event_x, bool relative);
 
+	/**
+	 * This function is called while the user adjusts the velocity on a selection of notes
+	 * @param velocity the relative or absolute velocity, dependin on the value of relative
+	 * @param relative true if the given velocity represents a delta to be applied to all notes, false
+	 *        if the absolute value of the note shoud be set
+	 */
+	void change_velocity(uint8_t velocity, bool relative=false);
+
 	enum MouseState { None, Pressed, SelectTouchDragging, SelectRectDragging, AddDragging, EraseTouchDragging };
 	MouseState mouse_state() const { return _mouse_state; }
 
@@ -221,9 +230,17 @@
 	MouseState _mouse_state;
 	int _pressed_button;
 
+	/// currently selected CanvasMidiEvents
 	typedef std::set<ArdourCanvas::CanvasMidiEvent*> Selection;
 	Selection _selection;
 
+	/**
+	 * this enables vanilla notes to be marked for selection
+	 * they are added to _selection when redisplay_model is called
+	 * this is necessary for selecting notes during/after model manipulations 
+	 */
+	std::set<ARDOUR::Note *> _marked_for_selection;
+
 	std::vector<NoteResizeData *> _resize_data;
 };
 
Index: gtk2_ardour/canvas-midi-event.h
===================================================================
--- gtk2_ardour/canvas-midi-event.h	(Revision 3197)
+++ gtk2_ardour/canvas-midi-event.h	(Arbeitskopie)
@@ -21,6 +21,7 @@
 #define __gtk_ardour_canvas_midi_event_h__
 
 #include "simplerect.h"
+#include <libgnomecanvasmm/text.h>
 #include <ardour/midi_model.h>
 
 class Editor;
@@ -48,13 +49,18 @@
 			Item*                                 item,
 			const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>());
 
-	virtual ~CanvasMidiEvent() {}
+	virtual ~CanvasMidiEvent() { if(_text) delete _text; }
 
 	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();
+
 	virtual void set_outline_color(uint32_t c) = 0;
 	virtual void set_fill_color(uint32_t c) = 0;
 	
@@ -63,9 +69,6 @@
 	virtual double x2() = 0;
 	virtual double y2() = 0;
 
-	const Item* item() const { return _item; }
-	Item*       item()       { return _item; }
-
 	const boost::shared_ptr<ARDOUR::Note> note() { return _note; }
 
 protected:
@@ -73,6 +76,7 @@
 
 	MidiRegionView&                       _region;
 	Item* const                           _item;
+	Text* 		                          _text;
 	State                                 _state;
 	const boost::shared_ptr<ARDOUR::Note> _note;
 	bool                                  _own_note;
Index: gtk2_ardour/midi_region_view.cc
===================================================================
--- gtk2_ardour/midi_region_view.cc	(Revision 3197)
+++ gtk2_ardour/midi_region_view.cc	(Arbeitskopie)
@@ -423,8 +423,22 @@
 
 		_model->read_lock();
 
-		for (size_t i=0; i < _model->n_notes(); ++i)
+		/*
+		MidiModel::Notes notes = _model->notes();
+		cerr << endl << "Model contains " << notes.size() << " Notes:" << endl;
+		for(MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
+			Note note = *(*i).get();
+			cerr << "MODEL: Note time: " << note.time() << " duration: " << note.duration() 
+			     << "   end-time: " << note.end_time() 
+			     << "   velocity: " << int(note.velocity()) 
+			     //<< " Note-on: " << note.on_event(). 
+			     //<< " Note-off: " << note.off_event() 
+			     << endl;
+		}*/
+		
+		for (size_t i=0; i < _model->n_notes(); ++i) {
 			add_note(_model->note_at(i));
+		}
 
 		end_write();
 
@@ -538,11 +552,15 @@
 						note->show();
 					}
 
+					note->hide_velocity();
 					note->property_y1() = y1;
 					note->property_y2() = y2;
+					if(note->selected()) {
+						note->show_velocity();
 				}
 			}
 		}
+		}
 
 		_model->read_unlock();
 	}
@@ -609,6 +627,7 @@
 {
 	delete[] _active_notes;
 	_active_notes = NULL;
+	_marked_for_selection.clear();
 }
 
 
@@ -657,6 +676,8 @@
 
 	ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
 
+	CanvasMidiEvent *event = 0;
+	
 	if (midi_view()->note_mode() == Sustained) {
 
 		//cerr << "MRV::add_note sustained " << note->note() << " @ " << note->time()
@@ -687,6 +708,7 @@
 
 		ev_rect->show();
 		_events.push_back(ev_rect);
+		event = ev_rect;
 
 		MidiGhostRegion* gr;
 
@@ -711,9 +733,19 @@
 		ev_diamond->property_fill_color_rgba() = note_fill_color(note->velocity());
 		ev_diamond->property_outline_color_rgba() = note_outline_color(note->velocity());
 		_events.push_back(ev_diamond);
+		event = ev_diamond;
+	} else {
+		event = 0;
+	}
 
+	if(event) {
+		Note *note = event->note().get();
+		
+		if(_marked_for_selection.find(note) != _marked_for_selection.end()) {
+			note_selected(event, true);
 	}
 }
+}
 
 void
 MidiRegionView::delete_selection()
@@ -813,7 +845,7 @@
 MidiRegionView::move_selection(double dx, double dy)
 {
 	for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i)
-		(*i)->item()->move(dx, dy);
+		(*i)->move_event(dx, dy);
 }
 
 
@@ -851,7 +883,6 @@
 		// Make sure the note pitch does not exceed the MIDI standard range
 		if (dnote <= 127 && (highest_note_in_selection + dnote > 127)) {
 			highest_note_difference = highest_note_in_selection - 127;
-			cerr << "Highest note difference: " << (int) highest_note_difference;
 		}
 		
 		start_delta_command();
@@ -868,9 +899,15 @@
 				copy->set_time(0);
 			}
 
-			uint8_t new_pitch = (*i)->note()->note() + dnote - highest_note_difference;
-			if(new_pitch > 127) {
-				new_pitch = 127;
+			uint8_t original_pitch = (*i)->note()->note();
+			uint8_t new_pitch =  original_pitch + dnote - highest_note_difference;
+			
+			// keep notes in standard midi range
+			clamp_0_to_127(new_pitch);
+			
+			//notes which are dragged beyond the standard midi range snap back to their original place
+			if((original_pitch != 0 && new_pitch == 0) || (original_pitch != 127 && new_pitch == 127)) {
+				new_pitch = original_pitch;
 			}
 
 			lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
@@ -880,19 +917,16 @@
 			
 			command_add_note(copy);
 
-			_selection.erase(i);
+			_marked_for_selection.insert(copy.get());
 			i = next;
 		}
 				
 		apply_command();
 		
-		//cerr << "new lowest note (selection): "  << int(lowest_note_in_selection) << " new highest note(selection): " << int(highest_note_in_selection) << endl;
-
 		// care about notes being moved beyond the upper/lower bounds on the canvas
 		if(lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
 		   highest_note_in_selection > midi_stream_view()->highest_note()
 		) {
-			//cerr << "resetting note range" << endl;
 			midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
 	}
 }
@@ -1031,12 +1065,15 @@
 			command_remove_note(canvas_note);
 			copy->on_event().time() = current_frame;
 			command_add_note(copy);
+			_marked_for_selection.insert(copy.get());
 		}
 		// resize end of note
 		if (note_end == CanvasNote::NOTE_OFF && current_frame > copy->time()) {
 			command_remove_note(canvas_note);
+			command_remove_note(canvas_note);
 			copy->off_event().time() = current_frame;
 			command_add_note(copy);
+			_marked_for_selection.insert(copy.get());
 		}
 
 		delete resize_rect;
@@ -1045,10 +1082,46 @@
 
 	_resize_data.clear();
 	apply_command();
-	clear_selection();
 }
 
+
 void
+MidiRegionView::change_velocity(uint8_t velocity, bool relative)
+{
+	start_delta_command();
+	for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
+		Selection::iterator next = i;
+		++next;
+
+		CanvasMidiEvent *event = *i;
+		const boost::shared_ptr<Note> copy(new Note(*(event->note().get())));
+
+		if(relative) {
+			uint8_t new_velocity = copy->velocity() + velocity;
+			clamp_0_to_127(new_velocity);
+				
+			copy->set_velocity(new_velocity);
+		} else { // absolute
+			copy->set_velocity(velocity);			
+		}
+		
+		command_remove_note(event);
+		command_add_note(copy);
+		
+		_marked_for_selection.insert(copy.get());
+		i = next;
+	}
+	
+	// dont keep notes selected if tweaking a single note
+	if(_marked_for_selection.size() == 1) {
+		_marked_for_selection.clear();
+	}
+	
+	apply_command();
+}
+
+
+void
 MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev)
 {
 	if (ev->note() && _mouse_state == EraseTouchDragging) {
