OSDN Git Service

・Java8 VM 環境対応
authorAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sun, 19 Jun 2016 16:29:40 +0000 (01:29 +0900)
committerAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sun, 19 Jun 2016 16:29:40 +0000 (01:29 +0900)
・MIDIデバイス周りのリファクタリング(入れ子クラスの外出しなど)

13 files changed:
.classpath
.settings/org.eclipse.jdt.core.prefs
src/camidion/chordhelper/ChordHelperApplet.java
src/camidion/chordhelper/mididevice/DummyTransmitter.java [new file with mode: 0644]
src/camidion/chordhelper/mididevice/MidiCablePane.java
src/camidion/chordhelper/mididevice/MidiDeviceFrame.java
src/camidion/chordhelper/mididevice/MidiDeviceModel.java
src/camidion/chordhelper/mididevice/MidiDeviceModelList.java
src/camidion/chordhelper/mididevice/MidiOpenedDevicesView.java
src/camidion/chordhelper/mididevice/ReceiverListModel.java [new file with mode: 0644]
src/camidion/chordhelper/mididevice/ReceiverListView.java [moved from src/camidion/chordhelper/mididevice/MidiReceiverListView.java with 92% similarity]
src/camidion/chordhelper/mididevice/TransmitterListModel.java [new file with mode: 0644]
src/camidion/chordhelper/mididevice/TransmitterListView.java [moved from src/camidion/chordhelper/mididevice/MidiTransmitterListView.java with 89% similarity]

