1 /* 2 * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.awt.X11; 27 28 import java.awt.*; 29 import javax.swing.*; 30 import java.awt.event.*; 31 import java.awt.peer.*; 32 import java.io.*; 33 import java.util.Locale; 34 import java.util.Arrays; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 38 import sun.awt.AWTAccessor.ComponentAccessor; 39 import sun.util.logging.PlatformLogger; 40 import sun.awt.AWTAccessor; 41 42 class XFileDialogPeer extends XDialogPeer 43 implements FileDialogPeer, ActionListener, ItemListener, 44 KeyEventDispatcher, XChoicePeerListener { 45 46 private static final PlatformLogger log = 47 PlatformLogger.getLogger("sun.awt.X11.XFileDialogPeer"); 48 49 FileDialog target; 50 51 // This variable holds value exactly the same as value of the 'target.file' variable except: 52 // 1) is changed to null after quit (see handleQuitButton()) 53 // 2) keep the same value if 'target.file' is incorrect (see setFile()) 54 // It's not clear HOW we used it 55 // We should think about existence of this variable 56 String file; 57 58 String dir; 59 60 String title; 61 int mode; 62 FilenameFilter filter; 63 64 private static final int PATH_CHOICE_WIDTH = 20; 65 66 // Seems that the purpose of this variable is cashing of 'target.file' variable in order to help method show() 67 // We should think about using 'target.file' instead of 'savedFile' 68 // Perhaps, 'target.file' just more correct (see target.setFile()) 69 String savedFile; 70 71 // Holds value of the directory which was chosen before 72 // We use it in order to restore previously selected directory 73 // at the time of the next showing of the file dialog 74 String savedDir; 75 // Holds value of the system property 'user.dir' 76 // in order to init current directory 77 String userDir; 78 79 Dialog fileDialog; 80 81 GridBagLayout gbl; 82 GridBagLayout gblButtons; 83 GridBagConstraints gbc; 84 85 // ************** Components in the fileDialogWindow *************** 86 87 TextField filterField; 88 89 // This variable holds the current text of the file which user select through the navigation 90 // It's important that updating of this variable must be correct 91 // since this value is used at the time of the file dialog closing 92 // Namely, we invoke target.setFile() and then user can get this value 93 // We update this field in cases: 94 // - ITEM_STATE_CHANGED was triggered on the file list component: set to the current selected item 95 // - at the time of the 'show': set to savedFile 96 // - at the time of the programmatically setting: set to new value 97 TextField selectionField; 98 99 List directoryList; 100 101 // This is the list component which is used for the showing of the file list of the current directory 102 List fileList; 103 104 Panel buttons; 105 Button openButton; 106 Button filterButton; 107 Button cancelButton; 108 Choice pathChoice; 109 TextField pathField; 110 Panel pathPanel; 111 112 String cancelButtonText = null; 113 String enterFileNameLabelText = null; 114 String filesLabelText= null; 115 String foldersLabelText= null; 116 String pathLabelText= null; 117 String filterLabelText= null; 118 String openButtonText= null; 119 String saveButtonText= null; 120 String actionButtonText= null; 121 122 123 void installStrings() { 124 Locale l = target.getLocale(); 125 UIDefaults uid = XToolkit.getUIDefaults(); 126 cancelButtonText = uid.getString("FileChooser.cancelButtonText",l); 127 enterFileNameLabelText = uid.getString("FileChooser.enterFileNameLabelText",l); 128 filesLabelText = uid.getString("FileChooser.filesLabelText",l); 129 foldersLabelText = uid.getString("FileChooser.foldersLabelText",l); 130 pathLabelText = uid.getString("FileChooser.pathLabelText",l); 131 filterLabelText = uid.getString("FileChooser.filterLabelText",l); 132 openButtonText = uid.getString("FileChooser.openButtonText",l); 133 saveButtonText = uid.getString("FileChooser.saveButtonText",l); 134 135 } 136 137 XFileDialogPeer(FileDialog target) { 138 super((Dialog)target); 139 this.target = target; 140 } 141 142 @SuppressWarnings("deprecation") 143 private void init(FileDialog target) { 144 fileDialog = target; //new Dialog(target, target.getTitle(), false); 145 this.title = target.getTitle(); 146 this.mode = target.getMode(); 147 this.target = target; 148 this.filter = target.getFilenameFilter(); 149 150 savedFile = target.getFile(); 151 savedDir = target.getDirectory(); 152 // Shouldn't save 'user.dir' to 'savedDir' 153 // since getDirectory() will be incorrect after handleCancel 154 userDir = AccessController.doPrivileged( 155 new PrivilegedAction<String>() { 156 public String run() { 157 return System.getProperty("user.dir"); 158 } 159 }); 160 161 installStrings(); 162 gbl = new GridBagLayout(); 163 gblButtons = new GridBagLayout(); 164 gbc = new GridBagConstraints(); 165 fileDialog.setLayout(gbl); 166 167 // create components 168 buttons = new Panel(); 169 buttons.setLayout(gblButtons); 170 actionButtonText = (target.getMode() == FileDialog.SAVE) ? saveButtonText : openButtonText; 171 openButton = new Button(actionButtonText); 172 173 filterButton = new Button(filterLabelText); 174 cancelButton = new Button(cancelButtonText); 175 directoryList = new List(); 176 fileList = new List(); 177 filterField = new TextField(); 178 selectionField = new TextField(); 179 180 boolean isMultipleMode = 181 AWTAccessor.getFileDialogAccessor().isMultipleMode(target); 182 fileList.setMultipleMode(isMultipleMode); 183 184 // the insets used by the components in the fileDialog 185 Insets noInset = new Insets(0, 0, 0, 0); 186 Insets textFieldInset = new Insets(0, 8, 0, 8); 187 Insets leftListInset = new Insets(0, 8, 0, 4); 188 Insets rightListInset = new Insets(0, 4, 0, 8); 189 Insets separatorInset = new Insets(8, 0, 0, 0); 190 Insets labelInset = new Insets(0, 8, 0, 0); 191 Insets buttonsInset = new Insets(10, 8, 10, 8); 192 193 // add components to GridBagLayout "gbl" 194 195 Font f = new Font(Font.DIALOG, Font.PLAIN, 12); 196 197 Label label = new Label(pathLabelText); 198 label.setFont(f); 199 addComponent(label, gbl, gbc, 0, 0, 1, 200 GridBagConstraints.WEST, (Container)fileDialog, 201 1, 0, GridBagConstraints.NONE, labelInset); 202 203 // Fixed 6260650: FileDialog.getDirectory() does not return null when file dialog is cancelled 204 // After showing we should display 'user.dir' as current directory 205 // if user didn't set directory programatically 206 pathField = new TextField(savedDir != null ? savedDir : userDir); 207 @SuppressWarnings("serial") // Anonymous class 208 Choice tmp = new Choice() { 209 public Dimension getPreferredSize() { 210 return new Dimension(PATH_CHOICE_WIDTH, pathField.getPreferredSize().height); 211 } 212 }; 213 pathChoice = tmp; 214 pathPanel = new Panel(); 215 pathPanel.setLayout(new BorderLayout()); 216 217 pathPanel.add(pathField,BorderLayout.CENTER); 218 pathPanel.add(pathChoice,BorderLayout.EAST); 219 //addComponent(pathField, gbl, gbc, 0, 1, 2, 220 // GridBagConstraints.WEST, (Container)fileDialog, 221 // 1, 0, GridBagConstraints.HORIZONTAL, textFieldInset); 222 //addComponent(pathChoice, gbl, gbc, 1, 1, GridBagConstraints.RELATIVE, 223 // GridBagConstraints.WEST, (Container)fileDialog, 224 // 1, 0, GridBagConstraints.HORIZONTAL, textFieldInset); 225 addComponent(pathPanel, gbl, gbc, 0, 1, 2, 226 GridBagConstraints.WEST, (Container)fileDialog, 227 1, 0, GridBagConstraints.HORIZONTAL, textFieldInset); 228 229 230 231 label = new Label(filterLabelText); 232 233 label.setFont(f); 234 addComponent(label, gbl, gbc, 0, 2, 1, 235 GridBagConstraints.WEST, (Container)fileDialog, 236 1, 0, GridBagConstraints.NONE, labelInset); 237 addComponent(filterField, gbl, gbc, 0, 3, 2, 238 GridBagConstraints.WEST, (Container)fileDialog, 239 1, 0, GridBagConstraints.HORIZONTAL, textFieldInset); 240 241 label = new Label(foldersLabelText); 242 243 label.setFont(f); 244 addComponent(label, gbl, gbc, 0, 4, 1, 245 GridBagConstraints.WEST, (Container)fileDialog, 246 1, 0, GridBagConstraints.NONE, labelInset); 247 248 label = new Label(filesLabelText); 249 250 label.setFont(f); 251 addComponent(label, gbl, gbc, 1, 4, 1, 252 GridBagConstraints.WEST, (Container)fileDialog, 253 1, 0, GridBagConstraints.NONE, labelInset); 254 addComponent(directoryList, gbl, gbc, 0, 5, 1, 255 GridBagConstraints.WEST, (Container)fileDialog, 256 1, 1, GridBagConstraints.BOTH, leftListInset); 257 addComponent(fileList, gbl, gbc, 1, 5, 1, 258 GridBagConstraints.WEST, (Container)fileDialog, 259 1, 1, GridBagConstraints.BOTH, rightListInset); 260 261 label = new Label(enterFileNameLabelText); 262 263 label.setFont(f); 264 addComponent(label, gbl, gbc, 0, 6, 1, 265 GridBagConstraints.WEST, (Container)fileDialog, 266 1, 0, GridBagConstraints.NONE, labelInset); 267 addComponent(selectionField, gbl, gbc, 0, 7, 2, 268 GridBagConstraints.WEST, (Container)fileDialog, 269 1, 0, GridBagConstraints.HORIZONTAL, textFieldInset); 270 addComponent(new Separator(fileDialog.size().width, 2, Separator.HORIZONTAL), gbl, gbc, 0, 8, 15, 271 GridBagConstraints.WEST, (Container)fileDialog, 272 1, 0, GridBagConstraints.HORIZONTAL, separatorInset); 273 274 // add buttons to GridBagLayout Buttons 275 addComponent(openButton, gblButtons, gbc, 0, 0, 1, 276 GridBagConstraints.WEST, (Container)buttons, 277 1, 0, GridBagConstraints.NONE, noInset); 278 addComponent(filterButton, gblButtons, gbc, 1, 0, 1, 279 GridBagConstraints.CENTER, (Container)buttons, 280 1, 0, GridBagConstraints.NONE, noInset); 281 addComponent(cancelButton, gblButtons, gbc, 2, 0, 1, 282 GridBagConstraints.EAST, (Container)buttons, 283 1, 0, GridBagConstraints.NONE, noInset); 284 285 // add ButtonPanel to the GridBagLayout of this class 286 addComponent(buttons, gbl, gbc, 0, 9, 2, 287 GridBagConstraints.WEST, (Container)fileDialog, 288 1, 0, GridBagConstraints.HORIZONTAL, buttonsInset); 289 290 fileDialog.setSize(400, 400); 291 292 // Update choice's popup width 293 XChoicePeer choicePeer = AWTAccessor.getComponentAccessor() 294 .getPeer(pathChoice); 295 choicePeer.setDrawSelectedItem(false); 296 choicePeer.setAlignUnder(pathField); 297 298 filterField.addActionListener(this); 299 selectionField.addActionListener(this); 300 directoryList.addActionListener(this); 301 directoryList.addItemListener(this); 302 fileList.addItemListener(this); 303 fileList.addActionListener(this); 304 openButton.addActionListener(this); 305 filterButton.addActionListener(this); 306 cancelButton.addActionListener(this); 307 pathChoice.addItemListener(this); 308 pathField.addActionListener(this); 309 310 // b6227750 FileDialog is not disposed when clicking the 'close' (X) button on the top right corner, XToolkit 311 target.addWindowListener( 312 new WindowAdapter(){ 313 public void windowClosing(WindowEvent e){ 314 handleCancel(); 315 } 316 } 317 ); 318 319 // 6259434 PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit 320 pathChoice.addItemListener(this); 321 322 } 323 324 public void updateMinimumSize() { 325 } 326 327 public void updateIconImages() { 328 if (winAttr.icons == null){ 329 winAttr.iconsInherited = false; 330 winAttr.icons = getDefaultIconInfo(); 331 setIconHints(winAttr.icons); 332 } 333 } 334 335 /** 336 * add Component comp to the container cont. 337 * add the component to the correct GridBagLayout 338 */ 339 void addComponent(Component comp, GridBagLayout gb, GridBagConstraints c, int gridx, 340 int gridy, int gridwidth, int anchor, Container cont, int weightx, int weighty, 341 int fill, Insets in) { 342 c.gridx = gridx; 343 c.gridy = gridy; 344 c.gridwidth = gridwidth; 345 c.anchor = anchor; 346 c.weightx = weightx; 347 c.weighty = weighty; 348 c.fill = fill; 349 c.insets = in; 350 gb.setConstraints(comp, c); 351 cont.add(comp); 352 } 353 354 /** 355 * get fileName 356 */ 357 String getFileName(String str) { 358 if (str == null) { 359 return ""; 360 } 361 362 int index = str.lastIndexOf('/'); 363 364 if (index == -1) { 365 return str; 366 } else { 367 return str.substring(index + 1); 368 } 369 } 370 371 /** handleFilter 372 * 373 */ 374 void handleFilter(String f) { 375 376 if (f == null) { 377 return; 378 } 379 setFilterEntry(dir,f); 380 381 // Fixed within 6259434: PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit 382 // Here we restoring Motif behaviour 383 directoryList.select(0); 384 if (fileList.getItemCount() != 0) { 385 fileList.requestFocus(); 386 } else { 387 directoryList.requestFocus(); 388 } 389 } 390 391 /** 392 * handle the selection event 393 */ 394 void handleSelection(String file) { 395 396 int index = file.lastIndexOf(java.io.File.separatorChar); 397 398 if (index == -1) { 399 savedDir = this.dir; 400 savedFile = file; 401 } else { 402 savedDir = file.substring(0, index+1); 403 savedFile = file.substring(index+1); 404 } 405 406 String[] fileNames = fileList.getSelectedItems(); 407 int filesNumber = (fileNames != null) ? fileNames.length : 0; 408 File[] files = new File[filesNumber]; 409 for (int i = 0; i < filesNumber; i++) { 410 files[i] = new File(savedDir, fileNames[i]); 411 } 412 413 AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor(); 414 415 fileDialogAccessor.setDirectory(target, savedDir); 416 fileDialogAccessor.setFile(target, savedFile); 417 fileDialogAccessor.setFiles(target, files); 418 } 419 420 /** 421 * handle the cancel event 422 */ 423 @SuppressWarnings("deprecation") 424 void handleCancel() { 425 KeyboardFocusManager.getCurrentKeyboardFocusManager() 426 .removeKeyEventDispatcher(this); 427 428 setSelectionField(null); 429 setFilterField(null); 430 directoryList.clear(); 431 fileList.clear(); 432 433 AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor(); 434 435 fileDialogAccessor.setDirectory(target, null); 436 fileDialogAccessor.setFile(target, null); 437 fileDialogAccessor.setFiles(target, null); 438 439 handleQuitButton(); 440 } 441 442 /** 443 * handle the quit event 444 */ 445 @SuppressWarnings("deprecation") 446 void handleQuitButton() { 447 dir = null; 448 file = null; 449 target.hide(); 450 } 451 452 /** 453 * set the entry of the new dir with f 454 */ 455 @SuppressWarnings("deprecation") 456 void setFilterEntry(String d, String f) { 457 File fe = new File(d); 458 459 if (fe.isDirectory() && fe.canRead()) { 460 // Fixed 6260659: File Name set programmatically in FileDialog is overridden during navigation, XToolkit 461 // Here we restoring Motif behaviour 462 setSelectionField(target.getFile()); 463 464 if (f.equals("")) { 465 f = "*"; 466 setFilterField(f); 467 } else { 468 setFilterField(f); 469 } 470 String[] l; 471 472 if (f.equals("*")) { 473 l = fe.list(); 474 } else { 475 // REMIND: fileDialogFilter is not implemented yet 476 FileDialogFilter ff = new FileDialogFilter(f); 477 l = fe.list(ff); 478 } 479 // Fixed 6358953: handling was added in case of I/O error happens 480 if (l == null) { 481 this.dir = getParentDirectory(); 482 return; 483 } 484 directoryList.clear(); 485 fileList.clear(); 486 directoryList.setVisible(false); 487 fileList.setVisible(false); 488 489 directoryList.addItem(".."); 490 Arrays.sort(l); 491 for (int i = 0 ; i < l.length ; i++) { 492 File file = new File(d + l[i]); 493 if (file.isDirectory()) { 494 directoryList.addItem(l[i] + "/"); 495 } else { 496 if (filter != null) { 497 if (filter.accept(new File(l[i]),l[i])) fileList.addItem(l[i]); 498 } 499 else fileList.addItem(l[i]); 500 } 501 } 502 this.dir = d; 503 504 pathField.setText(dir); 505 506 // Some code was removed 507 // Now we do updating of the pathChoice at the time of the choice opening 508 509 target.setDirectory(this.dir); 510 directoryList.setVisible(true); 511 fileList.setVisible(true); 512 } 513 } 514 515 516 String[] getDirList(String dir) { 517 if (!dir.endsWith("/")) 518 dir = dir + "/"; 519 char[] charr = dir.toCharArray(); 520 int numSlashes = 0; 521 for (int i=0;i<charr.length;i++) { 522 if (charr[i] == '/') 523 numSlashes++; 524 } 525 String[] starr = new String[numSlashes]; 526 int j=0; 527 for (int i=charr.length-1;i>=0;i--) { 528 if (charr[i] == '/') 529 { 530 starr[j++] = new String(charr,0,i+1); 531 } 532 } 533 return starr; 534 } 535 536 /** 537 * set the text in the selectionField 538 */ 539 void setSelectionField(String str) { 540 selectionField.setText(str); 541 } 542 543 /** 544 * set the text in the filterField 545 */ 546 void setFilterField(String str) { 547 filterField.setText(str); 548 } 549 550 /** 551 * 552 * @see java.awt.event.ItemEvent 553 * ItemEvent.ITEM_STATE_CHANGED 554 */ 555 public void itemStateChanged(ItemEvent itemEvent){ 556 if (itemEvent.getID() != ItemEvent.ITEM_STATE_CHANGED || 557 itemEvent.getStateChange() == ItemEvent.DESELECTED) { 558 return; 559 } 560 561 Object source = itemEvent.getSource(); 562 563 if (source == pathChoice) { 564 /* 565 * Update the selection ('folder name' text field) after 566 * the current item changing in the unfurled choice by the arrow keys. 567 * See 6259434, 6240074 for more information 568 */ 569 String dir = pathChoice.getSelectedItem(); 570 pathField.setText(dir); 571 } else if (directoryList == source) { 572 setFilterField(getFileName(filterField.getText())); 573 } else if (fileList == source) { 574 String file = fileList.getItem((Integer)itemEvent.getItem()); 575 setSelectionField(file); 576 } 577 } 578 579 /* 580 * Updates the current directory only if directoryList-specific 581 * action occurred. Returns false if the forward directory is inaccessible 582 */ 583 boolean updateDirectoryByUserAction(String str) { 584 585 String dir; 586 if (str.equals("..")) { 587 dir = getParentDirectory(); 588 } 589 else { 590 dir = this.dir + str; 591 } 592 593 File fe = new File(dir); 594 if (fe.canRead()) { 595 this.dir = dir; 596 return true; 597 }else { 598 return false; 599 } 600 } 601 602 String getParentDirectory(){ 603 String parent = this.dir; 604 if (!this.dir.equals("/")) // If the current directory is "/" leave it alone. 605 { 606 if (dir.endsWith("/")) 607 parent = parent.substring(0,parent.lastIndexOf("/")); 608 609 parent = parent.substring(0,parent.lastIndexOf("/")+1); 610 } 611 return parent; 612 } 613 614 public void actionPerformed( ActionEvent actionEvent ) { 615 String actionCommand = actionEvent.getActionCommand(); 616 Object source = actionEvent.getSource(); 617 618 if (actionCommand.equals(actionButtonText)) { 619 handleSelection( selectionField.getText() ); 620 handleQuitButton(); 621 } else if (actionCommand.equals(filterLabelText)) { 622 handleFilter( filterField.getText() ); 623 } else if (actionCommand.equals(cancelButtonText)) { 624 handleCancel(); 625 } else if ( source instanceof TextField ) { 626 if ( selectionField == ((TextField)source) ) { 627 // Fixed within 6259434: PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit 628 // We should handle the action based on the selection field 629 // Looks like mistake 630 handleSelection(selectionField.getText()); 631 handleQuitButton(); 632 } else if (filterField == ((TextField)source)) { 633 handleFilter(filterField.getText()); 634 } else if (pathField == ((TextField)source)) { 635 target.setDirectory(pathField.getText()); 636 } 637 } else if (source instanceof List) { 638 if (directoryList == ((List)source)) { 639 //handleFilter( actionCommand + getFileName( filterField.getText() ) ); 640 if (updateDirectoryByUserAction(actionCommand)){ 641 handleFilter( getFileName( filterField.getText() ) ); 642 } 643 } else if (fileList == ((List)source)) { 644 handleSelection( actionCommand ); 645 handleQuitButton(); 646 } 647 } 648 } 649 650 public boolean dispatchKeyEvent(KeyEvent keyEvent) { 651 int id = keyEvent.getID(); 652 int keyCode = keyEvent.getKeyCode(); 653 654 if (id == KeyEvent.KEY_PRESSED && keyCode == KeyEvent.VK_ESCAPE) { 655 synchronized (target.getTreeLock()) { 656 Component comp = (Component) keyEvent.getSource(); 657 while (comp != null) { 658 // Fix for 6240084 Disposing a file dialog when the drop-down is active does not dispose the dropdown menu, on Xtoolkit 659 // See also 6259493 660 ComponentAccessor acc = AWTAccessor.getComponentAccessor(); 661 if (comp == pathChoice) { 662 XChoicePeer choicePeer = acc.getPeer(pathChoice); 663 if (choicePeer.isUnfurled()){ 664 return false; 665 } 666 } 667 Object peer = acc.getPeer(comp); 668 if (peer == this) { 669 handleCancel(); 670 return true; 671 } 672 comp = comp.getParent(); 673 } 674 } 675 } 676 677 return false; 678 } 679 680 681 /** 682 * set the file 683 */ 684 public void setFile(String file) { 685 686 if (file == null) { 687 this.file = null; 688 return; 689 } 690 691 if (this.dir == null) { 692 String d = "./"; 693 File f = new File(d, file); 694 695 if (f.isFile()) { 696 this.file = file; 697 setDirectory(d); 698 } 699 } else { 700 File f = new File(this.dir, file); 701 if (f.isFile()) { 702 this.file = file; 703 } 704 } 705 706 setSelectionField(file); 707 } 708 709 /** 710 * set the directory 711 * FIXME: we should update 'savedDir' after programmatically 'setDirectory' 712 * Otherwise, SavedDir will be not null before second showing 713 * So the current directory of the file dialog will be incorrect after second showing 714 * since 'setDirectory' will be ignored 715 * We cann't update savedDir here now since it used very often 716 */ 717 public void setDirectory(String dir) { 718 719 if (dir == null) { 720 this.dir = null; 721 return; 722 } 723 724 if (dir.equals(this.dir)) { 725 return; 726 } 727 728 int i; 729 if ((i=dir.indexOf("~")) != -1) { 730 731 dir = dir.substring(0,i) + System.getProperty("user.home") + dir.substring(i+1,dir.length()); 732 } 733 734 File fe = new File(dir).getAbsoluteFile(); 735 if (log.isLoggable(PlatformLogger.Level.FINE)) { 736 log.fine("Current directory : " + fe); 737 } 738 739 if (!fe.isDirectory()) { 740 dir = "./"; 741 fe = new File(dir).getAbsoluteFile(); 742 743 if (!fe.isDirectory()) { 744 return; 745 } 746 } 747 try { 748 dir = this.dir = fe.getCanonicalPath(); 749 } catch (java.io.IOException ie) { 750 dir = this.dir = fe.getAbsolutePath(); 751 } 752 pathField.setText(this.dir); 753 754 755 if (dir.endsWith("/")) { 756 this.dir = dir; 757 handleFilter(""); 758 } else { 759 this.dir = dir + "/"; 760 handleFilter(""); 761 } 762 763 // Some code was removed 764 // Now we do updating of the pathChoice at the time of the choice opening 765 // Fixed problem: 766 // The exception java.awt.IllegalComponentStateException will be thrown 767 // if the user invoke setDirectory after the closing of the file dialog 768 } 769 770 /** 771 * set filenameFilter 772 * 773 */ 774 public void setFilenameFilter(FilenameFilter filter) { 775 this.filter = filter; 776 } 777 778 779 public void dispose() { 780 FileDialog fd = (FileDialog)fileDialog; 781 if (fd != null) { 782 fd.removeAll(); 783 } 784 super.dispose(); 785 } 786 787 // 03/02/2005 b5097243 Pressing 'ESC' on a file dlg does not dispose the dlg on Xtoolkit 788 @SuppressWarnings("deprecation") 789 public void setVisible(boolean b){ 790 if (fileDialog == null) { 791 init(target); 792 } 793 794 if (savedDir != null || userDir != null) { 795 setDirectory(savedDir != null ? savedDir : userDir); 796 } 797 798 if (savedFile != null) { 799 // Actually in Motif implementation lost file value which was saved after prevously showing 800 // Seems we shouldn't restore Motif behaviour in this case 801 setFile(savedFile); 802 } 803 804 super.setVisible(b); 805 XChoicePeer choicePeer = AWTAccessor.getComponentAccessor() 806 .getPeer(pathChoice); 807 if (b == true){ 808 // See 6240074 for more information 809 choicePeer.addXChoicePeerListener(this); 810 KeyboardFocusManager.getCurrentKeyboardFocusManager() 811 .addKeyEventDispatcher(this); 812 }else{ 813 // See 6240074 for more information 814 choicePeer.removeXChoicePeerListener(); 815 KeyboardFocusManager.getCurrentKeyboardFocusManager() 816 .removeKeyEventDispatcher(this); 817 } 818 819 selectionField.requestFocusInWindow(); 820 } 821 822 /* 823 * Adding items to the path choice based on the text string 824 * See 6240074 for more information 825 */ 826 public void addItemsToPathChoice(String text){ 827 String[] dirList = getDirList(text); 828 for (int i = 0; i < dirList.length; i++) pathChoice.addItem(dirList[i]); 829 } 830 831 /* 832 * Refresh the unfurled choice at the time of the opening choice according to the text of the path field 833 * See 6240074 for more information 834 */ 835 public void unfurledChoiceOpening(ListHelper choiceHelper){ 836 837 // When the unfurled choice is opening the first time, we need only to add elements, otherwise we've got exception 838 if (choiceHelper.getItemCount() == 0){ 839 addItemsToPathChoice(pathField.getText()); 840 return; 841 } 842 843 // If the set of the directories the exactly same as the used to be then dummy 844 if (pathChoice.getItem(0).equals(pathField.getText())) 845 return; 846 847 pathChoice.removeAll(); 848 addItemsToPathChoice(pathField.getText()); 849 } 850 851 /* 852 * Refresh the file dialog at the time of the closing choice according to the selected item of the choice 853 * See 6240074 for more information 854 */ 855 public void unfurledChoiceClosing(){ 856 // This is the exactly same code as invoking later at the time of the itemStateChanged 857 // Here is we restore Windows behaviour: change current directory if user press 'ESC' 858 String dir = pathChoice.getSelectedItem(); 859 target.setDirectory(dir); 860 } 861 } 862 863 @SuppressWarnings("serial") // JDK-implementation class 864 class Separator extends Canvas { 865 public static final int HORIZONTAL = 0; 866 public static final int VERTICAL = 1; 867 int orientation; 868 869 @SuppressWarnings("deprecation") 870 public Separator(int length, int thickness, int orient) { 871 super(); 872 orientation = orient; 873 if (orient == HORIZONTAL) { 874 resize(length, thickness); 875 } else { 876 // VERTICAL 877 resize(thickness, length); 878 } 879 } 880 881 @SuppressWarnings("deprecation") 882 public void paint(Graphics g) { 883 int x1, y1, x2, y2; 884 Rectangle bbox = bounds(); 885 Color c = getBackground(); 886 Color brighter = c.brighter(); 887 Color darker = c.darker(); 888 889 if (orientation == HORIZONTAL) { 890 x1 = 0; 891 x2 = bbox.width - 1; 892 y1 = y2 = bbox.height/2 - 1; 893 894 } else { 895 // VERTICAL 896 x1 = x2 = bbox.width/2 - 1; 897 y1 = 0; 898 y2 = bbox.height - 1; 899 } 900 g.setColor(darker); 901 g.drawLine(x1, y2, x2, y2); 902 g.setColor(brighter); 903 if (orientation == HORIZONTAL) 904 g.drawLine(x1, y2+1, x2, y2+1); 905 else 906 g.drawLine(x1+1, y2, x2+1, y2); 907 } 908 } 909 910 /* 911 * Motif file dialogs let the user specify a filter that controls the files that 912 * are displayed in the dialog. This filter is generally specified as a regular 913 * expression. The class is used to implement Motif-like filtering. 914 */ 915 class FileDialogFilter implements FilenameFilter { 916 917 String filter; 918 919 public FileDialogFilter(String f) { 920 filter = f; 921 } 922 923 /* 924 * Tells whether or not the specified file should be included in a file list 925 */ 926 public boolean accept(File dir, String fileName) { 927 928 File f = new File(dir, fileName); 929 930 if (f.isDirectory()) { 931 return true; 932 } else { 933 return matches(fileName, filter); 934 } 935 } 936 937 /* 938 * Tells whether or not the input string matches the given filter 939 */ 940 private boolean matches(String input, String filter) { 941 String regex = convert(filter); 942 return input.matches(regex); 943 } 944 945 /* 946 * Converts the filter into the form which is acceptable by Java's regexps 947 */ 948 private String convert(String filter) { 949 String regex = "^" + filter + "$"; 950 regex = regex.replaceAll("\\.", "\\\\."); 951 regex = regex.replaceAll("\\?", "."); 952 regex = regex.replaceAll("\\*", ".*"); 953 return regex; 954 } 955 }