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