OSDN Git Service

MIDI Device Connection から MIDI Editor へ意図しないオブジェクトを
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / mididevice / MidiCablePane.java
1 package camidion.chordhelper.mididevice;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Graphics;
6 import java.awt.Graphics2D;
7 import java.awt.Point;
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;
18
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;
30
31 /**
32  * MIDI ケーブル描画面
33  */
34 public class MidiCablePane extends JComponent {
35         private Point draggingPoint;
36         /**
37          * {@link MidiTransceiverListModel} の {@link Transmitter}
38          * をドラッグしている最中に再描画するためのリスナー
39          */
40         public final DragSourceMotionListener midiConnecterMotionListener = new DragSourceMotionListener() {
41                 @Override
42                 public void dragMouseMoved(DragSourceDragEvent dsde) {
43                         Point origin = MidiCablePane.this.getLocationOnScreen();
44                         draggingPoint = dsde.getLocation();
45                         draggingPoint.translate(-origin.x, -origin.y);
46                         repaint();
47                 }
48         };
49         /**
50          * ドラッグ&ドロップの終了時に必要な再描画を行います。
51          */
52         public void dragDropEnd() { draggingPoint = null; repaint(); }
53         /**
54          * {@link MidiDeviceFrame} が移動または変形したときにケーブルを再描画するためのリスナー
55          */
56         public final ComponentListener midiDeviceFrameComponentListener = new ComponentAdapter() {
57                 @Override
58                 public void componentMoved(ComponentEvent e) { repaint(); }
59                 @Override
60                 public void componentResized(ComponentEvent e) { repaint(); }
61         };
62         /**
63          * {@link MidiDeviceFrame} が閉じたときにケーブルを再描画するためのリスナー
64          */
65         public final InternalFrameListener midiDeviceFrameListener = new InternalFrameAdapter() {
66                 @Override
67                 public void internalFrameClosed(InternalFrameEvent e) { repaint(); }
68                 @Override
69                 public void internalFrameDeactivated(InternalFrameEvent e) { repaint(); }
70                 @Override
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);
78                         repaint();
79                 }
80         };
81         /**
82          * {@link MidiTransceiverListModel} における {@link Transmitter}
83          * の増減や状態変更があった場合にケーブルを再描画するためのリスナー
84          */
85         public final ListDataListener midiConnecterListDataListener = new ListDataListener() {
86                 @Override
87                 public void contentsChanged(ListDataEvent e) { repaint(); }
88                 @Override
89                 public void intervalAdded(ListDataEvent e) { repaint(); }
90                 @Override
91                 public void intervalRemoved(ListDataEvent e) { repaint(); }
92         };
93
94         private MidiOpenedDevicesView desktopPane;
95         public MidiCablePane(MidiOpenedDevicesView desktopPane) {
96                 this.desktopPane = desktopPane;
97                 setOpaque(false);
98                 setVisible(true);
99         }
100
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)
110         );
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) ) {
119                                         colorMap.put(rx, c);
120                                         return c;
121                                 }
122                         }
123                         color = CABLE_COLORS.get((int)( Math.random() * CABLE_COLORS.size() ));
124                         colorMap.put(rx, color);
125                 }
126                 return color;
127         }
128         @Override
129         public void paint(Graphics g) {
130                 super.paint(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);
150                                         break;
151                                 }
152                         }
153                         List<Transmitter> txList = fromDevice.getTransmitters();
154                         for( Transmitter tx : txList ) {
155                                 // Transmitterの場所を特定
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);
166                                 }
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);
176                                         break;
177                                 }
178                         }
179                 }
180         }
181 }