*/
public static class VersionInfo {
public static final String NAME = "MIDI Chord Helper";
- public static final String VERSION = "Ver.20160505.1";
+ public static final String VERSION = "Ver.20160506.1";
public static final String COPYRIGHT = "Copyright (C) 2004-2016";
public static final String AUTHER = "@きよし - Akiyoshi Kamide";
public static final String URL = "http://www.yk.rim.or.jp/~kamide/music/chordhelper/";
import java.util.Hashtable;
import java.util.List;
+import javax.sound.midi.MidiDevice;
import javax.sound.midi.Receiver;
import javax.sound.midi.Transmitter;
import javax.swing.JComponent;
-import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
import javax.swing.event.InternalFrameAdapter;
}
};
/**
- * ドラッグ&ドロップの終了時にこのメソッドを呼び出します。
+ * ドラッグ&ドロップの終了時に必要な再描画を行います。
*/
public void dragDropEnd() { draggingPoint = null; repaint(); }
/**
- * {@link MidiDeviceFrame} ã\81®ç§»å\8b\95ã\82\84å¤\89å½¢ã\82\92ç\9b£è¦\96ã\81\97ã\81¦再描画するためのリスナー
+ * {@link MidiDeviceFrame} ã\81\8c移å\8b\95ã\81¾ã\81\9fã\81¯å¤\89å½¢ã\81\97ã\81\9fã\81¨ã\81\8dã\81«ã\82±ã\83¼ã\83\96ã\83«ã\82\92再描画するためのリスナー
*/
public final ComponentListener midiDeviceFrameComponentListener = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) { repaint(); }
};
/**
- * {@link MidiDeviceFrame} ã\81\8cé\96\89ã\81\98ã\81\9fã\82¿ã\82¤ã\83\9fã\83³ã\82°ã\81§再描画するためのリスナー
+ * {@link MidiDeviceFrame} ã\81\8cé\96\89ã\81\98ã\81\9fã\81¨ã\81\8dã\81«ã\82±ã\83¼ã\83\96ã\83«ã\82\92再描画するためのリスナー
*/
public final InternalFrameListener midiDeviceFrameListener = new InternalFrameAdapter() {
@Override
public void internalFrameClosing(InternalFrameEvent e) {
JInternalFrame frame = e.getInternalFrame();
if( ! (frame instanceof MidiDeviceFrame) ) return;
- MidiDeviceFrame devFrame = (MidiDeviceFrame)frame;
- MidiConnecterListModel devModel = devFrame.getMidiConnecterListView().getModel();
- if( devModel.rxSupported() ) {
- colorMap.remove(devModel.getMidiDevice().getReceivers().get(0));
- }
+ MidiDeviceFrame f = (MidiDeviceFrame)frame;
+ List<Receiver> rxList = f.getMidiConnecterListView().getModel().getMidiDevice().getReceivers();
+ for( Receiver rx : rxList ) colorMap.remove(rx);
repaint();
}
};
/**
* {@link MidiConnecterListModel} における {@link Transmitter}
- * の増減や状態変更があった場合に再描画するためのリスナー
+ * の増減や状態変更があった場合にケーブルを再描画するためのリスナー
*/
public final ListDataListener midiConnecterListDataListener = new ListDataListener() {
@Override
public void intervalRemoved(ListDataEvent e) { repaint(); }
};
- private JDesktopPane desktopPane;
- public MidiCablePane(JDesktopPane desktopPane) {
+ private MidiOpenedDevicesView desktopPane;
+ public MidiCablePane(MidiOpenedDevicesView desktopPane) {
this.desktopPane = desktopPane;
setOpaque(false);
setVisible(true);
new Color(0,191,191,144),
new Color(191,0,191,144),
};
+ private static final Color ADDING_CABLE_COLOR = new Color(0, 0, 0, 144);
+ private static final Color REMOVING_CABLE_COLOR = new Color(128, 128, 128, 144);
private int nextColorIndex = 0;
private Hashtable<Receiver,Color> colorMap = new Hashtable<>();
+ private Color colorOf(Receiver rx) {
+ Color color = colorMap.get(rx);
+ if( color == null ) {
+ colorMap.put(rx, color=CABLE_COLORS[nextColorIndex++]);
+ if( nextColorIndex >= CABLE_COLORS.length ) nextColorIndex = 0;
+ }
+ return color;
+ }
@Override
public void paint(Graphics g) {
super.paint(g);
JInternalFrame[] frames = desktopPane.getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER);
for( JInternalFrame frame : frames ) {
if( ! (frame instanceof MidiDeviceFrame) ) continue;
- MidiDeviceFrame txDeviceFrame = (MidiDeviceFrame)frame;
- MidiConnecterListView txView = txDeviceFrame.getMidiConnecterListView();
- Transmitter draggingTx = txView.getDraggingTransmitter();
- List<Transmitter> txList = txView.getModel().getMidiDevice().getTransmitters();
- for( Transmitter tx : txList ) {
- //
- // 送信端子の場所を特定
- Rectangle txRect = txView.getCellBounds(tx);
- if( txRect == null ) continue;
- txRect.translate(
- txDeviceFrame.getRootPane().getX() +
- txDeviceFrame.getContentPane().getX() + txDeviceFrame.getX(),
- txDeviceFrame.getRootPane().getY() +
- txDeviceFrame.getContentPane().getY() + txDeviceFrame.getY()
- );
- int d = txRect.height - 5;
- int r = d / 2;
- int fromX = txRect.x + r + 4;
- int fromY = txRect.y + r + 4;
- //
- // ドラッグ中であれば、マウスカーソルのある所まで線を引く
- if( tx.equals(draggingTx) && draggingPoint != null ) {
- g2.setColor(Color.BLACK);
- g2.drawLine(fromX, fromY, draggingPoint.x, draggingPoint.y);
- continue;
+ MidiDeviceFrame fromFrame = (MidiDeviceFrame)frame;
+ MidiConnecterListView fromView = fromFrame.getMidiConnecterListView();
+ MidiDevice fromDevice = fromView.getModel().getMidiDevice();
+ if( draggingPoint != null ) {
+ // Receiverからドラッグされた線を描画
+ List<Receiver> rxList = fromDevice.getReceivers();
+ for( Receiver rx : rxList ) {
+ if( ! rx.equals(fromView.getDraggingTransceiver()) ) continue;
+ Rectangle rxBounds = fromFrame.getBoundsOf(rx);
+ if( rxBounds == null ) continue;
+ int r = (rxBounds.height - 5) / 2;
+ rxBounds.translate(r+4, r+4);
+ g2.setColor(colorOf(rx));
+ g2.drawLine(rxBounds.x, rxBounds.y, draggingPoint.x, draggingPoint.y);
+ break;
}
- // 受信端子の場所を特定
+ }
+ List<Transmitter> txList = fromDevice.getTransmitters();
+ for( Transmitter tx : txList ) {
+ // Transmitterの場所を特定
+ Rectangle txBounds = fromFrame.getBoundsOf(tx);
+ if( txBounds == null ) continue;
+ int r = (txBounds.height - 5) / 2;
+ txBounds.translate(r+4, r+4);
+ AutoCloseable draggingTrx = fromView.getDraggingTransceiver();
Receiver rx = tx.getReceiver();
- if( rx == null ) continue;
- Rectangle rxRect = null;
- for( JInternalFrame anotherFrame : frames ) {
- if( ! (anotherFrame instanceof MidiDeviceFrame) ) continue;
- MidiDeviceFrame rxDeviceFrame = (MidiDeviceFrame)anotherFrame;
- rxRect = rxDeviceFrame.getMidiConnecterListView().getCellBounds(rx);
- if( rxRect == null ) continue;
- rxRect.translate(
- rxDeviceFrame.getRootPane().getX() +
- rxDeviceFrame.getContentPane().getX() + rxDeviceFrame.getX(),
- rxDeviceFrame.getRootPane().getY() +
- rxDeviceFrame.getContentPane().getY() + rxDeviceFrame.getY()
- );
- break;
+ if( draggingPoint != null && tx.equals(draggingTrx) ) {
+ // Transmitterからドラッグされた線を描画
+ g2.setColor(rx == null ? ADDING_CABLE_COLOR : colorOf(rx));
+ g2.drawLine(txBounds.x, txBounds.y, draggingPoint.x, draggingPoint.y);
}
- if( rxRect == null ) continue;
- //
- // 受信端子まで線を引く
- Color color = colorMap.get(rx);
- if( color == null ) {
- colorMap.put(rx, color=CABLE_COLORS[nextColorIndex++]);
- if( nextColorIndex >= CABLE_COLORS.length ) nextColorIndex = 0;
+ // TransmitterからReceiverへの接続線を描画
+ if( rx != null ) for( JInternalFrame toFrame : frames ) {
+ if( ! (toFrame instanceof MidiDeviceFrame) ) continue;
+ Rectangle rxBounds = ((MidiDeviceFrame)toFrame).getBoundsOf(rx);
+ if( rxBounds == null ) continue;
+ r = (rxBounds.height - 5) / 2;
+ rxBounds.translate(r+4, r+4);
+ g2.setColor(draggingTrx == tx ? REMOVING_CABLE_COLOR : colorOf(rx));
+ g2.drawLine(txBounds.x, txBounds.y, rxBounds.x, rxBounds.y);
+ break;
}
- g2.setColor(color);
- d = rxRect.height - 5;
- r = d / 2;
- int toX = rxRect.x + r + 4;
- int toY = rxRect.y + r + 4;
- g2.drawLine(fromX, fromY, toX, toY);
}
}
}
import java.util.Vector;
import javax.sound.midi.MidiDevice;
-import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Transmitter;
import javax.swing.AbstractListModel;
+import javax.swing.tree.TreePath;
/**
- * 1個の MIDI デバイスに属する Transmitter/Receiver のリストモデル
+ * 1個の{@link MidiDevice}で開かれている{@link Transmitter}/{@link Receiver}のリストモデル
*/
public class MidiConnecterListModel extends AbstractListModel<AutoCloseable> {
protected MidiDevice device;
- private List<MidiConnecterListModel> modelList;
+ private MidiDeviceModelList deviceModelList;
+ private MidiDeviceInOutType ioType;
+ private TreePath treePath;
/**
- * 実体のない新規Transmitterを表すインターフェース
+ * 実体のない新規{@link Transmitter}を表すインターフェース
*/
public interface NewTransmitter extends Transmitter {};
public NewTransmitter newTransmitter;
* 指定のMIDIデバイスに属する {@link Transmitter}/{@link Receiver} のリストモデルを構築します。
*
* @param device 対象MIDIデバイス
- * @param modelList リストモデルを格納している親リスト
+ * @param deviceModelList 接続相手となりうるMIDIデバイスのリスト
*/
- public MidiConnecterListModel(MidiDevice device, List<MidiConnecterListModel> modelList) {
+ public MidiConnecterListModel(MidiDevice device, MidiDeviceModelList deviceModelList) {
this.device = device;
- this.modelList = modelList;
+ this.deviceModelList = deviceModelList;
if( txSupported() ) {
newTransmitter = new NewTransmitter() {
@Override
public void close() { }
};
}
+ ioType = rxSupported() ?
+ (txSupported() ? MidiDeviceInOutType.MIDI_IN_OUT : MidiDeviceInOutType.MIDI_OUT) :
+ (txSupported() ? MidiDeviceInOutType.MIDI_IN : MidiDeviceInOutType.MIDI_NONE);
+ treePath = new TreePath(new Object[] {MidiDeviceModelList.TITLE, ioType ,this});
}
/**
* 対象MIDIデバイスを返します。
*/
public MidiDevice getMidiDevice() { return device; }
/**
+ * {@link javax.swing.JTree}で使用するツリー表示のパスを返します。
+ * @return ツリーパス
+ */
+ public TreePath getTreePath() { return treePath; }
+ /**
* 対象MIDIデバイスの名前を返します。
*/
@Override
/**
* このリストのMIDIデバイスの入出力タイプを返します。
* @return このリストのMIDIデバイスの入出力タイプ
- * <ul>
- * <li>MIDI_OUT: {@link Receiver}から受けた{@link MidiEvent}を音源や画面に出力するデバイス</li>
- * <li>MIDI_IN: キーボードやシーケンサから入力した{@link MidiEvent}を{@link Transmitter}から{@link Receiver}へ転送するデバイス</li>
- * <li>MIDI_IN_OUT: 上記両方の機能をサポートしたデバイス</li>
- * <li>MIDI_NONE: 上記両方ともサポートしていないデバイス</li>
- * </ul>
*/
- public MidiDeviceInOutType getMidiDeviceInOutType() {
- if( rxSupported() ) {
- if( txSupported() )
- return MidiDeviceInOutType.MIDI_IN_OUT;
- else
- return MidiDeviceInOutType.MIDI_OUT;
- }
- else {
- if( txSupported() )
- return MidiDeviceInOutType.MIDI_IN;
- else
- return MidiDeviceInOutType.MIDI_NONE;
- }
- }
- /**
- * 引数で指定されたトランスミッタをレシーバを接続します。
- * @param tx トランスミッタ
- * @param rx レシーバ
- * @return 接続されたレシーバ(このリストにないレシーバが指定された場合はnull)
- */
- public Receiver ConnectToReceiver(Transmitter tx, Receiver rx) {
- if( ! device.getReceivers().contains(rx) ) return null;
- tx.setReceiver(rx);
- fireContentsChanged(this,0,getSize());
- return rx;
- }
+ public MidiDeviceInOutType getMidiDeviceInOutType() { return ioType; }
/**
- * 未接続のトランスミッタを、
- * 引数で指定されたリストモデルの最初のレシーバに接続します。
- * @param anotherModel 接続先レシーバを持つリストモデル
+ * 未接続の{@link Transmitter}を、引数で指定されたリストモデルの最初の{@link Receiver}に接続します。
+ * @param anotherModel 接続可能な{@link Receiver}を持つリストモデル
*/
public void connectToReceiverOf(MidiConnecterListModel anotherModel) {
if( ! txSupported() || anotherModel == null || ! anotherModel.rxSupported() ) return;
openTransmitter().setReceiver(rxList.get(0));
}
/**
- * レシーバに未接続の最初のトランスミッタを返します。
+ * レシーバに未接続の最初の{@link Transmitter}を開いて返します。
* ない場合は {@link MidiDevice#getTransmitter} で新たに取得して返します。
*
- * @return æ\9cªæ\8e¥ç¶\9aã\81®ã\83\88ã\83©ã\83³ã\82¹ã\83\9fã\83\83ã\82¿
+ * @return æ\96°ã\81\97ã\81\8fé\96\8bã\81\8bã\82\8cã\81\9fæ\9cªæ\8e¥ç¶\9aã\81®{@link Transmitter}
*/
public Transmitter openTransmitter() {
if( ! txSupported() ) return null;
return tx;
}
/**
- * 指定のトランスミッタを閉じます。
- * このリストモデルにないトランスミッタが指定された場合、無視されます。
- * @param txToClose 閉じたいトランスミッタ
+ * このリストモデルで開いている指定の{@link Transmitter}があれば、
+ * それを閉じて表示を更新します。
+ * ない場合は無視されます。
+ * @param txToClose このリストモデルで開いている{@link Transmitter}
*/
public void closeTransmitter(Transmitter txToClose) {
if( ! device.getTransmitters().contains(txToClose) ) return;
public void closeDevice() {
if( rxSupported() ) {
Receiver rx = device.getReceivers().get(0);
- for( MidiConnecterListModel m : modelList ) {
+ for( MidiConnecterListModel m : deviceModelList ) {
if( m == this || ! m.txSupported() ) continue;
for( int i=0; i<m.getSize(); i++ ) {
AutoCloseable ac = m.getElementAt(i);
if( rxSupported() ) {
rx = device.getReceivers().get(0);
peerTxList = new Vector<Transmitter>();
- for( MidiConnecterListModel m : modelList ) {
+ for( MidiConnecterListModel m : deviceModelList ) {
if( m == this || ! m.txSupported() ) continue;
for( int i=0; i<m.getSize(); i++ ) {
Object obj = m.getElementAt(i);
package camidion.chordhelper.mididevice;
import java.awt.Component;
-import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
+import java.util.Arrays;
+import java.util.List;
import javax.sound.midi.Receiver;
import javax.sound.midi.Transmitter;
* </p>
*/
public class MidiConnecterListView extends JList<AutoCloseable> {
+
public static final Icon MIDI_CONNECTER_ICON = new ButtonIcon(ButtonIcon.MIDI_CONNECTOR_ICON);
/**
* リストに登録されている仮想MIDI端子の描画ツール
}
private static final DataFlavor transmitterFlavor = new DataFlavor(Transmitter.class, "Transmitter");
+ private static final DataFlavor receiverFlavor = new DataFlavor(Receiver.class, "Receiver");
/**
* ドラッグ対象を表すクラス
*/
private static class DraggingObject implements Transferable {
- private static final DataFlavor flavors[] = {transmitterFlavor};
- private Transmitter tx;
+ private static final List<DataFlavor> flavors = Arrays.asList(transmitterFlavor, receiverFlavor);
+ private AutoCloseable trx;
@Override
- public Object getTransferData(DataFlavor flavor) { return tx; }
+ public Object getTransferData(DataFlavor flavor) {
+ return flavor.getRepresentationClass().isInstance(trx) ? trx : null;
+ }
@Override
- public DataFlavor[] getTransferDataFlavors() { return flavors; }
+ public DataFlavor[] getTransferDataFlavors() { return (DataFlavor[]) flavors.toArray(); }
@Override
- public boolean isDataFlavorSupported(DataFlavor flavor) {
- return flavor.equals(transmitterFlavor);
- }
+ public boolean isDataFlavorSupported(DataFlavor flavor) { return flavors.contains(flavor); }
};
private static DraggingObject draggingObject = new DraggingObject();
/**
- * 現在ドラッグされているトランスミッタを返します。
- * @return 現在ドラッグされているトランスミッタ(ドラッグ中でなければnull)
+ * 現在ドラッグされている{@link Transmitter}または{@link Receiver}を返します。
+ * @return 現在ドラッグされている{@link Transmitter}または{@link Receiver}
+ * (ドラッグ中でなければnull)
*/
- public Transmitter getDraggingTransmitter() { return draggingObject.tx; }
+ public AutoCloseable getDraggingTransceiver() { return draggingObject.trx; }
private MidiCablePane cablePane;
+
private DragSourceListener dragSourceListener = new DragSourceAdapter() {
@Override
public void dragDropEnd(DragSourceDropEvent dsde) {
- if( ! dsde.getDropSuccess() ) getModel().closeTransmitter(getDraggingTransmitter());
- draggingObject.tx = null;
+ if( draggingObject.trx instanceof Transmitter && ! dsde.getDropSuccess() ) {
+ getModel().closeTransmitter((Transmitter)draggingObject.trx);
+ }
+ draggingObject.trx = null;
cablePane.dragDropEnd();
}
};
-
/**
* 仮想MIDI端子リストビューを生成します。
* @param model このビューから参照されるデータモデル
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
if( (dge.getDragAction() & DnDConstants.ACTION_COPY_OR_MOVE) == 0 ) return;
- Point dragStartPoint = dge.getDragOrigin();
- AutoCloseable transceiver = getModel().getElementAt(locationToIndex(dragStartPoint));
- if( transceiver instanceof Transmitter ) {
- if( transceiver instanceof MidiConnecterListModel.NewTransmitter ) {
- draggingObject.tx = getModel().openTransmitter();
- }
- else {
- draggingObject.tx = (Transmitter)transceiver;
- }
- dge.startDrag(DragSource.DefaultLinkDrop, draggingObject, dragSourceListener);
+ MidiConnecterListModel m = getModel();
+ AutoCloseable source = m.getElementAt(locationToIndex(dge.getDragOrigin()));
+ if( source instanceof MidiConnecterListModel.NewTransmitter ) {
+ draggingObject.trx = m.openTransmitter();
+ } else if( source instanceof Transmitter || source instanceof Receiver ) {
+ draggingObject.trx = source;
+ } else {
+ return;
}
+ dge.startDrag(DragSource.DefaultLinkDrop, draggingObject, dragSourceListener);
}
}
);
dragSource.addDragSourceMotionListener(cablePane.midiConnecterMotionListener);
- DropTargetAdapter dta= new DropTargetAdapter() {
+ DropTargetAdapter dta = new DropTargetAdapter() {
@Override
public void dragEnter(DropTargetDragEvent event) {
- if( event.isDataFlavorSupported(transmitterFlavor) )
+ if( event.isDataFlavorSupported(transmitterFlavor) || event.isDataFlavorSupported(receiverFlavor) ) {
event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
+ }
}
@Override
public void drop(DropTargetDropEvent event) {
event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
try {
int maskedBits = event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE;
- if( maskedBits == 0 || ! getModel().rxSupported() ) {
+ if( maskedBits == 0 ) {
event.dropComplete(false);
return;
}
- int index = locationToIndex(event.getLocation());
- AutoCloseable destination = getModel().getElementAt(index);
- if( ! (destination instanceof Receiver) ) {
- event.dropComplete(false);
- return;
- }
- Object source = event.getTransferable().getTransferData(transmitterFlavor);
- if( ! (source instanceof Transmitter) ) {
- event.dropComplete(false);
- return;
- }
- if( getModel().ConnectToReceiver((Transmitter)source, (Receiver)destination) == null ) {
- event.dropComplete(false);
- return;
+ MidiConnecterListModel m = getModel();
+ AutoCloseable destination = m.getElementAt(locationToIndex(event.getLocation()));
+ Transferable t = event.getTransferable();
+ Object source;
+ if( (source = t.getTransferData(transmitterFlavor)) != null ) {
+ if( destination instanceof Receiver ) {
+ ((Transmitter)source).setReceiver((Receiver)destination);
+ event.dropComplete(true);
+ return;
+ }
+ } else if( (source = t.getTransferData(receiverFlavor)) != null ) {
+ if( destination instanceof Transmitter ) {
+ Transmitter tx = (Transmitter)destination;
+ if( tx instanceof MidiConnecterListModel.NewTransmitter ) {
+ tx = m.openTransmitter();
+ }
+ tx.setReceiver((Receiver)source);
+ event.dropComplete(true);
+ return;
+ }
}
- event.dropComplete(true);
}
catch (Exception ex) {
ex.printStackTrace();
- event.dropComplete(false);
}
+ event.dropComplete(false);
}
};
new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dta, true );
@Override
public MidiConnecterListModel getModel() { return (MidiConnecterListModel)super.getModel(); }
/**
- * 指定されたMIDI端子(Transmitter または Receiver)が仮想MIDI端子リスト上にあれば、
- * そのセル範囲の矩形を返します。
- *
- * @param transceiver MIDI端子
- * @return セル範囲の矩形(ない場合はnull)
+ * 引数で指定された{@link Transmitter}または{@link Receiver}のセル範囲を示す、
+ * リストの座標系内の境界の矩形を返します。対応するセルがない場合はnullを返します。
+ * @param transceiver {@link Transmitter}または{@link Receiver}
+ * @return セル範囲を示す境界の矩形、またはnull
*/
public Rectangle getCellBounds(AutoCloseable transceiver) {
int index = getModel().indexOf(transceiver);
- Rectangle rect = getCellBounds(index,index);
- return rect == null ? null : rect;
+ return getCellBounds(index,index);
}
}
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
-import java.beans.PropertyVetoException;
-import java.util.List;
import javax.sound.midi.MidiDevice;
import javax.swing.JDialog;
/**
* MIDIデバイスダイアログ (View)
*/
-public class MidiDeviceDialog extends JDialog implements ActionListener
-{
- private MidiDeviceTreeView deviceTree;
+public class MidiDeviceDialog extends JDialog implements ActionListener {
private JEditorPane deviceInfoPane;
private MidiOpenedDevicesView desktopPane;
@Override
public void actionPerformed(ActionEvent event) { setVisible(true); }
-
- public MidiDeviceDialog(List<MidiConnecterListModel> deviceModelList) {
+ public MidiDeviceDialog(MidiDeviceModelList deviceModelList) {
setTitle("MIDI device connection");
setBounds( 300, 300, 800, 500 );
- deviceTree = new MidiDeviceTreeView(new MidiDeviceTreeModel(deviceModelList)) {{
+ MidiDeviceTreeView deviceTree = new MidiDeviceTreeView(new MidiDeviceTreeModel(deviceModelList)) {{
addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
- Object lastSelected = deviceTree.getLastSelectedPathComponent();
String html = "<html><head></head><body>";
+ Object lastSelected = e.getNewLeadSelectionPath().getLastPathComponent();
if( lastSelected instanceof MidiConnecterListModel ) {
MidiConnecterListModel deviceModel = (MidiConnecterListModel)lastSelected;
MidiDevice.Info info = deviceModel.getMidiDevice().getDeviceInfo();
+ "<tr><th>Description</th><td>"+info.getDescription()+"</td></tr>"
+ "<tr><th>Vendor</th><td>"+info.getVendor()+"</td></tr>"
+ "</tbody></table>";
- MidiDeviceFrame deviceFrame = desktopPane.getMidiDeviceFrameOf(deviceModel);
- if( deviceFrame != null ) {
- try {
- deviceFrame.setSelected(true);
- } catch( PropertyVetoException ex ) {
- ex.printStackTrace();
- }
- }
}
else if( lastSelected instanceof MidiDeviceInOutType ) {
MidiDeviceInOutType ioType = (MidiDeviceInOutType)lastSelected;
package camidion.chordhelper.mididevice;
+import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.sound.midi.MidiDevice;
+import javax.sound.midi.Receiver;
+import javax.sound.midi.Transmitter;
import javax.swing.BoxLayout;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
-import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;
import javax.swing.event.InternalFrameAdapter;
public class MidiDeviceFrame extends JInternalFrame {
private MidiConnecterListView listView;
+ private JScrollPane scrollPane;
+ private Timer timer;
+
/**
* このデバイスフレームに貼り付けられた仮想MIDI端子リストビューを取得します。
* @return 仮想MIDI端子リストビュー
*/
public MidiConnecterListView getMidiConnecterListView() { return listView; }
- private Timer timer;
/**
* このデバイスフレームのタイムスタンプ表示更新用タイマーを取得します。
* @return タイムスタンプ表示更新用タイマー
public MidiDeviceFrame(MidiConnecterListModel model, MidiCablePane cablePane) {
super( null, true, true, false, false );
listView = new MidiConnecterListView(model, cablePane);
+ addInternalFrameListener(cablePane.midiDeviceFrameListener);
+ addComponentListener(cablePane.midiDeviceFrameComponentListener);
setTitle("[" + model.getMidiDeviceInOutType().getShortName() + "] " + model);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addInternalFrameListener(new InternalFrameAdapter() {
setVisible(m.getMidiDevice().isOpen());
}
});
- setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
- add(new JScrollPane(listView));
- add(new JPanel() {{
- add(new JLabel() {{
- timer = new Timer(50, new ActionListener() {
- private long sec = -2;
- private MidiDevice dev = listView.getModel().getMidiDevice();
- @Override
- public void actionPerformed(ActionEvent e) {
- long usec = dev.getMicrosecondPosition();
- long sec = (usec == -1 ? -1 : usec/1000000);
- if( sec == this.sec ) return;
- this.sec = sec;
- setText(sec == -1?"No TimeStamp":String.format("TimeStamp: %02d:%02d",sec/60,sec%60));
- }
- });
- }});
+ add(new JLabel("--:--") {{
+ timer = new Timer(50, new ActionListener() {
+ private long sec = -2;
+ private MidiDevice dev = listView.getModel().getMidiDevice();
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ long usec = dev.getMicrosecondPosition();
+ long sec = (usec == -1 ? -1 : usec/1000000);
+ if( sec == this.sec ) return;
+ this.sec = sec;
+ setText(sec == -1?"--:--":String.format("%02d:%02d",sec/60,sec%60));
+ }
+ });
}});
+ add(scrollPane = new JScrollPane(listView));
+ setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
+ }
+ /**
+ * 引数で指定された{@link Transmitter}または{@link Receiver}のセル範囲を示す、
+ * デスクトップの座標系内の境界の矩形を返します。対応するセルがない場合はnullを返します。
+ * @param transceiver {@link Transmitter}または{@link Receiver}
+ * @return セル範囲を示す境界の矩形、またはnull
+ */
+ public Rectangle getBoundsOf(AutoCloseable transceiver) {
+ Rectangle rect = listView.getCellBounds(transceiver);
+ if( rect != null ) rect.translate(
+ getRootPane().getX() + getContentPane().getX() + scrollPane.getX() + getX(),
+ getRootPane().getY() + getContentPane().getY() + scrollPane.getY() + getY()
+ );
+ return rect;
}
}
package camidion.chordhelper.mididevice;
+import javax.sound.midi.MidiEvent;
+import javax.sound.midi.Receiver;
+import javax.sound.midi.Transmitter;
+
/**
* MIDIデバイス入出力タイプ
*/
public enum MidiDeviceInOutType {
+ /**
+ * {@link #MIDI_IN}も{@link #MIDI_OUT}もサポートしていないデバイスを表します。
+ */
MIDI_NONE("No MIDI input/output supported", "No I/O"),
+ /**
+ * {@link Receiver}から受けた{@link MidiEvent}を音源や画面に出力するデバイスを表します。
+ */
MIDI_OUT("MIDI output devices (MIDI synthesizer etc.)", "OUT"),
+ /**
+ * キーボードやシーケンサから入力した{@link MidiEvent}を
+ * {@link Transmitter}から{@link Receiver}へ転送するデバイスを表します。
+ */
MIDI_IN("MIDI input devices (MIDI keyboard etc.)", "IN"),
+ /**
+ * {@link #MIDI_IN}と{@link #MIDI_OUT}の両方をサポートしたデバイスを表します。
+ */
MIDI_IN_OUT("MIDI input/output devices (MIDI sequencer etc.)", "I/O");
private String description;
private String shortName;
import camidion.chordhelper.midieditor.MidiSequenceEditor;
/**
- * MIDIデバイスモデルリスト
+ * すべてのMIDIデバイスモデル {@link MidiConnecterListModel} を収容するリストです。
+ * {@link MidiDeviceTreeModel} もこのリストを参照します。
*/
public class MidiDeviceModelList extends Vector<MidiConnecterListModel> {
/**
+ * ツリー表示のルートに使用するタイトル
+ */
+ public static final String TITLE = "MIDI devices";
+ /**
* MIDIエディタ
*/
public MidiSequenceEditor editorDialog;
package camidion.chordhelper.mididevice;
-import java.util.List;
-
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreePath;
/**
- * MIDIデバイスツリーモデル
+ * {@link MidiDeviceModelList}に収容されたMIDIデバイスを
+ * {@link MidiDeviceInOutType}で分類したツリーとして扱えるようにするモデル
*/
public class MidiDeviceTreeModel implements TreeModel {
- List<MidiConnecterListModel> deviceModelList;
- public MidiDeviceTreeModel(List<MidiConnecterListModel> deviceModelList) {
+ MidiDeviceModelList deviceModelList;
+ public MidiDeviceTreeModel(MidiDeviceModelList deviceModelList) {
this.deviceModelList = deviceModelList;
}
@Override
- public Object getRoot() { return "MIDI devices"; }
+ public Object getRoot() { return MidiDeviceModelList.TITLE; }
@Override
public Object getChild(Object parent, int index) {
if( parent == getRoot() ) return MidiDeviceInOutType.values()[index + 1];
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceAdapter;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceListener;
+import javax.swing.JInternalFrame;
import javax.swing.JTree;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
/**
* MIDIデバイスツリービュー
*/
-public class MidiDeviceTreeView extends JTree
-{
- /**
- * {@link MidiDeviceFrame} を閉じたことを検知して再描画するためのリスナー
- */
- public final InternalFrameListener midiDeviceFrameListener = new InternalFrameAdapter() {
- @Override
- public void internalFrameClosing(InternalFrameEvent e) { repaint(); }
- };
+public class MidiDeviceTreeView extends JTree {
- public static final DataFlavor TREE_MODEL_FLAVOR = new DataFlavor(TreeModel.class, "TreeModel");
+ public static final DataFlavor TREE_MODEL_FLAVOR = new DataFlavor(TreeModel.class,"TreeModel");
private Transferable draggingObject = new Transferable() {
private DataFlavor flavors[] = {TREE_MODEL_FLAVOR};
return flavor.equals(TREE_MODEL_FLAVOR);
}
};
+ private DragSourceListener dragSourceListener = new DragSourceAdapter() {
+ @Override
+ public void dragDropEnd(DragSourceDropEvent dsde) { repaint(); }
+ };
+
+ /**
+ * {@link MidiDeviceFrame} が閉じられたり、選択されたりしたときに再描画するリスナー
+ */
+ public final InternalFrameListener midiDeviceFrameListener = new InternalFrameAdapter() {
+ @Override
+ public void internalFrameActivated(InternalFrameEvent e) {
+ JInternalFrame frame = e.getInternalFrame();
+ if( ! (frame instanceof MidiDeviceFrame ) ) return;
+ setSelectionPath(((MidiDeviceFrame)frame).getMidiConnecterListView().getModel().getTreePath());
+ }
+ @Override
+ public void internalFrameClosing(InternalFrameEvent e) { repaint(); }
+ };
/**
* MIDIデバイスツリービューを構築します。
* @param model このビューにデータを提供するモデル
*/
public MidiDeviceTreeView(MidiDeviceTreeModel model) {
super(model);
- (new DragSource()).createDefaultDragGestureRecognizer(
+ DragSource dragSource = new DragSource();
+ dragSource.createDefaultDragGestureRecognizer(
this, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() {
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
if( (dge.getDragAction() & DnDConstants.ACTION_COPY_OR_MOVE) == 0 ) return;
- dge.startDrag(DragSource.DefaultMoveDrop, draggingObject, null);
+ dge.startDrag(DragSource.DefaultMoveDrop, draggingObject, dragSourceListener);
}
}
);
return this;
}
});
+ for( int row = 0; row < getRowCount() ; row++ ) expandRow(row);
}
}
\ No newline at end of file
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
+import java.beans.PropertyVetoException;
import javax.sound.midi.MidiUnavailableException;
import javax.swing.JDesktopPane;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.Timer;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
/**
* 開いている MIDI デバイスを置くためのデスクトップビュー
*/
public class MidiOpenedDevicesView extends JDesktopPane {
- private MidiCablePane cablePane = new MidiCablePane(this);
+ /**
+ * ツリー上で選択状態が変わったとき、
+ * このデスクトップ上のフレームの選択状態に反映するためのリスナー
+ */
+ private TreeSelectionListener treeSelectionListener = new TreeSelectionListener() {
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ Object lastSelected = e.getNewLeadSelectionPath().getLastPathComponent();
+ if( lastSelected instanceof MidiConnecterListModel ) {
+ MidiConnecterListModel deviceModel = (MidiConnecterListModel)lastSelected;
+ if( deviceModel.getMidiDevice().isOpen() ) {
+ // 開いているMIDIデバイスがツリー上で選択されたら
+ // このデスクトップでも選択する
+ try {
+ getMidiDeviceFrameOf(deviceModel).setSelected(true);
+ } catch( PropertyVetoException ex ) {
+ ex.printStackTrace();
+ }
+ return;
+ }
+ }
+ // 閉じているMIDIデバイス、またはMIDIデバイス以外のノードがツリー上で選択されたら
+ // このデスクトップ上のMIDIデバイスフレームをすべて非選択にする
+ MidiDeviceFrame selectedDeviceFrame = getSelectedMidiDeviceFrame();
+ if( selectedDeviceFrame == null ) return;
+ try {
+ selectedDeviceFrame.setSelected(false);
+ } catch( PropertyVetoException ex ) {
+ ex.printStackTrace();
+ }
+ }
+ };
+ /**
+ * ツリー表示からこのデスクトップにドロップされたMIDIデバイスモデルに対応するフレームを表示するためのリスナー
+ */
private DropTargetAdapter dropTargetListener = new DropTargetAdapter() {
@Override
public void dragEnter(DropTargetDragEvent dtde) {
}
};
+ private MidiCablePane cablePane = new MidiCablePane(this);
+
public MidiOpenedDevicesView(MidiDeviceTreeView deviceTree) {
+ deviceTree.addTreeSelectionListener(treeSelectionListener);
add(cablePane, JLayeredPane.PALETTE_LAYER);
int openedFrameIndex = 0;
MidiDeviceTreeModel treeModel = (MidiDeviceTreeModel)deviceTree.getModel();
for( MidiConnecterListModel deviceModel : treeModel.deviceModelList ) {
deviceModel.addListDataListener(cablePane.midiConnecterListDataListener);
- MidiDeviceFrame frame = new MidiDeviceFrame(deviceModel, cablePane) {{
- setSize(250, 100);
- addInternalFrameListener(cablePane.midiDeviceFrameListener);
- addComponentListener(cablePane.midiDeviceFrameComponentListener);
- }};
+ MidiDeviceFrame frame = new MidiDeviceFrame(deviceModel, cablePane);
+ frame.setSize(250, 90);
frame.addInternalFrameListener(deviceTree.midiDeviceFrameListener);
add(frame);
if( ! deviceModel.getMidiDevice().isOpen() ) continue;
- frame.setLocation( 10+(openedFrameIndex%2)*260, 10+openedFrameIndex*55 );
+ frame.setLocation( 10+(openedFrameIndex%2)*260, 10+openedFrameIndex*50 );
frame.setVisible(true);
openedFrameIndex++;
}
+ // このデスクトップがリサイズされたらケーブル面もリサイズする
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) { cablePane.setSize(getSize()); }
return null;
}
+ /**
+ * このビュー上にある現在アクティブな{@link MidiDeviceFrame}を返します。
+ * アクティブな{@link MidiDeviceFrame}がない場合は null を返します。
+ * @return 現在アクティブな{@link MidiDeviceFrame}、または null
+ */
+ public MidiDeviceFrame getSelectedMidiDeviceFrame() {
+ JInternalFrame frame = getSelectedFrame();
+ return frame instanceof MidiDeviceFrame ? (MidiDeviceFrame)frame : null;
+ }
+
private boolean isTimerStarted;
/**
* タイムスタンプを更新するタイマーを開始または停止します。
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import javax.sound.midi.InvalidMidiDataException;
* MIDIシーケンサモデルを構築します。
* @param deviceModelList 親のMIDIデバイスモデルリスト
* @param sequencer シーケンサーMIDIデバイス
- * @param modelList MIDIコネクタリストモデルのリスト(タイムスタンプリセット対象)
+ * @param connecterListModel MIDIコネクタリストモデルのリスト(タイムスタンプリセット対象)
*/
public MidiSequencerModel(
MidiDeviceModelList deviceModelList,
Sequencer sequencer,
- List<MidiConnecterListModel> modelList
+ MidiDeviceModelList connecterListModel
) {
- super(sequencer, modelList);
+ super(sequencer, connecterListModel);
this.deviceModelList = deviceModelList;
}
/**
timeRangeUpdater.start();
SequenceTrackListTableModel sequenceTableModel = getSequenceTrackListTableModel();
if( sequenceTableModel != null && sequenceTableModel.hasRecordChannel() ) {
- for(MidiConnecterListModel m : deviceModelList)
- m.resetMicrosecondPosition();
+ for(MidiConnecterListModel m : deviceModelList) m.resetMicrosecondPosition();
System.gc();
sequencer.startRecording();
}
*/
private boolean valueIsAdjusting = false;
@Override
- public boolean getValueIsAdjusting() {
- return valueIsAdjusting;
- }
+ public boolean getValueIsAdjusting() { return valueIsAdjusting; }
@Override
public void setValueIsAdjusting(boolean valueIsAdjusting) {
this.valueIsAdjusting = valueIsAdjusting;
public void intervalRemoved(ListDataEvent e) { }
@Override
public void contentsChanged(ListDataEvent e) {
- getSequencer().setMasterSyncMode(
- (Sequencer.SyncMode)getSelectedItem()
- );
+ getSequencer().setMasterSyncMode((Sequencer.SyncMode)getSelectedItem());
}
});
}};
public void intervalRemoved(ListDataEvent e) { }
@Override
public void contentsChanged(ListDataEvent e) {
- getSequencer().setSlaveSyncMode(
- (Sequencer.SyncMode)getSelectedItem()
- );
+ getSequencer().setSlaveSyncMode((Sequencer.SyncMode)getSelectedItem());
}
});
}};