1 /* 2 * TransferHandlerTest14.java 3 * 4 * Created on January 15, 2007, 11:44 AM 5 */ 6 7 package test; 8 9 import ch.randelshofer.quaqua.QuaquaManager; 10 import java.awt.datatransfer.*; 11 import java.awt.event.*; 12 import java.io.*; 13 import java.util.*; 14 import javax.swing.*; 15 import javax.swing.table.*; 16 17 /** 18 * 19 * @author werni 20 */ 21 public class TransferHandlerTest14 extends javax.swing.JPanel { 22 static class TableTransferHandler extends TransferHandler { 23 TableTransferHandler()24 public TableTransferHandler() { 25 } 26 27 /** 28 * Create a Transferable to use as the source for a data transfer. 29 * 30 * @param c The component holding the data to be transfered. This 31 * argument is provided to enable sharing of TransferHandlers by 32 * multiple components. 33 * @return The representation of the data to be transfered. 34 * 35 */ createTransferable(JComponent c)36 protected Transferable createTransferable(JComponent c) { 37 if (c instanceof JTable) { 38 JTable table = (JTable) c; 39 int[] rows; 40 int[] cols; 41 42 if (!table.getRowSelectionAllowed() && !table.getColumnSelectionAllowed()) { 43 return null; 44 } 45 46 if (!table.getRowSelectionAllowed()) { 47 int rowCount = table.getRowCount(); 48 49 rows = new int[rowCount]; 50 for (int counter = 0; counter < rowCount; counter++) { 51 rows[counter] = counter; 52 } 53 } else { 54 rows = table.getSelectedRows(); 55 } 56 57 if (!table.getColumnSelectionAllowed()) { 58 int colCount = table.getColumnCount(); 59 60 cols = new int[colCount]; 61 for (int counter = 0; counter < colCount; counter++) { 62 cols[counter] = counter; 63 } 64 } else { 65 cols = table.getSelectedColumns(); 66 } 67 68 if (rows == null || cols == null || rows.length == 0 || cols.length == 0) { 69 return null; 70 } 71 72 StringBuffer plainBuf = new StringBuffer(); 73 StringBuffer htmlBuf = new StringBuffer(); 74 75 htmlBuf.append("<html>\n<body>\n<table>\n"); 76 77 for (int row = 0; row < rows.length; row++) { 78 htmlBuf.append("<tr>\n"); 79 for (int col = 0; col < cols.length; col++) { 80 Object obj = table.getValueAt(rows[row], cols[col]); 81 String val = ((obj == null) ? "" : obj.toString()); 82 plainBuf.append(val + "\t"); 83 htmlBuf.append(" <td>" + val + "</td>\n"); 84 } 85 // we want a newline at the end of each line and not a tab 86 plainBuf.deleteCharAt(plainBuf.length() - 1).append("\n"); 87 htmlBuf.append("</tr>\n"); 88 } 89 90 // remove the last newline 91 plainBuf.deleteCharAt(plainBuf.length() - 1); 92 htmlBuf.append("</table>\n</body>\n</html>"); 93 94 return new BasicTransferable(plainBuf.toString(), htmlBuf.toString()); 95 } 96 97 return null; 98 } 99 getSourceActions(JComponent c)100 public int getSourceActions(JComponent c) { 101 return COPY; 102 } canImport(JComponent comp, DataFlavor[] transferFlavors)103 public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) { 104 if (comp instanceof JTable) { 105 for (int i=0;i < transferFlavors.length; i++) { 106 if (transferFlavors[i].equals(DataFlavor.javaFileListFlavor)) { 107 return true; 108 } 109 } 110 } 111 return false; 112 } 113 /** 114 * Causes a transfer to a component from a clipboard or a 115 * DND drop operation. The <code>Transferable</code> represents 116 * the data to be imported into the component. 117 * 118 * @param comp the component to receive the transfer; this 119 * argument is provided to enable sharing of <code>TransferHandler</code>s 120 * by multiple components 121 * @param t the data to import 122 * @return true if the data was inserted into the component, false otherwise 123 */ importData(JComponent comp, Transferable t)124 public boolean importData(JComponent comp, Transferable t) { 125 JTable table = (JTable) comp; 126 System.out.println("TransferHandler.importData "+comp+" "+t); 127 if (table.getModel() instanceof DefaultTableModel) { 128 DefaultTableModel dtm = (DefaultTableModel) table.getModel(); 129 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { 130 java.util.List fileList; 131 try { 132 fileList = (java.util.List) t.getTransferData(DataFlavor.javaFileListFlavor); 133 for (Iterator i=fileList.iterator(); i.hasNext(); ) { 134 File file = (File) i.next(); 135 Object[] rowData = new Object[dtm.getColumnCount()]; 136 switch (rowData.length) { 137 case 0 : 138 break; 139 default : 140 // run through 141 case 4 : 142 rowData[3] = file.isDirectory() ? "Directory" : "File"; 143 // run through 144 case 3 : 145 rowData[2] = new Double(file.length()); 146 // run through 147 case 2 : 148 rowData[1] = new Date(file.lastModified()).toString(); 149 // run through 150 case 1 : 151 rowData[0] = file.getName(); 152 // run through 153 } 154 dtm.addRow(rowData); 155 } 156 return true; 157 } catch (UnsupportedFlavorException ex) { 158 ex.printStackTrace(); 159 } catch (IOException ex) { 160 ex.printStackTrace(); 161 } 162 } 163 } 164 return false; 165 } 166 } 167 168 static class BasicTransferable implements Transferable { 169 170 protected String plainData; 171 protected String htmlData; 172 173 private static DataFlavor[] htmlFlavors; 174 private static DataFlavor[] stringFlavors; 175 private static DataFlavor[] plainFlavors; 176 177 static { 178 try { 179 htmlFlavors = new DataFlavor[3]; 180 htmlFlavors[0] = new DataFlavor("text/html;class=java.lang.String"); 181 htmlFlavors[1] = new DataFlavor("text/html;class=java.io.Reader"); 182 htmlFlavors[2] = new DataFlavor("text/html;charset=unicode;class=java.io.InputStream"); 183 184 plainFlavors = new DataFlavor[3]; 185 plainFlavors[0] = new DataFlavor("text/plain;class=java.lang.String"); 186 plainFlavors[1] = new DataFlavor("text/plain;class=java.io.Reader"); 187 plainFlavors[2] = new DataFlavor("text/plain;charset=unicode;class=java.io.InputStream"); 188 189 stringFlavors = new DataFlavor[2]; 190 stringFlavors[0] = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType+";class=java.lang.String"); 191 stringFlavors[1] = DataFlavor.stringFlavor; 192 193 } catch (ClassNotFoundException cle) { 194 System.err.println("error initializing javax.swing.plaf.basic.BasicTranserable"); 195 } 196 } 197 BasicTransferable(String plainData, String htmlData)198 public BasicTransferable(String plainData, String htmlData) { 199 this.plainData = plainData; 200 this.htmlData = htmlData; 201 } 202 203 204 /** 205 * Returns an array of DataFlavor objects indicating the flavors the data 206 * can be provided in. The array should be ordered according to preference 207 * for providing the data (from most richly descriptive to least descriptive). 208 * @return an array of data flavors in which this data can be transferred 209 */ getTransferDataFlavors()210 public DataFlavor[] getTransferDataFlavors() { 211 DataFlavor[] richerFlavors = getRicherFlavors(); 212 int nRicher = (richerFlavors != null) ? richerFlavors.length : 0; 213 int nHTML = (isHTMLSupported()) ? htmlFlavors.length : 0; 214 int nPlain = (isPlainSupported()) ? plainFlavors.length: 0; 215 int nString = (isPlainSupported()) ? stringFlavors.length : 0; 216 int nFlavors = nRicher + nHTML + nPlain + nString; 217 DataFlavor[] flavors = new DataFlavor[nFlavors]; 218 219 // fill in the array 220 int nDone = 0; 221 if (nRicher > 0) { 222 System.arraycopy(richerFlavors, 0, flavors, nDone, nRicher); 223 nDone += nRicher; 224 } 225 if (nHTML > 0) { 226 System.arraycopy(htmlFlavors, 0, flavors, nDone, nHTML); 227 nDone += nHTML; 228 } 229 if (nPlain > 0) { 230 System.arraycopy(plainFlavors, 0, flavors, nDone, nPlain); 231 nDone += nPlain; 232 } 233 if (nString > 0) { 234 System.arraycopy(stringFlavors, 0, flavors, nDone, nString); 235 nDone += nString; 236 } 237 return flavors; 238 } 239 240 /** 241 * Returns whether or not the specified data flavor is supported for 242 * this object. 243 * @param flavor the requested flavor for the data 244 * @return boolean indicating whether or not the data flavor is supported 245 */ isDataFlavorSupported(DataFlavor flavor)246 public boolean isDataFlavorSupported(DataFlavor flavor) { 247 DataFlavor[] flavors = getTransferDataFlavors(); 248 for (int i = 0; i < flavors.length; i++) { 249 if (flavors[i].equals(flavor)) { 250 return true; 251 } 252 } 253 return false; 254 } 255 256 /** 257 * Returns an object which represents the data to be transferred. The class 258 * of the object returned is defined by the representation class of the flavor. 259 * 260 * @param flavor the requested flavor for the data 261 * @see DataFlavor#getRepresentationClass 262 * @exception IOException if the data is no longer available 263 * in the requested flavor. 264 * @exception UnsupportedFlavorException if the requested data flavor is 265 * not supported. 266 */ getTransferData(DataFlavor flavor)267 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 268 DataFlavor[] richerFlavors = getRicherFlavors(); 269 if (isRicherFlavor(flavor)) { 270 return getRicherData(flavor); 271 } else if (isHTMLFlavor(flavor)) { 272 String data = getHTMLData(); 273 data = (data == null) ? "" : data; 274 if (String.class.equals(flavor.getRepresentationClass())) { 275 return data; 276 } else if (Reader.class.equals(flavor.getRepresentationClass())) { 277 return new StringReader(data); 278 } else if (InputStream.class.equals(flavor.getRepresentationClass())) { 279 return new StringBufferInputStream(data); 280 } 281 // fall through to unsupported 282 } else if (isPlainFlavor(flavor)) { 283 String data = getPlainData(); 284 data = (data == null) ? "" : data; 285 if (String.class.equals(flavor.getRepresentationClass())) { 286 return data; 287 } else if (Reader.class.equals(flavor.getRepresentationClass())) { 288 return new StringReader(data); 289 } else if (InputStream.class.equals(flavor.getRepresentationClass())) { 290 return new StringBufferInputStream(data); 291 } 292 // fall through to unsupported 293 294 } else if (isStringFlavor(flavor)) { 295 String data = getPlainData(); 296 data = (data == null) ? "" : data; 297 return data; 298 } 299 throw new UnsupportedFlavorException(flavor); 300 } 301 302 // --- richer subclass flavors ---------------------------------------------- 303 isRicherFlavor(DataFlavor flavor)304 protected boolean isRicherFlavor(DataFlavor flavor) { 305 DataFlavor[] richerFlavors = getRicherFlavors(); 306 int nFlavors = (richerFlavors != null) ? richerFlavors.length : 0; 307 for (int i = 0; i < nFlavors; i++) { 308 if (richerFlavors[i].equals(flavor)) { 309 return true; 310 } 311 } 312 return false; 313 } 314 315 /** 316 * Some subclasses will have flavors that are more descriptive than HTML 317 * or plain text. If this method returns a non-null value, it will be 318 * placed at the start of the array of supported flavors. 319 */ getRicherFlavors()320 protected DataFlavor[] getRicherFlavors() { 321 return null; 322 } 323 getRicherData(DataFlavor flavor)324 protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { 325 return null; 326 } 327 328 // --- html flavors ---------------------------------------------------------- 329 330 /** 331 * Returns whether or not the specified data flavor is an HTML flavor that 332 * is supported. 333 * @param flavor the requested flavor for the data 334 * @return boolean indicating whether or not the data flavor is supported 335 */ isHTMLFlavor(DataFlavor flavor)336 protected boolean isHTMLFlavor(DataFlavor flavor) { 337 DataFlavor[] flavors = htmlFlavors; 338 for (int i = 0; i < flavors.length; i++) { 339 if (flavors[i].equals(flavor)) { 340 return true; 341 } 342 } 343 return false; 344 } 345 346 /** 347 * Should the HTML flavors be offered? If so, the method 348 * getHTMLData should be implemented to provide something reasonable. 349 */ isHTMLSupported()350 protected boolean isHTMLSupported() { 351 return htmlData != null; 352 } 353 354 /** 355 * Fetch the data in a text/html format 356 */ getHTMLData()357 protected String getHTMLData() { 358 return htmlData; 359 } 360 361 // --- plain text flavors ---------------------------------------------------- 362 363 /** 364 * Returns whether or not the specified data flavor is an plain flavor that 365 * is supported. 366 * @param flavor the requested flavor for the data 367 * @return boolean indicating whether or not the data flavor is supported 368 */ isPlainFlavor(DataFlavor flavor)369 protected boolean isPlainFlavor(DataFlavor flavor) { 370 DataFlavor[] flavors = plainFlavors; 371 for (int i = 0; i < flavors.length; i++) { 372 if (flavors[i].equals(flavor)) { 373 return true; 374 } 375 } 376 return false; 377 } 378 379 /** 380 * Should the plain text flavors be offered? If so, the method 381 * getPlainData should be implemented to provide something reasonable. 382 */ isPlainSupported()383 protected boolean isPlainSupported() { 384 return plainData != null; 385 } 386 387 /** 388 * Fetch the data in a text/plain format. 389 */ getPlainData()390 protected String getPlainData() { 391 return plainData; 392 } 393 394 // --- string flavorss -------------------------------------------------------- 395 396 /** 397 * Returns whether or not the specified data flavor is a String flavor that 398 * is supported. 399 * @param flavor the requested flavor for the data 400 * @return boolean indicating whether or not the data flavor is supported 401 */ isStringFlavor(DataFlavor flavor)402 protected boolean isStringFlavor(DataFlavor flavor) { 403 DataFlavor[] flavors = stringFlavors; 404 for (int i = 0; i < flavors.length; i++) { 405 if (flavors[i].equals(flavor)) { 406 return true; 407 } 408 } 409 return false; 410 } 411 } 412 413 414 /** 415 * Creates new form TransferHandlerTest14 416 */ TransferHandlerTest14()417 public TransferHandlerTest14() { 418 initComponents(); 419 table.setTransferHandler(new TableTransferHandler()); 420 table.setDragEnabled(true); 421 } 422 main(String[] args)423 public static void main(String[] args) { 424 425 try { 426 UIManager.setLookAndFeel(QuaquaManager.getLookAndFeel()); 427 UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); 428 } catch (Exception ex) { 429 ex.printStackTrace(); 430 } 431 432 433 JFrame f = new JFrame("TransferHandlerText"); 434 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 435 f.getContentPane().add(new TransferHandlerTest14()); 436 f.show(); 437 438 } 439 440 /** This method is called from within the constructor to 441 * initialize the form. 442 * WARNING: Do NOT modify this code. The content of this method is 443 * always regenerated by the Form Editor. 444 */ 445 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents initComponents()446 private void initComponents() { 447 java.awt.GridBagConstraints gridBagConstraints; 448 449 scrollPane = new javax.swing.JScrollPane(); 450 table = new javax.swing.JTable(); 451 jLabel1 = new javax.swing.JLabel(); 452 jButton1 = new javax.swing.JButton(); 453 454 setLayout(new java.awt.GridBagLayout()); 455 456 table.setModel(new javax.swing.table.DefaultTableModel( 457 new Object [][] { 458 {null, null, null, null} 459 }, 460 new String [] { 461 "Name", "Last Modified", "Length", "Kind" 462 } 463 ) { 464 Class[] types = new Class [] { 465 java.lang.String.class, java.lang.String.class, java.lang.Long.class, java.lang.String.class 466 }; 467 boolean[] canEdit = new boolean [] { 468 false, false, false, false 469 }; 470 471 public Class getColumnClass(int columnIndex) { 472 return types [columnIndex]; 473 } 474 475 public boolean isCellEditable(int rowIndex, int columnIndex) { 476 return canEdit [columnIndex]; 477 } 478 }); 479 scrollPane.setViewportView(table); 480 481 gridBagConstraints = new java.awt.GridBagConstraints(); 482 gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER; 483 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 484 gridBagConstraints.weightx = 1.0; 485 gridBagConstraints.weighty = 1.0; 486 add(scrollPane, gridBagConstraints); 487 488 jLabel1.setText("Drag a file into the table."); 489 gridBagConstraints = new java.awt.GridBagConstraints(); 490 gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 0); 491 add(jLabel1, gridBagConstraints); 492 493 jButton1.setText("JFileChooser"); 494 jButton1.addActionListener(new java.awt.event.ActionListener() { 495 public void actionPerformed(java.awt.event.ActionEvent evt) { 496 openFileChooser(evt); 497 } 498 }); 499 500 gridBagConstraints = new java.awt.GridBagConstraints(); 501 gridBagConstraints.gridy = 1; 502 add(jButton1, gridBagConstraints); 503 504 }// </editor-fold>//GEN-END:initComponents 505 openFileChooser(java.awt.event.ActionEvent evt)506 private void openFileChooser(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openFileChooser 507 JFileChooser fileChooser; 508 fileChooser = new JFileChooser(); 509 fileChooser.setDragEnabled(true); 510 511 final JDialog dialog = new JDialog(); 512 dialog.setContentPane(fileChooser); 513 dialog.pack(); 514 dialog.setVisible(true); 515 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 516 fileChooser.addActionListener(new ActionListener() { 517 public void actionPerformed(ActionEvent evt) { 518 dialog.dispose(); 519 } 520 }); 521 fileChooser.showOpenDialog(this); 522 }//GEN-LAST:event_openFileChooser 523 524 525 // Variables declaration - do not modify//GEN-BEGIN:variables 526 private javax.swing.JButton jButton1; 527 private javax.swing.JLabel jLabel1; 528 private javax.swing.JScrollPane scrollPane; 529 private javax.swing.JTable table; 530 // End of variables declaration//GEN-END:variables 531 532 } 533