OSDN Git Service

e3f21d780ddf49ef73e006152741b53d6a7c2b34
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / mididevice / MidiDeviceDesktopPane.java
1 package camidion.chordhelper.mididevice;
2
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;
9 import java.util.List;
10 import java.util.Map;
11
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;
23
24 /**
25  * 開いているMIDIデバイスを置くためのデスクトップビュー
26  */
27 public class MidiDeviceDesktopPane extends JDesktopPane implements TreeSelectionListener {
28         /**
29          * MIDIデバイスモデルからフレームを割り出すためのマップ
30          */
31         private Map<MidiDeviceModel, MidiDeviceFrame> frameMap = new HashMap<>();
32         /**
33          * MIDIデバイスモデルに対応するMIDIデバイスフレームを返すマップを返します。
34          */
35         public Map<MidiDeviceModel, MidiDeviceFrame> getFrameMap() {
36                 return frameMap;
37         }
38         /**
39          * ツリー上で選択状態が変わったとき、このデスクトップ上のフレームの選択状態に反映します。
40          */
41         @Override
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();
53                                         try {
54                                                 deviceFrame.setSelected(true);
55                                         } catch( PropertyVetoException ex ) {
56                                                 ex.printStackTrace();
57                                         }
58                                         return;
59                                 }
60                         }
61                 }
62                 // それ以外が選択されたら、現在選択されているフレームを非選択
63                 JInternalFrame frame = getSelectedFrame();
64                 if( ! (frame instanceof MidiDeviceFrame) ) return;
65                 try {
66                         ((MidiDeviceFrame)frame).setSelected(false);
67                 } catch( PropertyVetoException ex ) {
68                         ex.printStackTrace();
69                 }
70         }
71
72         public MidiDeviceDesktopPane(MidiDeviceTreeView deviceTreeView,
73                         MidiDeviceInfoPane deviceInfoPane, MidiDeviceDialog dialog)
74         {
75                 MidiCablePane cablePane = new MidiCablePane(this);
76                 add(cablePane, JLayeredPane.PALETTE_LAYER);
77                 //
78                 // リサイズ時、表示時にMIDIケーブルを再描画
79                 addComponentListener(new ComponentAdapter() {
80                         @Override
81                         public void componentResized(ComponentEvent e) {
82                                 cablePane.setSize(getSize());
83                                 cablePane.repaint();
84                         }
85                         @Override
86                         public void componentShown(ComponentEvent e) { cablePane.repaint(); }
87                 });
88                 // デバイスツリーが変更されたときの更新処理を予約
89                 MidiDeviceTreeModel deviceTreeModel = deviceTreeView.getModel();
90                 List<MidiDeviceModel> deviceModelList = deviceTreeModel.getDeviceModelList();
91                 TreeModelListener treeModelListener = new TreeModelListener() {
92                         @Override
93                         public void treeNodesChanged(TreeModelEvent e) { }
94                         @Override
95                         public void treeNodesInserted(TreeModelEvent e) { }
96                         @Override
97                         public void treeNodesRemoved(TreeModelEvent e) { }
98                         @Override
99                         public void treeStructureChanged(TreeModelEvent e) {
100                                 //
101                                 // 削除されたデバイスモデルに対するデバイスフレームをマップから外す
102                                 List<MidiDeviceModel> removingDeviceModels = new ArrayList<>();
103                                 for( MidiDeviceModel m : frameMap.keySet() ) {
104                                         if( ! deviceModelList.contains(m) ) removingDeviceModels.add(m);
105                                 }
106                                 for( MidiDeviceModel m : removingDeviceModels ) {
107                                         MidiDeviceFrame frame = frameMap.remove(m);
108                                         if( frame != null ) remove(frame);
109                                 }
110                                 removingDeviceModels.clear();
111                                 //
112                                 // 新しいデバイスモデルに対するデバイスフレームを生成してマップに登録
113                                 for( MidiDeviceModel deviceModel : deviceModelList ) {
114                                         if( frameMap.containsKey(deviceModel) ) continue;
115                                         MidiDeviceFrame frame = new MidiDeviceFrame(deviceModel, cablePane);
116                                         frameMap.put(deviceModel, frame);
117                                         //
118                                         // トランスミッタリストモデルが変化したときにMIDIケーブルを再描画
119                                         TransmitterListModel txListModel = deviceModel.getTransmitterListModel();
120                                         if( txListModel != null ) txListModel.addListDataListener(cablePane.midiConnecterListDataListener);
121                                         //
122                                         // レシーバリストモデルが変化したときにMIDIケーブルを再描画
123                                         ReceiverListModel rxListModel = deviceModel.getReceiverListModel();
124                                         if( rxListModel != null ) rxListModel.addListDataListener(cablePane.midiConnecterListDataListener);
125                                         //
126                                         // デバイスフレームが開閉したときの動作
127                                         frame.addInternalFrameListener(cablePane.midiDeviceFrameListener);
128                                         frame.addInternalFrameListener(deviceTreeView.midiDeviceFrameListener);
129                                         frame.addInternalFrameListener(deviceInfoPane.midiDeviceFrameListener);
130                                         //
131                                         // 移動または変形時の動作
132                                         frame.addComponentListener(cablePane.midiDeviceFrameComponentListener);
133                                         //
134                                         // サイズを設定したフレームをデスクトップに追加
135                                         frame.setSize(250, deviceModel.getInOutType() == MidiDeviceInOutType.MIDI_IN_OUT ? 90 : 70);
136                                         add(frame);
137                                         //
138                                         // デバイスが開いていたら表示
139                                         if( deviceModel.getMidiDevice().isOpen() ) {
140                                                 frame.setVisible(true);
141                                         }
142                                 }
143                         }
144                 };
145                 deviceTreeModel.addTreeModelListener(treeModelListener);
146                 treeModelListener.treeStructureChanged(null);
147                 //
148                 // 表示したデバイスフレームを整列
149                 int toX = 10;
150                 int toY = 10;
151                 for( MidiDeviceModel deviceModel : deviceModelList ) {
152                         if( ! deviceModel.getMidiDevice().isOpen() ) continue;
153                         frameMap.get(deviceModel).setLocation(toX, toY);
154                         toX = (toX == 10 ? 270 : 10);
155                         toY += 50;
156                 }
157                 deviceTreeView.expandAll();
158                 //
159                 // ドロップ設定
160                 setTransferHandler(new TransferHandler() {
161                         @Override
162                         public boolean canImport(TransferSupport support) {
163                                 if( ! support.isDrop() ) return false;
164                                 if( support.isDataFlavorSupported(MidiDeviceTreeView.deviceModelFlavor) ) {
165                                         // MIDIデバイスを開くためのドロップを受け付ける
166                                         return true;
167                                 }
168                                 if( support.isDataFlavorSupported(TransmitterListView.transmitterFlavor) ) {
169                                         cablePane.draggedOutOfDestination();
170                                         // Transmitterの切り離しができるよう、ドロップを容認
171                                         return true;
172                                 }
173                                 if( support.isDataFlavorSupported(ReceiverListView.receiverFlavor) ) {
174                                         cablePane.draggedOutOfDestination();
175                                         // Receiverはドロップ不可
176                                 }
177                                 return false;
178                         }
179                         @Override
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;
186                                 try {
187                                         deviceModel = (MidiDeviceModel)support.getTransferable().getTransferData(flavor);
188                                         MidiDeviceFrame deviceFrame = frameMap.get(deviceModel);
189                                         if( deviceFrame == null ) return false;
190                                         deviceModel.open();
191                                         if( ! deviceModel.getMidiDevice().isOpen() ) {
192                                                 throw new MidiUnavailableException("開いたはずのMIDIデバイスが、開かれた状態になっていません。");
193                                         }
194                                         if( ! deviceFrame.isVisible() ) {
195                                                 deviceFrame.setLocation(support.getDropLocation().getDropPoint());
196                                                 deviceFrame.setVisible(true);
197                                         }
198                                         return true;
199                                 } catch( MidiUnavailableException e ) {
200                                         //
201                                         // デバイスを開くのに失敗した場合
202                                         //
203                                         //   例えば、「Microsort MIDI マッパー」と「Microsoft GS Wavetable SW Synth」は
204                                         //   連動して開かれるため、同時に開こうとすると、ここにたどり着く。
205                                         //
206                                         String title = "Cannot open MIDI device";
207                                         String message = "MIDIデバイス "+deviceModel+" はすでに使用中です。\n"
208                                                 +"他のデバイスが連動して開いている可能性があります。\n\n"
209                                                 + e.getMessage();
210                                         JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
211                                 } catch (Exception ex) {
212                                         ex.printStackTrace();
213                                 }
214                                 return false;
215                         }
216                 });
217         }
218
219 }