+ bool changed = false;
+
+ if (_scale_root != original_root) {
+ _scale_root = original_root;
+ changed = true;
+ }
+ if (_root_octave != octave) {
+ _root_octave = octave;
+ changed = true;
+ }
+ if (_in_key != inkey) {
+ _in_key = inkey;
+ changed = true;
+ }
+ if (_mode != mode) {
+ _mode = mode;
+ changed = true;
+ }
+
+ if (changed) {
+ ScaleChange (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Push2::set_percussive_mode (bool yn)
+{
+ if (!yn) {
+ cerr << "back to scale\n";
+ set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
+ percussion = false;
+ return;
+ }
+
+ int drum_note = 36;
+
+ fn_pad_map.clear ();
+
+ for (int row = 0; row < 8; ++row) {
+
+ for (int col = 0; col < 4; ++col) {
+
+ int index = 36 + (row*8) + col;
+ Pad* pad = nn_pad_map[index];
+
+ pad->filtered = drum_note;
+ drum_note++;
+ }
+ }
+
+ for (int row = 0; row < 8; ++row) {
+
+ for (int col = 4; col < 8; ++col) {
+
+ int index = 36 + (row*8) + col;
+ Pad* pad = nn_pad_map[index];
+
+ pad->filtered = drum_note;
+ drum_note++;
+ }
+ }
+
+ percussion = true;
+}
+
+Push2Layout*
+Push2::current_layout () const
+{
+ Glib::Threads::Mutex::Lock lm (layout_lock);
+ return _current_layout;
+}
+
+void
+Push2::stripable_selection_change (StripableNotificationListPtr selected)
+{
+ boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
+ boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
+ boost::shared_ptr<MidiTrack> new_pad_target;
+
+ /* See if there's a MIDI track selected */
+
+ for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
+
+ new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
+
+ if (new_pad_target) {
+ break;
+ }
+ }
+
+ if (current_midi_track == new_pad_target) {
+ /* nothing to do */
+ return;
+ }
+
+ if (!new_pad_target) {
+ /* leave existing connection alone */
+ return;
+ }
+
+ /* disconnect from pad port, if appropriate */
+
+ if (current_midi_track && pad_port) {
+
+ /* XXX this could possibly leave dangling MIDI notes.
+ *
+ * A general libardour fix is required. It isn't obvious
+ * how note resolution can be done unless disconnecting
+ * becomes "slow" (i.e. deferred for as long as it takes
+ * to resolve notes).
+ */
+ current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
+ }
+
+ /* now connect the pad port to this (newly) selected midi
+ * track, if indeed there is one.
+ */
+
+ if (new_pad_target && pad_port) {
+ new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
+ current_pad_target = new_pad_target;
+ selection_color = get_color_index (new_pad_target->presentation_info().color());
+ contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
+ } else {
+ current_pad_target.reset ();
+ selection_color = LED::Green;
+ contrast_color = LED::Green;
+ }
+
+ reset_pad_colors ();
+}
+
+Push2::Button*
+Push2::button_by_id (ButtonID bid)
+{
+ return id_button_map[bid];
+}
+
+uint8_t
+Push2::get_color_index (ArdourCanvas::Color rgba)
+{
+ ColorMap::iterator i = color_map.find (rgba);
+
+ if (i != color_map.end()) {
+ return i->second;
+ }
+
+ double dr, dg, db, da;
+ int r, g, b;
+ ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
+ int w = 126; /* not sure where/when we should get this value */
+
+
+ r = (int) floor (255.0 * dr);
+ g = (int) floor (255.0 * dg);
+ b = (int) floor (255.0 * db);
+
+ /* get a free index */
+
+ uint8_t index;
+
+ if (color_map_free_list.empty()) {
+ /* random replacement of any entry above zero and below 122 (where the
+ * Ableton standard colors live)
+ */
+ index = 1 + (random() % 121);
+ } else {
+ index = color_map_free_list.top();
+ color_map_free_list.pop();
+ }
+
+ MidiByteArray palette_msg (17,
+ 0xf0,
+ 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
+ 0x00, /* index = 7 */
+ 0x00, 0x00, /* r = 8 & 9 */
+ 0x00, 0x00, /* g = 10 & 11 */
+ 0x00, 0x00, /* b = 12 & 13 */
+ 0x00, 0x00, /* w (a?) = 14 & 15*/
+ 0xf7);
+ palette_msg[7] = index;
+ palette_msg[8] = r & 0x7f;
+ palette_msg[9] = (r & 0x80) >> 7;
+ palette_msg[10] = g & 0x7f;
+ palette_msg[11] = (g & 0x80) >> 7;
+ palette_msg[12] = b & 0x7f;
+ palette_msg[13] = (b & 0x80) >> 7;
+ palette_msg[14] = w & 0x7f;
+ palette_msg[15] = w & 0x80;
+
+ write (palette_msg);
+
+ MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
+ write (update_pallette_msg);
+
+ color_map[rgba] = index;
+
+ return index;
+}
+
+void
+Push2::build_color_map ()
+{
+ /* These are "standard" colors that Ableton docs suggest will always be
+ there. Put them in our color map so that when we look up these
+ colors, we will use the Ableton indices for them.
+ */
+
+ color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
+ color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
+ color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
+ color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
+ color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
+ color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
+ color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
+
+ for (uint8_t n = 1; n < 122; ++n) {
+ color_map_free_list.push (n);
+ }
+}
+
+void
+Push2::fill_color_table ()
+{
+ colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
+ colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
+
+ colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
+
+ colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
+ colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
+ colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
+
+ colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
+ colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
+
+ colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
+ colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
+ colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
+ colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
+
+}
+
+ArdourCanvas::Color
+Push2::get_color (ColorName name)
+{
+ Colors::iterator c = colors.find (name);
+ if (c != colors.end()) {
+ return c->second;
+ }
+
+ return random();
+}
+
+void
+Push2::set_current_layout (Push2Layout* layout)
+{
+ if (layout && layout == _current_layout) {
+ _current_layout->show ();
+ } else {
+
+ if (_current_layout) {
+ _current_layout->hide ();
+ _canvas->root()->remove (_current_layout);
+ _previous_layout = _current_layout;
+ }
+
+ _current_layout = layout;
+
+ if (_current_layout) {
+ _canvas->root()->add (_current_layout);
+ _current_layout->show ();
+ }
+
+
+ _canvas->request_redraw ();
+ }
+}
+
+void
+Push2::use_previous_layout ()
+{
+ if (_previous_layout) {
+ set_current_layout (_previous_layout);
+ }
+}
+
+void
+Push2::request_pressure_mode ()
+{
+ MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
+ write (msg);
+}
+
+void
+Push2::set_pressure_mode (PressureMode pm)
+{
+ MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
+
+ switch (pm) {
+ case AfterTouch:
+ /* nothing to do, message is correct */
+ break;
+ case PolyPressure:
+ msg[7] = 0x1;
+ break;
+ default:
+ return;
+ }
+
+ write (msg);
+ cerr << "Sent PM message " << msg << endl;