From: Akiyoshi Kamide Date: Wed, 4 May 2016 15:13:10 +0000 (+0900) Subject: MIDI接続ドラッグ&ドロップの改良 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=eb9323ce5d9bf543258b1e440a09b0491898cbba;p=midichordhelper%2FMIDIChordHelper.git MIDI接続ドラッグ&ドロップの改良 ・ドラッグ&ドロップ中からケーブルを表示するようにした ・Rx以外にドロップしても接続されないようにした ・リファクタリング --- diff --git a/src/camidion/chordhelper/ChordHelperApplet.java b/src/camidion/chordhelper/ChordHelperApplet.java index 7c565ae..b602c1b 100644 --- a/src/camidion/chordhelper/ChordHelperApplet.java +++ b/src/camidion/chordhelper/ChordHelperApplet.java @@ -284,7 +284,7 @@ public class ChordHelperApplet extends JApplet { */ public static class VersionInfo { public static final String NAME = "MIDI Chord Helper"; - public static final String VERSION = "Ver.20160502.1"; + public static final String VERSION = "Ver.20160504.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/"; diff --git a/src/camidion/chordhelper/mididevice/MidiCablePane.java b/src/camidion/chordhelper/mididevice/MidiCablePane.java index 53582f7..bec5fa4 100644 --- a/src/camidion/chordhelper/mididevice/MidiCablePane.java +++ b/src/camidion/chordhelper/mididevice/MidiCablePane.java @@ -4,8 +4,12 @@ import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.Point; import java.awt.Rectangle; import java.awt.Stroke; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceMotionListener; +import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.util.Hashtable; @@ -17,6 +21,7 @@ import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; +import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; import javax.swing.event.InternalFrameListener; import javax.swing.event.ListDataEvent; @@ -25,51 +30,74 @@ import javax.swing.event.ListDataListener; /** * MIDI ケーブル描画面 */ -public class MidiCablePane extends JComponent - implements ListDataListener, ComponentListener, InternalFrameListener -{ +public class MidiCablePane extends JComponent { + private Point draggingPoint; + /** + * {@link MidiConnecterListModel} の {@link Transmitter} + * をドラッグしている最中に再描画するためのリスナー + */ + public final DragSourceMotionListener midiConnecterMotionListener = new DragSourceMotionListener() { + @Override + public void dragMouseMoved(DragSourceDragEvent dsde) { + Point origin = MidiCablePane.this.getLocationOnScreen(); + draggingPoint = dsde.getLocation(); + draggingPoint.translate(-origin.x, -origin.y); + repaint(); + } + }; + /** + * ドラッグ&ドロップの終了時にこのメソッドを呼び出します。 + */ + public void dragDropEnd() { draggingPoint = null; repaint(); } + /** + * {@link MidiDeviceFrame} の移動や変形を監視して再描画するためのリスナー + */ + public final ComponentListener midiDeviceFrameComponentListener = new ComponentAdapter() { + @Override + public void componentMoved(ComponentEvent e) { repaint(); } + @Override + public void componentResized(ComponentEvent e) { repaint(); } + }; + /** + * {@link MidiDeviceFrame} が閉じたタイミングで再描画するためのリスナー + */ + public final InternalFrameListener midiDeviceFrameListener = new InternalFrameAdapter() { + @Override + public void internalFrameClosed(InternalFrameEvent e) { repaint(); } + @Override + public void internalFrameDeactivated(InternalFrameEvent e) { repaint(); } + @Override + public void internalFrameClosing(InternalFrameEvent e) { + JInternalFrame frame = e.getInternalFrame(); + if( ! (frame instanceof MidiDeviceFrame) ) return; + MidiConnecterListModel devModel = ((MidiDeviceFrame)frame).listView.getModel(); + if( ! devModel.rxSupported() ) return; + colorMap.remove(devModel.getMidiDevice().getReceivers().get(0)); + repaint(); + } + }; + /** + * {@link MidiConnecterListModel} における {@link Transmitter} + * の増減や状態変更があった場合に再描画するためのリスナー + */ + public final ListDataListener midiConnecterListDataListener = new ListDataListener() { + @Override + public void contentsChanged(ListDataEvent e) { repaint(); } + @Override + public void intervalAdded(ListDataEvent e) { repaint(); } + @Override + public void intervalRemoved(ListDataEvent e) { repaint(); } + }; + private JDesktopPane desktopPane; - //private JTree tree; public MidiCablePane(JDesktopPane desktopPane) { this.desktopPane = desktopPane; setOpaque(false); setVisible(true); } - // - // MidiDeviceFrame の開閉を検出 - public void internalFrameActivated(InternalFrameEvent e) {} - public void internalFrameClosed(InternalFrameEvent e) { repaint(); } - public void internalFrameClosing(InternalFrameEvent e) { - JInternalFrame frame = e.getInternalFrame(); - if( ! (frame instanceof MidiDeviceFrame) ) - return; - MidiDeviceFrame devFrame = (MidiDeviceFrame)frame; - MidiConnecterListModel devModel = devFrame.listView.getModel(); - if( ! devModel.rxSupported() ) - return; - colorMap.remove(devModel.getMidiDevice().getReceivers().get(0)); - repaint(); - } - public void internalFrameDeactivated(InternalFrameEvent e) { repaint(); } - public void internalFrameDeiconified(InternalFrameEvent e) {} - public void internalFrameIconified(InternalFrameEvent e) {} - public void internalFrameOpened(InternalFrameEvent e) {} - // - // ウィンドウオペレーションの検出 - public void componentHidden(ComponentEvent e) {} - public void componentMoved(ComponentEvent e) { repaint(); } - public void componentResized(ComponentEvent e) { repaint(); } - public void componentShown(ComponentEvent e) {} - // - // MidiConnecterListModel における Transmitter リストの更新を検出 - public void contentsChanged(ListDataEvent e) { repaint(); } - public void intervalAdded(ListDataEvent e) { repaint(); } - public void intervalRemoved(ListDataEvent e) { repaint(); } - // - // ケーブル描画用 + private static final Stroke CABLE_STROKE = new BasicStroke( - 3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND - ); + 3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); private static final Color[] CABLE_COLORS = { new Color(255, 0, 0,144), new Color(0, 255, 0,144), @@ -85,67 +113,66 @@ public class MidiCablePane extends JComponent super.paint(g); Graphics2D g2 = (Graphics2D)g; g2.setStroke(CABLE_STROKE); - JInternalFrame[] frames = - desktopPane.getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER); + JInternalFrame[] frames = desktopPane.getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER); for( JInternalFrame frame : frames ) { - if( ! (frame instanceof MidiDeviceFrame) ) - continue; + if( ! (frame instanceof MidiDeviceFrame) ) continue; MidiDeviceFrame txDeviceFrame = (MidiDeviceFrame)frame; - List txList = txDeviceFrame.listView.getModel().getMidiDevice().getTransmitters(); + MidiConnecterListView txView = txDeviceFrame.listView; + Transmitter draggingTx = txView.getDraggingTransmitter(); + List txList = txView.getModel().getMidiDevice().getTransmitters(); for( Transmitter tx : txList ) { // - // 送信端子から接続されている受信端子の存在を確認 - Receiver rx = tx.getReceiver(); - if( rx == null ) - continue; + // 送信端子の場所を特定 + 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; // - // 送信端子の矩形を特定 - Rectangle txRect = txDeviceFrame.getListCellBounds(tx); - if( txRect == null ) + // ドラッグ中であれば、マウスカーソルのある所まで線を引く + if( tx.equals(draggingTx) && draggingPoint != null ) { + g2.setColor(Color.BLACK); + g2.drawLine(fromX, fromY, draggingPoint.x, draggingPoint.y); continue; - // - // 受信端子のあるMIDIデバイスを探す + } + // 受信端子の場所を特定 + Receiver rx = tx.getReceiver(); + if( rx == null ) continue; Rectangle rxRect = null; for( JInternalFrame anotherFrame : frames ) { - if( ! (anotherFrame instanceof MidiDeviceFrame) ) - continue; - // - // 受信端子の矩形を探す + if( ! (anotherFrame instanceof MidiDeviceFrame) ) continue; MidiDeviceFrame rxDeviceFrame = (MidiDeviceFrame)anotherFrame; - if((rxRect = rxDeviceFrame.getListCellBounds(rx)) == null) - continue; - rxRect.translate(rxDeviceFrame.getX(), rxDeviceFrame.getY()); + if( (rxRect = rxDeviceFrame.listView.getCellBounds(rx)) == null ) continue; + rxRect.translate( + rxDeviceFrame.getRootPane().getX() + + rxDeviceFrame.getContentPane().getX() + rxDeviceFrame.getX(), + rxDeviceFrame.getRootPane().getY() + + rxDeviceFrame.getContentPane().getY() + rxDeviceFrame.getY() + ); break; } - if( rxRect == null ) - continue; - txRect.translate(txDeviceFrame.getX(), txDeviceFrame.getY()); + 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; + if( nextColorIndex >= CABLE_COLORS.length ) nextColorIndex = 0; } g2.setColor(color); - // - // Tx 始点 - int fromX = txRect.x; - int fromY = txRect.y + 2; - int d = txRect.height - 5; - g2.fillOval(fromX, fromY, d, d); - // - // Tx → Rx 線 - int r = d / 2; - fromX += r; - fromY += r; d = rxRect.height - 5; r = d / 2; - int toX = rxRect.x + r; - int toY = rxRect.y + r + 2; + int toX = rxRect.x + r + 4; + int toY = rxRect.y + r + 4; g2.drawLine(fromX, fromY, toX, toY); } } } -} \ No newline at end of file +} diff --git a/src/camidion/chordhelper/mididevice/MidiConnecterListModel.java b/src/camidion/chordhelper/mididevice/MidiConnecterListModel.java index 8b9c074..97c9a27 100644 --- a/src/camidion/chordhelper/mididevice/MidiConnecterListModel.java +++ b/src/camidion/chordhelper/mididevice/MidiConnecterListModel.java @@ -29,11 +29,10 @@ public class MidiConnecterListModel extends AbstractListModel { public void close() { } }; /** - * 指定のMIDIデバイスに属する - * {@link Transmitter}/{@link Receiver} のリストモデルを構築します。 + * 指定のMIDIデバイスに属する {@link Transmitter}/{@link Receiver} のリストモデルを構築します。 * * @param device 対象MIDIデバイス - * @param modelList リストモデルのリスト + * @param modelList リストモデルを格納している親リスト */ public MidiConnecterListModel(MidiDevice device, List modelList) { this.device = device; diff --git a/src/camidion/chordhelper/mididevice/MidiConnecterListView.java b/src/camidion/chordhelper/mididevice/MidiConnecterListView.java index 9922bdf..d5165ab 100644 --- a/src/camidion/chordhelper/mididevice/MidiConnecterListView.java +++ b/src/camidion/chordhelper/mididevice/MidiConnecterListView.java @@ -1,21 +1,21 @@ 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.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; -import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceAdapter; import java.awt.dnd.DragSourceDropEvent; -import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; -import java.awt.dnd.DropTargetEvent; -import java.awt.dnd.DropTargetListener; import javax.sound.midi.Receiver; import javax.sound.midi.Transmitter; @@ -34,23 +34,20 @@ import camidion.chordhelper.ButtonIcon; * 仮想MIDI端子リストです。 *

*/ -public class MidiConnecterListView extends JList - implements DragGestureListener, DragSourceListener, Transferable, DropTargetListener -{ - public static final Icon MIDI_CONNECTER_ICON = - new ButtonIcon(ButtonIcon.MIDI_CONNECTOR_ICON); - private class CellRenderer extends JLabel implements ListCellRenderer { +public class MidiConnecterListView extends JList { + public static final Icon MIDI_CONNECTER_ICON = new ButtonIcon(ButtonIcon.MIDI_CONNECTOR_ICON); + /** + * リストに登録されている仮想MIDI端子の描画ツール + */ + private static class CellRenderer extends JLabel implements ListCellRenderer { public Component getListCellRendererComponent( - JList list, - AutoCloseable value, - int index, - boolean isSelected, - boolean cellHasFocus - ) { + JList list, AutoCloseable value, int index, + boolean isSelected, boolean cellHasFocus) + { String text; - if( value instanceof Receiver ) text = "Rx"; + if( value == null ) text = null; + else if( value instanceof Receiver ) text = "Rx"; else if( value instanceof Transmitter ) text = "Tx"; - else if( value == null ) text = null; else text = value.toString(); setText(text); setIcon(MIDI_CONNECTER_ICON); @@ -67,111 +64,124 @@ public class MidiConnecterListView extends JList return this; } } + + private static final DataFlavor transmitterFlavor = new DataFlavor(Transmitter.class, "Transmitter"); + /** + * ドラッグ対象を表すクラス + */ + private static class DraggingObject implements Transferable { + private static final DataFlavor flavors[] = {transmitterFlavor}; + private Transmitter tx; + @Override + public Object getTransferData(DataFlavor flavor) { return tx; } + @Override + public DataFlavor[] getTransferDataFlavors() { return flavors; } + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(transmitterFlavor); + } + }; + private static DraggingObject draggingObject = new DraggingObject(); + + /** + * 現在ドラッグされているトランスミッタを返します。 + * @return 現在ドラッグされているトランスミッタ(ドラッグ中でなければnull) + */ + public Transmitter getDraggingTransmitter() { return draggingObject.tx; } + + private MidiCablePane cablePane; + private DragSourceListener dragSourceListener = new DragSourceAdapter() { + @Override + public void dragDropEnd(DragSourceDropEvent dsde) { + if( ! dsde.getDropSuccess() ) getModel().closeTransmitter(getDraggingTransmitter()); + draggingObject.tx = null; + cablePane.dragDropEnd(); + } + }; + /** * 仮想MIDI端子リストビューを生成します。 * @param model このビューから参照されるデータモデル + * @param cablePane MIDIケーブル描画面 */ - public MidiConnecterListView(MidiConnecterListModel model) { + public MidiConnecterListView(MidiConnecterListModel model, MidiCablePane cablePane) { super(model); + this.cablePane = cablePane; setCellRenderer(new CellRenderer()); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); setLayoutOrientation(JList.HORIZONTAL_WRAP); setVisibleRowCount(0); - (new DragSource()).createDefaultDragGestureRecognizer( - this, DnDConstants.ACTION_COPY_OR_MOVE, this - ); - new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, this, true ); - } - - @Override - public void dragGestureRecognized(DragGestureEvent dge) { - if( (dge.getDragAction() & DnDConstants.ACTION_COPY_OR_MOVE) == 0 ) return; - AutoCloseable e = getModel().getElementAt(locationToIndex(dge.getDragOrigin())); - if( e instanceof Transmitter ) { - if( e instanceof MidiConnecterListModel.NewTransmitter ) { - transferringTx = getModel().getTransmitter(); + 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; + Point dragStartPoint = dge.getDragOrigin(); + AutoCloseable transceiver = getModel().getElementAt(locationToIndex(dragStartPoint)); + if( transceiver instanceof Transmitter ) { + if( transceiver instanceof MidiConnecterListModel.NewTransmitter ) { + draggingObject.tx = getModel().getTransmitter(); + } + else { + draggingObject.tx = (Transmitter)transceiver; + } + dge.startDrag(DragSource.DefaultLinkDrop, draggingObject, dragSourceListener); + } + } } - else { - transferringTx = (Transmitter)e; + ); + dragSource.addDragSourceMotionListener(cablePane.midiConnecterMotionListener); + DropTargetAdapter dta= new DropTargetAdapter() { + @Override + public void dragEnter(DropTargetDragEvent event) { + if( event.isDataFlavorSupported(transmitterFlavor) ) + event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); } - dge.startDrag(DragSource.DefaultLinkDrop, this, this); - } - } - - @Override - public void dragEnter(DragSourceDragEvent dsde) {} - @Override - public void dragOver(DragSourceDragEvent dsde) {} - @Override - public void dropActionChanged(DragSourceDragEvent dsde) {} - @Override - public void dragExit(DragSourceEvent dse) {} - @Override - public void dragDropEnd(DragSourceDropEvent dsde) { - if( ! dsde.getDropSuccess() ) getModel().closeTransmitter(transferringTx); - transferringTx = null; - } - - private Transmitter transferringTx = null; - private static final DataFlavor transmitterFlavor = - new DataFlavor(Transmitter.class, "Transmitter"); - private static final DataFlavor transmitterFlavors[] = {transmitterFlavor}; - @Override - public Object getTransferData(DataFlavor flavor) { return transferringTx; } - @Override - public DataFlavor[] getTransferDataFlavors() { return transmitterFlavors; } - @Override - public boolean isDataFlavorSupported(DataFlavor flavor) { - return flavor.equals(transmitterFlavor); - } - - @Override - public void dragEnter(DropTargetDragEvent event) { - if( event.isDataFlavorSupported(transmitterFlavor) ) - event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); - } - @Override - public void dragExit(DropTargetEvent dte) {} - @Override - public void dragOver(DropTargetDragEvent dtde) {} - @Override - public void dropActionChanged(DropTargetDragEvent dtde) {} - @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() ) { - event.dropComplete(false); - return; + @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() ) { + event.dropComplete(false); + return; + } + int index = locationToIndex(event.getLocation()); + AutoCloseable destination = getModel().getElementAt(index); + if( ! (destination instanceof Receiver) ) { + event.dropComplete(false); + return; + } + Transferable draggedObject = event.getTransferable(); + Transmitter sourceTx = (Transmitter)draggedObject.getTransferData(transmitterFlavor); + if( getModel().ConnectToReceiver(sourceTx, (Receiver)destination) == null ) { + event.dropComplete(false); + return; + } + event.dropComplete(true); + } + catch (Exception ex) { + ex.printStackTrace(); + event.dropComplete(false); + } } - AutoCloseable destination = getModel().getElementAt(locationToIndex(event.getLocation())); - if( ! (destination instanceof Receiver) ) { - event.dropComplete(false); - return; - } - Transmitter sourceTx = (Transmitter)event.getTransferable().getTransferData(transmitterFlavor); - if( getModel().ConnectToReceiver(sourceTx, (Receiver)destination) == null ) { - event.dropComplete(false); - return; - } - event.dropComplete(true); - } - catch (Exception ex) { - ex.printStackTrace(); - event.dropComplete(false); - } + }; + new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dta, true ); } @Override - public MidiConnecterListModel getModel() { - return (MidiConnecterListModel)super.getModel(); - } + public MidiConnecterListModel getModel() { return (MidiConnecterListModel)super.getModel(); } /** - * 選択項目がトランスミッタの実体であれば、それを閉じます。 + * 指定されたMIDI端子(Transmitter または Receiver)が仮想MIDI端子リスト上にあれば、 + * そのセル範囲の矩形を返します。 + * + * @param transceiver MIDI端子 + * @return セル範囲の矩形(ない場合はnull) */ - public void closeSelectedTransmitter() { - AutoCloseable ac = getSelectedValue(); - if( ac instanceof Transmitter ) - getModel().closeTransmitter((Transmitter)ac); + public Rectangle getCellBounds(AutoCloseable transceiver) { + int index = getModel().indexOf(transceiver); + Rectangle rect = getCellBounds(index,index); + return rect == null ? null : rect; } -} \ No newline at end of file +} diff --git a/src/camidion/chordhelper/mididevice/MidiDesktopPane.java b/src/camidion/chordhelper/mididevice/MidiDesktopPane.java deleted file mode 100644 index 8fa0059..0000000 --- a/src/camidion/chordhelper/mididevice/MidiDesktopPane.java +++ /dev/null @@ -1,158 +0,0 @@ -package camidion.chordhelper.mididevice; - -import java.awt.Point; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DropTarget; -import java.awt.dnd.DropTargetDragEvent; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.dnd.DropTargetEvent; -import java.awt.dnd.DropTargetListener; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; - -import javax.sound.midi.MidiUnavailableException; -import javax.swing.JDesktopPane; -import javax.swing.JInternalFrame; -import javax.swing.JLayeredPane; -import javax.swing.JOptionPane; -import javax.swing.Timer; - -/** - * 開いている MIDI デバイスを置くためのデスクトップビュー - */ -public class MidiDesktopPane extends JDesktopPane implements DropTargetListener { - private MidiCablePane cablePane = new MidiCablePane(this); - public MidiDesktopPane(MidiDeviceTree deviceTree) { - add(cablePane, JLayeredPane.PALETTE_LAYER); - int i=0; - MidiDeviceTreeModel treeModel = (MidiDeviceTreeModel)deviceTree.getModel(); - for( MidiConnecterListModel deviceModel : treeModel.deviceModelList ) { - MidiDeviceFrame frame = new MidiDeviceFrame(deviceModel) { - { - addInternalFrameListener(cablePane); - addComponentListener(cablePane); - } - }; - frame.addInternalFrameListener(deviceTree); - deviceModel.addListDataListener(cablePane); - add(frame); - if( deviceModel.getMidiDevice().isOpen() ) { - frame.setBounds( 10+(i%2)*260, 10+i*55, 250, 100 ); - frame.setVisible(true); - i++; - } - } - addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - cablePane.setSize(getSize()); - } - }); - new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, this, true ); - } - @Override - public void dragEnter(DropTargetDragEvent dtde) { - Transferable trans = dtde.getTransferable(); - if( trans.isDataFlavorSupported(MidiDeviceTree.TREE_MODEL_FLAVOR) ) { - dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); - } - } - @Override - public void dragExit(DropTargetEvent dte) {} - @Override - public void dragOver(DropTargetDragEvent dtde) {} - @Override - public void dropActionChanged(DropTargetDragEvent dtde) {} - @Override - public void drop(DropTargetDropEvent dtde) { - dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); - try { - int action = dtde.getDropAction() ; - if( (action & DnDConstants.ACTION_COPY_OR_MOVE) != 0 ) { - Transferable trans = dtde.getTransferable(); - Object data = trans.getTransferData(MidiDeviceTree.TREE_MODEL_FLAVOR); - if( data instanceof MidiConnecterListModel ) { - MidiConnecterListModel deviceModel = (MidiConnecterListModel)data; - try { - deviceModel.openDevice(); - } catch( MidiUnavailableException e ) { - // - // デバイスを開くのに失敗した場合 - // - // 例えば、「Microsort MIDI マッパー」と - // 「Microsoft GS Wavetable SW Synth」を - // 同時に開こうとするとここに来る。 - // - dtde.dropComplete(false); - String message = "MIDIデバイス " - + deviceModel - +" を開けません。\n" - + "すでに開かれているデバイスが" - + "このデバイスを連動して開いていないか確認してください。\n\n" - + e.getMessage(); - JOptionPane.showMessageDialog( - null, message, - "Cannot open MIDI device", - JOptionPane.ERROR_MESSAGE - ); - return; - } - if( deviceModel.getMidiDevice().isOpen() ) { - dtde.dropComplete(true); - // - // デバイスが正常に開かれたことを確認できたら - // ドロップした場所へフレームを配置して可視化する。 - // - JInternalFrame frame = getFrameOf(deviceModel); - if( frame != null ) { - Point loc = dtde.getLocation(); - loc.translate( -frame.getWidth()/2, 0 ); - frame.setLocation(loc); - frame.setVisible(true); - } - return; - } - } - } - } - catch (Exception ex) { - ex.printStackTrace(); - } - dtde.dropComplete(false); - } - /** - * 指定されたMIDIデバイスモデルに対するMIDIデバイスフレームを返します。 - * - * @param deviceModel MIDIデバイスモデル - * @return 対応するMIDIデバイスフレーム(ない場合 null) - */ - public MidiDeviceFrame getFrameOf(MidiConnecterListModel deviceModel) { - JInternalFrame[] frames = getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER); - for( JInternalFrame frame : frames ) { - if( ! (frame instanceof MidiDeviceFrame) ) - continue; - MidiDeviceFrame deviceFrame = (MidiDeviceFrame)frame; - if( deviceFrame.listView.getModel() == deviceModel ) - return deviceFrame; - } - return null; - } - private boolean isTimerStarted; - /** - * タイムスタンプを更新するタイマーを開始または停止します。 - * @param toStart trueで開始、falseで停止 - */ - public void setAllDeviceTimestampTimers(boolean toStart) { - if( isTimerStarted == toStart ) return; - isTimerStarted = toStart; - JInternalFrame[] frames = getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER); - for( JInternalFrame frame : frames ) { - if( ! (frame instanceof MidiDeviceFrame) ) - continue; - MidiDeviceFrame deviceFrame = (MidiDeviceFrame)frame; - Timer timer = deviceFrame.timer; - if( toStart ) timer.start(); else timer.stop(); - } - } -} \ No newline at end of file diff --git a/src/camidion/chordhelper/mididevice/MidiDeviceDialog.java b/src/camidion/chordhelper/mididevice/MidiDeviceDialog.java index 1d1c5ba..112ea1d 100644 --- a/src/camidion/chordhelper/mididevice/MidiDeviceDialog.java +++ b/src/camidion/chordhelper/mididevice/MidiDeviceDialog.java @@ -18,23 +18,56 @@ import javax.swing.event.TreeSelectionListener; /** * MIDIデバイスダイアログ (View) */ -public class MidiDeviceDialog extends JDialog - implements ActionListener, TreeSelectionListener +public class MidiDeviceDialog extends JDialog implements ActionListener { + private MidiDeviceTreeView deviceTree; private JEditorPane deviceInfoPane; - private MidiDesktopPane desktopPane; - private MidiDeviceTree deviceTree; + private MidiOpenedDevicesView desktopPane; + @Override + public void actionPerformed(ActionEvent event) { setVisible(true); } + public MidiDeviceDialog(List deviceModelList) { setTitle("MIDI device connection"); setBounds( 300, 300, 800, 500 ); - deviceTree = new MidiDeviceTree(new MidiDeviceTreeModel(deviceModelList)); - deviceTree.addTreeSelectionListener(this); - deviceInfoPane = new JEditorPane("text/html","") { - { - setEditable(false); - } - }; - desktopPane = new MidiDesktopPane(deviceTree); + deviceTree = new MidiDeviceTreeView(new MidiDeviceTreeModel(deviceModelList)) {{ + addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent e) { + Object lastSelected = deviceTree.getLastSelectedPathComponent(); + String html = ""; + if( lastSelected instanceof MidiConnecterListModel ) { + MidiConnecterListModel deviceModel = (MidiConnecterListModel)lastSelected; + MidiDevice.Info info = deviceModel.getMidiDevice().getDeviceInfo(); + html += ""+deviceModel+"
" + + "" + + "" + + "" + + "" + + "
Version"+info.getVersion()+"
Description"+info.getDescription()+"
Vendor"+info.getVendor()+"
"; + 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; + html += ""+ioType+"
"; + html += ioType.getDescription()+"
"; + } + else if( lastSelected != null ) { + html += lastSelected.toString(); + } + html += ""; + deviceInfoPane.setText(html); + } + }); + }}; + deviceInfoPane = new JEditorPane("text/html","") {{ setEditable(false); }}; + desktopPane = new MidiOpenedDevicesView(deviceTree); add(new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, new JSplitPane( @@ -50,51 +83,11 @@ public class MidiDeviceDialog extends JDialog setDividerLocation(250); }}); addWindowListener(new WindowAdapter() { + private void setTimer(boolean flag) { desktopPane.setAllDeviceTimestampTimers(flag); } @Override - public void windowClosing(WindowEvent e) { - desktopPane.setAllDeviceTimestampTimers(false); - } + public void windowClosing(WindowEvent e) { setTimer(false); } @Override - public void windowActivated(WindowEvent e) { - desktopPane.setAllDeviceTimestampTimers(true); - } + public void windowActivated(WindowEvent e) { setTimer(true); } }); } - @Override - public void actionPerformed(ActionEvent event) { - setVisible(true); - } - @Override - public void valueChanged(TreeSelectionEvent e) { - Object lastSelected = deviceTree.getLastSelectedPathComponent(); - String html = ""; - if( lastSelected instanceof MidiConnecterListModel ) { - MidiConnecterListModel deviceModel = (MidiConnecterListModel)lastSelected; - MidiDevice.Info info = deviceModel.getMidiDevice().getDeviceInfo(); - html += ""+deviceModel+"
" - + "" - + "" - + "" - + "" - + "
Version"+info.getVersion()+"
Description"+info.getDescription()+"
Vendor"+info.getVendor()+"
"; - MidiDeviceFrame frame = desktopPane.getFrameOf(deviceModel); - if( frame != null ) { - try { - frame.setSelected(true); - } catch( PropertyVetoException ex ) { - ex.printStackTrace(); - } - } - } - else if( lastSelected instanceof MidiDeviceInOutType ) { - MidiDeviceInOutType ioType = (MidiDeviceInOutType)lastSelected; - html += ""+ioType+"
"; - html += ioType.getDescription()+"
"; - } - else if( lastSelected != null ) { - html += lastSelected.toString(); - } - html += ""; - deviceInfoPane.setText(html); - } } diff --git a/src/camidion/chordhelper/mididevice/MidiDeviceFrame.java b/src/camidion/chordhelper/mididevice/MidiDeviceFrame.java index 5c97aab..65bce47 100644 --- a/src/camidion/chordhelper/mididevice/MidiDeviceFrame.java +++ b/src/camidion/chordhelper/mididevice/MidiDeviceFrame.java @@ -1,6 +1,5 @@ package camidion.chordhelper.mididevice; -import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -29,8 +28,9 @@ public class MidiDeviceFrame extends JInternalFrame { /** * MIDIデバイスのモデルからフレームビューを構築します。 * @param model MIDIデバイスのTransmitter/Receiverリストモデル + * @param cablePane MIDIケーブル描画面 */ - public MidiDeviceFrame(MidiConnecterListModel model) { + public MidiDeviceFrame(MidiConnecterListModel model, MidiCablePane cablePane) { super( null, true, true, false, false ); // // タイトルの設定 @@ -42,19 +42,19 @@ public class MidiDeviceFrame extends JInternalFrame { title = (model.rxSupported()?"[OUT] ":"[No I/O] ")+title; } setTitle(title); - listView = new MidiConnecterListView(model); + listView = new MidiConnecterListView(model, cablePane); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); addInternalFrameListener( new InternalFrameAdapter() { public void internalFrameOpened(InternalFrameEvent e) { - if( ! listView.getModel().getMidiDevice().isOpen() ) - setVisible(false); + boolean isOpen = listView.getModel().getMidiDevice().isOpen(); + if( ! isOpen ) setVisible(isOpen); } public void internalFrameClosing(InternalFrameEvent e) { MidiConnecterListModel m = listView.getModel(); m.closeDevice(); - if( ! m.getMidiDevice().isOpen() ) - setVisible(false); + boolean isOpen = m.getMidiDevice().isOpen(); + if( isVisible() != isOpen ) setVisible(isOpen); } } ); @@ -81,29 +81,4 @@ public class MidiDeviceFrame extends JInternalFrame { }}); }}); } - /** - * 指定されたインデックスが示す仮想MIDI端子リストの要素のセル範囲を返します。 - * - * @param index リスト要素のインデックス - * @return セル範囲の矩形 - */ - public Rectangle getListCellBounds(int index) { - Rectangle rect = listView.getCellBounds(index,index); - if( rect == null ) - return null; - rect.translate( - getRootPane().getX() + getContentPane().getX(), - getRootPane().getY() + getContentPane().getY() - ); - return rect; - } - /** - * 仮想MIDI端子リストの指定された要素のセル範囲を返します。 - * - * @param transciver 要素となるMIDI端子(Transmitter または Receiver) - * @return セル範囲の矩形 - */ - public Rectangle getListCellBounds(AutoCloseable transciver) { - return getListCellBounds(listView.getModel().indexOf(transciver)); - } -} \ No newline at end of file +} diff --git a/src/camidion/chordhelper/mididevice/MidiDeviceModelList.java b/src/camidion/chordhelper/mididevice/MidiDeviceModelList.java index 17b4167..9795671 100644 --- a/src/camidion/chordhelper/mididevice/MidiDeviceModelList.java +++ b/src/camidion/chordhelper/mididevice/MidiDeviceModelList.java @@ -34,54 +34,57 @@ public class MidiDeviceModelList extends Vector { private MidiConnecterListModel firstMidiOutModel; /** * MIDIデバイスモデルリストを生成します。 - * @param vmdList 仮想MIDIデバイスのリスト + * @param guiVirtualDeviceList GUI仮想MIDIデバイスのリスト */ - public MidiDeviceModelList(List vmdList) { - MidiDevice.Info[] devInfos = MidiSystem.getMidiDeviceInfo(); - MidiConnecterListModel guiModels[] = new MidiConnecterListModel[vmdList.size()]; + public MidiDeviceModelList(List guiVirtualDeviceList) { + MidiDevice.Info[] deviceInfos = MidiSystem.getMidiDeviceInfo(); + MidiConnecterListModel guiModels[] = new MidiConnecterListModel[guiVirtualDeviceList.size()]; MidiConnecterListModel firstMidiInModel = null; - for( int i=0; i { firstMidiInModel, editorDialogModel, }; - for( MidiConnecterListModel m : openModels ) { - if( m != null ) m.openDevice(); - } - for( MidiConnecterListModel m : guiModels ) { - m.openDevice(); - } + for( MidiConnecterListModel m : openModels ) if( m != null ) m.openDevice(); + for( MidiConnecterListModel m : guiModels ) m.openDevice(); } catch( MidiUnavailableException ex ) { ex.printStackTrace(); } // 初期接続 // for( MidiConnecterListModel mtx : guiModels ) { - for( MidiConnecterListModel mrx : guiModels ) - mtx.connectToReceiverOf(mrx); + for( MidiConnecterListModel mrx : guiModels ) mtx.connectToReceiverOf(mrx); mtx.connectToReceiverOf(sequencerModel); mtx.connectToReceiverOf(synthModel); mtx.connectToReceiverOf(firstMidiOutModel); } if( firstMidiInModel != null ) { - for( MidiConnecterListModel m : guiModels ) - firstMidiInModel.connectToReceiverOf(m); + for( MidiConnecterListModel m : guiModels ) firstMidiInModel.connectToReceiverOf(m); firstMidiInModel.connectToReceiverOf(sequencerModel); firstMidiInModel.connectToReceiverOf(synthModel); firstMidiInModel.connectToReceiverOf(firstMidiOutModel); } if( sequencerModel != null ) { - for( MidiConnecterListModel m : guiModels ) - sequencerModel.connectToReceiverOf(m); + for( MidiConnecterListModel m : guiModels ) sequencerModel.connectToReceiverOf(m); sequencerModel.connectToReceiverOf(synthModel); sequencerModel.connectToReceiverOf(firstMidiOutModel); } diff --git a/src/camidion/chordhelper/mididevice/MidiDeviceTree.java b/src/camidion/chordhelper/mididevice/MidiDeviceTree.java deleted file mode 100644 index 645b858..0000000 --- a/src/camidion/chordhelper/mididevice/MidiDeviceTree.java +++ /dev/null @@ -1,97 +0,0 @@ -package camidion.chordhelper.mididevice; - -import java.awt.Component; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DragGestureEvent; -import java.awt.dnd.DragGestureListener; -import java.awt.dnd.DragSource; - -import javax.swing.JTree; -import javax.swing.event.InternalFrameEvent; -import javax.swing.event.InternalFrameListener; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.TreeModel; - -/** - * MIDIデバイスツリービュー - */ -public class MidiDeviceTree extends JTree - implements Transferable, DragGestureListener, InternalFrameListener -{ - /** - * MIDIデバイスツリービューを構築します。 - * @param model このビューにデータを提供するモデル - */ - public MidiDeviceTree(MidiDeviceTreeModel model) { - super(model); - (new DragSource()).createDefaultDragGestureRecognizer( - this, DnDConstants.ACTION_COPY_OR_MOVE, this - ); - setCellRenderer(new DefaultTreeCellRenderer() { - @Override - public Component getTreeCellRendererComponent( - JTree tree, Object value, - boolean selected, boolean expanded, boolean leaf, int row, - boolean hasFocus - ) { - super.getTreeCellRendererComponent( - tree, value, selected, expanded, leaf, row, hasFocus - ); - if(leaf) { - setIcon(MidiConnecterListView.MIDI_CONNECTER_ICON); - setDisabledIcon(MidiConnecterListView.MIDI_CONNECTER_ICON); - MidiConnecterListModel listModel = (MidiConnecterListModel)value; - setEnabled( ! listModel.getMidiDevice().isOpen() ); - } - return this; - } - }); - } - /** - * このデバイスツリーからドラッグされるデータフレーバ - */ - public static final DataFlavor - TREE_MODEL_FLAVOR = new DataFlavor(TreeModel.class, "TreeModel"); - private static final DataFlavor - TREE_MODE_FLAVORS[] = {TREE_MODEL_FLAVOR}; - @Override - public Object getTransferData(DataFlavor flavor) { - return getLastSelectedPathComponent(); - } - @Override - public DataFlavor[] getTransferDataFlavors() { - return TREE_MODE_FLAVORS; - } - @Override - public boolean isDataFlavorSupported(DataFlavor flavor) { - return flavor.equals(TREE_MODEL_FLAVOR); - } - @Override - public void dragGestureRecognized(DragGestureEvent dge) { - int action = dge.getDragAction(); - if( (action & DnDConstants.ACTION_COPY_OR_MOVE) != 0 ) { - dge.startDrag(DragSource.DefaultMoveDrop, this, null); - } - } - @Override - public void internalFrameOpened(InternalFrameEvent e) {} - /** - * MidiDeviceFrame のクローズ処理中に再描画リクエストを送ります。 - */ - @Override - public void internalFrameClosing(InternalFrameEvent e) { - repaint(); - } - @Override - public void internalFrameClosed(InternalFrameEvent e) {} - @Override - public void internalFrameIconified(InternalFrameEvent e) {} - @Override - public void internalFrameDeiconified(InternalFrameEvent e) {} - @Override - public void internalFrameActivated(InternalFrameEvent e) {} - @Override - public void internalFrameDeactivated(InternalFrameEvent e) {} -} \ No newline at end of file diff --git a/src/camidion/chordhelper/mididevice/MidiDeviceTreeView.java b/src/camidion/chordhelper/mididevice/MidiDeviceTreeView.java new file mode 100644 index 0000000..6e2a3d1 --- /dev/null +++ b/src/camidion/chordhelper/mididevice/MidiDeviceTreeView.java @@ -0,0 +1,76 @@ +package camidion.chordhelper.mididevice; + +import java.awt.Component; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; + +import javax.swing.JTree; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; +import javax.swing.event.InternalFrameListener; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeModel; + +/** + * MIDIデバイスツリービュー + */ +public class MidiDeviceTreeView extends JTree +{ + /** + * {@link MidiDeviceFrame} を閉じたことを検知して再描画するためのリスナー + */ + public final InternalFrameListener midiDeviceFrameListener = new InternalFrameAdapter() { + @Override + public void internalFrameClosing(InternalFrameEvent e) { repaint(); } + }; + + public static final DataFlavor TREE_MODEL_FLAVOR = new DataFlavor(TreeModel.class, "TreeModel"); + + private Transferable draggingObject = new Transferable() { + private DataFlavor flavors[] = {TREE_MODEL_FLAVOR}; + @Override + public Object getTransferData(DataFlavor flavor) { return getLastSelectedPathComponent(); } + @Override + public DataFlavor[] getTransferDataFlavors() { return flavors; } + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(TREE_MODEL_FLAVOR); + } + }; + /** + * MIDIデバイスツリービューを構築します。 + * @param model このビューにデータを提供するモデル + */ + public MidiDeviceTreeView(MidiDeviceTreeModel model) { + super(model); + (new 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 ) { + dge.startDrag(DragSource.DefaultMoveDrop, draggingObject, null); + } + } + } + ); + setCellRenderer(new DefaultTreeCellRenderer() { + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) + { + super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); + if(leaf) { + setIcon(MidiConnecterListView.MIDI_CONNECTER_ICON); + setDisabledIcon(MidiConnecterListView.MIDI_CONNECTER_ICON); + MidiConnecterListModel listModel = (MidiConnecterListModel)value; + setEnabled( ! listModel.getMidiDevice().isOpen() ); + } + return this; + } + }); + } +} \ No newline at end of file diff --git a/src/camidion/chordhelper/mididevice/MidiOpenedDevicesView.java b/src/camidion/chordhelper/mididevice/MidiOpenedDevicesView.java new file mode 100644 index 0000000..e3b7a9a --- /dev/null +++ b/src/camidion/chordhelper/mididevice/MidiOpenedDevicesView.java @@ -0,0 +1,142 @@ +package camidion.chordhelper.mididevice; + +import java.awt.Point; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +import javax.sound.midi.MidiUnavailableException; +import javax.swing.JDesktopPane; +import javax.swing.JInternalFrame; +import javax.swing.JLayeredPane; +import javax.swing.JOptionPane; +import javax.swing.Timer; + +/** + * 開いている MIDI デバイスを置くためのデスクトップビュー + */ +public class MidiOpenedDevicesView extends JDesktopPane { + private MidiCablePane cablePane = new MidiCablePane(this); + private DropTargetAdapter dropTargetListener = new DropTargetAdapter() { + @Override + public void dragEnter(DropTargetDragEvent dtde) { + if( dtde.getTransferable().isDataFlavorSupported(MidiDeviceTreeView.TREE_MODEL_FLAVOR) ) { + dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); + } + } + @Override + public void drop(DropTargetDropEvent dtde) { + dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); + try { + int action = dtde.getDropAction() ; + if( (action & DnDConstants.ACTION_COPY_OR_MOVE) != 0 ) { + Object data = dtde.getTransferable().getTransferData(MidiDeviceTreeView.TREE_MODEL_FLAVOR); + if( data instanceof MidiConnecterListModel ) { + MidiConnecterListModel deviceModel = (MidiConnecterListModel)data; + try { + deviceModel.openDevice(); + } catch( MidiUnavailableException e ) { + // + // デバイスを開くのに失敗した場合 + // + // 例えば、「Microsort MIDI マッパー」と + // 「Microsoft GS Wavetable SW Synth」を + // 同時に開こうとするとここに来る。 + // + String title = "Cannot open MIDI device"; + String message = "MIDIデバイス "+deviceModel+" はすでに使用中です。\n" + +"他のデバイスが連動して開いていないか確認してください。\n\n" + + e.getMessage(); + dtde.dropComplete(false); + JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE); + return; + } + if( ! deviceModel.getMidiDevice().isOpen() ) { + // 例外が出なかったにも関わらずデバイスが開かれていない場合 + String title = "Cannot open MIDI device"; + String message = "MIDIデバイス "+deviceModel+" はすでに使用中です。\n" + +"他のデバイスが連動して開いていないか確認してください。"; + dtde.dropComplete(false); + JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE); + return; + } + dtde.dropComplete(true); + // + // デバイスが正常に開かれたことを確認できたら + // ドロップした場所へフレームを配置して可視化する。 + // + MidiDeviceFrame deviceFrame = getMidiDeviceFrameOf(deviceModel); + if( deviceFrame == null ) return; + Point loc = dtde.getLocation(); + loc.translate( -deviceFrame.getWidth()/2, 0 ); + deviceFrame.setLocation(loc); + deviceFrame.setVisible(true); + return; + } + } + } + catch (Exception ex) { + ex.printStackTrace(); + } + dtde.dropComplete(false); + } + }; + public MidiOpenedDevicesView(MidiDeviceTreeView deviceTree) { + add(cablePane, JLayeredPane.PALETTE_LAYER); + int frameIndex = 0; + MidiDeviceTreeModel treeModel = (MidiDeviceTreeModel)deviceTree.getModel(); + for( MidiConnecterListModel deviceModel : treeModel.deviceModelList ) { + deviceModel.addListDataListener(cablePane.midiConnecterListDataListener); + MidiDeviceFrame frame = new MidiDeviceFrame(deviceModel, cablePane) {{ + addInternalFrameListener(cablePane.midiDeviceFrameListener); + addComponentListener(cablePane.midiDeviceFrameComponentListener); + }}; + frame.addInternalFrameListener(deviceTree.midiDeviceFrameListener); + add(frame); + if( deviceModel.getMidiDevice().isOpen() ) { + frame.setBounds( 10+(frameIndex%2)*260, 10+frameIndex*55, 250, 100 ); + frame.setVisible(true); + frameIndex++; + } + } + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { cablePane.setSize(getSize()); } + }); + new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dropTargetListener, true ); + } + /** + * 指定されたMIDIデバイスモデルに対するMIDIデバイスフレームを返します。 + * + * @param deviceModel MIDIデバイスモデル + * @return 対応するMIDIデバイスフレーム(ない場合 null) + */ + public MidiDeviceFrame getMidiDeviceFrameOf(MidiConnecterListModel deviceModel) { + JInternalFrame[] frames = getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER); + for( JInternalFrame frame : frames ) { + if( ! (frame instanceof MidiDeviceFrame) ) continue; + MidiDeviceFrame deviceFrame = (MidiDeviceFrame)frame; + if( deviceFrame.listView.getModel() == deviceModel ) return deviceFrame; + } + return null; + } + private boolean isTimerStarted; + /** + * タイムスタンプを更新するタイマーを開始または停止します。 + * @param toStart trueで開始、falseで停止 + */ + public void setAllDeviceTimestampTimers(boolean toStart) { + if( isTimerStarted == toStart ) return; + isTimerStarted = toStart; + JInternalFrame[] frames = getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER); + for( JInternalFrame frame : frames ) { + if( ! (frame instanceof MidiDeviceFrame) ) continue; + Timer timer = ((MidiDeviceFrame)frame).timer; + if( toStart ) timer.start(); else timer.stop(); + } + } +}