1 package camidion.chordhelper.mididevice;
3 import java.awt.BasicStroke;
5 import java.awt.Graphics;
6 import java.awt.Graphics2D;
8 import java.awt.Rectangle;
9 import java.awt.Stroke;
10 import java.awt.dnd.DragSourceAdapter;
11 import java.awt.dnd.DragSourceDragEvent;
12 import java.awt.dnd.DragSourceDropEvent;
13 import java.awt.dnd.DragSourceListener;
14 import java.awt.dnd.DragSourceMotionListener;
15 import java.awt.event.ComponentAdapter;
16 import java.awt.event.ComponentEvent;
17 import java.awt.event.ComponentListener;
18 import java.util.Arrays;
19 import java.util.Hashtable;
20 import java.util.List;
22 import javax.sound.midi.Receiver;
23 import javax.sound.midi.Transmitter;
24 import javax.swing.JComponent;
25 import javax.swing.JInternalFrame;
26 import javax.swing.JLayeredPane;
27 import javax.swing.event.InternalFrameAdapter;
28 import javax.swing.event.InternalFrameEvent;
29 import javax.swing.event.InternalFrameListener;
30 import javax.swing.event.ListDataEvent;
31 import javax.swing.event.ListDataListener;
36 public class MidiCablePane extends JComponent implements DragSourceMotionListener {
37 static DraggingTransceiver dragging = new DraggingTransceiver();
38 private Point draggingPoint;
40 * ドラッグしている最中に再描画するためのソースモーションリスナー
43 public void dragMouseMoved(DragSourceDragEvent dsde) {
44 Point origin = getLocationOnScreen();
45 draggingPoint = dsde.getLocation();
46 draggingPoint.translate(-origin.x, -origin.y);
50 * ドラッグ終了時に再描画するためのソースリスナー
52 public final DragSourceListener dragSourceListener = new DragSourceAdapter() {
54 public void dragDropEnd(DragSourceDropEvent dsde) {
55 dragging.setData(null);
61 * {@link MidiDeviceFrame} が移動または変形したときにケーブルを再描画するためのリスナー
63 public final ComponentListener midiDeviceFrameComponentListener = new ComponentAdapter() {
65 public void componentMoved(ComponentEvent e) { repaint(); }
67 public void componentResized(ComponentEvent e) { repaint(); }
70 * {@link MidiDeviceFrame} が閉じたときにケーブルを再描画するためのリスナー
72 public final InternalFrameListener midiDeviceFrameListener = new InternalFrameAdapter() {
74 public void internalFrameClosed(InternalFrameEvent e) { repaint(); }
76 public void internalFrameDeactivated(InternalFrameEvent e) { repaint(); }
78 public void internalFrameClosing(InternalFrameEvent e) {
79 JInternalFrame frame = e.getInternalFrame();
80 if( ! (frame instanceof MidiDeviceFrame) ) return;
81 MidiDeviceFrame f = (MidiDeviceFrame)frame;
82 MidiDeviceModel m = f.getMidiDeviceModel();
83 List<Receiver> rxList = m.getMidiDevice().getReceivers();
84 for( Receiver rx : rxList ) colorMap.remove(rx);
89 * {@link MidiDeviceModel} における {@link Transmitter}
90 * の増減や状態変更があった場合にケーブルを再描画するためのリスナー
92 public final ListDataListener midiConnecterListDataListener = new ListDataListener() {
94 public void contentsChanged(ListDataEvent e) { repaint(); }
96 public void intervalAdded(ListDataEvent e) { repaint(); }
98 public void intervalRemoved(ListDataEvent e) { repaint(); }
101 private MidiOpenedDevicesView desktopPane;
102 public MidiCablePane(MidiOpenedDevicesView desktopPane) {
103 this.desktopPane = desktopPane;
108 private static final Stroke CABLE_STROKE = new BasicStroke(
109 3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
110 private static final List<Color> CABLE_COLORS = Arrays.asList(
111 new Color(255, 0, 0,144),
112 new Color(0, 255, 0,144),
113 new Color(0, 0, 255,144),
114 new Color(191,191,0,144),
115 new Color(0,191,191,144),
116 new Color(191,0,191,144)
118 private static final Color ADDING_CABLE_COLOR = new Color(0, 0, 0, 144);
119 private static final Color REMOVING_CABLE_COLOR = new Color(128, 128, 128, 144);
120 private Hashtable<Receiver,Color> colorMap = new Hashtable<>();
121 private Color colorOf(Receiver rx) {
122 Color color = colorMap.get(rx);
123 if( color == null ) {
124 for( Color c : CABLE_COLORS ) {
125 if( ! colorMap.containsValue(c) ) {
130 color = CABLE_COLORS.get((int)( Math.random() * CABLE_COLORS.size() ));
131 colorMap.put(rx, color);
136 public void paint(Graphics g) {
138 Graphics2D g2 = (Graphics2D)g;
139 g2.setStroke(CABLE_STROKE);
140 JInternalFrame[] frames = desktopPane.getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER);
141 for( JInternalFrame frame : frames ) {
142 if( ! (frame instanceof MidiDeviceFrame) ) continue;
143 MidiDeviceFrame fromFrame = (MidiDeviceFrame)frame;
144 MidiDeviceModel fromDeviceModel = fromFrame.getMidiDeviceModel();
146 // Receiverからドラッグされている線を描画
147 if( draggingPoint != null && fromDeviceModel.getMidiDevice().getReceivers().contains(dragging.getData()) ) {
148 Receiver rx = (Receiver)dragging.getData();
149 Rectangle rxBounds = fromFrame.getBoundsOf(rx);
150 if( rxBounds == null ) continue;
151 int r = (rxBounds.height - 5) / 2;
152 rxBounds.translate(r+4, r+4);
153 g2.setColor(colorOf(rx));
154 g2.drawLine(rxBounds.x, rxBounds.y, draggingPoint.x, draggingPoint.y);
156 // Transmitterを全部スキャン
157 TransmitterListModel txListModel = fromDeviceModel.getTransmitterListModel();
158 int ntx = txListModel == null ? 0 : txListModel.getSize();
159 for( int index=0 ; index < ntx; index++ ) {
160 Transmitter tx = txListModel.getElementAt(index);
163 Rectangle txBounds = fromFrame.getBoundsOf(tx);
164 if( txBounds == null ) continue;
165 int r = (txBounds.height - 5) / 2;
166 txBounds.translate(r+4, r+4);
168 // Transmitterに現在接続されているReceiverを把握
169 Receiver rx = tx.getReceiver();
170 if( draggingPoint != null && tx.equals(dragging.getData()) ) {
172 // Transmitterからドラッグされている線を描画
173 g2.setColor(rx == null ? ADDING_CABLE_COLOR : colorOf(rx));
174 g2.drawLine(txBounds.x, txBounds.y, draggingPoint.x, draggingPoint.y);
176 if( rx == null ) continue;
177 for( JInternalFrame toFrame : frames ) {
178 if( ! (toFrame instanceof MidiDeviceFrame) ) continue;
181 Rectangle rxBounds = ((MidiDeviceFrame)toFrame).getBoundsOf(rx);
182 if( rxBounds == null ) continue;
183 r = (rxBounds.height - 5) / 2;
184 rxBounds.translate(r+4, r+4);
186 // Transmitter⇔Receiver間の線を描画
187 g2.setColor(tx.equals(dragging.getData()) ? REMOVING_CABLE_COLOR : colorOf(rx));
188 g2.drawLine(txBounds.x, txBounds.y, rxBounds.x, rxBounds.y);