1 package camidion.chordhelper.mididevice;
3 import java.awt.datatransfer.DataFlavor;
4 import java.awt.event.ComponentAdapter;
5 import java.awt.event.ComponentEvent;
6 import java.beans.PropertyVetoException;
7 import java.util.ArrayList;
8 import java.util.HashMap;
12 import javax.sound.midi.MidiUnavailableException;
13 import javax.swing.JDesktopPane;
14 import javax.swing.JInternalFrame;
15 import javax.swing.JLayeredPane;
16 import javax.swing.JOptionPane;
17 import javax.swing.TransferHandler;
18 import javax.swing.event.TreeModelEvent;
19 import javax.swing.event.TreeModelListener;
20 import javax.swing.event.TreeSelectionEvent;
21 import javax.swing.event.TreeSelectionListener;
22 import javax.swing.tree.TreePath;
25 * 開いているMIDIデバイスを置くためのデスクトップビュー
27 public class MidiDeviceDesktopPane extends JDesktopPane implements TreeSelectionListener {
29 * MIDIデバイスモデルからフレームを割り出すためのマップ
31 private Map<MidiDeviceModel, MidiDeviceFrame> frameMap = new HashMap<>();
33 * MIDIデバイスモデルに対応するMIDIデバイスフレームを返すマップを返します。
35 public Map<MidiDeviceModel, MidiDeviceFrame> getFrameMap() {
39 * ツリー上で選択状態が変わったとき、このデスクトップ上のフレームの選択状態に反映します。
42 public void valueChanged(TreeSelectionEvent tse) {
43 TreePath treePath = tse.getNewLeadSelectionPath();
44 if( treePath != null ) {
45 Object lastSelected = treePath.getLastPathComponent();
46 if( lastSelected instanceof MidiDeviceModel ) {
47 MidiDeviceModel deviceModel = (MidiDeviceModel)lastSelected;
48 if( deviceModel.getMidiDevice().isOpen() ) {
49 // 開いているMIDIデバイスがツリー上で選択されたら、対応するフレームを選択
50 MidiDeviceFrame deviceFrame = frameMap.get(deviceModel);
51 if( deviceFrame == null ) return;
52 deviceFrame.toFront();
54 deviceFrame.setSelected(true);
55 } catch( PropertyVetoException ex ) {
62 // それ以外が選択されたら、現在選択されているフレームを非選択
63 JInternalFrame frame = getSelectedFrame();
64 if( ! (frame instanceof MidiDeviceFrame) ) return;
66 ((MidiDeviceFrame)frame).setSelected(false);
67 } catch( PropertyVetoException ex ) {
72 public MidiDeviceDesktopPane(MidiDeviceTreeView deviceTreeView,
73 MidiDeviceInfoPane deviceInfoPane, MidiDeviceDialog dialog)
75 MidiCablePane cablePane = new MidiCablePane(this);
76 add(cablePane, JLayeredPane.PALETTE_LAYER);
78 // リサイズ時、表示時にMIDIケーブルを再描画
79 addComponentListener(new ComponentAdapter() {
81 public void componentResized(ComponentEvent e) {
82 cablePane.setSize(getSize());
86 public void componentShown(ComponentEvent e) { cablePane.repaint(); }
88 // デバイスツリーが変更されたときの更新処理を予約
89 MidiDeviceTreeModel deviceTreeModel = deviceTreeView.getModel();
90 List<MidiDeviceModel> deviceModelList = deviceTreeModel.getDeviceModelList();
91 TreeModelListener treeModelListener = new TreeModelListener() {
93 public void treeNodesChanged(TreeModelEvent e) { }
95 public void treeNodesInserted(TreeModelEvent e) { }
97 public void treeNodesRemoved(TreeModelEvent e) { }
99 public void treeStructureChanged(TreeModelEvent e) {
101 // 削除されたデバイスモデルに対するデバイスフレームをマップから外す
102 List<MidiDeviceModel> removingDeviceModels = new ArrayList<>();
103 for( MidiDeviceModel m : frameMap.keySet() ) {
104 if( ! deviceModelList.contains(m) ) removingDeviceModels.add(m);
106 for( MidiDeviceModel m : removingDeviceModels ) {
107 MidiDeviceFrame frame = frameMap.remove(m);
108 if( frame != null ) remove(frame);
110 removingDeviceModels.clear();
112 // 新しいデバイスモデルに対するデバイスフレームを生成してマップに登録
113 for( MidiDeviceModel deviceModel : deviceModelList ) {
114 if( frameMap.containsKey(deviceModel) ) continue;
115 MidiDeviceFrame frame = new MidiDeviceFrame(deviceModel, cablePane);
116 frameMap.put(deviceModel, frame);
118 // トランスミッタリストモデルが変化したときにMIDIケーブルを再描画
119 TransmitterListModel txListModel = deviceModel.getTransmitterListModel();
120 if( txListModel != null ) txListModel.addListDataListener(cablePane.midiConnecterListDataListener);
122 // レシーバリストモデルが変化したときにMIDIケーブルを再描画
123 ReceiverListModel rxListModel = deviceModel.getReceiverListModel();
124 if( rxListModel != null ) rxListModel.addListDataListener(cablePane.midiConnecterListDataListener);
126 // デバイスフレームが開閉したときの動作
127 frame.addInternalFrameListener(cablePane.midiDeviceFrameListener);
128 frame.addInternalFrameListener(deviceTreeView.midiDeviceFrameListener);
129 frame.addInternalFrameListener(deviceInfoPane.midiDeviceFrameListener);
132 frame.addComponentListener(cablePane.midiDeviceFrameComponentListener);
134 // サイズを設定したフレームをデスクトップに追加
135 frame.setSize(250, deviceModel.getInOutType() == MidiDeviceInOutType.MIDI_IN_OUT ? 90 : 70);
139 if( deviceModel.getMidiDevice().isOpen() ) {
140 frame.setVisible(true);
145 deviceTreeModel.addTreeModelListener(treeModelListener);
146 treeModelListener.treeStructureChanged(null);
151 for( MidiDeviceModel deviceModel : deviceModelList ) {
152 if( ! deviceModel.getMidiDevice().isOpen() ) continue;
153 frameMap.get(deviceModel).setLocation(toX, toY);
154 toX = (toX == 10 ? 270 : 10);
157 deviceTreeView.expandAll();
160 setTransferHandler(new TransferHandler() {
162 public boolean canImport(TransferSupport support) {
163 if( ! support.isDrop() ) return false;
164 if( support.isDataFlavorSupported(MidiDeviceTreeView.deviceModelFlavor) ) {
165 // MIDIデバイスを開くためのドロップを受け付ける
168 if( support.isDataFlavorSupported(TransmitterListView.transmitterFlavor) ) {
169 cablePane.draggedOutOfDestination();
170 // Transmitterの切り離しができるよう、ドロップを容認
173 if( support.isDataFlavorSupported(ReceiverListView.receiverFlavor) ) {
174 cablePane.draggedOutOfDestination();
180 public boolean importData(TransferSupport support) {
181 // canImport()がTransmitterを容認しているので、ここにTransmitterが来ることがある。
182 // そこで、DataFlavorをチェックし、MIDIデバイスでなければ拒否する。
183 DataFlavor flavor = MidiDeviceTreeView.deviceModelFlavor;
184 if( ! support.isDataFlavorSupported(flavor) ) return false;
185 MidiDeviceModel deviceModel = null;
187 deviceModel = (MidiDeviceModel)support.getTransferable().getTransferData(flavor);
188 MidiDeviceFrame deviceFrame = frameMap.get(deviceModel);
189 if( deviceFrame == null ) return false;
191 if( ! deviceModel.getMidiDevice().isOpen() ) {
192 throw new MidiUnavailableException("開いたはずのMIDIデバイスが、開かれた状態になっていません。");
194 if( ! deviceFrame.isVisible() ) {
195 deviceFrame.setLocation(support.getDropLocation().getDropPoint());
196 deviceFrame.setVisible(true);
199 } catch( MidiUnavailableException e ) {
203 // 例えば、「Microsort MIDI マッパー」と「Microsoft GS Wavetable SW Synth」は
204 // 連動して開かれるため、同時に開こうとすると、ここにたどり着く。
206 String title = "Cannot open MIDI device";
207 String message = "MIDIデバイス "+deviceModel+" はすでに使用中です。\n"
208 +"他のデバイスが連動して開いている可能性があります。\n\n"
210 JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
211 } catch (Exception ex) {
212 ex.printStackTrace();