/** * */ package photoSpreadTable; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.InvalidDnDOperationException; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.TooManyListenersException; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.TransferHandler; import javax.swing.border.BevelBorder; import javax.swing.border.Border; import photoSpread.PhotoSpread; import photoSpreadObjects.photoSpreadComponents.DraggableLabel; import photoSpreadObjects.photoSpreadComponents.WorkspaceObjectsPanel; import photoSpreadUtilities.CellCoordinates; import photoSpreadUtilities.Const; import photoSpreadUtilities.Misc; import photoSpreadUtilities.OsmoticOverlayTracker; /** * @author paepcke * * Visualy helps user while drag-n-dropping from/onto PhotoSpread cells. */ public class DnDSupport { protected static JLabel _dragInfoLabel = new JLabel(); public static enum DnDVizStyle { CURSOR_TRACKER, STILL_LABEL }; private static DragSource _dragSource; private static DragGestureListener _dragGestureListenerTable; // private static NaySayerTransferHandlerTable _transferHandlerNaySayerTable; private static DragGestureListener _dragGestureListenerWorkspace; private static TransferHandlerWorkspace _transferHandlerWorkspace; private static DragGestureListener _dragGestureListenerLabel; private static TransferHandlerLabel _transferHandlerLabel; protected static PhotoSpreadCell _sourceCell = null; private static PhotoSpreadTable _table = null; // private static ImageIcon _rightArrowIcon; private static JLabel _trashLabel = new JLabel(); protected static Border _dndCellInfoBorder1 = BorderFactory.createBevelBorder( BevelBorder.LOWERED, Const.dndGhostBorderHighlightColor1, Const.dndGhostBorderShadowColor); protected static Border _dndCellInfoBorder2 = BorderFactory.createBevelBorder( BevelBorder.LOWERED, Const.dndGhostBorderHighlightColor2, Const.dndGhostBorderShadowColor); protected static OsmoticOverlayTracker _dndCellInfoPanel = new OsmoticOverlayTracker(PhotoSpread.getCurrentSheetWindow()); public static final String mimeAndClassInfo = DataFlavor.javaJVMLocalObjectMimeType + ";class=photoSpreadTable.PhotoSpreadCell"; public static PhotoSpreadCellFlavor photoSpreadCellFlavor; // Make an instance of our new DataFlavor: static{ try{ photoSpreadCellFlavor = new PhotoSpreadCellFlavor(mimeAndClassInfo); } catch(ClassNotFoundException e){ System.out.println("Class not found:PhotoSpread.PhotoSpreadCell"); } } public static DataFlavor[] supportedImportExportFlavorsForTable = { photoSpreadCellFlavor, DataFlavor.javaFileListFlavor }; // A more convenient ArrayList version of the table-supported flavors: public static ArrayList<DataFlavor> supportedImportExportFlavorsForTableArrayList; /**************************************************** * Constructor(s) *****************************************************/ public DnDSupport (JFrame tableWindow) { // Our info panel will be the given window's glass pane: tableWindow.setGlassPane(_dndCellInfoPanel); // Make the cursor trail and components invisible for now: _dndCellInfoPanel.setCursorTrailVisible(false); _dndCellInfoPanel.setComponentsVisible(false); _dragInfoLabel.setPreferredSize(PhotoSpread.photoSpreadPrefs.getDimension(PhotoSpread.dragGhostSizeKey)); _dragInfoLabel.setBackground(Const.dndGhostBackgroundColor1); _dragInfoLabel.setBorder(_dndCellInfoBorder1); _dragInfoLabel.setOpaque(true); _dragInfoLabel.setHorizontalAlignment(SwingConstants.CENTER); _dragInfoLabel.setText(""); // _rightArrowIcon = Misc.createImageIcon("rightArrow.png", _dragInfoLabel.getPreferredSize()); ImageIcon trashIcon = Misc.createImageIcon("img/grouchSmiling.png"); _trashLabel.setIcon(trashIcon); _dndCellInfoPanel.add(_trashLabel, BorderLayout.CENTER); _trashLabel.setVisible(false); // Initialize the more convenient ArrayList version of the table-supported flavors: supportedImportExportFlavorsForTableArrayList = new ArrayList<DataFlavor>(); for (int i=0; i<supportedImportExportFlavorsForTable.length; i++) supportedImportExportFlavorsForTableArrayList.add(supportedImportExportFlavorsForTable[i]); initDropTargetTable (); initDragSourcing (); } /**************************************************** * PhotoSpreadFileListFlavor Inner Class *****************************************************/ /** * @author paepcke * * DataFlavor that represents the built-in javaFileListFlavor * DataFlavor. We have this subclass so that we can use * polymorphism when asked to deliver a transferable in * PhotoSpreadCell.java. */ public static class PhotoSpreadFileListFlavor extends DataFlavor { public PhotoSpreadFileListFlavor () throws ClassNotFoundException { super(DataFlavor.javaFileListFlavor.getMimeType(), DataFlavor.javaFileListFlavor.getHumanPresentableName(), Class.forName("java.util.List").getClassLoader()); } } /**************************************************** * PhotoSpreadCellFlavor Inner Class *****************************************************/ /** * @author paepcke * * DataFlavor for a PhotoSpreadCell. This flavor is used * just for dnd within the PhotoSpread application. It's * obviously not understood outside. */ public static class PhotoSpreadCellFlavor extends DataFlavor { public PhotoSpreadCellFlavor (String mimeAndClassInfo) throws ClassNotFoundException { super(mimeAndClassInfo); } } /**************************************************** * Methods *****************************************************/ public static DnDSupport TableDnDVizFactory ( JFrame tableWindow, PhotoSpreadTable table, DnDVizStyle vizStyle) { _table = table; switch (vizStyle) { case CURSOR_TRACKER: return new DnDSupport(tableWindow).new StillLabel(tableWindow); case STILL_LABEL: return new DnDSupport(tableWindow).new StillLabel(tableWindow); default: return null; } } private void initDropTargetTable () { DropTargetTable cellLocAndDropListener = new DropTargetTable(_table, this); DropTarget dropTarget = new DropTarget(); dropTarget.setComponent(_table); dropTarget.setDefaultActions(DnDConstants.ACTION_COPY_OR_MOVE); try { dropTarget.addDropTargetListener(cellLocAndDropListener); } catch (TooManyListenersException e) { Misc.showErrorMsgAndStackTrace(e, ""); //e.printStackTrace(); } dropTarget.setActive(true); _table.setDropTarget(dropTarget); } /** * We create a single DragSource that will be used * as the source manager of all drags within PhotoSpread */ private void initDragSourcing () { _dragSource = new DragSource(); // Give table its own drag starter sensor and install it: _dragGestureListenerTable = new DragGestureRecognizerTable(); // _transferHandlerNaySayerTable = new NaySayerTransferHandlerTable(); initComponentAsDragSource(_table); // Create a drag-start listener for the Workspace and for // labels. That is: handlers for when a drag is started // within the Workspace but not on a label, or directly // on a label, whether it's in the Workspace or in a cell. // Additionally, create a transfer handler for the Workspace. // The labels' transfer handler is part of the label class: _dragGestureListenerWorkspace = new DragGestureRecognizerWorkspace(); _transferHandlerWorkspace = new TransferHandlerWorkspace(); _dragGestureListenerLabel = new DragGestureRecognizerLabel(); } /** * Generates a drag gesture recognizer. Associates the * given component with the (single) drag gesture listener * that specializes on drags originating from the workspace. * The unused WorkspaceObjectsPanel just allows us in the * future to create initComponentAsDragSource() methods * that associate JComponents not in the Workspace with * our (single) DragSource object. * @param wsop Used only for polymorphism. * @param sourceComponent JComponent in the WorkspaceObjectsPanel */ public static void initComponentAsDragSource (WorkspaceObjectsPanel wsop, JComponent sourceComponent) { _dragSource.createDefaultDragGestureRecognizer( sourceComponent, DnDConstants.ACTION_COPY_OR_MOVE, _dragGestureListenerWorkspace); sourceComponent.setTransferHandler(_transferHandlerWorkspace); } public static void initComponentAsDragSource (JComponent sourceLabel) { _dragSource.createDefaultDragGestureRecognizer( sourceLabel, DnDConstants.ACTION_COPY_OR_MOVE, _dragGestureListenerLabel); sourceLabel.setTransferHandler(_transferHandlerLabel); } public static void initComponentAsDragSource (PhotoSpreadTable sourceTable) { _dragSource.createDefaultDragGestureRecognizer( sourceTable, DnDConstants.ACTION_COPY_OR_MOVE, _dragGestureListenerTable); // Currently our own Table transfer handler class is not used. // We let the JTable's default transfer handler manage the transfer. // sourceComponent.setTransferHandler(_transferHandlerTable); } public void setVisible (boolean visible) { } public void setText (String txt) { _dragInfoLabel.setText(txt); _dragInfoLabel.setIcon(null); } public void setText (CellCoordinates coords) { Misc.getCellAddress(coords); _dragInfoLabel.setIcon(null); } public void setIcon (ImageIcon icon) { _dragInfoLabel.setText(""); _dragInfoLabel.setIcon(icon); _dragInfoLabel.repaint(); } /** * Called at the end of a drag-n-drop operation, * or after its premature termination. */ public void clearVisualDnDFeedback() { this.setVisible(false); _dndCellInfoPanel.setVisible(false); clearTrashHover (); _dndCellInfoPanel.repaint(); } public void showTrashHover () { _trashLabel.setVisible(true); } public void clearTrashHover () { _trashLabel.setVisible(false); } public void visualCellChangeFeedback() { if (_dragInfoLabel.getBackground() == Const.dndGhostBackgroundColor1) { _dragInfoLabel.setBackground(Const.dndGhostBackgroundColor2); _dragInfoLabel.setBorder(_dndCellInfoBorder2); } else { _dragInfoLabel.setBackground(Const.dndGhostBackgroundColor1); _dragInfoLabel.setBorder(_dndCellInfoBorder1); } } public boolean isTrashDropZone (CellCoordinates coords) { if (coords == null) return false; return ((coords.row() == 0) && (coords.column() == 0)); } public boolean isTrashDropZone (int row, int col) { return ((row == 0) && (col == 0)); } /**************************************************** * Inner Subclass CursorTracker *****************************************************/ public class CursorTracker extends DnDSupport { public CursorTracker(JFrame tableWindow) { super(tableWindow); // The little label will be the cursor trailer: _dndCellInfoPanel.setCursorTrailer(_dragInfoLabel); // How far should upper left corner be from // tip of cursor? _dndCellInfoPanel.setCursorTrailOffset( new Dimension ( ((int) (_dragInfoLabel.getPreferredSize().width / 2)), ((int)_dragInfoLabel.getPreferredSize().height / 2))); // Glass panes are invisible by default. Make visible: _dndCellInfoPanel.setVisible(true); } public void setVisible (boolean visible) { _dndCellInfoPanel.setCursorTrailVisible(visible); _dndCellInfoPanel.setVisible(visible); } public void setText (String txt) { super.setText(txt); _dndCellInfoPanel.setCursorTrailer(_dragInfoLabel); } } /**************************************************** * Inner Subclass StillLabel *****************************************************/ public class StillLabel extends DnDSupport { public StillLabel(JFrame tableWindow) { super(tableWindow); JPanel stuffPanel = new JPanel(); stuffPanel.setOpaque(false); Dimension fillDim = new Dimension ( ((int) _dndCellInfoPanel.getWidth() / 2) - ((int) _dragInfoLabel.getPreferredSize().width / 2), _dragInfoLabel.getPreferredSize().height); Misc.SpaceFiller leftFill = new Misc.SpaceFiller (fillDim, Const.IS_TRANSPARENT); Misc.SpaceFiller rightFill = new Misc.SpaceFiller (fillDim, Const.IS_TRANSPARENT); stuffPanel.add(leftFill); stuffPanel.add(_dragInfoLabel); stuffPanel.add(rightFill); // _dndCellInfoPanel.add(_dragInfoLabel, BorderLayout.NORTH); _dndCellInfoPanel.add(stuffPanel, BorderLayout.NORTH); // Glass panes are invisible by default. Make visible: //****_dndCellInfoPanel.setVisible(true); } /* (non-Javadoc) * @see photoSpreadTable.DnDSupport#setVisible(boolean) */ @Override public void setVisible(boolean visible) { // _dndCellInfoPanel.setComponentsVisible(visible); _dndCellInfoPanel.setVisible(visible); } public void setText (String txt) { super.setText(txt); } } /**************************************************** * DragDropCellLoactor Inner Class *****************************************************/ class DropTargetTable extends DropTarget { private static final long serialVersionUID = 1L; PhotoSpreadTable _table = null; DnDSupport _dndVisualizer = null; CellCoordinates _oldCellCoords = null; CellCoordinates _newCellCoords = null; public DropTargetTable (PhotoSpreadTable table, DnDSupport dndVisualizer) { _table = table; _dndVisualizer = dndVisualizer; } public void updateDropTargetViz(CellCoordinates targetCellCoordinates) { try { _newCellCoords = targetCellCoordinates; // This method is called all the time during // drag. Even when there is no change. So // in that case return as quickly as possible: if (_newCellCoords.equals(_oldCellCoords)) return; // If cell coordinates cant' be read, don't worry about it: if ((_newCellCoords == null) || (_newCellCoords.row() == -1) || (_newCellCoords.column() == -1)) return; if (isTrashDropZone(_oldCellCoords) && !isTrashDropZone(_newCellCoords)) clearTrashHover (); _dndVisualizer.visualCellChangeFeedback(); _oldCellCoords = _newCellCoords; _dndVisualizer.setText(_newCellCoords.toString()); } catch (Exception e) { // just ignore the unlucky event data } } public void dragEnter(DropTargetDragEvent dragTargetEvent) { // printTrace ("DragEnter called"); CellCoordinates sourceCoords = _table.getCellAddressUnderCursor(); _dndVisualizer.setVisible(true); if (sourceCoords.column() == 0) { // _dndVisualizer.setIcon(_rightArrowIcon); return; } if (_sourceCell == null) { try { _sourceCell = _table.getCell(sourceCoords.row(), sourceCoords.column()); } catch (Exception e) { } } } public void dragExit(DropTargetEvent dropEvent) { _dndVisualizer.setVisible(false); } /* (non-Javadoc) * @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent) */ public void dragOver(DropTargetDragEvent dragEvent) { // printTrace ("dragOver called"); // setText("Origin"); // printTrace ("dragOver: " + _table.getColumnUnderCursor()); // printTrace(_table.getCursor().getName()); // JTable *seemingly* allows users to drag items out of // cells without those cells being activated first. // However, when users then drop, nothing happens. // The easiest way to find whether the source cell // is activated is to see whether it is the displayed // cell of the Workspace: if (_sourceCell != _table.getWorkspace().getDisplayedCell()) { dragEvent.rejectDrag(); return; } int targetRow = _table.getRowUnderCursor(); int targetCol = _table.getColumnUnderCursor(); if (_trashLabel.isVisible()) { if (! isTrashDropZone(targetRow, targetCol)) clearTrashHover(); } // Mouse cursor outside of sheet?: if ((targetRow == -1) || (targetCol == -1)) { dragEvent.rejectDrag(); return; } PhotoSpreadCell targetCell = _table.getCell(targetRow, targetCol); if (targetCell.equals(_sourceCell) || _sourceCell.isFormulaCell()) { updateDropTargetViz(new CellCoordinates(targetRow, targetCol)); dragEvent.rejectDrag(); return; } CellCoordinates targetCoords = targetCell.getCellCoordinates(); if ((targetCol == 0) && (! isTrashDropZone(targetCoords))) { dragEvent.rejectDrag(); // _dndVisualizer.setIcon(_rightArrowIcon); // Ensure that updateDropTargetViz() knows // to switch its display properly when user // leaves column 0 again: _oldCellCoords = null; return; } else { dragEvent.acceptDrag(dragEvent.getDropAction()); if (isTrashDropZone(targetCoords)) { showTrashHover (); // Ensure that updateDropTargetViz() knows // to switch its display properly when user // leaves column 0 again: _oldCellCoords = null; return; } updateDropTargetViz(new CellCoordinates(targetRow, targetCol)); return; } } public void drop(DropTargetDropEvent dropEvent) { boolean dropSuccessful = false; CellCoordinates targetCoords = null; PhotoSpreadCell targetCell = null; // printTrace("drop was called"); try { targetCoords = _table.getCellAddressAt(dropEvent.getLocation()); if ((targetCoords.column() == 0) && (! isTrashDropZone(targetCoords))) { dropEvent.rejectDrop(); return; } targetCell = _table.getCell(targetCoords.row(), targetCoords.column()); clearVisualDnDFeedback(); if (dropEvent.isDataFlavorSupported(photoSpreadCellFlavor)) dropSuccessful = dropPhotoSpreadInternal(dropEvent, targetCell); else if (dropEvent.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) dropSuccessful = dropFromWindowsApp(dropEvent, targetCell); if (dropSuccessful) dropEvent.dropComplete(Const.DROP_SUCCEEDED); else dropEvent.dropComplete(Const.DROP_FAILED); } finally { _sourceCell = null; } } private boolean dropPhotoSpreadInternal( DropTargetDropEvent dropEvent, PhotoSpreadCell targetCell) { PhotoSpreadDragDropManager.setSourceCell(_sourceCell); PhotoSpreadDragDropManager.setDestCell(targetCell); // Did user drop over the trash can? if (isTrashDropZone(targetCell.getCellCoordinates())) { _sourceCell.removeObjects(_sourceCell.getSelectedObjects()); if (_sourceCell.getObjects().size() == 0) _sourceCell.clear(Const.DO_EVAL, Const.DO_REDRAW); } else PhotoSpreadDragDropManager.executeDragDrop(); // _table.getCellEditorFor(targetCell.getRow(), targetCell.getColumn()); _table.getTableModel().fireTableCellUpdated(targetCell.getRow(), targetCell.getColumn()); if (_sourceCell == _table.getWorkspace().getDisplayedCell()) _table.getWorkspace().redraw(); return true; } @SuppressWarnings("unchecked") private boolean dropFromWindowsApp( DropTargetDropEvent dropEvent, PhotoSpreadCell targetCell) { java.util.List<File> fileList = null; ArrayList<File> resFileList = new ArrayList<File>(); Transferable trans = dropEvent.getTransferable(); try { //xferFlavors = trans.getTransferDataFlavors(); //for (int i=0; i<xferFlavors.length; i++) // printTrace(xferFlavors[i].getHumanPresentableName()); // See whether the Java array list flavor is supported // by the drag source. That's what (at least) Windows explorer // provides: if (trans.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { dropEvent.acceptDrop (DnDConstants.ACTION_MOVE); Object rawTransData = trans.getTransferData(DataFlavor.javaFileListFlavor); if (Class.forName("java.util.List").isInstance(rawTransData)) { fileList = (java.util.List) rawTransData; } // The DnD delivers Array$ArrayList, rather than ArrayList. // One can't apparently be cast to the other. Bite the bullet // and just copy over. It's not that much: resFileList.addAll(fileList); PhotoSpreadTable.loadFiles(targetCell, resFileList); return true; } if (trans.isDataFlavorSupported(DataFlavor.imageFlavor)) { } if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) { } } catch (Exception e) { Misc.showErrorMsgAndStackTrace(e, ""); //e.printStackTrace(); } return false; } public void dropActionChanged(DropTargetDragEvent dropAction) { printTrace ("dropActionChanged called"); } private void printTrace (String msg) { System.out.println(msg); } } /**************************************************** * NaySayerTransferHandlerTable Inner Class *****************************************************/ static class NaySayerTransferHandlerTable extends TransferHandler implements Transferable { private static final long serialVersionUID = 1L; public boolean canImport (TransferHandler.TransferSupport support) { // Can't import anything empty: return false; } public int getSourceActions(JComponent comp) { // Can't participate in copying, moving, or anything else: return NONE; } /* (non-Javadoc) * @see java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer.DataFlavor) */ @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { return null; } @Override public DataFlavor[] getTransferDataFlavors() { DataFlavor res[] = {}; return res; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { return false; } } /**************************************************** * TransferHandlerTable Inner Class *****************************************************/ /** * Currently this class is not used. We let the JTable's default * transfer handler manage the transfer. However, the * NaySayerTransferHandlerTable class is used sometimes. * */ static class TransferHandlerTable extends TransferHandler implements Transferable { private static final long serialVersionUID = 1L; public boolean canImport (TransferHandler.TransferSupport dndSupport) { int sourceActions = dndSupport.getSourceDropActions(); switch (sourceActions) { case Const.COPY: case Const.COPY_FORCE: case Const.MOVE: case Const.MOVE_FORCE: case Const.COPY_OR_MOVE: case Const.COPY_OR_MOVE_FORCE_OR_NOT: return true; default: break; } return false; } protected Transferable createTransferable (DraggableLabel label) { return label.getCell(); } /* (non-Javadoc) * @see java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer.DataFlavor) */ @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (_sourceCell != null) return _sourceCell.getTransferData(flavor); else return null; } /* (non-Javadoc) * @see java.awt.datatransfer.Transferable#getTransferDataFlavors() */ @Override public DataFlavor[] getTransferDataFlavors() { if (_sourceCell != null) return _sourceCell.getTransferDataFlavors(); else return null; } /* (non-Javadoc) * @see java.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt.datatransfer.DataFlavor) */ @Override public boolean isDataFlavorSupported(DataFlavor flavor) { if (_sourceCell != null) return _sourceCell.isDataFlavorSupported(flavor); else return false; } } /**************************************************** * DragGestureRecognizerTable Inner Class *****************************************************/ /** * An instance of this class will be responsible for deciding * about drag starts within the table when: (1) no cell is selected, * or (2) when a cell is selected and the user begins a drag on * the cell background, instead of on a label. The most tricky * is when (3) no cell is selected and the user begins a drag on * a label in a cell. This class' dragGestureRecocnized() will * be called in all these cases, and we do want to start a drag * only in case (3). In the other cases we gracefully do nothing, * causing no drag operation to commence. */ static class DragGestureRecognizerTable implements DragGestureListener { @Override public void dragGestureRecognized(DragGestureEvent dragGestureEvent) { // Point cursorLoc = dragGestureEvent.getDragOrigin(); // CellCoordinates coordsCellUnderCursor = _table.getCellAddressAt(cursorLoc); // PhotoSpreadCell cell = _table.getCell(coordsCellUnderCursor); // _table.getCellEditor(coordsCellUnderCursor.row(), coordsCellUnderCursor.column()); // _table.getCellEditorFor(coordsCellUnderCursor); return; /* try { dragGestureEvent.startDrag(Const.USE_DEFAULT_CURSOR, _transferHandlerNaySayerTable); } catch (InvalidDnDOperationException e) { return; // drag start failed. Maybe b/c another drag is in progress. Just ignore } */ } } /**************************************************** * TransferHandlerWorkspace Inner Class *****************************************************/ static class TransferHandlerWorkspace extends TransferHandler implements Transferable { private static final long serialVersionUID = 1L; public boolean canImport (TransferHandler.TransferSupport support) { return false; } protected Transferable createTransferable (DraggableLabel label) { return label.getCell(); } /* (non-Javadoc) * @see java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer.DataFlavor) */ @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (_sourceCell != null) return _sourceCell.getTransferData(flavor); else return null; } /* (non-Javadoc) * @see java.awt.datatransfer.Transferable#getTransferDataFlavors() */ @Override public DataFlavor[] getTransferDataFlavors() { if (_sourceCell != null) return _sourceCell.getTransferDataFlavors(); else return null; } /* (non-Javadoc) * @see java.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt.datatransfer.DataFlavor) */ @Override public boolean isDataFlavorSupported(DataFlavor flavor) { if (_sourceCell != null) return _sourceCell.isDataFlavorSupported(flavor); else return false; } } /**************************************************** * DragGestureRecognizerWorkspace Inner Class *****************************************************/ /** * An instance of this class will be responsible for deciding * about drag starts anywhere within the Workspace window, but * on top of a label. In that case the instance of this class' * dragGestureRecognized() method will be called. It initiates * a drag via an instance of the TransferHandlerWorkspace if * the drag began over a label. Else no drag operation is * initiated. */ static class DragGestureRecognizerWorkspace implements DragGestureListener { @Override public void dragGestureRecognized(DragGestureEvent dragGestureEvent) { _sourceCell = ((DraggableLabel) dragGestureEvent.getComponent()).getCell(); if (_sourceCell.getSelectedObjects().isEmpty()) return; try { dragGestureEvent.startDrag(Const.USE_DEFAULT_CURSOR, _transferHandlerWorkspace); } catch (InvalidDnDOperationException e) { return; // drag start failed. Maybe b/c another drag is in progress. Just ignore } } } /**************************************************** * TransferHandlerLabel Inner Class *****************************************************/ public static class TransferHandlerLabel extends TransferHandler { private static final long serialVersionUID = 1L; public boolean canImport (TransferHandler.TransferSupport support) { // Labels can't import anything. At least for now: return false; } public int getSourceActions(JComponent comp) { return Const.COPY_OR_MOVE_FORCE_OR_NOT; } public Transferable createTransferable(DraggableLabel label) { return label.getCell(); } // public void exportDone(JComponent comp, Transferable trans, int action) { // return; // } } /**************************************************** * DragGestureRecognizerLabel Inner Class *****************************************************/ static class DragGestureRecognizerLabel implements DragGestureListener { @Override public void dragGestureRecognized(DragGestureEvent dragGestureEvent) { _sourceCell = ((DraggableLabel) dragGestureEvent.getComponent()).getCell(); if (_sourceCell.getSelectedObjects().isEmpty()) return; try { // dragGestureEvent.startDrag(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), _transferHandlerWorkspace); dragGestureEvent.startDrag(Const.USE_DEFAULT_CURSOR, _transferHandlerWorkspace); } catch (InvalidDnDOperationException e) { return; // drag start failed. Maybe b/c another drag is in progress. Just ignore } } } }