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