/*
* Created on Jun 14, 2007
*
* Copyright (c) 2006-2007 P.J.Leonard
*
* http://www.frinika.com
*
* This file is part of Frinika.
*
* Frinika 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.
* Frinika 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 Frinika; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.frinika.sequencer.gui.pianoroll;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.frinika.sequencer.gui.Layout;
import com.frinika.sequencer.gui.selection.SelectionContainer;
import com.frinika.sequencer.gui.selection.SelectionListener;
import com.frinika.sequencer.model.MidiLane;
import com.frinika.sequencer.model.MidiPart;
import com.frinika.sequencer.model.Part;
public class PadPanel extends JPanel implements MouseListener,
AdjustmentListener, SelectionListener<Part> {
private static final long serialVersionUID = 1L;
final int ON = 0, OFF = 1;
final int nNote = 127;
protected Key keys[] = new Key[nNote];
// Config config = null;
Key prevKey;
int lastKeyPress = 0;
PianoRoll pianoRoll;
int timePanelHeight;
final int keyDepth = 50;
int yScroll;
int yBot;
PadPanelIF padIF;
PadPanelIF pianoPad;
PadPanelIF drumPad;
MidiLane midiLane;
// public PadPanel(Config config) {
// super(false);
// this.yScroll = 0;
// this.config = config;
// this.pianoRoll = null;
// this.timePanelHeight = 0;
// addMouseListener(this);
// }
public PadPanel(PianoRoll pr, int top, int scroll) {
this.pianoRoll = pr;
this.timePanelHeight = top;
this.yScroll = scroll;
addMouseListener(this);
pianoPad = new VirtualPiano();
drumPad = new DrumPad();
yBot = 129 * Layout.getNoteItemHeight();
setSize(new Dimension(keyDepth, yBot));
setPreferredSize(new Dimension(keyDepth, yBot));
setMaximumSize(new Dimension(keyDepth, 20000));
}
public Key createkey(int x, int y, int width, int height, int num) {
return new Key(x, y, width, height, num);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (padIF != null) {
padIF.paintComponent(g);
}
}
public class Key extends Rectangle {
/**
*
*/
private static final long serialVersionUID = 1L;
Receiver recv = null;
int chan = 0;
int noteState = OFF;
int kNum;
public Key(int x, int y, int width, int height, int num) {
super(x, y, width, height);
kNum = num;
}
public boolean isNoteOn() {
return noteState == ON;
}
public void on() {
setNoteState(ON);
if (pianoRoll == null)
return;
// Part focusPart = pianoRoll.getProjectContainer().getPartSelection()
// .getFocus();
// if (focusPart == null || !(focusPart instanceof MidiPart))
// return;
// midiLane = ((MidiLane) (focusPart.getLane()));
recv = midiLane.getReceiver();
chan = midiLane.getMidiChannel();
if (recv == null)
return;
ShortMessage shm = new ShortMessage();
int kk = midiLane.mapNote(kNum);
try {
shm.setMessage(ShortMessage.NOTE_ON, chan, kk, 100);
} catch (InvalidMidiDataException e) {
e.printStackTrace();
}
recv.send(shm, -1);
}
public void off() {
setNoteState(OFF);
if (recv == null)
return;
ShortMessage shm = new ShortMessage();
int kk = midiLane.mapNote(kNum);
try {
shm.setMessage(ShortMessage.NOTE_ON, chan, kk, 0);
} catch (InvalidMidiDataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
recv.send(shm, -1);
recv = null;
}
public void setNoteState(int state) {
noteState = state;
if (state == ON)
lastKeyPress = kNum;
}
}// End class Key
public void adjustmentValueChanged(AdjustmentEvent e) {
yScroll = e.getValue();
repaint();
}
public int getLastKeytPress() {
return lastKeyPress;
}
public Key getKey(int i) {
return keys[i];
}
protected Key getKey(Point p) {
return padIF.getKey(p);
}
public void mousePressed(MouseEvent e) {
if (padIF == null) return;
prevKey = getKey(e.getPoint());
if (prevKey != null) {
prevKey.on();
repaint();
}
if (e.getButton() == MouseEvent.BUTTON3) {
if (midiLane.isDrumLane()) {
popupMapper(e.getX(), e.getY(), prevKey);
}
}
}
void popupMapper(int x, int y, final Key key) {
// JPopupMenu menu = new JPopupMenu();
//
// Font plain = getGraphics().getFont();
// Font small = new Font(plain.getFamily(), Font.PLAIN, 9);
// menu.setFont(small);
// int count = 0;
String[] keyNames = midiLane.getKeyNames();
if (keyNames == null ) return;
class XXX {
String name;
int key;
public XXX(int i, String string) {
key = i;
name = string;
}
public String toString() {
return name;
}
}
;
Vector<XXX> vec = new Vector<XXX>();
XXX xx = null;
XXX zz = null;
for (int i = 0; i < keyNames.length; i++) {
if (keyNames[i] != null)
vec.add(0, zz = new XXX(i, keyNames[i]));
if (i == key.kNum)
xx = zz;
}
final JList list = new JList(vec);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectedValue(xx, true);
list.addListSelectionListener(new ListSelectionListener() {
int zzz = -1;
public void valueChanged(ListSelectionEvent e) {
int jj = ((XXX) list.getSelectedValue()).key;
midiLane.setDrumMapping(key.kNum, jj);
if (zzz != jj) {
key.on();
} else {
key.off();
}
zzz = jj;
}
});
JScrollPane listScroller = new JScrollPane(list);
listScroller.setPreferredSize(new Dimension(200, 800));
JFrame frame = new JFrame();
frame.setContentPane(listScroller);
frame.pack();
frame.setVisible(true);
}
public void mouseReleased(MouseEvent e) {
if (prevKey != null) {
prevKey.off();
repaint();
}
}
public void mouseExited(MouseEvent e) {
if (prevKey != null) {
prevKey.off();
repaint();
prevKey = null;
}
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
interface PadPanelIF {
void resizeKeys();
void paintComponent(Graphics g);
Key getKey(Point point);
}
class DrumPad implements PadPanelIF {
/**
*
*/
private static final long serialVersionUID = 1L;
int noteItemHeight;
public void resizeKeys() {
noteItemHeight = Layout.getNoteItemHeight();
yBot = (nNote + 1) * noteItemHeight;
int y = 0;
for (int j = 0; j < nNote; j++, y += noteItemHeight) {
keys[j] = createkey(0, yBot - y, keyDepth, noteItemHeight, j);
}
validate();
}
public void paintComponent(Graphics g) {
if (noteItemHeight != Layout.getNoteItemHeight())
resizeKeys();
String keyNames[] = midiLane.getKeyNames();
Font plain = g.getFont();
Font small = new Font(plain.getFamily(), Font.PLAIN, 9);
Graphics2D g2 = (Graphics2D) g;
g2.setFont(small);
g2.translate(0, timePanelHeight - yScroll);
g2.setColor(MY_DRUM_COLOR);
g2.fillRect(0, 0, keyDepth, yBot);
for (int i = 0; i < keys.length; i++) {
// if (keyNames != null)
// System.out.println(keyNames[i]);
Key key = keys[i];
if (key.isNoteOn()) {
g2.setColor(MY_KEYDOWN_COLOR);
g2.fill(key);
}
int kk = midiLane.mapNote(i);
g2.setColor(Color.black);
g2.draw(key);
if (keyNames != null && kk < keyNames.length
&& keyNames[kk] != null) {
if (kk != i)
g2.setColor(Color.red);
g2.drawString(keyNames[kk], key.x + 2, key.y
+ noteItemHeight - 2);
}
}
g2.translate(0, -(timePanelHeight - yScroll));
}
public Key getKey(Point point) {
point.translate(0, -timePanelHeight + yScroll);
for (Key key : keys) {
if (key == null)
continue;
if (key.contains(point)) {
return key;
}
}
return null;
}
}
static Color MY_PIANO_COLOR = new Color(0xffeeee);
static Color MY_DRUM_COLOR = new Color(0xeeeeff);
static Color MY_KEYDOWN_COLOR = new Color(0xaaaaaa);
class VirtualPiano implements PadPanelIF {
//
// final Color jfcBlue = new Color(204, 204, 255);
//
// final Color pink = new Color(255, 175, 175);
Vector<Key> blackKeys = new Vector<Key>();
Vector<Key> whiteKeys = new Vector<Key>();
final int nWhiteNote = (nNote / 12) * 7 + 5;
final int nOctave = nNote / 12 + 1;
int whiteKeyWidth, blackKeyDepth = (int) (keyDepth * .6);
int blackWhiteGap;
int noteItemHeight;
public void resizeKeys() {
blackKeys.clear();
whiteKeys.clear();
noteItemHeight = Layout.getNoteItemHeight();
// keyDepth=30;
this.whiteKeyWidth = (noteItemHeight * 12) / 7;
this.blackWhiteGap = whiteKeyWidth / 3;
int whiteIDs[] = { 0, 2, 4, 5, 7, 9, 11 };
// yBot = (((nWhiteNote +2) *
// whiteKeyWidth)/noteItemHeight)*noteItemHeight-whiteKeyWidth;
yBot = 129 * noteItemHeight - whiteKeyWidth;
// / You will be the size I want . . . .
// setSize(new Dimension(keyDepth, yBot));
// // setMinimumSize(new Dimension(keyDepth, yBot));
// setPreferredSize(new Dimension(keyDepth, yBot));
// setMaximumSize(new Dimension(keyDepth, 20000));
for (int i = 0, y = 0; i < nOctave; i++) {
for (int j = 0; j < 7; j++, y += whiteKeyWidth) {
int keyNum = i * 12 + whiteIDs[j];
if (keyNum >= nNote)
break;
whiteKeys.add(new Key(0, yBot - y, keyDepth, whiteKeyWidth,
keyNum));
}
}
int halfGap = blackWhiteGap / 2;
int yBot1 = yBot + 7 * whiteKeyWidth / 8;
int blackKeyWidth = 2 * whiteKeyWidth / 3;
for (int i = 0, y = 0; i < nOctave; i++, y += whiteKeyWidth) {
int keyNum = i * 12;
if (keyNum >= nNote - 1)
break;
blackKeys.add(new Key(0, yBot1 - (y += whiteKeyWidth) - halfGap
/ 2, blackKeyDepth, blackKeyWidth, keyNum + 1));
if (keyNum >= nNote - 3)
break;
blackKeys
.add(new Key(0, yBot1 - (y += whiteKeyWidth) - 3
* halfGap / 2, blackKeyDepth, blackKeyWidth,
keyNum + 3));
if (keyNum >= nNote - 6)
break;
y += whiteKeyWidth;
blackKeys.add(new Key(0, yBot1 - (y += whiteKeyWidth) - halfGap
/ 2 + 1, blackKeyDepth, blackKeyWidth, keyNum + 6));
if (keyNum >= nNote - 8)
break;
blackKeys.add(new Key(0,
yBot1 - (y += whiteKeyWidth) - halfGap, blackKeyDepth,
blackKeyWidth, keyNum + 8));
if (keyNum >= nNote - 10)
break;
blackKeys.add(new Key(0, yBot1 - (y += whiteKeyWidth) - 3
* halfGap / 2 - 1, blackKeyDepth, blackKeyWidth,
keyNum + 10));
}
for (Key key : blackKeys) {
keys[key.kNum] = key;
}
for (Key key : whiteKeys) {
keys[key.kNum] = key;
}
// validate();
}
public void paintComponent(Graphics g) {
if (noteItemHeight != Layout.getNoteItemHeight())
// || (config != null && noteItemHeight != config.noteHeight))
resizeKeys();
Graphics2D g2 = (Graphics2D) g;
g2.translate(0, timePanelHeight - yScroll);
g2.setColor(MY_PIANO_COLOR);
g2.fillRect(0, 0, keyDepth, yBot);
for (int i = 0; i < whiteKeys.size(); i++) {
Key key = (Key) whiteKeys.get(i);
if (key.isNoteOn()) {
// || (config != null && key.kNum == lastKeyPress)) {
g2.setColor(MY_KEYDOWN_COLOR);
g2.fill(key);
}
g2.setColor(Color.black);
g2.draw(key);
if (key.kNum % 12 == 0) {
// g2.setColor(Color.red);
//
// g2.fill(key);
g2.setColor(Color.BLACK);
g2.drawString(String.valueOf(key.kNum / 12), keyDepth - 15,
key.y + key.height - 1);
// g2.setPaintMode();
}
}
for (int i = 0; i < blackKeys.size(); i++) {
Key key = (Key) blackKeys.get(i);
if (key.isNoteOn()) {
// || (config != null && key.kNum == lastKeyPress)) {
g2.setColor(MY_KEYDOWN_COLOR);
g2.fill(key);
g2.setColor(Color.black);
g2.draw(key);
} else {
g2.setColor(Color.black);
g2.fill(key);
}
}
g2.translate(0, -(timePanelHeight - yScroll));
// Thread.yield();
}
public Key getKey(Point point) {
point.translate(0, -timePanelHeight + yScroll);
for (Key key : blackKeys) {
if (key == null)
continue;
if (key.contains(point)) {
return key;
}
}
for (Key key : whiteKeys) {
if (key == null)
continue;
if (key.contains(point)) {
return key;
}
}
return null;
}
}
public void selectionChanged(SelectionContainer<? extends Part> src) {
Part focus = pianoRoll.getProjectContainer().getPartSelection().getFocus();
if (focus == null)
return;
if (!(focus instanceof MidiPart))
return;
// System.out.println("PadPanel focus change ");
midiLane = (MidiLane) focus.getLane();
if (midiLane.isDrumLane()) {
if (padIF != drumPad) {
padIF=drumPad;
padIF.resizeKeys();
}
} else {
if (padIF != pianoPad) {
padIF=pianoPad;
padIF.resizeKeys();
}
}
validate();
repaint();
}
}