File Source: ReorderableJList.java
/*
P/P * Method: com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList__static_init
*/
1 /*
2 * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 package com.dmdirc.addons.ui_swing.components.reorderablelist;
24
25
26 import com.dmdirc.addons.ui_swing.components.renderers.ReorderableJListCellRenderer;
27 import com.dmdirc.logger.ErrorLevel;
28 import com.dmdirc.logger.Logger;
29
30 import java.awt.Cursor;
31 import java.awt.Point;
32 import java.awt.Rectangle;
33 import java.awt.datatransfer.DataFlavor;
34 import java.awt.datatransfer.Transferable;
35 import java.awt.datatransfer.UnsupportedFlavorException;
36 import java.awt.dnd.DnDConstants;
37 import java.awt.dnd.DragGestureEvent;
38 import java.awt.dnd.DragGestureListener;
39 import java.awt.dnd.DragSource;
40 import java.awt.dnd.DragSourceDragEvent;
41 import java.awt.dnd.DragSourceDropEvent;
42 import java.awt.dnd.DragSourceEvent;
43 import java.awt.dnd.DragSourceListener;
44 import java.awt.dnd.DropTarget;
45 import java.awt.dnd.DropTargetDragEvent;
46 import java.awt.dnd.DropTargetDropEvent;
47 import java.awt.dnd.DropTargetEvent;
48 import java.awt.dnd.DropTargetListener;
49 import java.io.IOException;
50 import java.util.ArrayList;
51
52 import javax.swing.DefaultListModel;
53 import javax.swing.JList;
54 import javax.swing.ListSelectionModel;
55
56 /**
57 * Reorderable JList.
58 */
/*
P/P * Method: ListModel getModel()
*
* Postconditions:
* init'ed(return_value)
*/
59 public final class ReorderableJList extends JList implements DragSourceListener,
60 DropTargetListener, DragGestureListener {
61
62 /**
63 * A version number for this class. It should be changed whenever the class
64 * structure is changed (or anything else that would prevent serialized
65 * objects being unserialized with the new class).
66 */
67 private static final long serialVersionUID = 1;
68
69 /** Drag source. */
70 private final DragSource dragSource;
71 /** Drag target. */
72 private final DropTarget dropTarget;
73 /** Drop target. */
74 private Object dropTargetCell;
75 /** Dragged index. */
76 private int draggedIndex = -1;
77 /** Data flavor. */
78 private DataFlavor dataFlavor;
79 /** Below drop target. */
80 private boolean belowTarget;
81
82 /** Instantiate new ReorderableJList. */
83 public ReorderableJList() {
/*
P/P * Method: void com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList()
*
* Presumptions:
* java.awt.dnd.DragSource:getDefaultDragSource(...)@99 != null
*
* Postconditions:
* this.dataFlavor == One-of{&new DataFlavor(ReorderableJList#4), null}
* this.dataFlavor in Addr_Set{null,&new DataFlavor(ReorderableJList#4)}
* this.dragSource != null
* init'ed(this.draggedIndex)
* this.dropTarget == &new DropTarget(ReorderableJList#3)
* new DataFlavor(ReorderableJList#4) num objects <= 1
* new DropTarget(ReorderableJList#3) num objects == 1
*/
84 this(new DefaultListModel());
85 }
86
87 /**
88 * Instantiate new ReorderableJList.
89 *
90 * @param model Model
91 */
92 public ReorderableJList(final DefaultListModel model) {
/*
P/P * Method: void com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList(DefaultListModel)
*
* Presumptions:
* init'ed(com.dmdirc.logger.ErrorLevel.LOW)
* java.awt.dnd.DragSource:getDefaultDragSource(...)@99 != null
*
* Postconditions:
* this.dataFlavor in Addr_Set{null,&new DataFlavor(ReorderableJList#4)}
* this.dragSource != null
* init'ed(this.draggedIndex)
* this.dropTarget == &new DropTarget(ReorderableJList#3)
* new DataFlavor(ReorderableJList#4) num objects <= 1
* new DropTarget(ReorderableJList#3) num objects == 1
*/
93 super(model);
94
95 setCellRenderer(new ReorderableJListCellRenderer(this));
96 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
97 setTransferHandler(new ArrayListTransferHandler());
98
99 dragSource = DragSource.getDefaultDragSource();
100 dropTarget = new DropTarget(this, this);
101
102 dragSource.createDefaultDragGestureRecognizer(this,
103 DnDConstants.ACTION_MOVE, this);
104 try {
105
106 dataFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType
107 + ";class=java.util.ArrayList");
108 } catch (ClassNotFoundException e) {
109 Logger.userError(ErrorLevel.LOW, "unable to create data flavor: "
110 + e.getMessage());
111 dataFlavor = null;
112 }
113 }
114
115 /** {@inheritDoc} */
116 @Override
117 public DefaultListModel getModel() {
/*
P/P * Method: DefaultListModel getModel()
*
* Postconditions:
* init'ed(return_value)
*/
118 return (DefaultListModel) super.getModel();
119 }
120
121 /**
122 * Sets the model for the list.
123 *
124 * @param model Model for the list
125 */
126 public void setModel(final DefaultListModel model) { //NOPMD stupid
/*
P/P * Method: void setModel(DefaultListModel)
*/
127 super.setModel(model);
128 }
129
130 /**
131 * Returns the target drop item.
132 *
133 * @return Drop target cell
134 */
135 public Object getTargetCell() {
/*
P/P * Method: Object getTargetCell()
*
* Preconditions:
* init'ed(this.dropTargetCell)
*
* Postconditions:
* return_value == this.dropTargetCell
* init'ed(return_value)
*/
136 return dropTargetCell;
137 }
138
139 /**
140 * Returns whether the target is below the drop cell.
141 *
142 * @return if the target is above or below the point
143 */
144 public boolean getBelowTarget() {
/*
P/P * Method: bool getBelowTarget()
*
* Preconditions:
* init'ed(this.belowTarget)
*
* Postconditions:
* return_value == this.belowTarget
* init'ed(return_value)
*/
145 return belowTarget;
146 }
147
148 /** {@inheritDoc} */
149 @Override
150 public void dragEnter(final DragSourceDragEvent dsde) {
151 //Ignore
/*
P/P * Method: void dragEnter(DragSourceDragEvent)
*/
152 }
153
154 /** {@inheritDoc} */
155 @Override
156 public void dragOver(final DragSourceDragEvent dsde) {
157 //Ignore
/*
P/P * Method: void dragOver(DragSourceDragEvent)
*/
158 }
159
160 /** {@inheritDoc} */
161 @Override
162 public void dropActionChanged(final DragSourceDragEvent dsde) {
163 //Ignore
/*
P/P * Method: void dropActionChanged(DragSourceDragEvent)
*/
164 }
165
166 /** {@inheritDoc} */
167 @Override
168 public void dragExit(final DragSourceEvent dse) {
169 //Ignore
/*
P/P * Method: void dragExit(DragSourceEvent)
*/
170 }
171
172 /** {@inheritDoc} */
173 @Override
174 public void dragDropEnd(final DragSourceDropEvent dsde) {
175 //clear drop variables and repaint
/*
P/P * Method: void dragDropEnd(DragSourceDropEvent)
*
* Postconditions:
* this.draggedIndex == -1
* this.dropTargetCell == null
*/
176 dropTargetCell = null;
177 draggedIndex = -1;
178 repaint();
179 }
180
181 /** {@inheritDoc} */
182 @Override
183 public void dragEnter(final DropTargetDragEvent dtde) {
184 //check whether to accept drag
/*
P/P * Method: void dragEnter(DropTargetDragEvent)
*
* Preconditions:
* dtde != null
*/
185 if (dtde.getSource() == dropTarget) {
186 dtde.acceptDrag(DnDConstants.ACTION_MOVE);
187 } else {
188 dtde.rejectDrag();
189 }
190 }
191
192 /** {@inheritDoc} */
193 @Override
194 public void dragOver(final DropTargetDragEvent dtde) {
195 //Reject drops on self
/*
P/P * Method: void dragOver(DropTargetDragEvent)
*
* Preconditions:
* dtde != null
*
* Presumptions:
* com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList:getCellBounds(...)@210 != null
* java.awt.dnd.DropTargetDragEvent:getLocation(...)@201 != null
* javax.swing.JList:getModel(...)@118 != null
*
* Postconditions:
* possibly_updated(this.belowTarget)
* init'ed(this.dropTargetCell)
*
* Test Vectors:
* com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList:locationToIndex(...)@202: {-231..-2, 0..232-2}, {-1}
*/
196 if (dtde.getSource() != dropTarget) {
197 dtde.rejectDrag();
198 }
199
200 //get location and index
201 final Point dragPoint = dtde.getLocation();
202 final int index = locationToIndex(dragPoint);
203
204 //set drag variables and repaint
205 if (index == -1) {
206 dropTargetCell = null;
207 } else {
208 dropTargetCell = getModel().getElementAt(index);
209 //check whether the drop point is after the last index
210 final Rectangle bounds = getCellBounds(index, index);
211 if (index == getModel().getSize() - 1
212 && dragPoint.y > bounds.y + bounds.height) {
213 belowTarget = true;
214 } else {
215 belowTarget = false;
216 }
217 }
218
219 repaint();
220 }
221
222 /** {@inheritDoc} */
223 @Override
224 public void dropActionChanged(final DropTargetDragEvent dtde) {
225 //Ignore
/*
P/P * Method: void dropActionChanged(DropTargetDragEvent)
*/
226 }
227
228 /** {@inheritDoc} */
229 @Override
230 public void dragExit(final DropTargetEvent dte) {
231 //Ignore
/*
P/P * Method: void dragExit(DropTargetEvent)
*/
232 }
233
234 /** {@inheritDoc} */
235 @Override
236 public void drop(final DropTargetDropEvent dtde) {
237 //check source and reject
/*
P/P * Method: void drop(DropTargetDropEvent)
*
* Preconditions:
* dtde != null
* (soft) init'ed(this.belowTarget)
* (soft) init'ed(this.dataFlavor)
* (soft) init'ed(this.draggedIndex)
*
* Presumptions:
* com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList:getSelectionModel(...)@277 != null
* com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList:locationToIndex(...)@244 <= 232-2
* java.awt.datatransfer.Transferable:getTransferData(...)@261 != null
* java.awt.dnd.DropTargetDropEvent:getTransferable(...)@261 != null
* java.util.ArrayList:iterator(...)@273 != null
* ...
*
* Test Vectors:
* this.belowTarget: {0}, {1}
* java.util.Iterator:hasNext(...)@273: {0}, {1}
*/
238 if (dtde.getSource() != dropTarget) {
239 dtde.rejectDrop();
240 return;
241 }
242 //get object location and index
243 final Point dropPoint = dtde.getLocation();
244 int index = locationToIndex(dropPoint);
245 if (belowTarget) {
246 index++;
247 }
248
249 //reject invalid drops
250 if ((index == -1) || (index == draggedIndex)) {
251 dtde.rejectDrop();
252 return;
253 }
254
255 //accept drop as a move
256 dtde.acceptDrop(DnDConstants.ACTION_MOVE);
257
258 //get dropped item
259 Object dragged;
260 try {
261 dragged = dtde.getTransferable().getTransferData(dataFlavor);
262 } catch (UnsupportedFlavorException e) {
263 return;
264 } catch (IOException e) {
265 return;
266 }
267
268 //move items
269 final boolean sourceBeforeTarget = draggedIndex < index;
270 final DefaultListModel mod = getModel();
271 final int newIndex = sourceBeforeTarget ? index - 1 : index;
272 mod.remove(draggedIndex);
273 for (Object item : (ArrayList) dragged) {
274 mod.add(newIndex, item);
275 }
276
277 getSelectionModel().setSelectionInterval(newIndex, newIndex);
278
279 //drop complete
280 dtde.dropComplete(true);
281 }
282
283 /** {@inheritDoc} */
284 @Override
285 public void dragGestureRecognized(final DragGestureEvent dge) {
286 //find the objects location and index
/*
P/P * Method: void dragGestureRecognized(DragGestureEvent)
*
* Preconditions:
* dge != null
* (soft) this.dragSource != null
*
* Presumptions:
* javax.swing.JList:getModel(...)@118 != null
*
* Postconditions:
* possibly_updated(this.draggedIndex)
*
* Test Vectors:
* com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList:locationToIndex(...)@288: {-231..-2, 0..232-1}, {-1}
*/
287 final Point clickPoint = dge.getDragOrigin();
288 final int index = locationToIndex(clickPoint);
289
290 if (index == -1) {
291 return;
292 }
293
294 //get the list object
295 final Object target = getModel().getElementAt(index);
296 //create the trasnferable object
297 final ArrayList<Object> transferObject = new ArrayList<Object>();
298 transferObject.add(target);
299 final Transferable trans = new ArrayListTransferable(transferObject);
300 //start drag
301 draggedIndex = index;
302 dragSource.startDrag(dge, Cursor.getDefaultCursor(), trans, this);
303 }
304 }
305
306
SofCheck Inspector Build Version : 2.17854
| ReorderableJList.java |
2009-Jun-25 01:54:24 |
| ReorderableJList.class |
2009-Sep-02 17:04:15 |