6705345: Enable multiple file selection in AWT FileDialog
Reviewed-by: art, anthony, alexp
--- a/jdk/src/share/classes/java/awt/FileDialog.java Mon Feb 08 17:02:43 2010 +0300
+++ b/jdk/src/share/classes/java/awt/FileDialog.java Fri Feb 12 19:58:10 2010 +0300
@@ -28,6 +28,8 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
+import java.io.File;
+import sun.awt.AWTAccessor;
/**
* The <code>FileDialog</code> class displays a dialog window
@@ -93,6 +95,25 @@
*/
String file;
+ /**
+ * Contains the File instances for all the files that the user selects.
+ *
+ * @serial
+ * @see getFiles
+ * @since 1.7
+ */
+ private File[] files;
+
+ /**
+ * Represents whether the file dialog allows the multiple file selection.
+ *
+ * @serial
+ * @see #setMultipleMode
+ * @see #isMultipleMode
+ * @since 1.7
+ */
+ private boolean multipleMode = false;
+
/*
* The filter used as the file dialog's filename filter.
* The file dialog will only be displaying files whose
@@ -123,6 +144,26 @@
}
}
+ static {
+ AWTAccessor.setFileDialogAccessor(
+ new AWTAccessor.FileDialogAccessor() {
+ public void setFiles(FileDialog fileDialog, String directory, String files[]) {
+ fileDialog.setFiles(directory, files);
+ }
+ public void setFile(FileDialog fileDialog, String file) {
+ fileDialog.file = ("".equals(file)) ? null : file;
+ }
+ public void setDirectory(FileDialog fileDialog, String directory) {
+ fileDialog.dir = ("".equals(directory)) ? null : directory;
+ }
+ public boolean isMultipleMode(FileDialog fileDialog) {
+ synchronized (fileDialog.getObjectLock()) {
+ return fileDialog.multipleMode;
+ }
+ }
+ });
+ }
+
/**
* Initialize JNI field and method IDs for fields that may be
accessed from C.
@@ -371,6 +412,51 @@
}
/**
+ * Returns files that the user selects.
+ * <p>
+ * If the user cancels the file dialog,
+ * then the method returns an empty array.
+ *
+ * @return files that the user selects or an empty array
+ * if the user cancels the file dialog.
+ * @see #setFile(String)
+ * @see #getFile
+ * @since 1.7
+ */
+ public File[] getFiles() {
+ synchronized (getObjectLock()) {
+ if (files != null) {
+ return files.clone();
+ } else {
+ return new File[0];
+ }
+ }
+ }
+
+ /**
+ * Stores the names of all the files that the user selects.
+ *
+ * Note that the method is private and it's intended to be used
+ * by the peers through the AWTAccessor API.
+ *
+ * @param directory the current directory
+ * @param files the array that contains the short names of
+ * all the files that the user selects.
+ *
+ * @see #getFiles
+ * @since 1.7
+ */
+ private void setFiles(String directory, String files[]) {
+ synchronized (getObjectLock()) {
+ int filesNumber = (files != null) ? files.length : 0;
+ this.files = new File[filesNumber];
+ for (int i = 0; i < filesNumber; i++) {
+ this.files[i] = new File(directory, files[i]);
+ }
+ }
+ }
+
+ /**
* Sets the selected file for this file dialog window to be the
* specified file. This file becomes the default file if it is set
* before the file dialog window is first shown.
@@ -380,7 +466,8 @@
* as the file.
*
* @param file the file being set
- * @see java.awt.FileDialog#getFile
+ * @see #getFile
+ * @see #getFiles
*/
public void setFile(String file) {
this.file = (file != null && file.equals("")) ? null : file;
@@ -391,6 +478,34 @@
}
/**
+ * Enables or disables multiple file selection for the file dialog.
+ *
+ * @param enable if {@code true}, multiple file selection is enabled;
+ * {@code false} - disabled.
+ * @see #isMultipleMode
+ * @since 1.7
+ */
+ public void setMultipleMode(boolean enable) {
+ synchronized (getObjectLock()) {
+ this.multipleMode = enable;
+ }
+ }
+
+ /**
+ * Returns whether the file dialog allows the multiple file selection.
+ *
+ * @return {@code true} if the file dialog allows the multiple
+ * file selection; {@code false} otherwise.
+ * @see #setMultipleMode
+ * @since 1.7
+ */
+ public boolean isMultipleMode() {
+ synchronized (getObjectLock()) {
+ return multipleMode;
+ }
+ }
+
+ /**
* Determines this file dialog's filename filter. A filename filter
* allows the user to specify which files appear in the file dialog
* window. Filename filters do not function in Sun's reference
--- a/jdk/src/share/classes/sun/awt/AWTAccessor.java Mon Feb 08 17:02:43 2010 +0300
+++ b/jdk/src/share/classes/sun/awt/AWTAccessor.java Fri Feb 12 19:58:10 2010 +0300
@@ -390,6 +390,30 @@
boolean isTrayIconPopup(PopupMenu popupMenu);
}
+ /*
+ * An accessor for the FileDialog class
+ */
+ public interface FileDialogAccessor {
+ /*
+ * Sets the files the user selects
+ */
+ void setFiles(FileDialog fileDialog, String directory, String files[]);
+
+ /*
+ * Sets the file the user selects
+ */
+ void setFile(FileDialog fileDialog, String file);
+
+ /*
+ * Sets the directory the user selects
+ */
+ void setDirectory(FileDialog fileDialog, String directory);
+
+ /*
+ * Returns whether the file dialog allows the multiple file selection.
+ */
+ boolean isMultipleMode(FileDialog fileDialog);
+ }
/*
* The java.awt.Component class accessor object.
@@ -432,6 +456,11 @@
private static PopupMenuAccessor popupMenuAccessor;
/*
+ * The java.awt.FileDialog class accessor object.
+ */
+ private static FileDialogAccessor fileDialogAccessor;
+
+ /*
* Set an accessor object for the java.awt.Component class.
*/
public static void setComponentAccessor(ComponentAccessor ca) {
@@ -567,4 +596,22 @@
}
return popupMenuAccessor;
}
+
+ /*
+ * Set an accessor object for the java.awt.FileDialog class.
+ */
+ public static void setFileDialogAccessor(FileDialogAccessor fda) {
+ fileDialogAccessor = fda;
+ }
+
+ /*
+ * Retrieve the accessor object for the java.awt.FileDialog class.
+ */
+ public static FileDialogAccessor getFileDialogAccessor() {
+ if (fileDialogAccessor == null) {
+ unsafe.ensureClassInitialized(FileDialog.class);
+ }
+ return fileDialogAccessor;
+ }
+
}
--- a/jdk/src/solaris/classes/sun/awt/X11/XFileDialogPeer.java Mon Feb 08 17:02:43 2010 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/XFileDialogPeer.java Fri Feb 12 19:58:10 2010 +0300
@@ -37,6 +37,7 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
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");
@@ -171,6 +172,10 @@
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);
@@ -380,7 +385,8 @@
* handle the selection event
*/
void handleSelection(String file) {
- int index = file.lastIndexOf('/');
+
+ int index = file.lastIndexOf(java.io.File.separatorChar);
if (index == -1) {
savedDir = this.dir;
@@ -389,8 +395,12 @@
savedDir = file.substring(0, index+1);
savedFile = file.substring(index+1);
}
- target.setDirectory(savedDir);
- target.setFile(savedFile);
+
+ AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
+
+ fileDialogAccessor.setDirectory(target, savedDir);
+ fileDialogAccessor.setFile(target, savedFile);
+ fileDialogAccessor.setFiles(target, savedDir, fileList.getSelectedItems());
}
/**
@@ -404,8 +414,13 @@
setFilterField(null);
directoryList.clear();
fileList.clear();
- target.setFile(null);
- target.setDirectory(null);
+
+ AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
+
+ fileDialogAccessor.setDirectory(target, null);
+ fileDialogAccessor.setFile(target, null);
+ fileDialogAccessor.setFiles(target, null, null);
+
handleQuitButton();
}
--- a/jdk/src/windows/classes/sun/awt/windows/WFileDialogPeer.java Mon Feb 08 17:02:43 2010 +0300
+++ b/jdk/src/windows/classes/sun/awt/windows/WFileDialogPeer.java Fri Feb 12 19:58:10 2010 +0300
@@ -117,26 +117,57 @@
}
}
- // NOTE: This method is called by privileged threads.
- // DO NOT INVOKE CLIENT CODE ON THIS THREAD!
- void handleSelected(final String file) {
- final FileDialog fileDialog = (FileDialog)target;
- WToolkit.executeOnEventHandlerThread(fileDialog, new Runnable() {
- public void run() {
- int index = file.lastIndexOf(java.io.File.separatorChar);/*2509*//*ibm*/
- String dir;
+ /*
+ * The function converts the file names (the buffer parameter)
+ * in the Windows format into the Java format and saves the results
+ * into the FileDialog instance.
+ *
+ * If it's the multi-select mode, the buffer contains the current
+ * directory followed by the short names of the files.
+ * The directory and file name strings are NULL separated.
+ * If it's the single-select mode, the buffer doesn't have the NULL
+ * separator between the path and the file name.
+ *
+ * NOTE: This method is called by privileged threads.
+ * DO NOT INVOKE CLIENT CODE ON THIS THREAD!
+ */
+ void handleSelected(final char[] buffer)
+ {
+ String[] wFiles = (new String(buffer)).split("\0"); // NULL is the delimiter
+ boolean multiple = (wFiles.length > 1);
+
+ String jDirectory = null;
+ String jFile = null;
+ String jFiles[] = null;
- if (index == -1) {
- dir = "."+java.io.File.separator;
- fileDialog.setFile(file);
- }
- else {
- dir = file.substring(0, index + 1);
- fileDialog.setFile(file.substring(index + 1));
- }
- fileDialog.setDirectory(dir);
- fileDialog.hide();
+ if (multiple) {
+ jDirectory = wFiles[0];
+ jFiles = new String[wFiles.length - 1];
+ System.arraycopy(wFiles, 1, jFiles, 0, jFiles.length);
+ jFile = jFiles[1]; // choose any file
+ } else {
+ int index = wFiles[0].lastIndexOf(java.io.File.separatorChar);
+ if (index == -1) {
+ jDirectory = "."+java.io.File.separator;
+ jFile = wFiles[0];
+ } else {
+ jDirectory = wFiles[0].substring(0, index + 1);
+ jFile = wFiles[0].substring(index + 1);
}
+ jFiles = new String[] { jFile };
+ }
+
+ final FileDialog fileDialog = (FileDialog)target;
+ AWTAccessor.FileDialogAccessor fileDialogAccessor = AWTAccessor.getFileDialogAccessor();
+
+ fileDialogAccessor.setDirectory(fileDialog, jDirectory);
+ fileDialogAccessor.setFile(fileDialog, jFile);
+ fileDialogAccessor.setFiles(fileDialog, jDirectory, jFiles);
+
+ WToolkit.executeOnEventHandlerThread(fileDialog, new Runnable() {
+ public void run() {
+ fileDialog.hide();
+ }
});
} // handleSelected()
@@ -144,11 +175,14 @@
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
void handleCancel() {
final FileDialog fileDialog = (FileDialog)target;
+
+ AWTAccessor.getFileDialogAccessor().setFile(fileDialog, null);
+ AWTAccessor.getFileDialogAccessor().setFiles(fileDialog, null, null);
+
WToolkit.executeOnEventHandlerThread(fileDialog, new Runnable() {
- public void run() {
- fileDialog.setFile(null);
- fileDialog.hide();
- }
+ public void run() {
+ fileDialog.hide();
+ }
});
} // handleCancel()
@@ -244,4 +278,9 @@
public void createScreenSurface(boolean isResize) {}
@Override
public void replaceSurfaceData() {}
+
+ public boolean isMultipleMode() {
+ FileDialog fileDialog = (FileDialog)target;
+ return AWTAccessor.getFileDialogAccessor().isMultipleMode(fileDialog);
+ }
}
--- a/jdk/src/windows/native/sun/windows/awt_FileDialog.cpp Mon Feb 08 17:02:43 2010 +0300
+++ b/jdk/src/windows/native/sun/windows/awt_FileDialog.cpp Fri Feb 12 19:58:10 2010 +0300
@@ -44,6 +44,7 @@
jmethodID AwtFileDialog::handleSelectedMID;
jmethodID AwtFileDialog::handleCancelMID;
jmethodID AwtFileDialog::checkFilenameFilterMID;
+jmethodID AwtFileDialog::isMultipleModeMID;
/* FileDialog ids */
jfieldID AwtFileDialog::modeID;
@@ -57,6 +58,13 @@
/* Non-localized suffix of the filter string */
static const TCHAR s_additionalString[] = TEXT(" (*.*)\0*.*\0");
+// Default limit of the output buffer.
+#define SINGLE_MODE_BUFFER_LIMIT MAX_PATH+1
+#define MULTIPLE_MODE_BUFFER_LIMIT 32768
+
+// The name of the property holding the pointer to the OPENFILENAME structure.
+static LPCTSTR OpenFileNameProp = TEXT("AWT_OFN");
+
/***********************************************************************/
void
@@ -140,6 +148,8 @@
FileDialogWndProc);
::SetProp(parent, NativeDialogWndProcProp, reinterpret_cast<HANDLE>(lpfnWndProc));
+ ::SetProp(parent, OpenFileNameProp, (void *)lParam);
+
break;
}
case WM_DESTROY: {
@@ -149,6 +159,7 @@
lpfnWndProc);
::RemoveProp(parent, ModalDialogPeerProp);
::RemoveProp(parent, NativeDialogWndProcProp);
+ ::RemoveProp(parent, OpenFileNameProp);
break;
}
case WM_NOTIFY: {
@@ -174,6 +185,30 @@
// to unblock all the windows blocked by this dialog as it will
// be closed soon
env->CallVoidMethod(peer, AwtFileDialog::setHWndMID, (jlong)0);
+ } else if (notifyEx->hdr.code == CDN_SELCHANGE) {
+ // reallocate the buffer if the buffer is too small
+ LPOPENFILENAME lpofn = (LPOPENFILENAME)GetProp(parent, OpenFileNameProp);
+
+ UINT nLength = CommDlg_OpenSave_GetSpec(parent, NULL, 0) +
+ CommDlg_OpenSave_GetFolderPath(parent, NULL, 0);
+
+ if (lpofn->nMaxFile < nLength)
+ {
+ // allocate new buffer
+ LPTSTR newBuffer = new TCHAR[nLength];
+
+ if (newBuffer) {
+ memset(newBuffer, 0, nLength * sizeof(TCHAR));
+ LPTSTR oldBuffer = lpofn->lpstrFile;
+ lpofn->lpstrFile = newBuffer;
+ lpofn->nMaxFile = nLength;
+ // free the previously allocated buffer
+ if (oldBuffer) {
+ delete[] oldBuffer;
+ }
+
+ }
+ }
}
}
break;
@@ -193,7 +228,6 @@
WCHAR unicodeChar = L' ';
LPTSTR fileBuffer = NULL;
LPTSTR currentDirectory = NULL;
- OPENFILENAME ofn;
jint mode = 0;
BOOL result = FALSE;
DWORD dlgerr;
@@ -204,6 +238,10 @@
jobject target = NULL;
jobject parent = NULL;
AwtComponent* awtParent = NULL;
+ jboolean multipleMode = JNI_FALSE;
+
+ OPENFILENAME ofn;
+ memset(&ofn, 0, sizeof(ofn));
/*
* There's a situation (see bug 4906972) when InvokeFunction (by which this method is called)
@@ -233,7 +271,16 @@
(jstring)env->GetObjectField(target, AwtFileDialog::dirID);
JavaStringBuffer directoryBuffer(env, directory);
- fileBuffer = new TCHAR[MAX_PATH+1];
+ multipleMode = env->CallBooleanMethod(peer, AwtFileDialog::isMultipleModeMID);
+
+ UINT bufferLimit;
+ if (multipleMode == JNI_TRUE) {
+ bufferLimit = MULTIPLE_MODE_BUFFER_LIMIT;
+ } else {
+ bufferLimit = SINGLE_MODE_BUFFER_LIMIT;
+ }
+ LPTSTR fileBuffer = new TCHAR[bufferLimit];
+ memset(fileBuffer, 0, bufferLimit * sizeof(TCHAR));
file = (jstring)env->GetObjectField(target, AwtFileDialog::fileID);
if (file != NULL) {
@@ -244,8 +291,6 @@
fileBuffer[0] = _T('\0');
}
- memset(&ofn, 0, sizeof(ofn));
-
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFilter = s_fileFilterString;
ofn.nFilterIndex = 1;
@@ -265,19 +310,23 @@
ofn.hwndOwner = NULL;
}
ofn.lpstrFile = fileBuffer;
- ofn.nMaxFile = MAX_PATH;
+ ofn.nMaxFile = bufferLimit;
ofn.lpstrTitle = titleBuffer;
ofn.lpstrInitialDir = directoryBuffer;
ofn.Flags = OFN_LONGNAMES | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY |
OFN_ENABLEHOOK | OFN_EXPLORER | OFN_ENABLESIZING;
fileFilter = env->GetObjectField(peer,
AwtFileDialog::fileFilterID);
- if (!JNU_IsNull(env,fileFilter)) {
- ofn.Flags |= OFN_ENABLEINCLUDENOTIFY;
- }
+ if (!JNU_IsNull(env,fileFilter)) {
+ ofn.Flags |= OFN_ENABLEINCLUDENOTIFY;
+ }
ofn.lCustData = (LPARAM)peer;
ofn.lpfnHook = (LPOFNHOOKPROC)FileDialogHookProc;
+ if (multipleMode == JNI_TRUE) {
+ ofn.Flags |= OFN_ALLOWMULTISELECT;
+ }
+
// Save current directory, so we can reset if it changes.
currentDirectory = new TCHAR[MAX_PATH+1];
@@ -318,11 +367,12 @@
// Report result to peer.
if (result) {
- jstring tmpJString = (_tcslen(ofn.lpstrFile) == 0 ?
- JNU_NewStringPlatform(env, L"") :
- JNU_NewStringPlatform(env, ofn.lpstrFile));
- env->CallVoidMethod(peer, AwtFileDialog::handleSelectedMID, tmpJString);
- env->DeleteLocalRef(tmpJString);
+ jint length = (jint)GetBufferLength(ofn.lpstrFile, ofn.nMaxFile);
+ jcharArray jnames = env->NewCharArray(length);
+ env->SetCharArrayRegion(jnames, 0, length, (jchar*)ofn.lpstrFile);
+
+ env->CallVoidMethod(peer, AwtFileDialog::handleSelectedMID, jnames);
+ env->DeleteLocalRef(jnames);
} else {
env->CallVoidMethod(peer, AwtFileDialog::handleCancelMID);
}
@@ -338,7 +388,8 @@
env->DeleteGlobalRef(peer);
delete[] currentDirectory;
- delete[] fileBuffer;
+ if (ofn.lpstrFile)
+ delete[] ofn.lpstrFile;
throw;
}
@@ -351,7 +402,8 @@
env->DeleteGlobalRef(peer);
delete[] currentDirectory;
- delete[] fileBuffer;
+ if (ofn.lpstrFile)
+ delete[] ofn.lpstrFile;
}
BOOL
@@ -416,6 +468,18 @@
env->DeleteGlobalRef(self);
}
+// Returns the length of the double null terminated output buffer
+UINT AwtFileDialog::GetBufferLength(LPTSTR buffer, UINT limit)
+{
+ UINT index = 0;
+ while ((index < limit) &&
+ (buffer[index] != NULL || buffer[index+1] != NULL))
+ {
+ index++;
+ }
+ return index;
+}
+
/************************************************************************
* WFileDialogPeer native methods
*/
@@ -434,11 +498,12 @@
AwtFileDialog::setHWndMID =
env->GetMethodID(cls, "setHWnd", "(J)V");
AwtFileDialog::handleSelectedMID =
- env->GetMethodID(cls, "handleSelected", "(Ljava/lang/String;)V");
+ env->GetMethodID(cls, "handleSelected", "([C)V");
AwtFileDialog::handleCancelMID =
env->GetMethodID(cls, "handleCancel", "()V");
AwtFileDialog::checkFilenameFilterMID =
env->GetMethodID(cls, "checkFilenameFilter", "(Ljava/lang/String;)Z");
+ AwtFileDialog::isMultipleModeMID = env->GetMethodID(cls, "isMultipleMode", "()Z");
/* java.awt.FileDialog fields */
cls = env->FindClass("java/awt/FileDialog");
@@ -455,6 +520,7 @@
DASSERT(AwtFileDialog::setHWndMID != NULL);
DASSERT(AwtFileDialog::handleSelectedMID != NULL);
DASSERT(AwtFileDialog::handleCancelMID != NULL);
+ DASSERT(AwtFileDialog::isMultipleModeMID != NULL);
DASSERT(AwtFileDialog::modeID != NULL);
DASSERT(AwtFileDialog::dirID != NULL);
--- a/jdk/src/windows/native/sun/windows/awt_FileDialog.h Mon Feb 08 17:02:43 2010 +0300
+++ b/jdk/src/windows/native/sun/windows/awt_FileDialog.h Fri Feb 12 19:58:10 2010 +0300
@@ -49,6 +49,7 @@
static jmethodID handleSelectedMID;
static jmethodID handleCancelMID;
static jmethodID checkFilenameFilterMID;
+ static jmethodID isMultipleModeMID;
/* java.awt.FileDialog field and method ids */
static jfieldID modeID;
@@ -68,6 +69,9 @@
static void _DisposeOrHide(void *param);
static void _ToFront(void *param);
static void _ToBack(void *param);
+
+private:
+ static UINT GetBufferLength(LPTSTR buffer, UINT limit);
};
#endif /* FILE_DIALOG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/FileDialog/MultipleMode/MultipleMode.html Fri Feb 12 19:58:10 2010 +0300
@@ -0,0 +1,20 @@
+<html>
+<!--
+ @test
+ @bug 6467204
+ @summary Need to implement "extended" native FileDialog for JFileChooser
+ @author dmitry.cherepanov@sun.com area=awt.filedialog
+ @run applet/manual=yesno MultipleMode.html
+ -->
+<head>
+<title> MultipleMode </title>
+</head>
+<body>
+
+<h1>MultipleMode<br>Bug ID: 6467204</h1>
+
+<p> See the dialog box (usually in upper left corner) for instructions</p>
+
+<APPLET CODE="MultipleMode.class" WIDTH=200 HEIGHT=200></APPLET>
+</body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/FileDialog/MultipleMode/MultipleMode.java Fri Feb 12 19:58:10 2010 +0300
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc. 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.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ test
+ @bug 6467204
+ @summary Need to implement "extended" native FileDialog for JFileChooser
+ @author dmitry.cherepanov@sun.com area=awt.filedialog
+ @run applet/manual=yesno MultipleMode.html
+*/
+
+// Note there is no @ in front of test above. This is so that the
+// harness will not mistake this file as a test file. It should
+// only see the html file as a test file. (the harness runs all
+// valid test files, so it would run this test twice if this file
+// were valid as well as the html file.)
+// Also, note the area= after Your Name in the author tag. Here, you
+// should put which functional area the test falls in. See the
+// AWT-core home page -> test areas and/or -> AWT team for a list of
+// areas.
+// There are several places where ManualYesNoTest appear. It is
+// recommended that these be changed by a global search and replace,
+// such as ESC-% in xemacs.
+
+
+
+/**
+ * MultipleMode.java
+ *
+ * summary:
+ */
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.File;
+
+
+//Manual tests should run as applet tests if possible because they
+// get their environments cleaned up, including AWT threads, any
+// test created threads, and any system resources used by the test
+// such as file descriptors. (This is normally not a problem as
+// main tests usually run in a separate VM, however on some platforms
+// such as the Mac, separate VMs are not possible and non-applet
+// tests will cause problems). Also, you don't have to worry about
+// synchronisation stuff in Applet tests the way you do in main
+// tests...
+
+
+public class MultipleMode extends Applet
+{
+ //Declare things used in the test, like buttons and labels here
+
+ public void init()
+ {
+ //Create instructions for the user here, as well as set up
+ // the environment -- set the layout manager, add buttons,
+ // etc.
+ this.setLayout (new BorderLayout ());
+
+ String[] instructions =
+ {
+ " 1. Turn the 'multiple' checkbox off and press the 'open' button ",
+ " 2. Verify that the file dialog doesn't allow the multiple file selection ",
+ " 3. Select any file and close the file dialog ",
+ " 4. The results will be displayed, verify the results ",
+ " 5. Turn the 'multiple' checkbox on and press the 'open' button ",
+ " 6. Verify that the file dialog allows the multiple file selection ",
+ " 7. Select several files and close the file dialog ",
+ " 8. The results will be displayed, verify the results "
+ };
+ Sysout.createDialogWithInstructions( instructions );
+
+ }//End init()
+
+ public void start ()
+ {
+ final Checkbox mode = new Checkbox("multiple", true);
+ Button open = new Button("open");
+ open.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ FileDialog d = new FileDialog((Frame)null);
+ d.setMultipleMode(mode.getState());
+ d.setVisible(true);
+
+ // print the results
+ Sysout.println("DIR:");
+ Sysout.println(d.getDirectory());
+ Sysout.println("FILE:");
+ Sysout.println(d.getFile());
+ Sysout.println("FILES:");
+ File files[] = d.getFiles();
+ for (File f : files) {
+ Sysout.println(String.valueOf(f));
+ }
+ }
+ });
+
+ setLayout(new FlowLayout());
+ add(mode);
+ add(open);
+
+ //Get things going. Request focus, set size, et cetera
+ setSize (200,200);
+ setVisible(true);
+ validate();
+
+ }// start()
+
+ //The rest of this class is the actions which perform the test...
+
+ //Use Sysout.println to communicate with the user NOT System.out!!
+ //Sysout.println ("Something Happened!");
+
+}// class ManualYesNoTest
+
+/* Place other classes related to the test after this line */
+
+
+
+
+
+/****************************************************
+ Standard Test Machinery
+ DO NOT modify anything below -- it's a standard
+ chunk of code whose purpose is to make user
+ interaction uniform, and thereby make it simpler
+ to read and understand someone else's test.
+ ****************************************************/
+
+/**
+ This is part of the standard test machinery.
+ It creates a dialog (with the instructions), and is the interface
+ for sending text messages to the user.
+ To print the instructions, send an array of strings to Sysout.createDialog
+ WithInstructions method. Put one line of instructions per array entry.
+ To display a message for the tester to see, simply call Sysout.println
+ with the string to be displayed.
+ This mimics System.out.println but works within the test harness as well
+ as standalone.
+ */
+
+class Sysout
+{
+ private static TestDialog dialog;
+ private static boolean numbering = false;
+ private static int messageNumber = 0;
+
+ public static void createDialogWithInstructions( String[] instructions )
+ {
+ dialog = new TestDialog( new Frame(), "Instructions" );
+ dialog.printInstructions( instructions );
+ dialog.setVisible(true);
+ println( "Any messages for the tester will display here." );
+ }
+
+ public static void createDialog( )
+ {
+ dialog = new TestDialog( new Frame(), "Instructions" );
+ String[] defInstr = { "Instructions will appear here. ", "" } ;
+ dialog.printInstructions( defInstr );
+ dialog.setVisible(true);
+ println( "Any messages for the tester will display here." );
+ }
+
+ /* Enables message counting for the tester. */
+ public static void enableNumbering(boolean enable){
+ numbering = enable;
+ }
+
+ public static void printInstructions( String[] instructions )
+ {
+ dialog.printInstructions( instructions );
+ }
+
+
+ public static void println( String messageIn )
+ {
+ if (numbering) {
+ messageIn = "" + messageNumber + " " + messageIn;
+ messageNumber++;
+ }
+ dialog.displayMessage( messageIn );
+ }
+
+}// Sysout class
+
+/**
+ This is part of the standard test machinery. It provides a place for the
+ test instructions to be displayed, and a place for interactive messages
+ to the user to be displayed.
+ To have the test instructions displayed, see Sysout.
+ To have a message to the user be displayed, see Sysout.
+ Do not call anything in this dialog directly.
+ */
+class TestDialog extends Dialog
+{
+
+ TextArea instructionsText;
+ TextArea messageText;
+ int maxStringLength = 80;
+
+ //DO NOT call this directly, go through Sysout
+ public TestDialog( Frame frame, String name )
+ {
+ super( frame, name );
+ int scrollBoth = TextArea.SCROLLBARS_BOTH;
+ instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
+ add( "North", instructionsText );
+
+ messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
+ add("Center", messageText);
+
+ pack();
+
+ setVisible(true);
+ }// TestDialog()
+
+ //DO NOT call this directly, go through Sysout
+ public void printInstructions( String[] instructions )
+ {
+ //Clear out any current instructions
+ instructionsText.setText( "" );
+
+ //Go down array of instruction strings
+
+ String printStr, remainingStr;
+ for( int i=0; i < instructions.length; i++ )
+ {
+ //chop up each into pieces maxSringLength long
+ remainingStr = instructions[ i ];
+ while( remainingStr.length() > 0 )
+ {
+ //if longer than max then chop off first max chars to print
+ if( remainingStr.length() >= maxStringLength )
+ {
+ //Try to chop on a word boundary
+ int posOfSpace = remainingStr.
+ lastIndexOf( ' ', maxStringLength - 1 );
+
+ if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
+
+ printStr = remainingStr.substring( 0, posOfSpace + 1 );
+ remainingStr = remainingStr.substring( posOfSpace + 1 );
+ }
+ //else just print
+ else
+ {
+ printStr = remainingStr;
+ remainingStr = "";
+ }
+
+ instructionsText.append( printStr + "\n" );
+
+ }// while
+
+ }// for
+
+ }//printInstructions()
+
+ //DO NOT call this directly, go through Sysout
+ public void displayMessage( String messageIn )
+ {
+ messageText.append( messageIn + "\n" );
+ System.out.println(messageIn);
+ }
+
+}// TestDialog class