OSDN Git Service

リファクタリング
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / mididevice / MidiTransceiverListView.java
1 package camidion.chordhelper.mididevice;
2
3 import java.awt.Component;
4 import java.awt.Rectangle;
5 import java.awt.datatransfer.DataFlavor;
6 import java.awt.datatransfer.Transferable;
7 import java.awt.dnd.DnDConstants;
8 import java.awt.dnd.DragGestureEvent;
9 import java.awt.dnd.DragGestureListener;
10 import java.awt.dnd.DragSource;
11 import java.awt.dnd.DragSourceAdapter;
12 import java.awt.dnd.DragSourceDropEvent;
13 import java.awt.dnd.DragSourceListener;
14 import java.awt.dnd.DropTarget;
15 import java.awt.dnd.DropTargetAdapter;
16 import java.awt.dnd.DropTargetDragEvent;
17 import java.awt.dnd.DropTargetDropEvent;
18 import java.awt.dnd.DropTargetListener;
19 import java.util.Arrays;
20 import java.util.List;
21
22 import javax.sound.midi.Receiver;
23 import javax.sound.midi.Transmitter;
24 import javax.swing.Icon;
25 import javax.swing.JLabel;
26 import javax.swing.JList;
27 import javax.swing.ListCellRenderer;
28 import javax.swing.ListSelectionModel;
29
30 import camidion.chordhelper.ButtonIcon;
31
32 /**
33  * MIDI端子({@link Transmitter}と{@link Receiver})のリストビューです。
34  * リストの要素をドラッグ&ドロップすることで、
35  * 2つのMIDI端子を仮想的なMIDIケーブルで接続することができます。
36  */
37 public class MidiTransceiverListView extends JList<AutoCloseable> {
38
39         public static final Icon MIDI_CONNECTER_ICON = new ButtonIcon(ButtonIcon.MIDI_CONNECTOR_ICON);
40         /**
41          * リストに登録されている仮想MIDI端子の描画ツール
42          */
43         private static class CellRenderer extends JLabel implements ListCellRenderer<AutoCloseable> {
44                 public Component getListCellRendererComponent(
45                                 JList<? extends AutoCloseable> list, AutoCloseable value, int index,
46                                 boolean isSelected, boolean cellHasFocus)
47                 {
48                         String text;
49                         if( value == null ) text = null;
50                         else if( value instanceof Receiver ) text = "Rx";
51                         else if( value instanceof Transmitter ) text = "Tx";
52                         else text = value.toString();
53                         setText(text);
54                         setIcon(MIDI_CONNECTER_ICON);
55                         if (isSelected) {
56                                 setBackground(list.getSelectionBackground());
57                                 setForeground(list.getSelectionForeground());
58                         } else {
59                                 setBackground(list.getBackground());
60                                 setForeground(list.getForeground());
61                         }
62                         setEnabled(list.isEnabled());
63                         setFont(list.getFont());
64                         setOpaque(true);
65                         return this;
66                 }
67         }
68
69         private static final DataFlavor transmitterFlavor = new DataFlavor(Transmitter.class, "Transmitter");
70         private static final DataFlavor receiverFlavor = new DataFlavor(Receiver.class, "Receiver");
71         /**
72          * ドラッグ対象を表すクラス
73          */
74         private static class DraggingObject implements Transferable {
75                 private static final List<DataFlavor> flavors = Arrays.asList(transmitterFlavor, receiverFlavor);
76                 private AutoCloseable trx;
77                 @Override
78                 public Object getTransferData(DataFlavor flavor) {
79                         return flavor.getRepresentationClass().isInstance(trx) ? trx : null;
80                 }
81                 @Override
82                 public DataFlavor[] getTransferDataFlavors() { return (DataFlavor[]) flavors.toArray(); }
83                 @Override
84                 public boolean isDataFlavorSupported(DataFlavor flavor) { return flavors.contains(flavor); }
85         };
86         private static DraggingObject draggingObject = new DraggingObject();
87
88         /**
89          * 現在ドラッグされている{@link Transmitter}または{@link Receiver}を返します。
90          * ドラッグ中でなければnullを返します。
91          */
92         public AutoCloseable getDraggingTransceiver() { return draggingObject.trx; }
93
94         private MidiCablePane cablePane;
95
96         private DragSourceListener dragSourceListener = new DragSourceAdapter() {
97                 @Override
98                 public void dragDropEnd(DragSourceDropEvent dsde) {
99                         if( draggingObject.trx instanceof Transmitter && ! dsde.getDropSuccess() ) {
100                                 getModel().closeTransmitter((Transmitter)draggingObject.trx);
101                         }
102                         draggingObject.trx = null;
103                         cablePane.dragDropEnd();
104                 }
105         };
106         /**
107          * 仮想MIDI端子リストビューを生成します。
108          * @param model このビューから参照されるデータモデル
109          * @param cablePane MIDIケーブル描画面
110          */
111         public MidiTransceiverListView(MidiTransceiverListModel model, MidiCablePane cablePane) {
112                 super(model);
113                 this.cablePane = cablePane;
114                 setCellRenderer(new CellRenderer());
115                 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
116                 setLayoutOrientation(JList.HORIZONTAL_WRAP);
117                 setVisibleRowCount(0);
118                 DragSource dragSource = new DragSource();
119                 DragGestureListener dgl = new DragGestureListener() {
120                         @Override
121                         public void dragGestureRecognized(DragGestureEvent dge) {
122                                 if( (dge.getDragAction() & DnDConstants.ACTION_COPY_OR_MOVE) == 0 ) return;
123                                 MidiTransceiverListModel m = getModel();
124                                 AutoCloseable source = m.getElementAt(locationToIndex(dge.getDragOrigin()));
125                                 if( source instanceof MidiTransceiverListModel.NewTransmitter ) {
126                                         draggingObject.trx = m.openTransmitter();
127                                 } else if( source instanceof Transmitter || source instanceof Receiver ) {
128                                         draggingObject.trx = source;
129                                 } else {
130                                         return;
131                                 }
132                                 dge.startDrag(DragSource.DefaultLinkDrop, draggingObject, dragSourceListener);
133                         }
134                 };
135                 dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
136                 dragSource.addDragSourceMotionListener(cablePane);
137                 DropTargetListener dtl = new DropTargetAdapter() {
138                         @Override
139                         public void dragEnter(DropTargetDragEvent event) {
140                                 if( event.isDataFlavorSupported(transmitterFlavor) || event.isDataFlavorSupported(receiverFlavor) ) {
141                                         event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
142                                 }
143                         }
144                         @Override
145                         public void drop(DropTargetDropEvent event) {
146                                 event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
147                                 try {
148                                         int maskedBits = event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE;
149                                         if( maskedBits == 0 ) {
150                                                 event.dropComplete(false);
151                                                 return;
152                                         }
153                                         MidiTransceiverListModel m = getModel();
154                                         AutoCloseable destination = m.getElementAt(locationToIndex(event.getLocation()));
155                                         Transferable t = event.getTransferable();
156                                         if( t.isDataFlavorSupported(transmitterFlavor) || t.isDataFlavorSupported(receiverFlavor) ) {
157                                                 Object source;
158                                                 if( (source = t.getTransferData(transmitterFlavor)) != null ) {
159                                                         if( destination instanceof Receiver ) {
160                                                                 ((Transmitter)source).setReceiver((Receiver)destination);
161                                                                 event.dropComplete(true);
162                                                                 return;
163                                                         }
164                                                 } else if( (source = t.getTransferData(receiverFlavor)) != null ) {
165                                                         if( destination instanceof Transmitter ) {
166                                                                 Transmitter tx = (Transmitter)destination;
167                                                                 if( tx instanceof MidiTransceiverListModel.NewTransmitter ) {
168                                                                         tx = m.openTransmitter();
169                                                                 }
170                                                                 tx.setReceiver((Receiver)source);
171                                                                 event.dropComplete(true);
172                                                                 return;
173                                                         }
174                                                 }
175                                         }
176                                 }
177                                 catch (Exception ex) {
178                                         ex.printStackTrace();
179                                 }
180                                 event.dropComplete(false);
181                         }
182                 };
183                 new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true );
184         }
185         @Override
186         public MidiTransceiverListModel getModel() { return (MidiTransceiverListModel)super.getModel(); }
187         /**
188          * 引数で指定された{@link Transmitter}または{@link Receiver}のセル範囲を示す、
189          * リストの座標系内の境界の矩形を返します。対応するセルがない場合はnullを返します。
190          * @param transceiver {@link Transmitter}または{@link Receiver}
191          * @return セル範囲を示す境界の矩形、またはnull
192          */
193         public Rectangle getCellBounds(AutoCloseable transceiver) {
194                 int index = getModel().indexOf(transceiver);
195                 return getCellBounds(index,index);
196         }
197 }