/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.awt.X11;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.peer.*;
import java.io.*;
import java.util.Locale;
import java.util.Arrays;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.util.logging.PlatformLogger;
import sun.awt.AWTAccessor;
class XFileDialogPeer extends XDialogPeer
implements FileDialogPeer, ActionListener, ItemListener,
KeyEventDispatcher, XChoicePeerListener {
private static final PlatformLogger log =
PlatformLogger.getLogger("sun.awt.X11.XFileDialogPeer");
FileDialog target;
// This variable holds value exactly the same as value of the 'target.file' variable except:
// 1) is changed to null after quit (see handleQuitButton())
// 2) keep the same value if 'target.file' is incorrect (see setFile())
// It's not clear HOW we used it
// We should think about existence of this variable
String file;
String dir;
String title;
int mode;
FilenameFilter filter;
private static final int PATH_CHOICE_WIDTH = 20;
// Seems that the purpose of this variable is cashing of 'target.file' variable in order to help method show()
// We should think about using 'target.file' instead of 'savedFile'
// Perhaps, 'target.file' just more correct (see target.setFile())
String savedFile;
// Holds value of the directory which was chosen before
// We use it in order to restore previously selected directory
// at the time of the next showing of the file dialog
String savedDir;
// Holds value of the system property 'user.dir'
// in order to init current directory
String userDir;
Dialog fileDialog;
GridBagLayout gbl;
GridBagLayout gblButtons;
GridBagConstraints gbc;
// ************** Components in the fileDialogWindow ***************
TextField filterField;
// This variable holds the current text of the file which user select through the navigation
// It's important that updating of this variable must be correct
// since this value is used at the time of the file dialog closing
// Namely, we invoke target.setFile() and then user can get this value
// We update this field in cases:
// - ITEM_STATE_CHANGED was triggered on the file list component: set to the current selected item
// - at the time of the 'show': set to savedFile
// - at the time of the programmatically setting: set to new value
TextField selectionField;
List directoryList;
// This is the list component which is used for the showing of the file list of the current directory
List fileList;
Panel buttons;
Button openButton;
Button filterButton;
Button cancelButton;
Choice pathChoice;
TextField pathField;
Panel pathPanel;
String cancelButtonText = null;
String enterFileNameLabelText = null;
String filesLabelText= null;
String foldersLabelText= null;
String pathLabelText= null;
String filterLabelText= null;
String openButtonText= null;
String saveButtonText= null;
String actionButtonText= null;
void installStrings() {
Locale l = target.getLocale();
UIDefaults uid = XToolkit.getUIDefaults();
cancelButtonText = uid.getString("FileChooser.cancelButtonText",l);
enterFileNameLabelText = uid.getString("FileChooser.enterFileNameLabelText",l);
filesLabelText = uid.getString("FileChooser.filesLabelText",l);
foldersLabelText = uid.getString("FileChooser.foldersLabelText",l);
pathLabelText = uid.getString("FileChooser.pathLabelText",l);
filterLabelText = uid.getString("FileChooser.filterLabelText",l);
openButtonText = uid.getString("FileChooser.openButtonText",l);
saveButtonText = uid.getString("FileChooser.saveButtonText",l);
}
XFileDialogPeer(FileDialog target) {
super((Dialog)target);
this.target = target;
}
@SuppressWarnings("deprecation")
private void init(FileDialog target) {
fileDialog = target; //new Dialog(target, target.getTitle(), false);
this.title = target.getTitle();
this.mode = target.getMode();
this.target = target;
this.filter = target.getFilenameFilter();
savedFile = target.getFile();
savedDir = target.getDirectory();
// Shouldn't save 'user.dir' to 'savedDir'
// since getDirectory() will be incorrect after handleCancel
userDir = AccessController.doPrivileged(
new PrivilegedAction<String>() {
public String run() {
return System.getProperty("user.dir");
}
});
installStrings();
gbl = new GridBagLayout();
gblButtons = new GridBagLayout();
gbc = new GridBagConstraints();
fileDialog.setLayout(gbl);
// create components
buttons = new Panel();
buttons.setLayout(gblButtons);
actionButtonText = (target.getMode() == FileDialog.SAVE) ? saveButtonText : openButtonText;
openButton = new Button(actionButtonText);
filterButton = new Button(filterLabelText);
cancelButton = new Button(cancelButtonText);
directoryList = new List();
fileList = new List();
filterField = new TextField();
selectionField = new TextField();
boolean isMultipleMode =
AWTAccessor.getFileDialogAccessor().isMultipleMode(target);
fileList.setMultipleMode(isMultipleMode);
// the insets used by the components in the fileDialog
Insets noInset = new Insets(0, 0, 0, 0);
Insets textFieldInset = new Insets(0, 8, 0, 8);
Insets leftListInset = new Insets(0, 8, 0, 4);
Insets rightListInset = new Insets(0, 4, 0, 8);
Insets separatorInset = new Insets(8, 0, 0, 0);
Insets labelInset = new Insets(0, 8, 0, 0);
Insets buttonsInset = new Insets(10, 8, 10, 8);
// add components to GridBagLayout "gbl"
Font f = new Font(Font.DIALOG, Font.PLAIN, 12);
Label label = new Label(pathLabelText);
label.setFont(f);
addComponent(label, gbl, gbc, 0, 0, 1,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.NONE, labelInset);
// Fixed 6260650: FileDialog.getDirectory() does not return null when file dialog is cancelled
// After showing we should display 'user.dir' as current directory
// if user didn't set directory programatically
pathField = new TextField(savedDir != null ? savedDir : userDir);
@SuppressWarnings("serial") // Anonymous class
Choice tmp = new Choice() {
public Dimension getPreferredSize() {
return new Dimension(PATH_CHOICE_WIDTH, pathField.getPreferredSize().height);
}
};
pathChoice = tmp;
pathPanel = new Panel();
pathPanel.setLayout(new BorderLayout());
pathPanel.add(pathField,BorderLayout.CENTER);
pathPanel.add(pathChoice,BorderLayout.EAST);
//addComponent(pathField, gbl, gbc, 0, 1, 2,
// GridBagConstraints.WEST, (Container)fileDialog,
// 1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
//addComponent(pathChoice, gbl, gbc, 1, 1, GridBagConstraints.RELATIVE,
// GridBagConstraints.WEST, (Container)fileDialog,
// 1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
addComponent(pathPanel, gbl, gbc, 0, 1, 2,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
label = new Label(filterLabelText);
label.setFont(f);
addComponent(label, gbl, gbc, 0, 2, 1,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.NONE, labelInset);
addComponent(filterField, gbl, gbc, 0, 3, 2,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
label = new Label(foldersLabelText);
label.setFont(f);
addComponent(label, gbl, gbc, 0, 4, 1,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.NONE, labelInset);
label = new Label(filesLabelText);
label.setFont(f);
addComponent(label, gbl, gbc, 1, 4, 1,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.NONE, labelInset);
addComponent(directoryList, gbl, gbc, 0, 5, 1,
GridBagConstraints.WEST, (Container)fileDialog,
1, 1, GridBagConstraints.BOTH, leftListInset);
addComponent(fileList, gbl, gbc, 1, 5, 1,
GridBagConstraints.WEST, (Container)fileDialog,
1, 1, GridBagConstraints.BOTH, rightListInset);
label = new Label(enterFileNameLabelText);
label.setFont(f);
addComponent(label, gbl, gbc, 0, 6, 1,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.NONE, labelInset);
addComponent(selectionField, gbl, gbc, 0, 7, 2,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.HORIZONTAL, textFieldInset);
addComponent(new Separator(fileDialog.size().width, 2, Separator.HORIZONTAL), gbl, gbc, 0, 8, 15,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.HORIZONTAL, separatorInset);
// add buttons to GridBagLayout Buttons
addComponent(openButton, gblButtons, gbc, 0, 0, 1,
GridBagConstraints.WEST, (Container)buttons,
1, 0, GridBagConstraints.NONE, noInset);
addComponent(filterButton, gblButtons, gbc, 1, 0, 1,
GridBagConstraints.CENTER, (Container)buttons,
1, 0, GridBagConstraints.NONE, noInset);
addComponent(cancelButton, gblButtons, gbc, 2, 0, 1,
GridBagConstraints.EAST, (Container)buttons,
1, 0, GridBagConstraints.NONE, noInset);
// add ButtonPanel to the GridBagLayout of this class
addComponent(buttons, gbl, gbc, 0, 9, 2,
GridBagConstraints.WEST, (Container)fileDialog,
1, 0, GridBagConstraints.HORIZONTAL, buttonsInset);
fileDialog.setSize(400, 400);
// Update choice's popup width
XChoicePeer choicePeer = AWTAccessor.getComponentAccessor()
.getPeer(pathChoice);
choicePeer.setDrawSelectedItem(false);
choicePeer.setAlignUnder(pathField);
filterField.addActionListener(this);
selectionField.addActionListener(this);
directoryList.addActionListener(this);
directoryList.addItemListener(this);
fileList.addItemListener(this);
fileList.addActionListener(this);
openButton.addActionListener(this);
filterButton.addActionListener(this);
cancelButton.addActionListener(this);
pathChoice.addItemListener(this);
pathField.addActionListener(this);
// b6227750 FileDialog is not disposed when clicking the 'close' (X) button on the top right corner, XToolkit
target.addWindowListener(
new WindowAdapter(){
public void windowClosing(WindowEvent e){
handleCancel();
}
}
);
// 6259434 PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit
pathChoice.addItemListener(this);
}
public void updateMinimumSize() {
}
public void updateIconImages() {
if (winAttr.icons == null){
winAttr.iconsInherited = false;
winAttr.icons = getDefaultIconInfo();
setIconHints(winAttr.icons);
}
}
/**
* add Component comp to the container cont.
* add the component to the correct GridBagLayout
*/
void addComponent(Component comp, GridBagLayout gb, GridBagConstraints c, int gridx,
int gridy, int gridwidth, int anchor, Container cont, int weightx, int weighty,
int fill, Insets in) {
c.gridx = gridx;
c.gridy = gridy;
c.gridwidth = gridwidth;
c.anchor = anchor;
c.weightx = weightx;
c.weighty = weighty;
c.fill = fill;
c.insets = in;
gb.setConstraints(comp, c);
cont.add(comp);
}
/**
* get fileName
*/
String getFileName(String str) {
if (str == null) {
return "";
}
int index = str.lastIndexOf('/');
if (index == -1) {
return str;
} else {
return str.substring(index + 1);
}
}
/** handleFilter
*
*/
void handleFilter(String f) {
if (f == null) {
return;
}
setFilterEntry(dir,f);
// Fixed within 6259434: PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit
// Here we restoring Motif behaviour
directoryList.select(0);
if (fileList.getItemCount() != 0) {
fileList.requestFocus();
} else {
directoryList.requestFocus();
}
}
/**
* handle the selection event
*/
void handleSelection(String file) {
int index = file.lastIndexOf(java.io.File.separatorChar);
if (index == -1) {
savedDir = this.dir;
savedFile = file;
} else {
savedDir = file.substring(0, index+1);
savedFile = file.substring(index+1);
}
String[] fileNames = fileList.getSelectedItems();
int filesNumber = (fileNames != null) ? fileNames.length : 0;
File[] files = new File[filesNumber];
for (int i = 0; i < filesNumber; i++) {
files[i] = new File(savedDir, fileNames[i]);
}
AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
fileDialogAccessor.setDirectory(target, savedDir);
fileDialogAccessor.setFile(target, savedFile);
fileDialogAccessor.setFiles(target, files);
}
/**
* handle the cancel event
*/
@SuppressWarnings("deprecation")
void handleCancel() {
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removeKeyEventDispatcher(this);
setSelectionField(null);
setFilterField(null);
directoryList.clear();
fileList.clear();
AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
fileDialogAccessor.setDirectory(target, null);
fileDialogAccessor.setFile(target, null);
fileDialogAccessor.setFiles(target, null);
handleQuitButton();
}
/**
* handle the quit event
*/
@SuppressWarnings("deprecation")
void handleQuitButton() {
dir = null;
file = null;
target.hide();
}
/**
* set the entry of the new dir with f
*/
@SuppressWarnings("deprecation")
void setFilterEntry(String d, String f) {
File fe = new File(d);
if (fe.isDirectory() && fe.canRead()) {
// Fixed 6260659: File Name set programmatically in FileDialog is overridden during navigation, XToolkit
// Here we restoring Motif behaviour
setSelectionField(target.getFile());
if (f.isEmpty()) {
f = "*";
setFilterField(f);
} else {
setFilterField(f);
}
String[] l;
if (f.equals("*")) {
l = fe.list();
} else {
// REMIND: fileDialogFilter is not implemented yet
FileDialogFilter ff = new FileDialogFilter(f);
l = fe.list(ff);
}
// Fixed 6358953: handling was added in case of I/O error happens
if (l == null) {
this.dir = getParentDirectory();
return;
}
directoryList.clear();
fileList.clear();
directoryList.setVisible(false);
fileList.setVisible(false);
directoryList.addItem("..");
Arrays.sort(l);
for (int i = 0 ; i < l.length ; i++) {
File file = new File(d + l[i]);
if (file.isDirectory()) {
directoryList.addItem(l[i] + "/");
} else {
if (filter != null) {
if (filter.accept(new File(l[i]),l[i])) fileList.addItem(l[i]);
}
else fileList.addItem(l[i]);
}
}
this.dir = d;
pathField.setText(dir);
// Some code was removed
// Now we do updating of the pathChoice at the time of the choice opening
target.setDirectory(this.dir);
directoryList.setVisible(true);
fileList.setVisible(true);
}
}
String[] getDirList(String dir) {
if (!dir.endsWith("/"))
dir = dir + "/";
char[] charr = dir.toCharArray();
int numSlashes = 0;
for (int i=0;i<charr.length;i++) {
if (charr[i] == '/')
numSlashes++;
}
String[] starr = new String[numSlashes];
int j=0;
for (int i=charr.length-1;i>=0;i--) {
if (charr[i] == '/')
{
starr[j++] = new String(charr,0,i+1);
}
}
return starr;
}
/**
* set the text in the selectionField
*/
void setSelectionField(String str) {
selectionField.setText(str);
}
/**
* set the text in the filterField
*/
void setFilterField(String str) {
filterField.setText(str);
}
/**
*
* @see java.awt.event.ItemEvent
* ItemEvent.ITEM_STATE_CHANGED
*/
public void itemStateChanged(ItemEvent itemEvent){
if (itemEvent.getID() != ItemEvent.ITEM_STATE_CHANGED ||
itemEvent.getStateChange() == ItemEvent.DESELECTED) {
return;
}
Object source = itemEvent.getSource();
if (source == pathChoice) {
/*
* Update the selection ('folder name' text field) after
* the current item changing in the unfurled choice by the arrow keys.
* See 6259434, 6240074 for more information
*/
String dir = pathChoice.getSelectedItem();
pathField.setText(dir);
} else if (directoryList == source) {
setFilterField(getFileName(filterField.getText()));
} else if (fileList == source) {
String file = fileList.getItem((Integer)itemEvent.getItem());
setSelectionField(file);
}
}
/*
* Updates the current directory only if directoryList-specific
* action occurred. Returns false if the forward directory is inaccessible
*/
boolean updateDirectoryByUserAction(String str) {
String dir;
if (str.equals("..")) {
dir = getParentDirectory();
}
else {
dir = this.dir + str;
}
File fe = new File(dir);
if (fe.canRead()) {
this.dir = dir;
return true;
}else {
return false;
}
}
String getParentDirectory(){
String parent = this.dir;
if (!this.dir.equals("/")) // If the current directory is "/" leave it alone.
{
if (dir.endsWith("/"))
parent = parent.substring(0,parent.lastIndexOf("/"));
parent = parent.substring(0,parent.lastIndexOf("/")+1);
}
return parent;
}
public void actionPerformed( ActionEvent actionEvent ) {
String actionCommand = actionEvent.getActionCommand();
Object source = actionEvent.getSource();
if (actionCommand.equals(actionButtonText)) {
handleSelection( selectionField.getText() );
handleQuitButton();
} else if (actionCommand.equals(filterLabelText)) {
handleFilter( filterField.getText() );
} else if (actionCommand.equals(cancelButtonText)) {
handleCancel();
} else if ( source instanceof TextField ) {
if ( selectionField == ((TextField)source) ) {
// Fixed within 6259434: PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit
// We should handle the action based on the selection field
// Looks like mistake
handleSelection(selectionField.getText());
handleQuitButton();
} else if (filterField == ((TextField)source)) {
handleFilter(filterField.getText());
} else if (pathField == ((TextField)source)) {
target.setDirectory(pathField.getText());
}
} else if (source instanceof List) {
if (directoryList == ((List)source)) {
//handleFilter( actionCommand + getFileName( filterField.getText() ) );
if (updateDirectoryByUserAction(actionCommand)){
handleFilter( getFileName( filterField.getText() ) );
}
} else if (fileList == ((List)source)) {
handleSelection( actionCommand );
handleQuitButton();
}
}
}
public boolean dispatchKeyEvent(KeyEvent keyEvent) {
int id = keyEvent.getID();
int keyCode = keyEvent.getKeyCode();
if (id == KeyEvent.KEY_PRESSED && keyCode == KeyEvent.VK_ESCAPE) {
synchronized (target.getTreeLock()) {
Component comp = (Component) keyEvent.getSource();
while (comp != null) {
// Fix for 6240084 Disposing a file dialog when the drop-down is active does not dispose the dropdown menu, on Xtoolkit
// See also 6259493
ComponentAccessor acc = AWTAccessor.getComponentAccessor();
if (comp == pathChoice) {
XChoicePeer choicePeer = acc.getPeer(pathChoice);
if (choicePeer.isUnfurled()){
return false;
}
}
Object peer = acc.getPeer(comp);
if (peer == this) {
handleCancel();
return true;
}
comp = comp.getParent();
}
}
}
return false;
}
/**
* set the file
*/
public void setFile(String file) {
if (file == null) {
this.file = null;
return;
}
if (this.dir == null) {
String d = "./";
File f = new File(d, file);
if (f.isFile()) {
this.file = file;
setDirectory(d);
}
} else {
File f = new File(this.dir, file);
if (f.isFile()) {
this.file = file;
}
}
setSelectionField(file);
}
/**
* set the directory
* FIXME: we should update 'savedDir' after programmatically 'setDirectory'
* Otherwise, SavedDir will be not null before second showing
* So the current directory of the file dialog will be incorrect after second showing
* since 'setDirectory' will be ignored
* We cann't update savedDir here now since it used very often
*/
public void setDirectory(String dir) {
if (dir == null) {
this.dir = null;
return;
}
if (dir.equals(this.dir)) {
return;
}
int i;
if ((i=dir.indexOf("~")) != -1) {
dir = dir.substring(0,i) + System.getProperty("user.home") + dir.substring(i+1,dir.length());
}
File fe = new File(dir).getAbsoluteFile();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Current directory : " + fe);
}
if (!fe.isDirectory()) {
dir = "./";
fe = new File(dir).getAbsoluteFile();
if (!fe.isDirectory()) {
return;
}
}
try {
dir = this.dir = fe.getCanonicalPath();
} catch (java.io.IOException ie) {
dir = this.dir = fe.getAbsolutePath();
}
pathField.setText(this.dir);
if (dir.endsWith("/")) {
this.dir = dir;
handleFilter("");
} else {
this.dir = dir + "/";
handleFilter("");
}
// Some code was removed
// Now we do updating of the pathChoice at the time of the choice opening
// Fixed problem:
// The exception java.awt.IllegalComponentStateException will be thrown
// if the user invoke setDirectory after the closing of the file dialog
}
/**
* set filenameFilter
*
*/
public void setFilenameFilter(FilenameFilter filter) {
this.filter = filter;
}
public void dispose() {
FileDialog fd = (FileDialog)fileDialog;
if (fd != null) {
fd.removeAll();
}
super.dispose();
}
// 03/02/2005 b5097243 Pressing 'ESC' on a file dlg does not dispose the dlg on Xtoolkit
@SuppressWarnings("deprecation")
public void setVisible(boolean b){
if (fileDialog == null) {
init(target);
}
if (savedDir != null || userDir != null) {
setDirectory(savedDir != null ? savedDir : userDir);
}
if (savedFile != null) {
// Actually in Motif implementation lost file value which was saved after prevously showing
// Seems we shouldn't restore Motif behaviour in this case
setFile(savedFile);
}
super.setVisible(b);
XChoicePeer choicePeer = AWTAccessor.getComponentAccessor()
.getPeer(pathChoice);
if (b == true){
// See 6240074 for more information
choicePeer.addXChoicePeerListener(this);
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addKeyEventDispatcher(this);
}else{
// See 6240074 for more information
choicePeer.removeXChoicePeerListener();
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removeKeyEventDispatcher(this);
}
selectionField.requestFocusInWindow();
}
/*
* Adding items to the path choice based on the text string
* See 6240074 for more information
*/
public void addItemsToPathChoice(String text){
String[] dirList = getDirList(text);
for (int i = 0; i < dirList.length; i++) pathChoice.addItem(dirList[i]);
}
/*
* Refresh the unfurled choice at the time of the opening choice according to the text of the path field
* See 6240074 for more information
*/
public void unfurledChoiceOpening(ListHelper choiceHelper){
// When the unfurled choice is opening the first time, we need only to add elements, otherwise we've got exception
if (choiceHelper.getItemCount() == 0){
addItemsToPathChoice(pathField.getText());
return;
}
/**代码未完, 请加载全部代码(NowJava.com).**/