index 5c26a12..70858d0 100644 (file)
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<classpath>\r
-       <classpathentry kind="src" path="src"/>\r
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>\r
-       <classpathentry kind="lib" path="lib/commons-codec-1.4.jar"/>\r
-       <classpathentry kind="output" path="bin"/>\r
-</classpath>\r
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="lib" path="lib/commons-codec-1.4.jar"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
index 838bd9d..0c68a61 100644 (file)
@@ -1,11 +1,7 @@
-eclipse.preferences.version=1\r
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7\r
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve\r
-org.eclipse.jdt.core.compiler.compliance=1.7\r
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate\r
-org.eclipse.jdt.core.compiler.debug.localVariable=generate\r
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate\r
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
-org.eclipse.jdt.core.compiler.source=1.7\r
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
index d567cbd..fcda283 100644 (file)
@@ -285,7 +285,7 @@ public class ChordHelperApplet extends JApplet {
         */
        public static class VersionInfo {
                public static final String      NAME = "MIDI Chord Helper";
-               public static final String      VERSION = "Ver.20160619.1";
+               public static final String      VERSION = "Ver.20160619.2";
                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/DummyTransmitter.java b/src/camidion/chordhelper/mididevice/DummyTransmitter.java
new file mode 100644 (file)
index 0000000..0f84d14
--- /dev/null
@@ -0,0 +1,17 @@
+package camidion.chordhelper.mididevice;
+
+import javax.sound.midi.Receiver;
+import javax.sound.midi.Transmitter;
+
+/**
+ * 実体のない新規{@link Transmitter}
+ */
+public class DummyTransmitter implements Transmitter {
+       private Receiver receiver;
+       @Override
+       public void setReceiver(Receiver receiver) { this.receiver = receiver; }
+       @Override
+       public Receiver getReceiver() { return receiver; }
+       @Override
+       public void close() { }
+}
index 5788ee9..e4f4b03 100644 (file)
@@ -154,7 +154,7 @@ public class MidiCablePane extends JComponent implements DragSourceMotionListene
                                g2.drawLine(rxBounds.x, rxBounds.y, draggingPoint.x, draggingPoint.y);
                        }
                        // Transmitterを全部スキャン
-                       MidiDeviceModel.TransmitterListModel txListModel = fromDeviceModel.getTransmitterListModel();
+                       TransmitterListModel txListModel = fromDeviceModel.getTransmitterListModel();
                        int ntx = txListModel == null ? 0 : txListModel.getSize();
                        for( int index=0 ; index < ntx; index++ ) {
                                Transmitter tx = txListModel.getElementAt(index);
index 8399c9a..90785e8 100644 (file)
@@ -23,8 +23,8 @@ import javax.swing.Timer;
  */
 public class MidiDeviceFrame extends JInternalFrame {
        private MidiDeviceModel deviceModel;
-       private MidiTransmitterListView transmitterListView;
-       private MidiReceiverListView receiverListView;
+       private TransmitterListView transmitterListView;
+       private ReceiverListView receiverListView;
        private JScrollPane scrollPane;
        private JPanel trxPanel, txPanel, rxPanel;
        private Timer timer;
@@ -37,12 +37,12 @@ public class MidiDeviceFrame extends JInternalFrame {
         * このデバイスフレームに貼り付けられたMIDIトランスミッタリストビューを取得します。
         * @return MIDIトランスミッタリストビュー
         */
-       public MidiTransmitterListView getMidiTransmitterListView() { return transmitterListView; }
+       public TransmitterListView getMidiTransmitterListView() { return transmitterListView; }
        /**
         * このデバイスフレームに貼り付けられたMIDIトランシーバリストビューを取得します。
         * @return MIDIトランシーバリストビュー
         */
-       public MidiReceiverListView getMidiReceiverListView() { return receiverListView; }
+       public ReceiverListView getMidiReceiverListView() { return receiverListView; }
        /**
         * ダイアログウィンドウがアクティブなときだけタイムスタンプ更新を有効にするためのリスナー
         */
@@ -78,18 +78,18 @@ public class MidiDeviceFrame extends JInternalFrame {
                }}, BorderLayout.SOUTH);
                add(scrollPane = new JScrollPane(trxPanel = new JPanel() {{
                        setLayout(new BorderLayout());
-                       MidiDeviceModel.ReceiverListModel rxListModel = getMidiDeviceModel().getReceiverListModel();
+                       ReceiverListModel rxListModel = getMidiDeviceModel().getReceiverListModel();
                        if( rxListModel != null ) {
-                               receiverListView = new MidiReceiverListView(rxListModel, cablePane);
+                               receiverListView = new ReceiverListView(rxListModel, cablePane);
                                add(rxPanel = new JPanel() {{
                                        setLayout(new BorderLayout());
                                        add(new JLabel("Rx") {{ setVerticalAlignment(TOP); }}, BorderLayout.WEST);
                                        add(receiverListView);
                                }}, BorderLayout.NORTH);
                        }
-                       MidiDeviceModel.TransmitterListModel txListModel = getMidiDeviceModel().getTransmitterListModel();
+                       TransmitterListModel txListModel = getMidiDeviceModel().getTransmitterListModel();
                        if( txListModel != null ) {
-                               transmitterListView = new MidiTransmitterListView(txListModel, cablePane);
+                               transmitterListView = new TransmitterListView(txListModel, cablePane);
                                add(txPanel = new JPanel() {{
                                        setLayout(new BorderLayout());
                                        add(new JLabel("Tx") {{ setVerticalAlignment(TOP); }}, BorderLayout.WEST);
index 43c39ac..71160d6 100644 (file)
 package camidion.chordhelper.mididevice;
 
-import java.util.List;
-import java.util.Vector;
-
 import javax.sound.midi.MidiDevice;
 import javax.sound.midi.MidiUnavailableException;
 import javax.sound.midi.Receiver;
-import javax.sound.midi.Sequencer;
 import javax.sound.midi.Transmitter;
-import javax.swing.AbstractListModel;
 import javax.swing.tree.TreePath;
 
 /**
  * 1個の{@link MidiDevice}を表すモデル
  */
 public class MidiDeviceModel {
-
-       /**
-        * {@link Receiver} のリストを表す {@link javax.swing.ListModel}
-        */
-       public class ReceiverListModel extends AbstractListModel<Receiver> {
-               @Override
-               public Receiver getElementAt(int index) { return device.getReceivers().get(index); }
-               @Override
-               public int getSize() { return device.getReceivers().size(); }
-               /**
-                * 指定の要素がこのリストモデルで最初に見つかった位置を返します。
-                *
-                * @param element 探したい要素
-                * @return 位置のインデックス(先頭が 0、見つからないとき -1)
-                */
-               public int indexOf(Object element) { return device.getReceivers().indexOf(element); }
-               /**
-                * {@link MidiDevice#getReceiver()} でレシーバを1個開きます。
-                * すでに1個開いている場合は無視されます。
-                *
-                * @throws MidiUnavailableException リソースの制約のためにレシーバを使用できない場合にスローされる
-                */
-               public void openReceiver() throws MidiUnavailableException {
-                       if( device.getReceivers().isEmpty() ) device.getReceiver();
-               }
-               /**
-                * このリストモデルの{@link Receiver}に接続された他デバイスの{@link Transmitter}を全て閉じます。
-                */
-               public void closePeerTransmitters() {
-                       List<Receiver> rxList = device.getReceivers();
-                       for( Receiver rx : rxList ) {
-                               for( MidiDeviceModel m : deviceModelList ) {
-                                       if( m == MidiDeviceModel.this || m.txListModel == null ) continue;
-                                       m.txListModel.closePeerTransmitterOf(rx);
-                               }
-                       }
-               }
-       }
-
-       /**
-        * 実体のない新規{@link Transmitter}を表すインターフェース
-        */
-       interface NewTransmitter extends Transmitter {};
        /**
-        * {@link Transmitter} のリストを表す {@link javax.swing.ListModel}
+        * {@link javax.swing.JTree}で使用するツリー表示のパスを返します。
+        * @return ツリーパス
         */
-       public class TransmitterListModel extends AbstractListModel<Transmitter> {
-               private NewTransmitter newTransmitter = new NewTransmitter() {
-                       private Receiver receiver;
-                       @Override
-                       public void setReceiver(Receiver receiver) { this.receiver = receiver; }
-                       @Override
-                       public Receiver getReceiver() { return receiver; }
-                       @Override
-                       public void close() { }
-               };
-               @Override
-               public Transmitter getElementAt(int index) {
-                       List<Transmitter> txList = device.getTransmitters();
-                       int length = txList.size();
-                       if( index == length ) return newTransmitter;
-                       if( index > length || index < 0 ) return null;
-                       return txList.get(index);
-               }
-               @Override
-               public int getSize() { return device.getTransmitters().size() + 1; }
-               /**
-                * 指定の要素がこのリストモデルで最初に見つかった位置(先頭が 0)を返します。
-                * 見つからなかった場合は -1 を返します。
-                *
-                * @param element 探したい要素
-                * @return 位置
-                */
-               public int indexOf(Object element) {
-                       List<Transmitter> txList = device.getTransmitters();
-                       return element.equals(newTransmitter) ? txList.size() : txList.indexOf(element);
-               }
-               /**
-                * レシーバに未接続の最初の{@link Transmitter}を返します。
-                * ない場合は{@link MidiDevice#getTransmitter}で新たに取得して返します。
-                *
-                * @return 未接続の{@link Transmitter}
-                * @throws MidiUnavailableException リソースの制約のためにトランスミッタを使用できない場合にスローされる
-                */
-               public Transmitter getUnconnectedTransmitter() throws MidiUnavailableException {
-                       List<Transmitter> txList = device.getTransmitters();
-                       for( Transmitter tx : txList ) if( tx.getReceiver() == null ) return tx;
-                       Transmitter tx;
-                       tx = device.getTransmitter();
-                       fireIntervalAdded(this, 0, getSize());
-                       return tx;
-               }
-               /**
-                * このリストモデルの{@link #getUnconnectedTransmitter()}が返した{@link Transmitter}を、
-                * 相手のMIDIデバイスが持つ最初の{@link Receiver}に接続します。
-                *
-                * @param anotherDeviceModel 接続相手のMIDIデバイス
-                * @throws MidiUnavailableException リソースの制約のためにトランスミッタを使用できない場合にスローされる
-                */
-               public void connectToFirstReceiverOfDevice(MidiDeviceModel anotherDeviceModel) throws MidiUnavailableException {
-                       List<Receiver> rxList = anotherDeviceModel.device.getReceivers();
-                       if( ! rxList.isEmpty() ) txListModel.getUnconnectedTransmitter().setReceiver(rxList.get(0));
-               }
-               /**
-                * 指定の{@link Transmitter}がリストにあれば、それを閉じます。
-                * 閉じるとリストから自動的に削除されるので、表示の更新も行います。
-                *
-                * @param tx このリストモデルで開いている{@link Transmitter}
-                */
-               public void close(Transmitter tx) {
-                       if( ! device.getTransmitters().contains(tx) ) return;
-                       tx.close();
-                       fireIntervalRemoved(this, 0, getSize());
-               }
-               /**
-                * このリストモデルにある{@link Transmitter}のうち、
-                * 引数で指定された{@link Receiver}へデータを送信しているものを全て閉じます。
-                * 閉じるとリストから自動的に削除されるので、表示の更新も行います。
-                */
-               private void closePeerTransmitterOf(Receiver rx) {
-                       List<Transmitter> originalTxList = device.getTransmitters();
-                       List<Transmitter> closingTxList = new Vector<Transmitter>();
-                       for( Transmitter tx : originalTxList ) if( tx.getReceiver() == rx ) closingTxList.add(tx);
-                       if( closingTxList.isEmpty() ) return;
-                       int length = getSize();
-                       for( Transmitter tx : closingTxList ) tx.close();
-                       fireIntervalRemoved(this, 0, length);
-               }
-               /**
-                * マイクロ秒位置をリセットします。
-                * <p>マイクロ秒位置はMIDIデバイスを開いてからの時間で表されます。
-                * このメソッドではMIDIデバイスをいったん閉じて再び開くことによって
-                * 時間位置をリセットします。
-                * 接続相手のデバイスがあった場合、元通りに接続を復元します。
-                * </p>
-                * <p>MIDIデバイスからリアルタイムレコーディングを開始するときは、
-                * 必ずマイクロ秒位置をリセットする必要があります。
-                * (リセットされていないマイクロ秒位置がそのままシーケンサに記録されると、
-                * 大幅に後ろのほうにずれて記録されてしまいます)
-                * </p>
-                */
-               public void resetMicrosecondPosition() {
-                       //
-                       // シーケンサはこのメソッドでのリセット対象外
-                       if( device instanceof Sequencer ) return;
-                       //
-                       // デバイスを閉じる前に接続状態を把握
-                       List<Transmitter> myTxList = device.getTransmitters();
-                       List<Receiver> peerRxList = new Vector<Receiver>();
-                       for( Transmitter tx : myTxList ) {
-                               Receiver rx = tx.getReceiver();
-                               if( rx != null ) peerRxList.add(rx);
-                       }
-                       List<Receiver> myRxList = device.getReceivers();
-                       List<Transmitter> peerTxList = new Vector<Transmitter>();
-                       for( Receiver rx : myRxList ) {
-                               for( MidiDeviceModel m : deviceModelList ) {
-                                       if( m == MidiDeviceModel.this ) continue;
-                                       List<Transmitter> peerSourceTxList = m.getMidiDevice().getTransmitters();
-                                       for( Transmitter tx : peerSourceTxList ) if( tx.getReceiver() == rx ) peerTxList.add(tx);
-                               }
-                       }
-                       // いったん閉じて開く(ここでマイクロ秒位置がリセットされる)
-                       // その後、元通りに接続し直す
-                       device.close();
-                       try {
-                               device.open();
-                               for( Receiver peerRx : peerRxList ) getUnconnectedTransmitter().setReceiver(peerRx);
-                               if( ! myRxList.isEmpty() ) {
-                                       Receiver rx = myRxList.get(0);
-                                       for( Transmitter peerTx : peerTxList ) peerTx.setReceiver(rx);
-                               }
-                       } catch( MidiUnavailableException e ) {
-                               e.printStackTrace();
-                       }
-               }
-       }
-
+       public TreePath getTreePath() { return treePath; }
+       private TreePath treePath;
        /**
         * このリストのMIDIデバイスの入出力タイプを返します。
         * @return このリストのMIDIデバイスの入出力タイプ
         */
        public MidiDeviceInOutType getMidiDeviceInOutType() { return ioType; }
        private MidiDeviceInOutType ioType;
-
-       /**
-        * {@link javax.swing.JTree}で使用するツリー表示のパスを返します。
-        * @return ツリーパス
-        */
-       public TreePath getTreePath() { return treePath; }
-       private TreePath treePath;
-
        /**
         * 対象MIDIデバイスを返します。
         * @return 対象MIDIデバイス
@@ -219,17 +34,6 @@ public class MidiDeviceModel {
        @Override
        public String toString() { return device.getDeviceInfo().toString(); }
        /**
-        * このデバイスモデルが {@link Transmitter} をサポートしているか調べます。
-        * @return {@link Transmitter} をサポートしていたら true
-        */
-       public boolean txSupported() { return device.getMaxTransmitters() != 0; }
-       /**
-        * このデバイスモデルが {@link Receiver} をサポートしているか調べます。
-        * @return {@link Receiver} をサポートしていたら true
-        */
-       public boolean rxSupported() { return device.getMaxReceivers() != 0; }
-
-       /**
         * {@link Transmitter} のリストモデルを返します。サポートしていない場合はnullを返します。
         * @return リストモデルまたはnull
         */
@@ -241,26 +45,27 @@ public class MidiDeviceModel {
         */
        public ReceiverListModel getReceiverListModel() { return rxListModel; }
        private ReceiverListModel rxListModel;
-
+       /**
+        * このMIDIデバイスモデルを収容しているリストを返します。
+        */
+       public MidiDeviceModelList getDeviceModelList() { return deviceModelList; }
        protected MidiDeviceModelList deviceModelList;
        /**
         * MIDIデバイスモデルを構築します。
         *
         * @param device 対象MIDIデバイス
-        * @param deviceModelList 接続相手となりうるMIDIデバイスのリスト
+        * @param deviceModelList このMIDIデバイスモデルを収容しているリスト(接続相手となりうるMIDIデバイス)
         */
        public MidiDeviceModel(MidiDevice device, MidiDeviceModelList deviceModelList) {
                this.device = device;
                this.deviceModelList = deviceModelList;
-               if( txSupported() ) {
-                       txListModel = new TransmitterListModel();
-               }
-               if( rxSupported() ) {
-                       ioType = txSupported() ? MidiDeviceInOutType.MIDI_IN_OUT :MidiDeviceInOutType.MIDI_OUT;
-                       rxListModel = new ReceiverListModel();
+               if( device.getMaxTransmitters() != 0 ) txListModel = new TransmitterListModel(this);
+               if( device.getMaxReceivers() != 0 ) {
+                       rxListModel = new ReceiverListModel(this);
+                       ioType = txListModel != null ? MidiDeviceInOutType.MIDI_IN_OUT :MidiDeviceInOutType.MIDI_OUT;
                }
                else {
-                       ioType = txSupported() ? MidiDeviceInOutType.MIDI_IN :MidiDeviceInOutType.MIDI_NONE;
+                       ioType = txListModel != null ? MidiDeviceInOutType.MIDI_IN :MidiDeviceInOutType.MIDI_NONE;
                }
                treePath = new TreePath(new Object[] {deviceModelList, ioType ,this});
        }
@@ -274,7 +79,7 @@ public class MidiDeviceModel {
         */
        public void open() throws MidiUnavailableException {
                device.open();
-               if( rxListModel != null ) rxListModel.openReceiver();
+               if( rxListModel != null ) rxListModel.openSingleReceiver();
        }
        /**
         * このMIDIデバイスを {@link MidiDevice#close()} で閉じます。
index 22f21dd..6fcbbd9 100644 (file)
@@ -67,8 +67,8 @@ public class MidiDeviceModelList extends Vector<MidiDeviceModel> {
                        // その他のMIDIデバイス
                        MidiDeviceModel m;
                        addElement(m = new MidiDeviceModel(device, this));
-                       if( m.rxSupported() && firstMidiOutModel == null ) firstMidiOutModel = m;
-                       if( m.txSupported() && firstMidiInModel == null ) firstMidiInModel = m;
+                       if( firstMidiOutModel == null && m.getReceiverListModel() != null ) firstMidiOutModel = m;
+                       if( firstMidiInModel == null && m.getTransmitterListModel() != null ) firstMidiInModel = m;
                }
                // MIDIデバイスを開く。
                //   NOTE: 必ず MIDI OUT Rx デバイスを先に開くこと。
@@ -92,7 +92,7 @@ public class MidiDeviceModelList extends Vector<MidiDeviceModel> {
                        for( MidiDeviceModel m : guiModels ) m.open();
                        //
                        // 初期接続
-                       MidiDeviceModel.TransmitterListModel txListModel;
+                       TransmitterListModel txListModel;
                        for( MidiDeviceModel mtx : guiModels ) {
                                if( (txListModel = mtx.getTransmitterListModel() ) != null) {
                                        for( MidiDeviceModel m : guiModels ) txListModel.connectToFirstReceiverOfDevice(m);
@@ -122,7 +122,7 @@ public class MidiDeviceModelList extends Vector<MidiDeviceModel> {
         */
        public void resetMicrosecondPosition() {
                for(MidiDeviceModel m : this) {
-                       MidiDeviceModel.TransmitterListModel txListModel = m.getTransmitterListModel();
+                       TransmitterListModel txListModel = m.getTransmitterListModel();
                        if( txListModel != null ) txListModel.resetMicrosecondPosition();
                }
        }
index deb6bbc..1234078 100644 (file)
@@ -157,13 +157,13 @@ public class MidiOpenedDevicesView extends JDesktopPane implements TreeSelection
                        modelToFrame.put(deviceModel, frame);
                        //
                        // トランスミッタリストモデルが変化したときにMIDIケーブルを再描画
-                       if( deviceModel.txSupported() ) {
-                               deviceModel.getTransmitterListModel().addListDataListener(cablePane.midiConnecterListDataListener);
-                       }
+                       TransmitterListModel txListModel = deviceModel.getTransmitterListModel();
+                       if( txListModel != null ) txListModel.addListDataListener(cablePane.midiConnecterListDataListener);
+                       //
                        // レシーバリストモデルが変化したときにMIDIケーブルを再描画
-                       if( deviceModel.rxSupported() ) {
-                               deviceModel.getReceiverListModel().addListDataListener(cablePane.midiConnecterListDataListener);
-                       }
+                       ReceiverListModel rxListModel = deviceModel.getReceiverListModel();
+                       if( rxListModel != null ) rxListModel.addListDataListener(cablePane.midiConnecterListDataListener);
+                       //
                        // デバイスフレームが開閉したときの動作
                        frame.addInternalFrameListener(cablePane.midiDeviceFrameListener);
                        frame.addInternalFrameListener(deviceTreeView.midiDeviceFrameListener);
diff --git a/src/camidion/chordhelper/mididevice/ReceiverListModel.java b/src/camidion/chordhelper/mididevice/ReceiverListModel.java
new file mode 100644 (file)
index 0000000..4af7c69
--- /dev/null
@@ -0,0 +1,53 @@
+package camidion.chordhelper.mididevice;
+
+import java.util.List;
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.Receiver;
+import javax.sound.midi.Transmitter;
+import javax.swing.AbstractListModel;
+
+/**
+ * {@link Receiver} のリストを表す {@link javax.swing.ListModel}
+ */
+public class ReceiverListModel extends AbstractListModel<Receiver> {
+       protected MidiDeviceModel deviceModel;
+       public ReceiverListModel(MidiDeviceModel deviceModel) {
+               this.deviceModel = deviceModel;
+       }
+       @Override
+       public Receiver getElementAt(int index) {
+               return deviceModel.getMidiDevice().getReceivers().get(index);
+       }
+       @Override
+       public int getSize() {
+               return deviceModel.getMidiDevice().getReceivers().size();
+       }
+       public int indexOf(Object element) {
+               return deviceModel.getMidiDevice().getReceivers().indexOf(element);
+       }
+       /**
+        * {@link Receiver}を最大1個開きます。
+        * @throws MidiUnavailableException リソースの制約のためにレシーバを使用できない場合にスローされる
+        */
+       public void openSingleReceiver() throws MidiUnavailableException {
+               MidiDevice device = deviceModel.getMidiDevice();
+               if( device.getReceivers().isEmpty() ) device.getReceiver();
+       }
+       /**
+        * このリストモデルの{@link Receiver}に接続された他デバイスの{@link Transmitter}を全て閉じます。
+        */
+       public void closePeerTransmitters() {
+               List<Receiver> rxList = deviceModel.getMidiDevice().getReceivers();
+               MidiDeviceModelList deviceModelList = deviceModel.getDeviceModelList();
+               for( Receiver rx : rxList ) {
+                       for( MidiDeviceModel m : deviceModelList ) {
+                               if( m == deviceModel ) continue;
+                               TransmitterListModel txListModel = m.getTransmitterListModel();
+                               if( txListModel == null ) continue;
+                               txListModel.closePeerTransmitterOf(rx);
+                       }
+               }
+       }
+}
@@ -23,9 +23,9 @@ import javax.swing.ListSelectionModel;
 /**
  * MIDIレシーバ({@link Receiver})のリストビューです。
  * レシーバをこのビューからドラッグし、
- * {@link MidiTransmitterListView} のトランスミッタにドロップして接続できます。
+ * {@link TransmitterListView} のトランスミッタにドロップして接続できます。
  */
-public class MidiReceiverListView extends JList<Receiver> {
+public class ReceiverListView extends JList<Receiver> {
        /**
         * レシーバを描画するクラス
         */
@@ -62,15 +62,15 @@ public class MidiReceiverListView extends JList<Receiver> {
         * @return 表示される{@link Receiver}のリストを提供するデータモデル
         */
        @Override
-       public MidiDeviceModel.ReceiverListModel getModel() {
-               return (MidiDeviceModel.ReceiverListModel) super.getModel();
+       public ReceiverListModel getModel() {
+               return (ReceiverListModel) super.getModel();
        }
        /**
         * 仮想MIDI端子リストビューを生成します。
         * @param model このビューから参照されるデータモデル
         * @param cablePane MIDIケーブル描画面
         */
-       public MidiReceiverListView(MidiDeviceModel.ReceiverListModel model, final MidiCablePane cablePane) {
+       public ReceiverListView(ReceiverListModel model, final MidiCablePane cablePane) {
                super(model);
                setCellRenderer(new CellRenderer());
                setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
diff --git a/src/camidion/chordhelper/mididevice/TransmitterListModel.java b/src/camidion/chordhelper/mididevice/TransmitterListModel.java
new file mode 100644 (file)
index 0000000..37dbecb
--- /dev/null
@@ -0,0 +1,141 @@
+package camidion.chordhelper.mididevice;
+
+import java.util.List;
+import java.util.Vector;
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.Receiver;
+import javax.sound.midi.Sequencer;
+import javax.sound.midi.Transmitter;
+import javax.swing.AbstractListModel;
+
+/**
+ * {@link Transmitter} のリストを表す {@link javax.swing.ListModel}
+ */
+public class TransmitterListModel extends AbstractListModel<Transmitter> {
+       protected Transmitter dummyTransmitter = new DummyTransmitter();
+       protected MidiDeviceModel deviceModel;
+       public TransmitterListModel(MidiDeviceModel deviceModel) {
+               this.deviceModel = deviceModel;
+       }
+       @Override
+       public Transmitter getElementAt(int index) {
+               List<Transmitter> txList = deviceModel.getMidiDevice().getTransmitters();
+               int length = txList.size();
+               if( index == length ) return dummyTransmitter;
+               if( index > length || index < 0 ) return null;
+               return txList.get(index);
+       }
+       @Override
+       public int getSize() {
+               return deviceModel.getMidiDevice().getTransmitters().size() + 1;
+       }
+       public int indexOf(Object element) {
+               List<Transmitter> txList = deviceModel.getMidiDevice().getTransmitters();
+               return dummyTransmitter.equals(element) ? txList.size() : txList.indexOf(element);
+       }
+       /**
+        * レシーバに未接続の最初の{@link Transmitter}を返します。
+        * ない場合は{@link MidiDevice#getTransmitter}で新たに取得して返します。
+        *
+        * @return 未接続の{@link Transmitter}
+        * @throws MidiUnavailableException リソースの制約のためにトランスミッタを使用できない場合にスローされる
+        */
+       public Transmitter getUnconnectedTransmitter() throws MidiUnavailableException {
+               MidiDevice device = deviceModel.getMidiDevice();
+               List<Transmitter> txList = device.getTransmitters();
+               for( Transmitter tx : txList ) if( tx.getReceiver() == null ) return tx;
+               Transmitter tx;
+               tx = device.getTransmitter();
+               fireIntervalAdded(this, 0, getSize());
+               return tx;
+       }
+       /**
+        * このリストモデルの{@link #getUnconnectedTransmitter()}が返した{@link Transmitter}を、
+        * 相手のMIDIデバイスが持つ最初の{@link Receiver}に接続します。
+        *
+        * @param anotherDeviceModel 接続相手のMIDIデバイス
+        * @throws MidiUnavailableException リソースの制約のためにトランスミッタを使用できない場合にスローされる
+        */
+       public void connectToFirstReceiverOfDevice(MidiDeviceModel anotherDeviceModel) throws MidiUnavailableException {
+               List<Receiver> rxList = anotherDeviceModel.getMidiDevice().getReceivers();
+               if( ! rxList.isEmpty() )
+                       deviceModel.getTransmitterListModel().getUnconnectedTransmitter().setReceiver(rxList.get(0));
+       }
+       /**
+        * 指定の{@link Transmitter}がリストにあれば、それを閉じます。
+        * 閉じるとリストから自動的に削除されるので、表示の更新も行います。
+        *
+        * @param tx このリストモデルで開いている{@link Transmitter}
+        */
+       public void close(Transmitter tx) {
+               if( ! deviceModel.getMidiDevice().getTransmitters().contains(tx) ) return;
+               tx.close();
+               fireIntervalRemoved(this, 0, getSize());
+       }
+       /**
+        * このリストモデルにある{@link Transmitter}のうち、
+        * 引数で指定された{@link Receiver}へデータを送信しているものを全て閉じます。
+        * 閉じるとリストから自動的に削除されるので、表示の更新も行います。
+        */
+       public void closePeerTransmitterOf(Receiver rx) {
+               List<Transmitter> closingTxList = new Vector<Transmitter>();
+               List<Transmitter> txList = deviceModel.getMidiDevice().getTransmitters();
+               for( Transmitter tx : txList ) if( tx.getReceiver() == rx ) closingTxList.add(tx);
+               if( closingTxList.isEmpty() ) return;
+               int length = getSize();
+               for( Transmitter tx : closingTxList ) tx.close();
+               fireIntervalRemoved(this, 0, length);
+       }
+       /**
+        * マイクロ秒位置をリセットします。
+        * <p>マイクロ秒位置はMIDIデバイスを開いてからの時間で表されます。
+        * このメソッドではMIDIデバイスをいったん閉じて再び開くことによって
+        * 時間位置をリセットします。
+        * 接続相手のデバイスがあった場合、元通りに接続を復元します。
+        * </p>
+        * <p>MIDIデバイスからリアルタイムレコーディングを開始するときは、
+        * 必ずマイクロ秒位置をリセットする必要があります。
+        * (リセットされていないマイクロ秒位置がそのままシーケンサに記録されると、
+        * 大幅に後ろのほうにずれて記録されてしまいます)
+        * </p>
+        */
+       public void resetMicrosecondPosition() {
+               MidiDevice device = deviceModel.getMidiDevice();
+               //
+               // シーケンサはこのメソッドでのリセット対象外
+               if( device instanceof Sequencer ) return;
+               //
+               // デバイスを閉じる前に接続状態を把握
+               List<Receiver> peerRxList = new Vector<Receiver>();
+               List<Transmitter> txList = device.getTransmitters();
+               for( Transmitter tx : txList ) {
+                       Receiver rx = tx.getReceiver();
+                       if( rx != null ) peerRxList.add(rx);
+               }
+               List<Receiver> myRxList = device.getReceivers();
+               List<Transmitter> peerTxList = new Vector<Transmitter>();
+               MidiDeviceModelList deviceModelList = deviceModel.getDeviceModelList();
+               for( Receiver rx : myRxList ) {
+                       for( MidiDeviceModel m : deviceModelList ) {
+                               if( m == deviceModel ) continue;
+                               List<Transmitter> peerSourceTxList = m.getMidiDevice().getTransmitters();
+                               for( Transmitter tx : peerSourceTxList ) if( tx.getReceiver() == rx ) peerTxList.add(tx);
+                       }
+               }
+               // いったん閉じて開く(ここでマイクロ秒位置がリセットされる)
+               // その後、元通りに接続し直す
+               device.close();
+               try {
+                       device.open();
+                       for( Receiver peerRx : peerRxList ) getUnconnectedTransmitter().setReceiver(peerRx);
+                       if( ! myRxList.isEmpty() ) {
+                               Receiver rx = myRxList.get(0);
+                               for( Transmitter peerTx : peerTxList ) peerTx.setReceiver(rx);
+                       }
+               } catch( MidiUnavailableException e ) {
+                       e.printStackTrace();
+               }
+       }
+}
\ No newline at end of file
@@ -22,14 +22,12 @@ import javax.swing.JList;
 import javax.swing.ListCellRenderer;
 import javax.swing.ListSelectionModel;
 
-import camidion.chordhelper.mididevice.MidiDeviceModel.NewTransmitter;
-
 /**
  * MIDIトランスミッタ({@link Transmitter})のリストビューです。
  * トランスミッタをこのビューからドラッグし、
- * {@link MidiReceiverListView} のレシーバにドロップして接続できます。
+ * {@link ReceiverListView} のレシーバにドロップして接続できます。
  */
-public class MidiTransmitterListView extends JList<Transmitter> {
+public class TransmitterListView extends JList<Transmitter> {
        /**
         * トランスミッタを描画するクラス
         */
@@ -48,7 +46,7 @@ public class MidiTransmitterListView extends JList<Transmitter> {
                                setForeground(list.getForeground());
                        }
                        setIcon(MidiDeviceDialog.MIDI_CONNECTER_ICON);
-                       if( value instanceof MidiDeviceModel.NewTransmitter ) {
+                       if( value instanceof DummyTransmitter ) {
                                setToolTipText("未接続の送信端子(Tx):ドラッグ&ドロップしてRxに接続できます。");
                        } else {
                                setToolTipText("接続済の送信端子(Tx):ドラッグ&ドロップして接続先Rxを変更、または切断できます。");
@@ -70,15 +68,15 @@ public class MidiTransmitterListView extends JList<Transmitter> {
         * @return 表示される{@link Transmitter}のリストを提供するデータモデル
         */
        @Override
-       public MidiDeviceModel.TransmitterListModel getModel() {
-               return (MidiDeviceModel.TransmitterListModel) super.getModel();
+       public TransmitterListModel getModel() {
+               return (TransmitterListModel) super.getModel();
        }
        /**
         * 仮想MIDI端子リストビューを生成します。
         * @param model このビューから参照されるデータモデル
         * @param cablePane MIDIケーブル描画面
         */
-       public MidiTransmitterListView(MidiDeviceModel.TransmitterListModel model, final MidiCablePane cablePane) {
+       public TransmitterListView(TransmitterListModel model, final MidiCablePane cablePane) {
                super(model);
                setCellRenderer(new CellRenderer());
                setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@@ -99,7 +97,7 @@ public class MidiTransmitterListView extends JList<Transmitter> {
                                                if( ! event.getDropSuccess() ) {
                                                        // 所定の場所にドロップされなかったトランスミッタを閉じる
                                                        getModel().close(droppedTx);
-                                               } else if( droppedTx instanceof MidiDeviceModel.NewTransmitter ) {
+                                               } else if( droppedTx instanceof DummyTransmitter ) {
                                                        // ドロップされたダミートランスミッタに接続されたレシーバを
                                                        // 新しい本物のトランスミッタに付け替える
                                                        try {
@@ -143,7 +141,7 @@ public class MidiTransmitterListView extends JList<Transmitter> {
                                        Object sourceRx = t.getTransferData(DraggingTransceiver.receiverFlavor);
                                        if( sourceRx != null ) {
                                                Transmitter tx = getModel().getElementAt(locationToIndex(event.getLocation()));
-                                               if( tx instanceof NewTransmitter ) tx = getModel().getUnconnectedTransmitter();
+                                               if( tx instanceof DummyTransmitter ) tx = getModel().getUnconnectedTransmitter();
                                                tx.setReceiver((Receiver)sourceRx);
                                                event.dropComplete(true);
                                                return;