diff --git a/src/share/classes/java/awt/FileDialog.java b/src/share/classes/java/awt/FileDialog.java index 973ba4e16ee2f07cbb7565b15ebecdb13489ab12..34cd2f8179a372a708b22b57e13309d8f1b7f700 100644 --- a/src/share/classes/java/awt/FileDialog.java +++ b/src/share/classes/java/awt/FileDialog.java @@ -28,6 +28,8 @@ import java.awt.peer.FileDialogPeer; import java.io.FilenameFilter; import java.io.IOException; import java.io.ObjectInputStream; +import java.io.File; +import sun.awt.AWTAccessor; /** * The FileDialog class displays a dialog window @@ -93,6 +95,25 @@ public class FileDialog extends Dialog { */ 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 @@ public class FileDialog extends Dialog { } } + 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. @@ -370,6 +411,51 @@ public class FileDialog extends Dialog { return file; } + /** + * Returns files that the user selects. + *

+ * 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 @@ -380,7 +466,8 @@ public class FileDialog extends Dialog { * 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; @@ -390,6 +477,34 @@ public class FileDialog extends Dialog { } } + /** + * 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 diff --git a/src/share/classes/sun/awt/AWTAccessor.java b/src/share/classes/sun/awt/AWTAccessor.java index 964e3e0e7ecf1ede260b05cc5a25f5211ced1990..79a75937c253491eef87b0ecc47a2000a29b2184 100644 --- a/src/share/classes/sun/awt/AWTAccessor.java +++ b/src/share/classes/sun/awt/AWTAccessor.java @@ -390,6 +390,30 @@ public final class AWTAccessor { 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. @@ -431,6 +455,11 @@ public final class AWTAccessor { */ 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. */ @@ -567,4 +596,22 @@ public final class AWTAccessor { } 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; + } + } diff --git a/src/solaris/classes/sun/awt/X11/XFileDialogPeer.java b/src/solaris/classes/sun/awt/X11/XFileDialogPeer.java index a50437a1ee8bf0ead2d41f20dadc770d8284c2ba..24acdee572103e84ed8371a9800c8179c6f95152 100644 --- a/src/solaris/classes/sun/awt/X11/XFileDialogPeer.java +++ b/src/solaris/classes/sun/awt/X11/XFileDialogPeer.java @@ -37,6 +37,7 @@ import javax.swing.plaf.ComponentUI; 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 @@ class XFileDialogPeer extends XDialogPeer implements FileDialogPeer, ActionListe 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 @@ class XFileDialogPeer extends XDialogPeer implements FileDialogPeer, ActionListe * 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 @@ class XFileDialogPeer extends XDialogPeer implements FileDialogPeer, ActionListe 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 @@ class XFileDialogPeer extends XDialogPeer implements FileDialogPeer, ActionListe 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(); } diff --git a/src/windows/classes/sun/awt/windows/WFileDialogPeer.java b/src/windows/classes/sun/awt/windows/WFileDialogPeer.java index bc4e8470f4c7cdb42ed538c7ff80b66ccddfa012..e5843dda7d5960e60d12dd5e18d8f05c2c8903a4 100644 --- a/src/windows/classes/sun/awt/windows/WFileDialogPeer.java +++ b/src/windows/classes/sun/awt/windows/WFileDialogPeer.java @@ -117,26 +117,57 @@ public class WFileDialogPeer extends WWindowPeer implements FileDialogPeer { } } - // 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); - 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(); + String jDirectory = null; + String jFile = null; + String jFiles[] = null; + + 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 @@ public class WFileDialogPeer extends WWindowPeer implements FileDialogPeer { // 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 class WFileDialogPeer extends WWindowPeer implements FileDialogPeer { public void createScreenSurface(boolean isResize) {} @Override public void replaceSurfaceData() {} + + public boolean isMultipleMode() { + FileDialog fileDialog = (FileDialog)target; + return AWTAccessor.getFileDialogAccessor().isMultipleMode(fileDialog); + } } diff --git a/src/windows/native/sun/windows/awt_FileDialog.cpp b/src/windows/native/sun/windows/awt_FileDialog.cpp index 080e8d42741e4aa1912cc0651978cb6c6f288138..8d81cf8aa2602c46704c489055d6c40421f12f7a 100644 --- a/src/windows/native/sun/windows/awt_FileDialog.cpp +++ b/src/windows/native/sun/windows/awt_FileDialog.cpp @@ -44,6 +44,7 @@ jmethodID AwtFileDialog::setHWndMID; jmethodID AwtFileDialog::handleSelectedMID; jmethodID AwtFileDialog::handleCancelMID; jmethodID AwtFileDialog::checkFilenameFilterMID; +jmethodID AwtFileDialog::isMultipleModeMID; /* FileDialog ids */ jfieldID AwtFileDialog::modeID; @@ -57,6 +58,13 @@ static TCHAR s_fileFilterString[MAX_FILTER_STRING]; /* 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 @@ FileDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) FileDialogWndProc); ::SetProp(parent, NativeDialogWndProcProp, reinterpret_cast(lpfnWndProc)); + ::SetProp(parent, OpenFileNameProp, (void *)lParam); + break; } case WM_DESTROY: { @@ -149,6 +159,7 @@ FileDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) lpfnWndProc); ::RemoveProp(parent, ModalDialogPeerProp); ::RemoveProp(parent, NativeDialogWndProcProp); + ::RemoveProp(parent, OpenFileNameProp); break; } case WM_NOTIFY: { @@ -174,6 +185,30 @@ FileDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) // 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 @@ AwtFileDialog::Show(void *p) WCHAR unicodeChar = L' '; LPTSTR fileBuffer = NULL; LPTSTR currentDirectory = NULL; - OPENFILENAME ofn; jint mode = 0; BOOL result = FALSE; DWORD dlgerr; @@ -204,6 +238,10 @@ AwtFileDialog::Show(void *p) 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 @@ AwtFileDialog::Show(void *p) (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 @@ AwtFileDialog::Show(void *p) fileBuffer[0] = _T('\0'); } - memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); ofn.lpstrFilter = s_fileFilterString; ofn.nFilterIndex = 1; @@ -265,19 +310,23 @@ AwtFileDialog::Show(void *p) 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 @@ AwtFileDialog::Show(void *p) // 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 @@ AwtFileDialog::Show(void *p) env->DeleteGlobalRef(peer); delete[] currentDirectory; - delete[] fileBuffer; + if (ofn.lpstrFile) + delete[] ofn.lpstrFile; throw; } @@ -351,7 +402,8 @@ AwtFileDialog::Show(void *p) env->DeleteGlobalRef(peer); delete[] currentDirectory; - delete[] fileBuffer; + if (ofn.lpstrFile) + delete[] ofn.lpstrFile; } BOOL @@ -416,6 +468,18 @@ void AwtFileDialog::_ToBack(void *param) 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 @@ Java_sun_awt_windows_WFileDialogPeer_initIDs(JNIEnv *env, jclass cls) 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 @@ Java_sun_awt_windows_WFileDialogPeer_initIDs(JNIEnv *env, jclass cls) DASSERT(AwtFileDialog::setHWndMID != NULL); DASSERT(AwtFileDialog::handleSelectedMID != NULL); DASSERT(AwtFileDialog::handleCancelMID != NULL); + DASSERT(AwtFileDialog::isMultipleModeMID != NULL); DASSERT(AwtFileDialog::modeID != NULL); DASSERT(AwtFileDialog::dirID != NULL); diff --git a/src/windows/native/sun/windows/awt_FileDialog.h b/src/windows/native/sun/windows/awt_FileDialog.h index 82638d30752b51c82dd8e6fa0bcb95fb2fa452dc..d20a355d2c01f667ed1dee93dacd6cb40ac0117a 100644 --- a/src/windows/native/sun/windows/awt_FileDialog.h +++ b/src/windows/native/sun/windows/awt_FileDialog.h @@ -49,6 +49,7 @@ public: 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 @@ public: 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 */ diff --git a/test/java/awt/FileDialog/MultipleMode/MultipleMode.html b/test/java/awt/FileDialog/MultipleMode/MultipleMode.html new file mode 100644 index 0000000000000000000000000000000000000000..a52ee581ebad9806222f402bdf762d8ee1b8eb47 --- /dev/null +++ b/test/java/awt/FileDialog/MultipleMode/MultipleMode.html @@ -0,0 +1,20 @@ + + + + MultipleMode + + + +

MultipleMode
Bug ID: 6467204

+ +

See the dialog box (usually in upper left corner) for instructions

+ + + + diff --git a/test/java/awt/FileDialog/MultipleMode/MultipleMode.java b/test/java/awt/FileDialog/MultipleMode/MultipleMode.java new file mode 100644 index 0000000000000000000000000000000000000000..b231b9e9c84993f16a06e9293e27428b41fb19f2 --- /dev/null +++ b/test/java/awt/FileDialog/MultipleMode/MultipleMode.java @@ -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