1 package camidion.chordhelper.mididevice;
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;
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;
30 import camidion.chordhelper.ButtonIcon;
33 * MIDI端子({@link Transmitter}と{@link Receiver})のリストビューです。
34 * リストの要素をドラッグ&ドロップすることで、
35 * 2つのMIDI端子を仮想的なMIDIケーブルで接続することができます。
37 public class MidiTransceiverListView extends JList<AutoCloseable> {
39 public static final Icon MIDI_CONNECTER_ICON = new ButtonIcon(ButtonIcon.MIDI_CONNECTOR_ICON);
41 * リストに登録されている仮想MIDI端子の描画ツール
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)
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();
54 setIcon(MIDI_CONNECTER_ICON);
56 setBackground(list.getSelectionBackground());
57 setForeground(list.getSelectionForeground());
59 setBackground(list.getBackground());
60 setForeground(list.getForeground());
62 setEnabled(list.isEnabled());
63 setFont(list.getFont());
69 private static final DataFlavor transmitterFlavor = new DataFlavor(Transmitter.class, "Transmitter");
70 private static final DataFlavor receiverFlavor = new DataFlavor(Receiver.class, "Receiver");
74 private static class DraggingObject implements Transferable {
75 private static final List<DataFlavor> flavors = Arrays.asList(transmitterFlavor, receiverFlavor);
76 private AutoCloseable trx;
78 public Object getTransferData(DataFlavor flavor) {
79 return flavor.getRepresentationClass().isInstance(trx) ? trx : null;
82 public DataFlavor[] getTransferDataFlavors() { return (DataFlavor[]) flavors.toArray(); }
84 public boolean isDataFlavorSupported(DataFlavor flavor) { return flavors.contains(flavor); }
86 private static DraggingObject draggingObject = new DraggingObject();
89 * 現在ドラッグされている{@link Transmitter}または{@link Receiver}を返します。
90 * ドラッグ中でなければnullを返します。
92 public AutoCloseable getDraggingTransceiver() { return draggingObject.trx; }
94 private MidiCablePane cablePane;
96 private DragSourceListener dragSourceListener = new DragSourceAdapter() {
98 public void dragDropEnd(DragSourceDropEvent dsde) {
99 if( draggingObject.trx instanceof Transmitter && ! dsde.getDropSuccess() ) {
100 getModel().closeTransmitter((Transmitter)draggingObject.trx);
102 draggingObject.trx = null;
103 cablePane.dragDropEnd();
107 * 仮想MIDI端子リストビューを生成します。
108 * @param model このビューから参照されるデータモデル
109 * @param cablePane MIDIケーブル描画面
111 public MidiTransceiverListView(MidiTransceiverListModel model, MidiCablePane cablePane) {
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() {
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;
132 dge.startDrag(DragSource.DefaultLinkDrop, draggingObject, dragSourceListener);
135 dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
136 dragSource.addDragSourceMotionListener(cablePane);
137 DropTargetListener dtl = new DropTargetAdapter() {
139 public void dragEnter(DropTargetDragEvent event) {
140 if( event.isDataFlavorSupported(transmitterFlavor) || event.isDataFlavorSupported(receiverFlavor) ) {
141 event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
145 public void drop(DropTargetDropEvent event) {
146 event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
148 int maskedBits = event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE;
149 if( maskedBits == 0 ) {
150 event.dropComplete(false);
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) ) {
158 if( (source = t.getTransferData(transmitterFlavor)) != null ) {
159 if( destination instanceof Receiver ) {
160 ((Transmitter)source).setReceiver((Receiver)destination);
161 event.dropComplete(true);
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();
170 tx.setReceiver((Receiver)source);
171 event.dropComplete(true);
177 catch (Exception ex) {
178 ex.printStackTrace();
180 event.dropComplete(false);
183 new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true );
186 public MidiTransceiverListModel getModel() { return (MidiTransceiverListModel)super.getModel(); }
188 * 引数で指定された{@link Transmitter}または{@link Receiver}のセル範囲を示す、
189 * リストの座標系内の境界の矩形を返します。対応するセルがない場合はnullを返します。
190 * @param transceiver {@link Transmitter}または{@link Receiver}
191 * @return セル範囲を示す境界の矩形、またはnull
193 public Rectangle getCellBounds(AutoCloseable transceiver) {
194 int index = getModel().indexOf(transceiver);
195 return getCellBounds(index,index);