OSDN Git Service

ドラッグ&ドロップ処理をTransferHandlerに切り替えてMIDIデバイス接続のケーブル描画を改善
authorAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sat, 2 Jul 2016 16:10:43 +0000 (01:10 +0900)
committerAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sat, 2 Jul 2016 16:10:43 +0000 (01:10 +0900)
src/camidion/chordhelper/ChordHelperApplet.java
src/camidion/chordhelper/mididevice/AbstractTransceiverListView.java
src/camidion/chordhelper/mididevice/DraggingTransceiver.java [deleted file]
src/camidion/chordhelper/mididevice/MidiCablePane.java
src/camidion/chordhelper/mididevice/MidiDeviceFrame.java
src/camidion/chordhelper/mididevice/MidiDeviceTreeView.java
src/camidion/chordhelper/mididevice/MidiOpenedDevicesView.java
src/camidion/chordhelper/mididevice/ReceiverListView.java
src/camidion/chordhelper/mididevice/TransmitterListView.java
src/camidion/chordhelper/midieditor/MidiSequenceEditor.java

index 38c2e0b..27563f1 100644 (file)
@@ -5,8 +5,6 @@ import java.awt.Desktop;
 import java.awt.Dimension;
 import java.awt.Image;
 import java.awt.Insets;
-import java.awt.dnd.DnDConstants;
-import java.awt.dnd.DropTarget;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ComponentAdapter;
@@ -285,7 +283,7 @@ public class ChordHelperApplet extends JApplet {
         */
        public static class VersionInfo {
                public static final String      NAME = "MIDI Chord Helper";
-               public static final String      VERSION = "Ver.20160630.1";
+               public static final String      VERSION = "Ver.20160702.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/";
@@ -478,7 +476,7 @@ public class ChordHelperApplet extends JApplet {
                (midiEditor = new MidiSequenceEditor(playlistModel, guiMidiDevice)).setIconImage(iconImage);
                //
                // メイン画面へのMIDIファイルのドラッグ&ドロップ受付開始
-               new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, midiEditor.dropTargetListener, true);
+               setTransferHandler(midiEditor.transferHandler);
                //
                // MIDIエディタのイベントダイアログを、ピアノ鍵盤のイベント送出ダイアログと共用
                keyboardPanel.setEventDialog(midiEditor.eventDialog);
index 4a71351..9def514 100644 (file)
@@ -6,6 +6,7 @@ import java.awt.Rectangle;
 
 import javax.sound.midi.Receiver;
 import javax.sound.midi.Transmitter;
+import javax.swing.DropMode;
 import javax.swing.JList;
 import javax.swing.ListSelectionModel;
 
@@ -22,7 +23,7 @@ public abstract class AbstractTransceiverListView<E> extends JList<E> {
                return (AbstractTransceiverListModel<E>) super.getModel();
        }
        /**
-        * このリストの座標系内の指定された位置にある要素を返します。
+        * このリストの座標系内の指定された位置にある最寄りの要素を返します。
         * @param p 位置
         */
        public E getElementAt(Point p) {
@@ -62,5 +63,7 @@ public abstract class AbstractTransceiverListView<E> extends JList<E> {
                                return this;
                        }
                });
+               setDragEnabled(true);
+               setDropMode(DropMode.ON);
        }
 }
diff --git a/src/camidion/chordhelper/mididevice/DraggingTransceiver.java b/src/camidion/chordhelper/mididevice/DraggingTransceiver.java
deleted file mode 100644 (file)
index 1b81470..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-package camidion.chordhelper.mididevice;
-
-import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.Transferable;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.sound.midi.Receiver;
-import javax.sound.midi.Transmitter;
-
-/**
- * ドラッグ&ドロップで転送する{@link Transmitter}または{@link Receiver}の収容箱
- */
-public class DraggingTransceiver implements Transferable {
-       public static final DataFlavor receiverFlavor = new DataFlavor(Receiver.class, "Receiver");
-       public static final DataFlavor transmitterFlavor = new DataFlavor(Transmitter.class, "Transmitter");
-       private static final List<DataFlavor> flavors = Arrays.asList(transmitterFlavor, receiverFlavor);
-       private Object data;
-       public Object getData() { return data; }
-       public void setData(Object data) { this.data = data; }
-       @Override
-       public Object getTransferData(DataFlavor flavor) {
-               return flavor.getRepresentationClass().isInstance(data) ? data : null;
-       }
-       @Override
-       public DataFlavor[] getTransferDataFlavors() { return (DataFlavor[]) flavors.toArray(); }
-       @Override
-       public boolean isDataFlavorSupported(DataFlavor flavor) { return flavors.contains(flavor); }
-}
index c6186de..681bd25 100644 (file)
@@ -7,6 +7,9 @@ import java.awt.Graphics2D;
 import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.Stroke;
