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.DragSourceDragEvent;
11 import java.awt.dnd.DragSourceMotionListener;
12 import java.awt.event.ComponentAdapter;
13 import java.awt.event.ComponentEvent;
14 import java.awt.event.ComponentListener;
15 import java.util.Arrays;
16 import java.util.Hashtable;
17 import java.util.List;
19 import javax.sound.midi.MidiDevice;
20 import javax.sound.midi.Receiver;
21 import javax.sound.midi.Transmitter;
22 import javax.swing.JComponent;
23 import javax.swing.JInternalFrame;
24 import javax.swing.JLayeredPane;
25 import javax.swing.event.InternalFrameAdapter;
26 import javax.swing.event.InternalFrameEvent;
27 import javax.swing.event.InternalFrameListener;
28 import javax.swing.event.ListDataEvent;
29 import javax.swing.event.ListDataListener;
34 public class MidiCablePane extends JComponent {
35 private Point draggingPoint;
37 * {@link MidiTransceiverListModel} の {@link Transmitter}
38 * をドラッグしている最中に再描画するためのリスナー
40 public final DragSourceMotionListener midiConnecterMotionListener = new DragSourceMotionListener() {
42 public void dragMouseMoved(DragSourceDragEvent dsde) {
43 Point origin = MidiCablePane.this.getLocationOnScreen();
44 draggingPoint = dsde.getLocation();
45 draggingPoint.translate(-origin.x, -origin.y);
50 * ドラッグ&ドロップの終了時に必要な再描画を行います。
52 public void dragDropEnd() { draggingPoint = null; repaint(); }
54 * {@link MidiDeviceFrame} が移動または変形したときにケーブルを再描画するためのリスナー
56 public final ComponentListener midiDeviceFrameComponentListener = new ComponentAdapter() {
58 public void componentMoved(ComponentEvent e) { repaint(); }
60 public void componentResized(ComponentEvent e) { repaint(); }
63 * {@link MidiDeviceFrame} が閉じたときにケーブルを再描画するためのリスナー
65 public final InternalFrameListener midiDeviceFrameListener = new InternalFrameAdapter() {
67 public void internalFrameClosed(InternalFrameEvent e) { repaint(); }
69 public void internalFrameDeactivated(InternalFrameEvent e) { repaint(); }
71 public void internalFrameClosing(InternalFrameEvent e) {
72 JInternalFrame frame = e.getInternalFrame();
73 if( ! (frame instanceof MidiDeviceFrame) ) return;
74 MidiDeviceFrame f = (MidiDeviceFrame)frame;
75 MidiTransceiverListModel m = f.getMidiTransceiverListView().getModel();
76 List<Receiver> rxList = m.getMidiDevice().getReceivers();
77 for( Receiver rx : rxList ) colorMap.remove(rx);
82 * {@link MidiTransceiverListModel} における {@link Transmitter}
83 * の増減や状態変更があった場合にケーブルを再描画するためのリスナー
85 public final ListDataListener midiConnecterListDataListener = new ListDataListener() {
87 public void contentsChanged(ListDataEvent e) { repaint(); }
89 public void intervalAdded(ListDataEvent e) { repaint(); }
91 public void intervalRemoved(ListDataEvent e) { repaint(); }
94 private MidiOpenedDevicesView desktopPane;
95 public MidiCablePane(MidiOpenedDevicesView desktopPane) {
96 this.desktopPane = desktopPane;
101 private static final Stroke CABLE_STROKE = new BasicStroke(
102 3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
103 private static final List<Color> CABLE_COLORS = Arrays.asList(
104 new Color(255, 0, 0,144),
105 new Color(0, 255, 0,144),
106 new Color(0, 0, 255,144),
107 new Color(191,191,0,144),
108 new Color(0,191,191,144),
109 new Color(191,0,191,144)
111 private static final Color ADDING_CABLE_COLOR = new Color(0, 0, 0, 144);
112 private static final Color REMOVING_CABLE_COLOR = new Color(128, 128, 128, 144);
113 private Hashtable<Receiver,Color> colorMap = new Hashtable<>();
114 private Color colorOf(Receiver rx) {
115 Color color = colorMap.get(rx);
116 if( color == null ) {
117 for( Color c : CABLE_COLORS ) {
118 if( ! colorMap.containsValue(c) ) {
123 color = CABLE_COLORS.get((int)( Math.random() * CABLE_COLORS.size() ));
124 colorMap.put(rx, color);
129 public void paint(Graphics g) {
131 Graphics2D g2 = (Graphics2D)g;
132 g2.setStroke(CABLE_STROKE);
133 JInternalFrame[] frames = desktopPane.getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER);
134 for( JInternalFrame frame : frames ) {
135 if( ! (frame instanceof MidiDeviceFrame) ) continue;
136 MidiDeviceFrame fromFrame = (MidiDeviceFrame)frame;
137 MidiTransceiverListView fromView = fromFrame.getMidiTransceiverListView();
138 MidiDevice fromDevice = fromView.getModel().getMidiDevice();
139 if( draggingPoint != null ) {
140 // Receiverからドラッグされた線を描画
141 List<Receiver> rxList = fromDevice.getReceivers();
142 for( Receiver rx : rxList ) {
143 if( ! rx.equals(fromView.getDraggingTransceiver()) ) continue;
144 Rectangle rxBounds = fromFrame.getBoundsOf(rx);
145 if( rxBounds == null ) continue;
146 int r = (rxBounds.height - 5) / 2;
147 rxBounds.translate(r+4, r+4);
148 g2.setColor(colorOf(rx));
149 g2.drawLine(rxBounds.x, rxBounds.y, draggingPoint.x, draggingPoint.y);
153 List<Transmitter> txList = fromDevice.getTransmitters();
154 for( Transmitter tx : txList ) {
156 Rectangle txBounds = fromFrame.getBoundsOf(tx);
157 if( txBounds == null ) continue;
158 int r = (txBounds.height - 5) / 2;
159 txBounds.translate(r+4, r+4);
160 AutoCloseable draggingTrx = fromView.getDraggingTransceiver();
161 Receiver rx = tx.getReceiver();
162 if( draggingPoint != null && tx.equals(draggingTrx) ) {
163 // Transmitterからドラッグされた線を描画
164 g2.setColor(rx == null ? ADDING_CABLE_COLOR : colorOf(rx));
165 g2.drawLine(txBounds.x, txBounds.y, draggingPoint.x, draggingPoint.y);
167 // TransmitterからReceiverへの接続線を描画
168 if( rx != null ) for( JInternalFrame toFrame : frames ) {
169 if( ! (toFrame instanceof MidiDeviceFrame) ) continue;
170 Rectangle rxBounds = ((MidiDeviceFrame)toFrame).getBoundsOf(rx);
171 if( rxBounds == null ) continue;
172 r = (rxBounds.height - 5) / 2;
173 rxBounds.translate(r+4, r+4);
174 g2.setColor(draggingTrx == tx ? REMOVING_CABLE_COLOR : colorOf(rx));
175 g2.drawLine(txBounds.x, txBounds.y, rxBounds.x, rxBounds.y);