OSDN Git Service

MIDIデバイス画面の改良
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / mididevice / MidiOpenedDevicesView.java
1 package camidion.chordhelper.mididevice;
2
3 import java.awt.dnd.DnDConstants;
4 import java.awt.dnd.DropTarget;
5 import java.awt.dnd.DropTargetAdapter;
6 import java.awt.dnd.DropTargetDragEvent;
7 import java.awt.dnd.DropTargetDropEvent;
8 import java.awt.event.ComponentAdapter;
9 import java.awt.event.ComponentEvent;
10 import java.beans.PropertyVetoException;
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.Timer;
18 import javax.swing.event.TreeSelectionEvent;
19 import javax.swing.event.TreeSelectionListener;
20
21 /**
22  * 開いている MIDI デバイスを置くためのデスクトップビュー
23  */
24 public class MidiOpenedDevicesView extends JDesktopPane {
25
26         /**
27          * ツリー上で選択状態が変わったとき、
28          * このデスクトップ上のフレームの選択状態に反映するためのリスナー
29          */
30         private TreeSelectionListener treeSelectionListener = new TreeSelectionListener() {
31                 @Override
32                 public void valueChanged(TreeSelectionEvent e) {
33                         Object lastSelected = e.getNewLeadSelectionPath().getLastPathComponent();
34                         if( lastSelected instanceof MidiConnecterListModel ) {
35                                 MidiConnecterListModel deviceModel = (MidiConnecterListModel)lastSelected;
36                                 if( deviceModel.getMidiDevice().isOpen() ) {
37                                         // 開いているMIDIデバイスがツリー上で選択されたら
38                                         // このデスクトップでも選択する
39                                         try {
40                                                 getMidiDeviceFrameOf(deviceModel).setSelected(true);
41                                         } catch( PropertyVetoException ex ) {
42                                                 ex.printStackTrace();
43                                         }
44                                         return;
45                                 }
46                         }
47                         // 閉じているMIDIデバイス、またはMIDIデバイス以外のノードがツリー上で選択されたら
48                         // このデスクトップ上のMIDIデバイスフレームをすべて非選択にする
49                         MidiDeviceFrame selectedDeviceFrame = getSelectedMidiDeviceFrame();
50                         if( selectedDeviceFrame == null ) return;
51                         try {
52                                 selectedDeviceFrame.setSelected(false);
53                         } catch( PropertyVetoException ex ) {
54                                 ex.printStackTrace();
55                         }
56                 }
57         };
58
59         /**
60          * ツリー表示からこのデスクトップにドロップされたMIDIデバイスモデルに対応するフレームを表示するためのリスナー
61          */
62         private DropTargetAdapter dropTargetListener = new DropTargetAdapter() {
63                 @Override
64                 public void dragEnter(DropTargetDragEvent dtde) {
65                         if( dtde.getTransferable().isDataFlavorSupported(MidiDeviceTreeView.TREE_MODEL_FLAVOR) ) {
66                                 dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
67                         }
68                 }
69                 @Override
70                 public void drop(DropTargetDropEvent dtde) {
71                         dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
72                         try {
73                                 int maskedBits = dtde.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE;
74                                 if( maskedBits == 0 ) {
75                                         dtde.dropComplete(false);
76                                         return;
77                                 }
78                                 Object data = dtde.getTransferable().getTransferData(MidiDeviceTreeView.TREE_MODEL_FLAVOR);
79                                 if( ! (data instanceof MidiConnecterListModel) ) {
80                                         dtde.dropComplete(false);
81                                         return;
82                                 }
83                                 MidiConnecterListModel deviceModel = (MidiConnecterListModel)data;
84                                 try {
85                                         deviceModel.openDevice();
86                                 } catch( MidiUnavailableException e ) {
87                                         //
88                                         // デバイスを開くのに失敗した場合
89                                         //
90                                         //   例えば、「Microsort MIDI マッパー」と
91                                         //   「Microsoft GS Wavetable SW Synth」を
92                                         //   同時に開こうとするとここに来る。
93                                         //
94                                         String title = "Cannot open MIDI device";
95                                         String message = "MIDIデバイス "+deviceModel+" はすでに使用中です。\n"
96                                                 +"他のデバイスが連動して開いていないか確認してください。\n\n"
97                                                 + e.getMessage();
98                                         dtde.dropComplete(false);
99                                         JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
100                                         return;
101                                 }
102                                 if( ! deviceModel.getMidiDevice().isOpen() ) {
103                                         // 例外が出なかったにも関わらずデバイスが開かれていない場合
104                                         String title = "Cannot open MIDI device";
105                                         String message = "MIDIデバイス "+deviceModel+" はすでに使用中です。\n"
106                                                 +"他のデバイスが連動して開いていないか確認してください。";
107                                         dtde.dropComplete(false);
108                                         JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
109                                         return;
110                                 }
111                                 dtde.dropComplete(true);
112                                 //
113                                 // デバイスが正常に開かれたことを確認できたら
114                                 // ドロップした場所へフレームを配置して可視化する。
115                                 //
116                                 MidiDeviceFrame deviceFrame = getMidiDeviceFrameOf(deviceModel);
117                                 deviceFrame.setLocation(dtde.getLocation());
118                                 deviceFrame.setVisible(true);
119                         }
120                         catch (Exception ex) {
121                                 ex.printStackTrace();
122                                 dtde.dropComplete(false);
123                         }
124                 }
125         };
126
127         private MidiCablePane cablePane = new MidiCablePane(this);
128
129         public MidiOpenedDevicesView(MidiDeviceTreeView deviceTree) {
130                 deviceTree.addTreeSelectionListener(treeSelectionListener);
131                 add(cablePane, JLayeredPane.PALETTE_LAYER);
132                 int openedFrameIndex = 0;
133                 MidiDeviceTreeModel treeModel = (MidiDeviceTreeModel)deviceTree.getModel();
134                 for( MidiConnecterListModel deviceModel : treeModel.deviceModelList ) {
135                         deviceModel.addListDataListener(cablePane.midiConnecterListDataListener);
136                         MidiDeviceFrame frame = new MidiDeviceFrame(deviceModel, cablePane);
137                         frame.setSize(250, 90);
138                         frame.addInternalFrameListener(deviceTree.midiDeviceFrameListener);
139                         add(frame);
140                         if( ! deviceModel.getMidiDevice().isOpen() ) continue;
141                         frame.setLocation( 10+(openedFrameIndex%2)*260, 10+openedFrameIndex*50 );
142                         frame.setVisible(true);
143                         openedFrameIndex++;
144                 }
145                 // このデスクトップがリサイズされたらケーブル面もリサイズする
146                 addComponentListener(new ComponentAdapter() {
147                         @Override
148                         public void componentResized(ComponentEvent e) { cablePane.setSize(getSize()); }
149                 });
150                 new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dropTargetListener, true );
151         }
152
153         /**
154          * 指定されたMIDIデバイスモデルに対するMIDIデバイスフレームを返します。
155          *
156          * @param deviceModel MIDIデバイスモデル
157          * @return 対応するMIDIデバイスフレーム(ない場合 null)
158          */
159         public MidiDeviceFrame getMidiDeviceFrameOf(MidiConnecterListModel deviceModel) {
160                 JInternalFrame[] frames = getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER);
161                 for( JInternalFrame frame : frames ) {
162                         if( ! (frame instanceof MidiDeviceFrame) ) continue;
163                         MidiDeviceFrame deviceFrame = (MidiDeviceFrame)frame;
164                         if( deviceModel.equals(deviceFrame.getMidiConnecterListView().getModel()) ) {
165                                 return deviceFrame;
166                         }
167                 }
168                 return null;
169         }
170
171         /**
172          * このビュー上にある現在アクティブな{@link MidiDeviceFrame}を返します。
173          * アクティブな{@link MidiDeviceFrame}がない場合は null を返します。
174          * @return 現在アクティブな{@link MidiDeviceFrame}、または null
175          */
176         public MidiDeviceFrame getSelectedMidiDeviceFrame() {
177                 JInternalFrame frame = getSelectedFrame();
178                 return frame instanceof MidiDeviceFrame ? (MidiDeviceFrame)frame : null;
179         }
180
181         private boolean isTimerStarted;
182         /**
183          * タイムスタンプを更新するタイマーを開始または停止します。
184          * @param toStart trueで開始、falseで停止
185          */
186         public void setAllDeviceTimestampTimers(boolean toStart) {
187                 if( isTimerStarted == toStart ) return;
188                 isTimerStarted = toStart;
189                 JInternalFrame[] frames = getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER);
190                 for( JInternalFrame frame : frames ) {
191                         if( ! (frame instanceof MidiDeviceFrame) ) continue;
192                         Timer timer = ((MidiDeviceFrame)frame).getTimer();
193                         if( toStart ) timer.start(); else timer.stop();
194                 }
195         }
196 }