1 package snow.utils.gui; 2 3 import java.awt.datatransfer.DataFlavor ; 4 import java.awt.datatransfer.Transferable ; 5 import javax.swing.border.Border ; 6 import java.awt.dnd.*; 7 import java.awt.Color ; 8 import java.awt.Dimension ; 9 import java.awt.GridBagConstraints ; 10 import snow.utils.storage.FileUtils; 11 import javax.swing.*; 12 import java.awt.GridBagLayout ; 13 import java.awt.event.*; 14 import javax.swing.event.*; 15 import java.io.*; 16 import java.util.*; 17 import snow.utils.SysUtils; 18 import snow.Basics; 19 20 24 public class FileField extends JPanel 25 { 26 final private JButton editPathButton = new JButton("..."); 27 final private JTextField pathField = new JTextField(""); 28 final private boolean saveMode; 29 final private String dialogTitle; 30 private final Vector<ActionListener> actionListeners = new Vector<ActionListener>(); 31 final private int fileChooserSelectionMode; 32 private boolean autoColorized = false; 33 34 final public Set<String > allowedExtensions = new HashSet<String >(); 36 public String fileTypeDescription = ""; 37 38 40 public FileField(File path, boolean saveMode, String dialogTitle, int fileChooserSelectionMode) 41 { 42 this(path!=null ? path.getAbsolutePath() : null, saveMode, dialogTitle, fileChooserSelectionMode); 43 } 44 45 47 public FileField(String path, boolean saveMode, String dialogTitle, int fileChooserSelectionMode) 48 { 49 super(); GridBagLayout gridbag = new GridBagLayout (); 51 GridBagConstraints constr = new GridBagConstraints (); 52 setLayout(gridbag); 53 54 this.saveMode = saveMode; 55 this.dialogTitle = dialogTitle; 56 this.fileChooserSelectionMode = fileChooserSelectionMode; 57 58 constr.weightx=1; 59 constr.fill = GridBagConstraints.HORIZONTAL; 60 gridbag.setConstraints(pathField, constr); 61 add(pathField); 62 pathField.setText(path); 63 64 constr.weightx=0; 65 gridbag.setConstraints(editPathButton, constr); 66 add(editPathButton); 67 68 69 Dimension dim = new Dimension ( 70 (int) pathField.getPreferredSize().getHeight(), 71 (int) pathField.getPreferredSize().getHeight()); 72 73 editPathButton.setPreferredSize(dim); 74 editPathButton.setMaximumSize(dim); 75 editPathButton.setMinimumSize(dim); 76 editPathButton.setFocusPainted(false); 77 78 79 editPathButton.addActionListener(new ActionListener() 80 { 81 public void actionPerformed(ActionEvent e) 82 { 83 browseFileAction(); 84 } 85 }); 86 87 pathField.addActionListener(new ActionListener() 88 { 89 public void actionPerformed(ActionEvent e) 90 { 91 notifyActionListeners(); 92 } 93 }); 94 95 editPathButton.addMouseListener(new MouseAdapter(){ 96 @Override public void mousePressed(MouseEvent me) 97 { 98 if(me.isPopupTrigger()) showPopup(me); 99 } 100 @Override public void mouseReleased(MouseEvent me) 101 { 102 if(me.isPopupTrigger()) showPopup(me); 103 } 104 105 private void showPopup(MouseEvent me) 106 { 107 final File dest = getPath(); 108 if(dest==null) return; 109 110 JPopupMenu pop = new JPopupMenu(); 111 112 113 if(dest.exists()) 114 { 115 if( dest.isFile()) 116 { 117 JMenuItem openSFS = new JMenuItem("Open parent dir in OS file explorer"); 118 pop.add(openSFS); 119 openSFS.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { 120 SysUtils.openFileExplorer( dest.getParent() ); 121 } }); 122 123 JMenuItem openSFSE = new JMenuItem("Open file (OS Execution)"); 124 pop.addSeparator(); 125 pop.add(openSFSE); 126 openSFSE.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { 127 SysUtils.openFileExplorer( dest.getAbsolutePath() ); 128 } }); 129 130 } 131 else 132 { 133 JMenuItem openSFS = new JMenuItem("Open in OS file explorer"); 134 pop.add(openSFS); 135 openSFS.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { 136 SysUtils.openFileExplorer( dest.getAbsolutePath() ); 137 } }); 138 139 140 } 141 } 142 143 pop.show( editPathButton, pop.getX(), pop.getY()+15); 144 } 145 }); 146 147 pathField.addKeyListener(new KeyAdapter() 148 { 149 @Override public void keyReleased(KeyEvent ke) 150 { 151 boolean isCaretAtEnd = pathField.getCaretPosition()==pathField.getText().length(); 152 153 if(isCaretAtEnd) 154 { 155 if(ke.getModifiers()==KeyEvent.CTRL_MASK && ke.getKeyCode()==ke.VK_T) 156 { 157 completePathForward(); 158 } 159 160 if(ke.getKeyCode()==ke.VK_TAB) 161 { 162 if(pathField.getCaretPosition()==pathField.getText().length()) 163 { 164 completePathForward(); 165 } 166 } 167 if(ke.getKeyCode()==ke.VK_RIGHT) 168 { 169 pathField.transferFocusBackward(); 170 } 171 } 172 173 if(ke.getKeyCode()==ke.VK_UP) 174 { 175 pathField.transferFocusBackward(); 176 } 177 178 if(ke.getKeyCode()==ke.VK_DOWN) 179 { 180 pathField.transferFocus(); 181 } 182 183 184 186 187 if(autoColorized) 188 { 189 updateColor(); 190 } 191 } 192 }); 193 194 Action doNothing = new AbstractAction() { 196 public void actionPerformed(ActionEvent e) { 197 System.out.println("DO NOTHING !"); 199 } 200 }; 201 205 getTextField().setFocusTraversalKeysEnabled(false); 208 209 210 } 212 214 public void setAutoColorized() 215 { 216 autoColorized = true; 217 updateColor(); 218 } 219 220 public JTextField getTextField() { return pathField; } 221 222 224 private void updateColor() 225 { 226 if(new File(pathField.getText()).exists()) 227 { 228 pathField.setForeground(UIManager.getColor("TextField.foreground")); 229 } 230 else 231 { 232 pathField.setForeground(Color.red.darker()); 233 } 234 } 235 236 240 public File getPath() 241 { 242 String ft = pathField.getText().trim(); 243 if(ft.length()==0) return null; 244 File file = new File( ft ); 245 return FileUtils.getCanonicalFileWithCase(file); 247 } 248 249 252 public boolean isPathValid() 253 { 254 if(saveMode) return true; 255 256 File file = getPath(); 257 if(file.exists()) return true; 258 return false; 259 } 260 261 private void browseFileAction() 262 { 263 JFileChooser fileChooser = new JFileChooser(); 264 fileChooser.setDialogTitle(dialogTitle); 265 File base = getPath(); 266 if(base!=null) 267 { 268 if(base.exists()) 269 { 270 fileChooser.setCurrentDirectory(base); 271 } 272 else 273 { 274 if(base.getParentFile()!=null) 276 { 277 fileChooser.setCurrentDirectory(base.getParentFile()); 278 } 279 else 280 { 281 File lastGood = this.getFirstValidPathStartingWith(); 282 if(lastGood!=null) 283 { 284 if(lastGood.isDirectory()) 285 { 286 fileChooser.setCurrentDirectory(lastGood); 287 } 288 else 289 { 290 fileChooser.setCurrentDirectory(lastGood.getParentFile()); 291 } 292 } 293 } 294 } 295 } 296 297 fileChooser.setFileSelectionMode(this.fileChooserSelectionMode); 298 FileViewWithAttributes view = new FileViewWithAttributes(); 299 fileChooser.setFileView(view); 300 301 if(allowedExtensions.size()>0) 302 { 303 fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter () 304 { 305 @Override public boolean accept(File f) 306 { 307 if(f.isDirectory()) return true; 308 String name = f.getName().toLowerCase(Locale.ENGLISH); 309 for(String ext: allowedExtensions) 310 { 311 if(name.endsWith("."+ext)) return true; 312 } 313 return false; 314 } 315 316 public String getDescription() 317 { 318 return fileTypeDescription; 319 } 320 }); 321 } 322 323 if(saveMode) 324 { 325 int rep = fileChooser.showSaveDialog(this); 326 if(rep==JFileChooser.APPROVE_OPTION) 327 { 328 File file = fileChooser.getSelectedFile(); 329 setPath(file.getAbsolutePath()); 330 } 331 } 332 else 333 { 334 FileChooserFilter fsf = new FileChooserFilter(fileChooser); 335 fileChooser.setAccessory(fsf); 336 337 int rep = fileChooser.showOpenDialog(this); 338 if(rep==JFileChooser.APPROVE_OPTION) 339 { 340 File file = fileChooser.getSelectedFile(); 341 setPath(file.getAbsolutePath()); 342 } 343 } 344 345 346 } 347 348 public void setEditable(boolean editable) 349 { 350 pathField.setEditable(editable); 351 editPathButton.setEnabled(editable); 352 } 353 354 356 public void makeVisible(boolean v) 357 { 358 pathField.setVisible(v); 359 editPathButton.setVisible(v); 360 } 361 362 public void setFile(File path) 363 { 364 setPath(path); 365 } 366 367 public void setPath(File path) 368 { 369 setPath(path.getAbsolutePath()); 370 } 371 372 373 public void setPath(String path) 374 { 375 pathField.setText(path); 376 if(autoColorized) this.updateColor(); 377 notifyActionListeners(); 378 } 379 380 public boolean isValidCandidate(File f) 381 { 382 if(f==null) return true; if(fileChooserSelectionMode == JFileChooser.FILES_ONLY && f.isDirectory()) return false; 384 if(fileChooserSelectionMode == JFileChooser.DIRECTORIES_ONLY && !f.isDirectory()) return false; 385 return true; 386 } 387 388 389 390 public void addActionListener(ActionListener al) 391 { 392 actionListeners.add(al); 393 } 394 395 public void removeActionListener(ActionListener al) 396 { 397 actionListeners.remove(al); 398 } 399 400 private void notifyActionListeners() 401 { 402 ActionListener[] als = actionListeners.toArray(new ActionListener[actionListeners.size()]); 403 for(ActionListener al : als) 404 { 405 al.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_FIRST, "File selected")); 406 } 407 } 408 409 public void setComponentWidth(int width) 410 { 411 Dimension dim = pathField.getPreferredSize(); 412 dim.width = width; 413 pathField.setMinimumSize(dim); 414 pathField.setPreferredSize(dim); 415 pathField.setMaximumSize(dim); 416 } 417 418 420 public File getFirstValidPathStartingWith() 421 { 422 StringBuilder partialPath = new StringBuilder (this.getTextField().getText()); 423 while(partialPath.length()>0) 424 { 425 File f = new File(partialPath.toString()); 426 if(f.exists()) return FileUtils.getCanonicalFileWithCase(f); 427 partialPath.setLength( partialPath.length()-1); 428 } 429 return null; 430 } 431 432 433 private void completePathForward() 434 { 435 final File firstValid = getFirstValidPathStartingWith(); 437 if(firstValid==null) return; 438 439 String firstValidP = FileUtils.getCanonicalName(firstValid); 440 System.out.println("firstValid="+firstValidP); 441 442 String typedpath = FileUtils.getCanonicalName(getPath()); 443 System.out.println("typedpath="+typedpath); 444 445 450 451 String invalidStartsWith = typedpath.substring(firstValidP.length()).toUpperCase(); 452 System.out.println("invalidStartsWith="+invalidStartsWith); 453 454 List<File> candidates = new ArrayList<File>(); 456 for(File f : firstValid.listFiles()) 457 { 458 if(this.fileChooserSelectionMode==JFileChooser.DIRECTORIES_ONLY && !f.isDirectory()) continue; 459 460 461 String can = FileUtils.getCanonicalName(f).toUpperCase(); 462 String canDiff = can.substring(firstValidP.length()).toUpperCase(); 463 if(canDiff.startsWith(invalidStartsWith)) 464 { 465 467 candidates.add(f); 468 } 469 } 470 471 if(candidates.size()==1) 472 { 473 this.setPath(candidates.get(0)); 474 } 475 else if(candidates.size()>1) 476 { 477 JPopupMenu pop = new JPopupMenu(); 479 int n=0; 480 for(final File c : candidates) 481 { 482 JMenuItem mi = new JMenuItem(""+c); 483 pop.add(mi); 484 mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { 485 setPath(c); 486 } }); 487 n++; 488 489 if(n>20) 490 { 491 break; 492 } 493 } 494 495 if(firstValid.getParentFile()!=null) 497 { 498 JMenuItem mi = new JMenuItem(""+firstValid.getParentFile()); 499 mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)); 500 pop.add(mi); 501 mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { 502 setPath(firstValid.getParentFile()); 503 } }); 504 505 } 506 507 508 if(n>20) 509 { 510 JMenuItem mib = new JMenuItem(""+(candidates.size()-n)+" more entries... use the file browser or be more precise"); 511 pop.add(mib); 512 mib.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { 513 browseFileAction(); 514 } }); 515 } 516 517 518 pop.show(this.getTextField(),0,15); 519 520 } 521 } 522 523 524 class FileDropTarget extends DropTarget 525 { 526 public FileDropTarget(JComponent c) 527 { 528 super(c, DnDConstants.ACTION_COPY, new FileDropTargetListener(c), true); 529 c.setDropTarget(this); 530 531 } 532 } 533 534 535 class FileDropTargetListener implements DropTargetListener 536 { 537 JComponent comp; 538 Border originalBorder = null; 539 Border redBorder = BorderFactory.createLineBorder(Color.red, 1); 540 Border greenBorder = BorderFactory.createLineBorder(Color.green, 1); 541 public FileDropTargetListener(JComponent comp) 542 { 543 this.comp = comp; 544 this.originalBorder = comp.getBorder(); 545 546 } 547 548 549 File fileToDrop = null; 550 551 public void dragEnter(DropTargetDragEvent dte) { 552 554 boolean accept = false; 555 for(DataFlavor df : dte.getCurrentDataFlavors()) 556 { 557 if(df.isFlavorJavaFileListType()) 558 { 559 Transferable tr = dte.getTransferable(); 560 try 561 { 562 Object data = tr.getTransferData( df.javaFileListFlavor ); 563 @SuppressWarnings ("unchecked") 565 List<File> lf = (List<File>) data; 566 if(lf.size()==1) 567 { 568 if(isValidCandidate(lf.get(0))) 569 { 570 fileToDrop = lf.get(0); 571 pathField.setToolTipText("Droping "+lf.get(0)); 572 dte.acceptDrag(DnDConstants.ACTION_COPY); 573 accept = true; 574 break; 575 } 576 } 577 } 578 catch(Exception e) 579 { 580 comp.setBorder( redBorder); 581 e.printStackTrace(); 582 } 583 } 584 else if (df.isFlavorTextType()) 585 { 586 Transferable tr = dte.getTransferable(); 587 try 588 { 589 String data = ""+tr.getTransferData( df.javaFileListFlavor ); 590 if(data.toLowerCase().startsWith("file:///")) data = data.substring(7); 591 592 File f = new File(data); 593 if(isValidCandidate(f)) 594 { 595 fileToDrop = f; 596 pathField.setToolTipText("Droping "+f); dte.acceptDrag(DnDConstants.ACTION_COPY); 599 accept = true; 600 break; 601 } 602 603 } 604 catch(Exception e) 605 { 606 comp.setBorder( redBorder); 607 e.printStackTrace(); 608 } 609 } 610 System.out.println(""+df); 611 } 612 613 if(accept) 614 { 615 comp.setBorder( greenBorder); 616 } 617 else 618 { 619 comp.setBorder( redBorder); 620 } 621 } 622 623 public void dragExit(DropTargetEvent dte) { 624 System.out.println("dragExit"); 625 pathField.setToolTipText(null); 626 comp.setBorder(originalBorder); 627 } 628 629 public void dragOver(DropTargetDragEvent dte) { 630 } 632 633 public void drop(DropTargetDropEvent dte) { 634 comp.setBorder(originalBorder); 635 pathField.setToolTipText(null); 636 if(fileToDrop!=null) 637 { 638 setFile(fileToDrop); 639 } 640 } 642 643 public void dropActionChanged(DropTargetDragEvent dte) { 644 System.out.println("dropActionChanged"); 645 } 646 647 } 648 649 public static void main(String [] arguments) 650 { 651 JFrame f = new JFrame(); 652 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 653 FileField ff = new FileField("", false, "Hello", JFileChooser.FILES_ONLY); 654 ff.setComponentWidth(250); 655 ff.setAutoColorized(); 656 f.setContentPane(ff); 657 f.pack(); 658 f.setLocationRelativeTo(null); 659 f.setVisible(true); 660 } 661 662 663 } | Popular Tags |