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.HashMap;
9 import java.util.stream.Collectors;
11 import javax.sound.midi.MidiUnavailableException;
12 import javax.swing.JDesktopPane;
13 import javax.swing.JInternalFrame;
14 import javax.swing.JLayeredPane;
15 import javax.swing.JOptionPane;
16 import javax.swing.TransferHandler;
17 import javax.swing.event.TreeModelEvent;
18 import javax.swing.event.TreeModelListener;
19 import javax.swing.event.TreeSelectionEvent;
20 import javax.swing.event.TreeSelectionListener;
21 import javax.swing.tree.TreePath;
23 import camidion.chordhelper.ChordHelperApplet;
26 * 開いているMIDIデバイスを置くためのデスクトップビュー
28 public class MidiDeviceDesktopPane extends JDesktopPane implements TreeSelectionListener {
30 * MIDIデバイスモデルからフレームを割り出すためのマップ
32 private Map<MidiDeviceModel, MidiDeviceFrame> frameOfModel = new HashMap<>();
34 * MIDIデバイスツリービューで選択状態が変わったとき、
35 * このデスクトップで表示しているフレームの選択状態に反映します。
38 public void valueChanged(TreeSelectionEvent tse) {
39 TreePath selectedTreePath = tse.getNewLeadSelectionPath();
40 if( selectedTreePath != null ) {
41 Object selectedLeaf = selectedTreePath.getLastPathComponent();
42 if( selectedLeaf instanceof MidiDeviceModel ) {
43 MidiDeviceModel selectedModel = (MidiDeviceModel)selectedLeaf;
44 if( selectedModel.getMidiDevice().isOpen() ) {
45 // 開いているMIDIデバイスがツリー上で選択されたら、対応するフレームを選択
46 MidiDeviceFrame deviceFrame = frameOfModel.get(selectedModel);
47 if( deviceFrame != null ) {
48 deviceFrame.toFront();
50 deviceFrame.setSelected(true);
51 } catch( PropertyVetoException ex ) {
59 // それ以外が選択されたら、現在選択されているフレームを非選択
60 JInternalFrame frame = getSelectedFrame();
61 if( frame instanceof MidiDeviceFrame ) try {
62 ((MidiDeviceFrame)frame).setSelected(false);
63 } catch( PropertyVetoException ex ) {
68 public MidiDeviceDesktopPane(MidiDeviceTreeView deviceTreeView,
69 MidiDeviceInfoPane deviceInfoPane, MidiDeviceDialog dialog)
71 MidiCablePane cablePane = new MidiCablePane(this);
72 add(cablePane, JLayeredPane.PALETTE_LAYER);
74 // リサイズ時、表示時にMIDIケーブルを再描画
75 addComponentListener(new ComponentAdapter() {
77 public void componentResized(ComponentEvent e) {
78 cablePane.setSize(getSize());
82 public void componentShown(ComponentEvent e) { cablePane.repaint(); }
84 // デバイスツリーが変更されたときの更新処理を予約
85 MidiDeviceTreeModel deviceTreeModel = deviceTreeView.getModel();
86 TreeModelListener treeModelListener = new TreeModelListener() {
88 public void treeNodesChanged(TreeModelEvent e) { }
90 public void treeNodesInserted(TreeModelEvent e) { }
92 public void treeNodesRemoved(TreeModelEvent e) { }
94 public void treeStructureChanged(TreeModelEvent e) {
95 // ツリーから削除されたMIDIデバイスモデルのフレームを削除
96 frameOfModel.keySet().stream()
97 .filter(m-> ! deviceTreeModel.contains(m))
98 .collect(Collectors.toSet()).stream()
99 .map(m-> frameOfModel.remove(m))
100 .filter(f-> f != null)
101 .forEach(f-> remove(f));
103 // ツリーに追加されたMIDIデバイスモデルのフレームを生成
104 deviceTreeModel.stream()
105 .filter(dm -> ! frameOfModel.containsKey(dm))
107 MidiDeviceFrame frame = new MidiDeviceFrame(dm, cablePane);
108 frameOfModel.put(dm, frame);
110 // トランスミッタリストモデルが変化したときにMIDIケーブルを再描画
111 TransmitterListModel txListModel = dm.getTransmitterListModel();
112 if( txListModel != null ) txListModel.addListDataListener(cablePane.midiConnecterListDataListener);
114 // レシーバリストモデルが変化したときにMIDIケーブルを再描画
115 ReceiverListModel rxListModel = dm.getReceiverListModel();
116 if( rxListModel != null ) rxListModel.addListDataListener(cablePane.midiConnecterListDataListener);
118 // デバイスフレームが開閉したときの動作
119 frame.addInternalFrameListener(cablePane.midiDeviceFrameListener);
120 frame.addInternalFrameListener(deviceTreeView.midiDeviceFrameListener);
121 frame.addInternalFrameListener(deviceInfoPane.midiDeviceFrameListener);
124 frame.addComponentListener(cablePane.midiDeviceFrameComponentListener);
126 // サイズを設定したフレームをデスクトップに追加
127 frame.setSize(250, dm.getInOutType() == MidiDeviceInOutType.MIDI_IN_OUT ? 90 : 70);
131 if( dm.getMidiDevice().isOpen() ) frame.setVisible(true);
135 deviceTreeModel.addTreeModelListener(treeModelListener);
136 treeModelListener.treeStructureChanged(null);
141 for( MidiDeviceModel deviceModel : deviceTreeModel ) {
142 if( ! deviceModel.getMidiDevice().isOpen() ) continue;
143 frameOfModel.get(deviceModel).setLocation(toX, toY);
144 toX = (toX == 10 ? 270 : 10);
147 deviceTreeView.expandAll();
150 setTransferHandler(new TransferHandler() {
152 public boolean canImport(TransferSupport support) {
153 if( ! support.isDrop() ) return false;
154 if( support.isDataFlavorSupported(MidiDeviceTreeView.deviceModelFlavor) ) {
155 // MIDIデバイスを開くためのドロップを受け付ける
158 if( support.isDataFlavorSupported(TransmitterListView.transmitterFlavor) ) {
159 cablePane.draggedOutOfDestination();
160 // Transmitterの切り離しができるよう、ドロップを容認
163 if( support.isDataFlavorSupported(ReceiverListView.receiverFlavor) ) {
164 cablePane.draggedOutOfDestination();
170 public boolean importData(TransferSupport support) {
171 // canImport()がTransmitterを容認しているので、ここにTransmitterが来ることがある。
172 // そこで、DataFlavorをチェックし、MIDIデバイスでなければ拒否する。
173 DataFlavor flavor = MidiDeviceTreeView.deviceModelFlavor;
174 if( ! support.isDataFlavorSupported(flavor) ) return false;
175 MidiDeviceModel deviceModel = null;
177 deviceModel = (MidiDeviceModel)support.getTransferable().getTransferData(flavor);
178 MidiDeviceFrame deviceFrame = frameOfModel.get(deviceModel);
179 if( deviceFrame == null ) return false;
181 if( ! deviceModel.getMidiDevice().isOpen() ) {
182 throw new MidiUnavailableException("開いたはずのMIDIデバイスが、開かれた状態になっていません。");
184 if( ! deviceFrame.isVisible() ) {
185 deviceFrame.setLocation(support.getDropLocation().getDropPoint());
186 deviceFrame.setVisible(true);
189 } catch( MidiUnavailableException e ) {
193 // 例えば、「Microsort MIDI マッパー」と「Microsoft GS Wavetable SW Synth」は
194 // 連動して開かれるため、同時に開こうとすると、ここにたどり着く。
196 String title = ChordHelperApplet.VersionInfo.NAME;
197 String message = "Cannot open MIDI device '"+deviceModel+"'"
198 + "\nMIDIデバイス "+deviceModel+" を開くことができません。\n"
199 +"すでに他のデバイスが連動して開いている可能性があります。\n\n"
201 JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
202 } catch (Exception ex) {
203 ex.printStackTrace();