+import java.awt.dnd.DragSource;
+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;
@@ -29,22 +32,37 @@ import javax.swing.event.ListDataListener;
  * MIDI ケーブル描画面
  */
 public class MidiCablePane extends JComponent {
-       static DraggingTransceiver dragging = new DraggingTransceiver();
+       private Object dragSourceTransceiver;
+       private Object dragDestinationTransceiver;
        private Point draggingPoint;
        /**
-        * ドラッグ中の場所を更新し、再描画を予約します。
-        * @param p ドラッグ中の場所(ドラッグ&ドロップ終了時はnullを指定)
+        * ドラッグ元の{@link Transmitter}または{@link Receiver}を設定します。
+        *
+        * @param trx ドラッグ元
         */
-       public void updateDraggingLocation(Point p) {
-               if( (draggingPoint = p) == null ) {
-                       dragging.setData(null);
-               } else {
-                       Point origin = getLocationOnScreen();
-                       draggingPoint.translate(-origin.x, -origin.y);
+       public void setDragSourceTransceiver(Object trx) {
+               if( (dragSourceTransceiver = trx) == null ) {
+                       draggingPoint = null;
+                       dragDestinationTransceiver = null;
                }
                repaint();
        }
        /**
+        * ドラッグ元の{@link Transmitter}または{@link Receiver}を返します。
+        */
+       public Object getDragSourceTransceiver() { return dragSourceTransceiver; }
+       /**
+        * ドラッグ先の{@link Transmitter}または{@link Receiver}を設定します。
+        * @param dragDestinationTransceiver 設定するドラッグ先
+        */
+       public void setDragDestinationTransceiver(Object dragDestinationTransceiver) {
+               this.dragDestinationTransceiver = dragDestinationTransceiver;
+       }
+       /**
+        * ドラッグ先の{@link Transmitter}または{@link Receiver}を返します。
+        */
+       public Object getDragDestinationTranceiver() { return dragDestinationTransceiver; }
+       /**
         * {@link MidiDeviceFrame} が移動または変形したときにケーブルを再描画するためのリスナー
         */
        public final ComponentListener midiDeviceFrameComponentListener = new ComponentAdapter() {
@@ -90,10 +108,22 @@ public class MidiCablePane extends JComponent {
                this.desktopPane = desktopPane;
                setOpaque(false);
                setVisible(true);
+               DragSource.getDefaultDragSource().addDragSourceMotionListener(new DragSourceMotionListener() {
+                       @Override
+                   public void dragMouseMoved(DragSourceDragEvent dsde) {
+                               // OSのスクリーン座標系から、このケーブル画面の座標系に変換する
+                       Point origin = getLocationOnScreen();
+                       (draggingPoint = dsde.getLocation()).translate(-origin.x, -origin.y);
+                       repaint();
+                   }
+               });
        }
 
        private static final Stroke CABLE_STROKE = new BasicStroke(
                        3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
+       private static final float dash[] = {1.0f, 5.0f};
+       private static final Stroke VIRTUAL_CABLE_STROKE = new BasicStroke(
+                       3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f, dash, 0.0f);
        private static final List<Color> CABLE_COLORS = Arrays.asList(
                new Color(255, 0, 0,144),
                new Color(0, 255, 0,144),
@@ -102,8 +132,7 @@ public class MidiCablePane extends JComponent {
                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 static final Color NEW_CABLE_COLOR = new Color(0, 0, 0, 144);
        private Hashtable<Receiver,Color> colorMap = new Hashtable<>();
        private Color colorOf(Receiver rx) {
                Color color = colorMap.get(rx);
@@ -123,7 +152,6 @@ public class MidiCablePane extends JComponent {
        public void paint(Graphics g) {
                super.paint(g);
                Graphics2D g2 = (Graphics2D)g;
-               g2.setStroke(CABLE_STROKE);
                JInternalFrame[] frames = desktopPane.getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER);
                for( JInternalFrame frame : frames ) {
                        if( ! (frame instanceof MidiDeviceFrame) ) continue;
@@ -131,12 +159,17 @@ public class MidiCablePane extends JComponent {
                        MidiDeviceModel fromDeviceModel = fromFrame.getMidiDeviceModel();
                        //
                        // Receiverからドラッグされている線を描画
-                       if( draggingPoint != null && fromDeviceModel.getMidiDevice().getReceivers().contains(dragging.getData()) ) {
-                               Receiver rx = (Receiver)dragging.getData();
+                       if( draggingPoint != null && fromDeviceModel.getMidiDevice().getReceivers().contains(dragSourceTransceiver) ) {
+                               Receiver rx = (Receiver)dragSourceTransceiver;
                                Rectangle rxBounds = fromFrame.getBoundsOf(rx);
                                if( rxBounds == null ) continue;
                                int r = (rxBounds.height - 5) / 2;
                                rxBounds.translate(r+4, r+4);
+                               if( dragDestinationTransceiver instanceof Transmitter ) {
+                                       g2.setStroke(CABLE_STROKE);
+                               } else {
+                                       g2.setStroke(VIRTUAL_CABLE_STROKE);
+                               }
                                g2.setColor(colorOf(rx));
                                g2.drawLine(rxBounds.x, rxBounds.y, draggingPoint.x, draggingPoint.y);
                        }
@@ -151,27 +184,32 @@ public class MidiCablePane extends JComponent {
                                if( txBounds == null ) continue;
                                int r = (txBounds.height - 5) / 2;
                                txBounds.translate(r+4, r+4);
-                               //
-                               // Transmitterに現在接続されているReceiverを把握
-                               Receiver rx = tx.getReceiver();
-                               if( draggingPoint != null && tx.equals(dragging.getData()) ) {
+                               if( draggingPoint != null && tx.equals(dragSourceTransceiver) ) {
                                        //
                                        // Transmitterからドラッグされている線を描画
-                                       g2.setColor(rx == null ? ADDING_CABLE_COLOR : colorOf(rx));
+                                       if( dragDestinationTransceiver instanceof Receiver ) {
+                                               g2.setStroke(CABLE_STROKE);
+                                               g2.setColor(colorOf((Receiver)dragDestinationTransceiver));
+                                       } else {
+                                               g2.setStroke(VIRTUAL_CABLE_STROKE);
+                                               g2.setColor(NEW_CABLE_COLOR);
+                                       }
                                        g2.drawLine(txBounds.x, txBounds.y, draggingPoint.x, draggingPoint.y);
                                }
+                               //
+                               // スキャン中のTransmitterに現在接続されているReceiverを把握
+                               Receiver rx = tx.getReceiver();
                                if( rx == null ) continue;
                                for( JInternalFrame toFrame : frames ) {
                                        if( ! (toFrame instanceof MidiDeviceFrame) ) continue;
-                                       //
-                                       // Receiverの場所を特定
                                        Rectangle rxBounds = ((MidiDeviceFrame)toFrame).getBoundsOf(rx);
                                        if( rxBounds == null ) continue;
                                        r = (rxBounds.height - 5) / 2;
                                        rxBounds.translate(r+4, r+4);
                                        //
                                        // Transmitter⇔Receiver間の線を描画
-                                       g2.setColor(tx.equals(dragging.getData()) ? REMOVING_CABLE_COLOR : colorOf(rx));
+                                       g2.setStroke(tx.equals(dragSourceTransceiver) ? VIRTUAL_CABLE_STROKE : CABLE_STROKE);
+                                       g2.setColor(colorOf(rx));
                                        g2.drawLine(txBounds.x, txBounds.y, rxBounds.x, rxBounds.y);
                                        break;
                                }
index 90785e8..6402788 100644 (file)
@@ -13,7 +13,6 @@ import javax.sound.midi.Receiver;
 import javax.sound.midi.Transmitter;
 import javax.swing.JInternalFrame;
 import javax.swing.JLabel;
-import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.Timer;
@@ -106,6 +105,7 @@ public class MidiDeviceFrame extends JInternalFrame {
        public Rectangle getBoundsOf(Transmitter tx) {
                if( transmitterListView == null ) return null;
                Rectangle rect = transmitterListView.getCellBounds(tx);
+               if( rect == null ) return null;
                translate(rect, txPanel, transmitterListView);
                return rect;
        }
@@ -117,11 +117,11 @@ public class MidiDeviceFrame extends JInternalFrame {
        public Rectangle getBoundsOf(Receiver rx) {
                if( receiverListView == null ) return null;
                Rectangle rect = receiverListView.getCellBounds(rx);
+               if( rect == null ) return null;
                translate(rect, rxPanel, receiverListView);
                return rect;
        }
-       private void translate(Rectangle rect, JPanel panel, JList<? extends AutoCloseable> list) {
-               if( rect == null ) return;
+       private void translate(Rectangle rect, JPanel panel, AbstractTransceiverListView<?> list) {
                int x = getX() + getRootPane().getX() + getContentPane().getX() +
                                scrollPane.getX() + trxPanel.getX() +
                                panel.getX() + list.getX();
index 9bcda1a..2099151 100644 (file)
@@ -1,19 +1,14 @@
 package camidion.chordhelper.mididevice;
 
 import java.awt.Component;
-import java.awt.Point;
 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.DragSourceAdapter;
-import java.awt.dnd.DragSourceDropEvent;
 
+import javax.swing.JComponent;
 import javax.swing.JInternalFrame;
 import javax.swing.JTree;
 import javax.swing.ToolTipManager;
+import javax.swing.TransferHandler;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 import javax.swing.event.InternalFrameListener;
@@ -23,24 +18,8 @@ import javax.swing.tree.DefaultTreeCellRenderer;
  * MIDIデバイスツリービュー
  */
 public class MidiDeviceTreeView extends JTree {
-
-       public static final DataFlavor DEVICE_MODEL_FLAVOR  = new DataFlavor(MidiDeviceModel.class,"MidiDeviceModel");
-
-       public class DraggingDevice implements Transferable {
-               private DataFlavor flavors[] = {DEVICE_MODEL_FLAVOR};
-               private MidiDeviceModel midiDeviceModel;
-               @Override
-               public Object getTransferData(DataFlavor flavor) {
-                       return flavor.getRepresentationClass().isInstance(midiDeviceModel) ? midiDeviceModel : null;
-               }
-               @Override
-               public DataFlavor[] getTransferDataFlavors() { return flavors; }
-               @Override
-               public boolean isDataFlavorSupported(DataFlavor flavor) {
-                       return flavors[0].equals(flavor);
-               }
-       };
-       private DraggingDevice draggingDevice = new DraggingDevice();
+       public static final DataFlavor deviceModelFlavor  = new DataFlavor(MidiDeviceModel.class,"MidiDeviceModel");
+       private static final DataFlavor flavors[] = {deviceModelFlavor};
        /**
         *      {@link MidiDeviceFrame} が閉じられたり、選択されたりしたときに再描画するリスナー
         */
@@ -62,24 +41,6 @@ public class MidiDeviceTreeView extends JTree {
         */
        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 ) return;
-                                       Point origin = dge.getDragOrigin();
-                                       Object leaf = getPathForLocation(origin.x, origin.y).getLastPathComponent();
-                                       if( ! (leaf instanceof MidiDeviceModel) ) return;
-                                       draggingDevice.midiDeviceModel = (MidiDeviceModel)leaf;
-                                       dge.startDrag(DragSource.DefaultMoveDrop, draggingDevice, new DragSourceAdapter() {
-                                               @Override
-                                               public void dragDropEnd(DragSourceDropEvent dsde) {
-                                                       if( dsde.getDropSuccess() ) repaint();
-                                               }
-                                       });
-                               }
-                       }
-               );
                setCellRenderer(new DefaultTreeCellRenderer() {
                        @Override
                        public Component getTreeCellRendererComponent(JTree tree, Object value,
@@ -107,6 +68,37 @@ public class MidiDeviceTreeView extends JTree {
                //
                // ツリーノードのToolTipを有効化
                ToolTipManager.sharedInstance().registerComponent(this);
-
+               //
+               // ドラッグを有効化
+               setDragEnabled(true);
+               setTransferHandler(new TransferHandler() {
+                       @Override
+                       public int getSourceActions(JComponent c) { return COPY_OR_MOVE; }
+                       @Override
+                       protected Transferable createTransferable(JComponent c) {
+                               JTree tree = (JTree) c;
+                               Object node = tree.getLastSelectedPathComponent();
+                               if( node instanceof MidiDeviceModel ) {
+                                       MidiDeviceModel midiDeviceModel = (MidiDeviceModel)node;
+                                       if( ! midiDeviceModel.getMidiDevice().isOpen() ) return new Transferable() {
+                                               @Override
+                                               public Object getTransferData(DataFlavor flavor) {
+                                                       return flavor.getRepresentationClass().isInstance(midiDeviceModel) ? midiDeviceModel : null;
+                                               }
+                                               @Override
+                                               public DataFlavor[] getTransferDataFlavors() { return flavors; }
+                                               @Override
+                                               public boolean isDataFlavorSupported(DataFlavor flavor) {
+                                                       return flavors[0].equals(flavor);
+                                               }
+                                       };
+                               }
+                               return null;
+                       }
+                       @Override
+                       protected void exportDone(JComponent source, Transferable data, int action) {
+                               if( action != NONE ) repaint();
+                       }
+               });
        }
 }
index 1234078..093d49f 100644 (file)
@@ -1,12 +1,5 @@
 package camidion.chordhelper.mididevice;
 
-import java.awt.datatransfer.Transferable;
-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.dnd.DropTargetListener;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
 import java.beans.PropertyVetoException;
@@ -18,17 +11,19 @@ import javax.swing.JDesktopPane;
 import javax.swing.JInternalFrame;
 import javax.swing.JLayeredPane;
 import javax.swing.JOptionPane;
+import javax.swing.TransferHandler;
 import javax.swing.event.TreeSelectionEvent;
 import javax.swing.event.TreeSelectionListener;
 import javax.swing.tree.TreePath;
 
 /**
- * 開いている MIDI デバイスを置くためのデスクトップビュー
+ * 開いているMIDIデバイスを置くためのデスクトップビュー
  */
 public class MidiOpenedDevicesView extends JDesktopPane implements TreeSelectionListener {
-
+       /**
+        * MIDIデバイスモデルからフレームを割り出すためのマップ
+        */
        private Map<MidiDeviceModel, MidiDeviceFrame> modelToFrame = new HashMap<>();
-
        /**
         * ツリー上で選択状態が変わったとき、このデスクトップ上のフレームの選択状態に反映します。
         */
@@ -61,84 +56,11 @@ public class MidiOpenedDevicesView extends JDesktopPane implements TreeSelection
                        ex.printStackTrace();
                }
        }
-       /**
-        * ツリー表示からこのデスクトップにドロップされたMIDIデバイスモデルに対応するフレームを表示するためのリスナー
-        */
-       private DropTargetListener dropTargetListener = new DropTargetAdapter() {
-               @Override
-               public void dragEnter(DropTargetDragEvent dtde) {
-                       if( dtde.getTransferable().isDataFlavorSupported(MidiDeviceTreeView.DEVICE_MODEL_FLAVOR) ) {
-                               dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
-                       }
-               }
-               @Override
-               public void drop(DropTargetDropEvent dtde) {
-                       dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-                       try {
-                               int maskedBits = dtde.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE;
-                               if( maskedBits == 0 ) {
-                                       dtde.dropComplete(false);
-                                       return;
-                               }
-                               Transferable t = dtde.getTransferable();
-                               if( ! t.isDataFlavorSupported(MidiDeviceTreeView.DEVICE_MODEL_FLAVOR) ) {
-                                       dtde.dropComplete(false);
-                                       return;
-                               }
-                               Object source = t.getTransferData(MidiDeviceTreeView.DEVICE_MODEL_FLAVOR);
-                               if( ! (source instanceof MidiDeviceModel) ) {
-                                       dtde.dropComplete(false);
-                                       return;
-                               }
-                               MidiDeviceModel deviceModel = (MidiDeviceModel)source;
-                               try {
-                                       deviceModel.open();
-                               } 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"
-                                                       +"他のデバイスが連動して開いている可能性があります。\n\n" ;
-                                       dtde.dropComplete(false);
-                                       JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
-                                       return;
-                               }
-                               //
-                               // デバイスが正常に開かれたことを確認できたら
-                               // ドロップした場所へフレームを配置して可視化する。
-                               //
-                               MidiDeviceFrame deviceFrame = modelToFrame.get(deviceModel);
-                               deviceFrame.setLocation(dtde.getLocation());
-                               deviceFrame.setVisible(true);
-                               dtde.dropComplete(true);
-                       }
-                       catch (Exception ex) {
-                               ex.printStackTrace();
-                               dtde.dropComplete(false);
-                       }
-               }
-       };
-
-       MidiCablePane cablePane = new MidiCablePane(this);
 
        public MidiOpenedDevicesView(MidiDeviceTreeView deviceTreeView,
                        MidiDeviceInfoPane deviceInfoPane, MidiDeviceDialog dialog)
        {
+               MidiCablePane cablePane = new MidiCablePane(this);
                add(cablePane, JLayeredPane.PALETTE_LAYER);
                addComponentListener(new ComponentAdapter() {
                        @Override
@@ -184,7 +106,62 @@ public class MidiOpenedDevicesView extends JDesktopPane implements TreeSelection
                                toY += 50;
                        }
                }
-               new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dropTargetListener, true );
+               setTransferHandler(new TransferHandler() {
+                       @Override
+                       public boolean canImport(TransferSupport support) {
+                               if( ! support.isDrop() ) return false;
+                               if( support.isDataFlavorSupported(MidiDeviceTreeView.deviceModelFlavor) ) {
+                                       // MIDIデバイスを開くためのドロップを受け付ける
+                                       return true;
+                               }
+                               if( support.isDataFlavorSupported(TransmitterListView.elementFlavor) ) {
+                                       cablePane.setDragDestinationTransceiver(null);
+                                       // Transmitterの切り離しができるよう、ドロップを容認
+                                       return true;
+                               }
+                               if( support.isDataFlavorSupported(ReceiverListView.elementFlavor) ) {
+                                       cablePane.setDragDestinationTransceiver(null);
+                                       // Receiverはドロップ不可
+                               }
+                               return false;
+                       }
+                       @Override
+                       public boolean importData(TransferSupport support) {
+                               MidiDeviceModel deviceModel = null;
+                               Object from;
+                               try {
+                                       from = support.getTransferable().getTransferData(MidiDeviceTreeView.deviceModelFlavor);
+                                       if( ! (from instanceof MidiDeviceModel) ) return false;
+                                       deviceModel = (MidiDeviceModel)from;
+                                       deviceModel.open();
+                                       if( ! deviceModel.getMidiDevice().isOpen() ) {
+                                               throw new MidiUnavailableException("開いたはずなのに、開かれた状態になっていません。");
+                                       }
+                                       MidiDeviceFrame deviceFrame = modelToFrame.get(deviceModel);
+                                       if( ! deviceFrame.isVisible() ) {
+                                               deviceFrame.setLocation(support.getDropLocation().getDropPoint());
+                                               deviceFrame.setVisible(true);
+                                       }
+                                       return true;
+                               } 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();
+                                       JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
+                                       return false;
+                               } catch (Exception ex) {
+                                       ex.printStackTrace();
+                                       return false;
+                               }
+                       }
+               });
        }
 
 }
index 31788fa..40cdc3a 100644 (file)
@@ -1,33 +1,27 @@
 package camidion.chordhelper.mididevice;
 
+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.DragSourceAdapter;
-import java.awt.dnd.DragSourceDragEvent;
-import java.awt.dnd.DragSourceDropEvent;
-import java.awt.dnd.DragSourceMotionListener;
-import java.awt.dnd.DropTarget;
-import java.awt.dnd.DropTargetAdapter;
-import java.awt.dnd.DropTargetDragEvent;
-import java.awt.dnd.DropTargetDropEvent;
-import java.awt.dnd.DropTargetListener;
+import java.util.Arrays;
+import java.util.List;
 
 import javax.sound.midi.Receiver;
 import javax.sound.midi.Transmitter;
+import javax.swing.JComponent;
+import javax.swing.TransferHandler;
 
 /**
  * {@link Receiver}のリストビュー
  */
 public class ReceiverListView extends AbstractTransceiverListView<Receiver> {
-       @Override
-       public ReceiverListModel getModel() { return (ReceiverListModel) super.getModel(); }
+       public static final DataFlavor elementFlavor = new DataFlavor(Receiver.class, "Receiver");
+       protected static final List<DataFlavor> flavors = Arrays.asList(elementFlavor);
        @Override
        protected String toolTipTextFor(Receiver rx) {
                return "受信端子(Rx):ドラッグ&ドロップしてTxに接続できます。";
        }
+       @Override
+       public ReceiverListModel getModel() { return (ReceiverListModel) super.getModel(); }
        /**
         * 仮想MIDI端子リストビューを生成します。
         * @param model このビューから参照されるデータモデル
@@ -35,73 +29,55 @@ public class ReceiverListView extends AbstractTransceiverListView<Receiver> {
         */
        public ReceiverListView(ReceiverListModel model, MidiCablePane cablePane) {
                super(model);
-               setupDrag(cablePane);
-               setupDrop();
-       }
-       /**
-        * {@link Receiver}をドラッグできるようにします。
-        * @param cablePane MIDIケーブル描画面
-        */
-       private void setupDrag(MidiCablePane cablePane) {
-               DragSource dragSource = new DragSource();
-               DragGestureListener dgl = new DragGestureListener() {
+               setTransferHandler(new TransferHandler() {
+                       @Override
+                       public int getSourceActions(JComponent compo) { return COPY_OR_MOVE; }
                        @Override
-                       public void dragGestureRecognized(DragGestureEvent event) {
-                               if( (event.getDragAction() & DnDConstants.ACTION_COPY_OR_MOVE) == 0 ) return;
-                               MidiCablePane.dragging.setData(getElementAt(event.getDragOrigin()));
-                               event.startDrag(DragSource.DefaultLinkDrop, MidiCablePane.dragging, new DragSourceAdapter() {
+                       protected Transferable createTransferable(JComponent compo) {
+                               cablePane.setDragSourceTransceiver(getSelectedValue());
+                               return new Transferable() {
+                                       @Override
+                                       public Object getTransferData(DataFlavor flavor) {
+                                               return cablePane.getDragSourceTransceiver();
+                                       }
                                        @Override
-                                       public void dragDropEnd(DragSourceDropEvent dsde) {
-                                               cablePane.updateDraggingLocation(null);
+                                       public DataFlavor[] getTransferDataFlavors() {
+                                               return (DataFlavor[]) flavors.toArray();
                                        }
-                               });
+                                       @Override
+                                       public boolean isDataFlavorSupported(DataFlavor flavor) {
+                                               return flavors.contains(flavor);
+                                       }
+                               };
                        }
-               };
-               dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
-               dragSource.addDragSourceMotionListener(new DragSourceMotionListener() {
                        @Override
-                       public void dragMouseMoved(DragSourceDragEvent dsde) {
-                               cablePane.updateDraggingLocation(dsde.getLocation());
+                       protected void exportDone(JComponent source, Transferable data, int action) {
+                               cablePane.setDragSourceTransceiver(null);
                        }
-               });
-       }
-       /**
-        * {@link Transmitter}のドロップを受け付けます。
-        */
-       private void setupDrop() {
-               DropTargetListener dtl = new DropTargetAdapter() {
                        @Override
-                       public void dragEnter(DropTargetDragEvent event) {
-                               if( event.isDataFlavorSupported(DraggingTransceiver.transmitterFlavor) ) {
-                                       event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
+                       public boolean canImport(TransferSupport support) {
+                               if( ! support.isDrop() ) return false;
+                               if( support.isDataFlavorSupported(TransmitterListView.elementFlavor) ) {
+                                       Receiver to = getElementAt(support.getDropLocation().getDropPoint());
+                                       cablePane.setDragDestinationTransceiver(to);
+                                       return true;
                                }
+                               cablePane.setDragDestinationTransceiver(null);
+                               return false;
                        }
                        @Override
-                       public void drop(DropTargetDropEvent event) {
-                               event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-                               if( (event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) == 0 ) {
-                                       event.dropComplete(false);
-                                       return;
-                               }
-                               Transferable t = event.getTransferable();
-                               if( ! t.isDataFlavorSupported(DraggingTransceiver.transmitterFlavor) ) {
-                                       event.dropComplete(false);
-                                       return;
-                               }
+                       public boolean importData(TransferSupport support) {
                                try {
-                                       Object tx = t.getTransferData(DraggingTransceiver.transmitterFlavor);
-                                       if( tx != null ) {
-                                               ((Transmitter)tx).setReceiver(getElementAt(event.getLocation()));
-                                               event.dropComplete(true);
-                                               return;
-                                       }
+                                       Object from = support.getTransferable().getTransferData(TransmitterListView.elementFlavor);
+                                       if( ! (from instanceof Transmitter) ) return false;
+                                       Receiver to = getElementAt(support.getDropLocation().getDropPoint());
+                                       ((Transmitter)from).setReceiver(to);
+                                       return true;
+                               } catch (Exception exception) {
+                                       exception.printStackTrace();
+                                       return false;
                                }
-                               catch (Exception ex) {
-                                       ex.printStackTrace();
-                               }
-                               event.dropComplete(false);
                        }
-               };
-               new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true );
+               });
        }
 }
index c30d71c..383ca45 100644 (file)
@@ -1,29 +1,21 @@
 package camidion.chordhelper.mididevice;
 
+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.DragSourceAdapter;
-import java.awt.dnd.DragSourceDragEvent;
-import java.awt.dnd.DragSourceDropEvent;
-import java.awt.dnd.DragSourceMotionListener;
-import java.awt.dnd.DropTarget;
-import java.awt.dnd.DropTargetAdapter;
-import java.awt.dnd.DropTargetDragEvent;
-import java.awt.dnd.DropTargetDropEvent;
-import java.awt.dnd.DropTargetListener;
+import java.util.Arrays;
+import java.util.List;
 
 import javax.sound.midi.Receiver;
 import javax.sound.midi.Transmitter;
+import javax.swing.JComponent;
+import javax.swing.TransferHandler;
 
 /**
  * {@link Transmitter}のリストビュー
  */
 public class TransmitterListView extends AbstractTransceiverListView<Transmitter> {
-       @Override
-       public TransmitterListModel getModel() { return (TransmitterListModel) super.getModel(); }
+       public static final DataFlavor elementFlavor = new DataFlavor(Transmitter.class, "Transmitter");
+       protected static final List<DataFlavor> flavors = Arrays.asList(elementFlavor);
        @Override
        protected String toolTipTextFor(Transmitter tx) {
                if( tx instanceof DummyTransmitter ) {
@@ -32,6 +24,8 @@ public class TransmitterListView extends AbstractTransceiverListView<Transmitter
                        return "接続済の送信端子(Tx):ドラッグ&ドロップして接続先Rxを変更、または切断できます。";
                }
        }
+       @Override
+       public TransmitterListModel getModel() { return (TransmitterListModel) super.getModel(); }
        /**
         * 仮想MIDI端子リストビューを生成します。
         * @param model このビューから参照されるデータモデル
@@ -39,87 +33,69 @@ public class TransmitterListView extends AbstractTransceiverListView<Transmitter
         */
        public TransmitterListView(TransmitterListModel model, MidiCablePane cablePane) {
                super(model);
-               setupDrag(cablePane);
-               setupDrop();
-       }
-       /**
-        * {@link Transmitter}をドラッグできるようにします。
-        * @param cablePane MIDIケーブル描画面
-        */
-       private void setupDrag(MidiCablePane cablePane) {
-               DragSource dragSource = new DragSource();
-               DragGestureListener dgl = new DragGestureListener() {
+               setTransferHandler(new TransferHandler() {
+                       @Override
+                       public int getSourceActions(JComponent compo) { return COPY_OR_MOVE; }
                        @Override
-                       public void dragGestureRecognized(DragGestureEvent event) {
-                               if( (event.getDragAction() & DnDConstants.ACTION_COPY_OR_MOVE) == 0 ) return;
-                               MidiCablePane.dragging.setData(getElementAt(event.getDragOrigin()));
-                               event.startDrag(DragSource.DefaultLinkDrop, MidiCablePane.dragging, new DragSourceAdapter() {
+                       protected Transferable createTransferable(JComponent compo) {
+                               cablePane.setDragSourceTransceiver(getSelectedValue());
+                               return new Transferable() {
+                                       @Override
+                                       public Object getTransferData(DataFlavor flavor) {
+                                               return cablePane.getDragSourceTransceiver();
+                                       }
+                                       @Override
+                                       public DataFlavor[] getTransferDataFlavors() {
+                                               return (DataFlavor[]) flavors.toArray();
+                                       }
                                        @Override
-                                       public void dragDropEnd(DragSourceDropEvent event) {
-                                               Transmitter tx = (Transmitter)MidiCablePane.dragging.getData();
-                                               if( ! event.getDropSuccess() ) {
-                                                       getModel().closeTransmitter(tx);
-                                               } else if( tx instanceof DummyTransmitter ) {
-                                                       Receiver rx = tx.getReceiver();
-                                                       tx.close();
-                                                       try {
-                                                               getModel().openTransmitter().setReceiver(rx);
-                                                       } catch (Exception exception) {
-                                                               exception.printStackTrace();
-                                                       }
-                                               }
-                                               cablePane.updateDraggingLocation(null);
+                                       public boolean isDataFlavorSupported(DataFlavor flavor) {
+                                               return flavors.contains(flavor);
                                        }
-                               });
+                               };
                        }
-               };
-               dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
-               dragSource.addDragSourceMotionListener(new DragSourceMotionListener() {
                        @Override
-                       public void dragMouseMoved(DragSourceDragEvent dsde) {
-                               cablePane.updateDraggingLocation(dsde.getLocation());
+                       protected void exportDone(JComponent source, Transferable data, int action) {
+                               try {
+                                       Transmitter tx = (Transmitter) data.getTransferData(elementFlavor);
+                                       if( action == NONE ) {
+                                               getModel().closeTransmitter(tx);
+                                       }
+                                       else if( tx instanceof DummyTransmitter ) {
+                                               Receiver rx = tx.getReceiver();
+                                               tx.close();
+                                               getModel().openTransmitter().setReceiver(rx);
+                                       }
+                               } catch (Exception exception) {
+                                       exception.printStackTrace();
+                               }
+                               cablePane.setDragSourceTransceiver(null);
                        }
-               });
-       }
-       /**
-        * {@link Receiver}のドロップを受け付けます。
-        */
-       private void setupDrop() {
-               DropTargetListener dtl = new DropTargetAdapter() {
                        @Override
-                       public void dragEnter(DropTargetDragEvent event) {
-                               if( event.isDataFlavorSupported(DraggingTransceiver.receiverFlavor) ) {
-                                       event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
+                       public boolean canImport(TransferSupport support) {
+                               if( ! support.isDrop() ) return false;
+                               if( support.isDataFlavorSupported(ReceiverListView.elementFlavor) ) {
+                                       Transmitter to = getElementAt(support.getDropLocation().getDropPoint());
+                                       cablePane.setDragDestinationTransceiver(to);
+                                       return true;
                                }
+                               cablePane.setDragDestinationTransceiver(null);
+                               return false;
                        }
                        @Override
-                       public void drop(DropTargetDropEvent event) {
-                               event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-                               if( (event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) == 0 ) {
-                                       event.dropComplete(false);
-                                       return;
-                               }
-                               Transferable t = event.getTransferable();
-                               if( ! t.isDataFlavorSupported(DraggingTransceiver.receiverFlavor) ) {
-                                       event.dropComplete(false);
-                                       return;
-                               }
+                       public boolean importData(TransferSupport support) {
                                try {
-                                       Object rx = t.getTransferData(DraggingTransceiver.receiverFlavor);
-                                       if( rx != null ) {
-                                               Transmitter tx = getElementAt(event.getLocation());
-                                               if( tx instanceof DummyTransmitter ) tx = getModel().openTransmitter();
-                                               tx.setReceiver((Receiver)rx);
-                                               event.dropComplete(true);
-                                               return;
-                                       }
-                               }
-                               catch (Exception ex) {
-                                       ex.printStackTrace();
+                                       Object from = support.getTransferable().getTransferData(ReceiverListView.elementFlavor);
+                                       if( ! (from instanceof Receiver) ) return false;
+                                       Transmitter to = getElementAt(support.getDropLocation().getDropPoint());
+                                       if( to instanceof DummyTransmitter ) to = getModel().openTransmitter();
+                                       to.setReceiver((Receiver)from);
+                                       return true;
+                               } catch (Exception exception) {
+                                       exception.printStackTrace();
+                                       return false;
                                }
-                               event.dropComplete(false);
                        }
-               };
-               new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true );
+               });
        }
 }
index 58ac513..85cabdc 100644 (file)
@@ -6,13 +6,6 @@ import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.Insets;
 import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.Transferable;
-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.dnd.DropTargetListener;
 import java.awt.event.ActionEvent;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
@@ -58,6 +51,7 @@ import javax.swing.JSplitPane;
 import javax.swing.JTable;
 import javax.swing.JToggleButton;
 import javax.swing.ListSelectionModel;
+import javax.swing.TransferHandler;
 import javax.swing.border.EtchedBorder;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
@@ -122,33 +116,23 @@ public class MidiSequenceEditor extends JDialog {
        }
 
        /**
-        * ã\83\89ã\83­ã\83\83ã\83\97ã\81\95ã\82\8cã\81\9fè¤\87æ\95°ã\81®MIDIã\83\95ã\82¡ã\82¤ã\83«ã\82\92読ã\81¿è¾¼ã\82\80ã\83ªã\82¹ã\83\8a
+        * ã\83\89ã\83­ã\83\83ã\83\97ã\81\95ã\82\8cã\81\9fè¤\87æ\95°ã\81®MIDIã\83\95ã\82¡ã\82¤ã\83«ã\82\92読ã\81¿è¾¼ã\82\80ã\83\8fã\83³ã\83\89ã\83©
         */
-       public final DropTargetListener dropTargetListener = new DropTargetAdapter() {
+       public final TransferHandler transferHandler = new TransferHandler() {
                @Override
-               public void dragEnter(DropTargetDragEvent event) {
-                       if( event.isDataFlavorSupported(DataFlavor.javaFileListFlavor) ) {
-                               event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
-                       }
+               public boolean canImport(TransferSupport support) {
+                       return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
                }
-               @Override
                @SuppressWarnings("unchecked")
-               public void drop(DropTargetDropEvent event) {
-                       event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
+               @Override
+               public boolean importData(TransferSupport support) {
                        try {
-                               if ( (event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0 ) {
-                                       Transferable t = event.getTransferable();
-                                       if( t.isDataFlavorSupported(DataFlavor.javaFileListFlavor) ) {
-                                               loadAndPlay((List<File>)t.getTransferData(DataFlavor.javaFileListFlavor));
-                                               event.dropComplete(true);
-                                               return;
-                                       }
-                               }
-                       }
-                       catch (Exception ex) {
-                               ex.printStackTrace();
+                               loadAndPlay((List<File>)support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor));
+                               return true;
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                               return false;
                        }
-                       event.dropComplete(false);
                }
        };
 
@@ -1155,7 +1139,7 @@ public class MidiSequenceEditor extends JDialog {
                setTitle("MIDI Editor/Playlist - MIDI Chord Helper");
                setBounds( 150, 200, 900, 500 );
                setLayout(new FlowLayout());
-               new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, dropTargetListener, true);
+               setTransferHandler(transferHandler);
                //
                // パネルレイアウト
                JPanel playlistPanel = new JPanel() {{