OSDN Git Service

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