001package jmri.jmrit.beantable.oblock; 002 003import java.awt.Point; 004import java.awt.datatransfer.DataFlavor; 005import java.awt.datatransfer.StringSelection; 006import java.awt.datatransfer.Transferable; 007import java.awt.datatransfer.UnsupportedFlavorException; 008import java.awt.dnd.DnDConstants; 009import java.awt.dnd.DragGestureEvent; 010import java.awt.dnd.DragGestureListener; 011import java.awt.dnd.DragSource; 012import java.awt.dnd.DragSourceDragEvent; 013import java.awt.dnd.DragSourceDropEvent; 014import java.awt.dnd.DragSourceEvent; 015import java.awt.dnd.DragSourceListener; 016import java.awt.dnd.DropTarget; 017import java.awt.dnd.DropTargetDragEvent; 018import java.awt.dnd.DropTargetDropEvent; 019import java.awt.dnd.DropTargetEvent; 020import java.awt.dnd.DropTargetListener; 021import java.io.IOException; 022import javax.annotation.Nonnull; 023import javax.swing.JComponent; 024import javax.swing.JInternalFrame; 025import javax.swing.JTable; 026import javax.swing.JTextField; 027import javax.swing.TransferHandler; 028import javax.swing.table.AbstractTableModel; 029import javax.swing.table.TableModel; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * Support for GUI to define OBlocks and its parts. 035 * <hr> 036 * This file is part of JMRI. 037 * <p> 038 * JMRI is free software; you can redistribute it and/or modify it under the 039 * terms of version 2 of the GNU General Public License as published by the Free 040 * Software Foundation. See the "COPYING" file for a copy of this license. 041 * <p> 042 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 043 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 044 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 045 * 046 * @author Pete Cressman (C) 2010 047 */ 048public class DnDJTable extends JTable implements DropTargetListener, 049 DragGestureListener, DragSourceListener, Transferable { 050 051 public static final String TableCellFlavorMime = DataFlavor.javaJVMLocalObjectMimeType 052 + ";class=jmri.jmrit.beantable.oblock.DnDJTable.TableCellSelection"; 053 public static final DataFlavor TABLECELL_FLAVOR = new DataFlavor( 054 jmri.jmrit.beantable.oblock.DnDJTable.TableCellSelection.class, 055 "application/x-jmri.jmrit.beantable.oblock.DnDJTable.TableCellSelection"); 056 057 private Point _dropPoint; 058 private int[] _skipCols; 059 060 DnDJTable(TableModel model, int[] skipCols) { 061 super(model); 062 this.setTransferHandler(new DnDHandler(this)); 063 _skipCols = skipCols; 064 DragSource dragSource = DragSource.getDefaultDragSource(); 065 dragSource.createDefaultDragGestureRecognizer(this, 066 DnDConstants.ACTION_COPY_OR_MOVE, this); 067 new DropTarget(this, DnDConstants.ACTION_COPY, this); 068 } 069 070 @Override 071 public boolean editCellAt(int row, int column, java.util.EventObject e) { 072 boolean res = super.editCellAt(row, column, e); 073 java.awt.Component c = this.getEditorComponent(); 074 if (c instanceof javax.swing.JTextField) { 075 ((JTextField) c).selectAll(); 076 } 077 return res; 078 } 079 080 Point getDropPoint() { 081 return _dropPoint; 082 } 083 084 private boolean dropOK(DropTargetDragEvent evt) { 085 Transferable tr = evt.getTransferable(); 086 if (tr.isDataFlavorSupported(TABLECELL_FLAVOR) 087 || tr.isDataFlavorSupported(DataFlavor.stringFlavor)) { 088 _dropPoint = evt.getLocation(); 089 //DnDHandler handler = (DnDHandler)getTransferHandler(); 090 int col = columnAtPoint(_dropPoint); 091 int row = rowAtPoint(_dropPoint); 092 for (int skipCol : _skipCols) { 093 if (skipCol == col) { 094 return false; 095 } 096 } 097 if (tr.isDataFlavorSupported(TABLECELL_FLAVOR)) { 098 try { 099 // don't allow a cell import back into the cell exported from 100 TableCellSelection tcss = (TableCellSelection) tr.getTransferData(TABLECELL_FLAVOR); 101 if (row == tcss.getRow() && col == tcss.getCol() && this == tcss.getTable()) { 102 return false; 103 } 104 } catch (UnsupportedFlavorException | IOException ex) { 105 log.warn("DnDJTable.importData: at table {} e= ", getName(), ex); 106 return false; 107 } 108 } 109 } else { 110 return false; 111 } 112 return true; 113 } 114 115 /** 116 * ************************* DropTargetListener *********************** 117 */ 118 @Override 119 public void dragExit(DropTargetEvent evt) { 120 // log.debug("DnDJTable.dragExit "); 121 //evt.getDropTargetContext().acceptDrag(DnDConstants.ACTION_COPY); 122 } 123 124 @Override 125 public void dragEnter(DropTargetDragEvent evt) { 126 // log.debug("DnDJTable.dragEnter "); 127 if (!dropOK(evt)) { 128 evt.rejectDrag(); 129 } 130 } 131 132 @Override 133 public void dragOver(DropTargetDragEvent evt) { 134 if (!dropOK(evt)) { 135 evt.rejectDrag(); 136 } 137 } 138 139 @Override 140 public void dropActionChanged(DropTargetDragEvent dtde) { 141 // log.debug("DnDJTable.dropActionChanged "); 142 } 143 144 @Override 145 public void drop(DropTargetDropEvent evt) { 146 try { 147 Point pt = evt.getLocation(); 148 String data; 149 Transferable tr = evt.getTransferable(); 150 if (tr.isDataFlavorSupported(TABLECELL_FLAVOR) 151 || tr.isDataFlavorSupported(DataFlavor.stringFlavor)) { 152 AbstractTableModel model = (AbstractTableModel) getModel(); 153 int col = columnAtPoint(pt); 154 int row = rowAtPoint(pt); 155 if (col >= 0 && row >= 0) { 156 row = convertRowIndexToModel(row); 157 TableCellSelection sel = (TableCellSelection) tr.getTransferData(TABLECELL_FLAVOR); 158 data = (String) sel.getTransferData(DataFlavor.stringFlavor); 159 model.setValueAt(data, row, col); 160 model.fireTableDataChanged(); 161 // log.debug("DnDJTable.drop: data= {} dropped at ({}, {})", data, row, col); 162 evt.dropComplete(true); 163 return; 164 } 165 } else { 166 log.warn("TransferHandler.importData: supported DataFlavors not avaialable at table from {}", tr.getClass().getName()); 167 } 168 } catch (IOException ioe) { 169 log.warn("caught IOException", ioe); 170 } catch (UnsupportedFlavorException ufe) { 171 log.warn("caught UnsupportedFlavorException", ufe); 172 } 173 log.debug("DropJTree.drop REJECTED!"); 174 evt.rejectDrop(); 175 } 176 177 /** 178 * ************** DragGestureListener ************** 179 */ 180 @Override 181 public void dragGestureRecognized(DragGestureEvent e) { 182 // log.debug("DnDJTable.dragGestureRecognized "); 183 //Transferable t = getTransferable(this); 184 //e.startDrag(DragSource.DefaultCopyDrop, this, this); 185 } 186 187 /** 188 * ************** DragSourceListener *********** 189 */ 190 @Override 191 public void dragDropEnd(DragSourceDropEvent e) { 192 // log.debug("DnDJTable.dragDropEnd "); 193 } 194 195 @Override 196 public void dragEnter(DragSourceDragEvent e) { 197 // log.debug("DnDJTable.DragSourceDragEvent "); 198 } 199 200 @Override 201 public void dragExit(DragSourceEvent e) { 202 // log.debug("DnDJTable.dragExit "); 203 } 204 205 @Override 206 public void dragOver(DragSourceDragEvent e) { 207 // log.debug("DnDJTable.dragOver "); 208 } 209 210 @Override 211 public void dropActionChanged(DragSourceDragEvent e) { 212 // log.debug("DnDJTable.dropActionChanged "); 213 } 214 215 /** 216 * ************* Transferable ******************** 217 */ 218 @Override 219 public DataFlavor[] getTransferDataFlavors() { 220 // log.debug("DnDJTable.getTransferDataFlavors "); 221 return new DataFlavor[]{TABLECELL_FLAVOR}; 222 } 223 224 @Override 225 public boolean isDataFlavorSupported(DataFlavor flavor) { 226 // log.debug("DnDJTable.isDataFlavorSupported "); 227 return TABLECELL_FLAVOR.equals(flavor); 228 } 229 230 @Nonnull 231 @Override 232 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 233 // log.debug("DnDJTable.getTransferData "); 234 if (isDataFlavorSupported(TABLECELL_FLAVOR)) { 235 int row = getSelectedRow(); 236 int col = getSelectedColumn(); 237 if (col >= 0 && row >= 0) { 238 row = convertRowIndexToModel(row); 239 return getValueAt(row, col); 240 } 241 } 242 return ""; 243 } 244 245 static class TableCellSelection extends StringSelection { 246 247 int _row; 248 int _col; 249 JTable _table; 250 251 TableCellSelection(String data, int row, int col, JTable table) { 252 super(data); 253 _row = row; 254 _col = col; 255 _table = table; 256 } 257 258 int getRow() { 259 return _row; 260 } 261 262 int getCol() { 263 return _col; 264 } 265 266 JTable getTable() { 267 return _table; 268 } 269 } 270 271 static class TableCellTransferable implements Transferable { 272 273 TableCellSelection _tcss; 274 275 TableCellTransferable(TableCellSelection tcss) { 276 _tcss = tcss; 277 } 278 279 @Override 280 public DataFlavor[] getTransferDataFlavors() { 281 return new DataFlavor[]{TABLECELL_FLAVOR, DataFlavor.stringFlavor}; 282 } 283 284 @Override 285 public boolean isDataFlavorSupported(DataFlavor flavor) { 286 return (flavor.equals(TABLECELL_FLAVOR) || flavor.equals(DataFlavor.stringFlavor)); 287 } 288 289 @Nonnull 290 @Override 291 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 292 if (flavor.equals(TABLECELL_FLAVOR)) { 293 return _tcss; 294 } else if (flavor.equals(DataFlavor.stringFlavor)) { 295 return _tcss.getTransferData(DataFlavor.stringFlavor); 296 } 297 throw new UnsupportedFlavorException(flavor); 298 } 299 } 300 301 static class DnDHandler extends TransferHandler { 302 303 JTable _table; 304 305 DnDHandler(JTable table) { 306 _table = table; 307 } 308 309 //////////////export 310 @Override 311 public int getSourceActions(JComponent c) { 312 return COPY; 313 } 314 315 @Override 316 public Transferable createTransferable(JComponent c) { 317 if (c instanceof JTable) { 318 JTable table = (JTable) c; 319 int col = table.getSelectedColumn(); 320 int row = table.getSelectedRow(); 321 if (col < 0 || row < 0) { 322 return null; 323 } 324 row = table.convertRowIndexToModel(row); 325 // log.debug("DnDHandler.createTransferable: at table "+ 326 // getName()+" from ("+row+", "+col+") data= \"" 327 // +table.getModel().getValueAt(row, col)+"\""); 328 TableCellSelection tcss = new TableCellSelection((String) table.getModel().getValueAt(row, col), row, col, _table); 329 return new TableCellTransferable(tcss); 330 } 331 return null; 332 } 333 334 @Override 335 public void exportDone(JComponent c, Transferable t, int action) { 336 // log.debug("DnDHandler.exportDone at table "); 337 } 338 339 /////////////////////import 340 @Override 341 public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) { 342 343 boolean canDoIt = false; 344 for (DataFlavor transferFlavor : transferFlavors) { 345 if (transferFlavor.equals(TABLECELL_FLAVOR) || transferFlavor.equals(DataFlavor.stringFlavor)) { 346 if (comp instanceof JTable) { 347 canDoIt = true; 348 break; 349 } 350 } 351 } 352 return canDoIt; 353 } 354 355 @Override 356 public boolean importData(JComponent comp, Transferable tr) { 357 DataFlavor[] flavors = new DataFlavor[]{TABLECELL_FLAVOR, DataFlavor.stringFlavor}; 358 359 if (!canImport(comp, flavors)) { 360 return false; 361 } 362 363 try { 364 if (tr.isDataFlavorSupported(TABLECELL_FLAVOR) 365 || tr.isDataFlavorSupported(DataFlavor.stringFlavor)) { 366 if (comp instanceof DnDJTable) { 367 DnDJTable table = (DnDJTable) comp; 368 AbstractTableModel model = (AbstractTableModel) table.getModel(); 369 int col = table.getSelectedColumn(); 370 int row = table.getSelectedRow(); 371 if (col >= 0 && row >= 0) { 372 row = table.convertRowIndexToView(row); 373 String data = (String) tr.getTransferData(DataFlavor.stringFlavor); 374 model.setValueAt(data, row, col); 375 model.fireTableDataChanged(); 376 java.awt.Container parent = table; 377 do { 378 parent = parent.getParent(); 379 } while (parent != null && !(parent instanceof JInternalFrame)); 380 if (parent != null) { 381 ((JInternalFrame) parent).moveToFront(); 382 } 383 log.debug("DnDHandler.importData: data= {} dropped at ({}, {})", data, row, col); 384 return true; 385 } 386 } 387 } 388 } catch (UnsupportedFlavorException | IOException ex) { 389 log.warn("DnDHandler.importData: at table e= ", ex); 390 } 391 return false; 392 } 393 } 394 395 private final static Logger log = LoggerFactory.getLogger(DnDJTable.class); 396 397}