X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fcanvas-note-event.cc;h=94b675b22466990c126da1ddf1b52b07afd81e7f;hb=5932b7903a7c43cc3b5cfd043e64d577e0125799;hp=c370530cfcc123ff1e19a0ee4b37c132d302651e;hpb=975d4109305c1e2b8dfece2d2a8798b90469f2fd;p=ardour.git diff --git a/gtk2_ardour/canvas-note-event.cc b/gtk2_ardour/canvas-note-event.cc index c370530cfc..94b675b224 100644 --- a/gtk2_ardour/canvas-note-event.cc +++ b/gtk2_ardour/canvas-note-event.cc @@ -1,6 +1,6 @@ /* Copyright (C) 2007 Paul Davis - Author: Dave Robillard + Author: David 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 @@ -18,6 +18,9 @@ */ #include + +#include "gtkmm2ext/keyboard.h" + #include "canvas-note-event.h" #include "midi_region_view.h" #include "public_editor.h" @@ -25,14 +28,23 @@ #include "keyboard.h" using namespace std; +using namespace Gtkmm2ext; using ARDOUR::MidiModel; namespace Gnome { namespace Canvas { +PBD::Signal1 CanvasNoteEvent::CanvasNoteEventDeleted; -CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item, - const boost::shared_ptr note) +/// dividing the hue circle in 16 parts, hand adjusted for equal look, courtesy Thorsten Wilms +const uint32_t CanvasNoteEvent::midi_channel_colors[16] = { + 0xd32d2dff, 0xd36b2dff, 0xd3972dff, 0xd3d12dff, + 0xa0d32dff, 0x7dd32dff, 0x2dd45eff, 0x2dd3c4ff, + 0x2da5d3ff, 0x2d6fd3ff, 0x432dd3ff, 0x662dd3ff, + 0x832dd3ff, 0xa92dd3ff, 0xd32dbfff, 0xd32d67ff + }; + +CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item, const boost::shared_ptr note) : _region(region) , _item(item) , _text(0) @@ -40,75 +52,79 @@ CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item, , _state(None) , _note(note) , _selected(false) + , _valid (true) + , _mouse_x_fraction (-1.0) + , _mouse_y_fraction (-1.0) + , _channel_selection (0xffff) { } -CanvasNoteEvent::~CanvasNoteEvent() -{ +CanvasNoteEvent::~CanvasNoteEvent() +{ + CanvasNoteEventDeleted (this); + if (_text) { _text->hide(); delete _text; } - - if (_channel_selector_widget) - delete _channel_selector_widget; + + delete _channel_selector_widget; } -void -CanvasNoteEvent::move_event(double dx, double dy) +void +CanvasNoteEvent::invalidate () { - _item->move(dx, dy); - if (_text) { - _text->hide(); - _text->move(dx, dy); - _text->show(); - } + _valid = false; +} + +void +CanvasNoteEvent::validate () +{ + _valid = true; } void -CanvasNoteEvent::show_velocity(void) +CanvasNoteEvent::show_velocity() { - hide_velocity(); - _text = new Text(*(_item->property_parent())); + if (!_text) { + _text = new NoEventText (*(_item->property_parent())); + _text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiNoteVelocityText.get(); + _text->property_justification() = Gtk::JUSTIFY_CENTER; + } + _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); + _text->raise_to_top(); } void -CanvasNoteEvent::hide_velocity(void) +CanvasNoteEvent::hide_velocity() { - if(_text) { + if (_text) { _text->hide(); delete _text; + _text = 0; } - _text = 0; } -void +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 the color according to the notes selection state - selected(_selected); - } + _channel_selection = selection; + + /* this takes into account whether or not the note should be drawn as inactive */ + set_selected (_selected); + // this forces the item to update..... maybe slow... _item->hide(); _item->show(); } -void +void CanvasNoteEvent::on_channel_change(uint8_t channel) { _region.note_selected(this, true); @@ -120,23 +136,32 @@ void CanvasNoteEvent::show_channel_selector(void) { if (_channel_selector_widget == 0) { - cerr << "Note has channel: " << int(_note->channel()) << endl; + + if(_region.channel_selector_scoped_note() != 0){ + _region.channel_selector_scoped_note()->hide_channel_selector(); + _region.set_channel_selector_scoped_note(0); + } + 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->clicked.connect ( + sigc::mem_fun (this, &CanvasNoteEvent::hide_channel_selector)); + + _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(); + + _region.set_channel_selector_scoped_note(this); } else { hide_channel_selector(); } @@ -153,209 +178,176 @@ CanvasNoteEvent::hide_channel_selector(void) } void -CanvasNoteEvent::selected(bool yn) +CanvasNoteEvent::set_selected(bool selected) { 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; -} + _selected = selected; + bool const active = (_channel_selection & (1 << _note->channel())) != 0; -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; + if (_selected && active) { + set_outline_color(calculate_outline(ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get())); - switch (ev->type) { - case GDK_SCROLL: - if (Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier)) { - d_velocity = 1; + if(_region.channel_selector_scoped_note() != 0){ + _region.channel_selector_scoped_note()->hide_channel_selector(); + _region.set_channel_selector_scoped_note(0); } - 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; + set_fill_color (base_color ()); + + } else { + + if (active) { + set_fill_color(base_color()); + set_outline_color(calculate_outline(base_color())); } else { - return false; + set_fill_color(ARDOUR_UI::config()->canvasvar_MidiNoteInactiveChannel.get()); + set_outline_color(calculate_outline(ARDOUR_UI::config()->canvasvar_MidiNoteInactiveChannel.get())); } - case GDK_KEY_PRESS: - if (_note && ev->key.keyval == GDK_Delete) { - selected(true); - _region.start_delta_command(); - _region.command_remove_note(this); - } - break; + hide_channel_selector(); + } +} - case GDK_KEY_RELEASE: - if (ev->key.keyval == GDK_Delete) { - _region.apply_command(); - } +#define SCALE_USHORT_TO_UINT8_T(x) ((x) / 257) + +uint32_t +CanvasNoteEvent::base_color() +{ + using namespace ARDOUR; + + ColorMode mode = _region.color_mode(); + + const uint8_t min_opacity = 15; + uint8_t opacity = std::max(min_opacity, uint8_t(_note->velocity() + _note->velocity())); + + switch (mode) { + case TrackColor: + { + Gdk::Color color = _region.midi_stream_view()->get_region_color(); + return UINT_INTERPOLATE (RGBA_TO_UINT( + SCALE_USHORT_TO_UINT8_T(color.get_red()), + SCALE_USHORT_TO_UINT8_T(color.get_green()), + SCALE_USHORT_TO_UINT8_T(color.get_blue()), + opacity), + ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(), 0.5); + } + + case ChannelColors: + return UINT_INTERPOLATE (UINT_RGBA_CHANGE_A (CanvasNoteEvent::midi_channel_colors[_note->channel()], + opacity), + ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(), 0.5); + + default: + return meter_style_fill_color(_note->velocity(), selected()); + }; + + return 0; +} + +void +CanvasNoteEvent::set_mouse_fractions (GdkEvent* ev) +{ + double ix, iy; + double bx1, bx2, by1, by2; + bool set_cursor = false; + + switch (ev->type) { + case GDK_MOTION_NOTIFY: + ix = ev->motion.x; + iy = ev->motion.y; + set_cursor = true; + break; + case GDK_ENTER_NOTIFY: + ix = ev->crossing.x; + iy = ev->crossing.y; + set_cursor = true; break; + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + ix = ev->button.x; + iy = ev->button.y; + break; + default: + _mouse_x_fraction = -1.0; + _mouse_y_fraction = -1.0; + return; + } + + _item->get_bounds (bx1, by1, bx2, by2); + _item->w2i (ix, iy); + /* hmm, something wrong here. w2i should give item-local coordinates + but it doesn't. for now, finesse this. + */ + ix = ix - bx1; + iy = iy - by1; + + /* fraction of width/height */ + double xf; + double yf; + bool notify = false; + + xf = ix / (bx2 - bx1); + yf = iy / (by2 - by1); + + if (xf != _mouse_x_fraction || yf != _mouse_y_fraction) { + notify = true; + } + + _mouse_x_fraction = xf; + _mouse_y_fraction = yf; + + if (notify) { + if (big_enough_to_trim()) { + _region.note_mouse_position (_mouse_x_fraction, _mouse_y_fraction, set_cursor); + } else { + /* pretend the mouse is in the middle, because this is not big enough + to trim right now. + */ + _region.note_mouse_position (0.5, 0.5, set_cursor); + } + } +} + +bool +CanvasNoteEvent::on_event(GdkEvent* ev) +{ + if (!_region.get_time_axis_view().editor().internal_editing()) { + return false; + } + switch (ev->type) { case GDK_ENTER_NOTIFY: - _region.note_entered(this); - _item->grab_focus(); - show_velocity(); - Keyboard::magic_widget_grab_focus(); + set_mouse_fractions (ev); + _region.note_entered (this); break; case GDK_LEAVE_NOTIFY: - Keyboard::magic_widget_drop_focus(); - if (! selected()) { - hide_velocity(); - } - _region.get_canvas_group()->grab_focus(); + set_mouse_fractions (ev); + _region.note_left (this); 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.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditSelect - && _region.mouse_state() != MidiRegionView::SelectTouchDragging - && _region.mouse_state() != MidiRegionView::EraseTouchDragging) { - _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); + set_mouse_fractions (ev); + break; + case GDK_BUTTON_PRESS: + set_mouse_fractions (ev); + if (ev->button.button == 3 && Keyboard::no_modifiers_active (ev->button.state) && _selected) { + show_channel_selector(); 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) { + set_mouse_fractions (ev); + if (ev->button.button == 3 && Keyboard::no_modifiers_active (ev->button.state)) { 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; - } + break; default: break; @@ -364,6 +356,19 @@ CanvasNoteEvent::on_event(GdkEvent* ev) return false; } +bool +CanvasNoteEvent::mouse_near_ends () const +{ + return (_mouse_x_fraction >= 0.0 && _mouse_x_fraction < 0.25) || + (_mouse_x_fraction >= 0.75 && _mouse_x_fraction < 1.0); +} + +bool +CanvasNoteEvent::big_enough_to_trim () const +{ + return (x2() - x1()) > 20; /* canvas units, really pixels */ +} + } // namespace Canvas } // namespace Gnome