]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/widgets/FileDialog.java
Remove invalid SHA-256-Digests
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / widgets / FileDialog.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2012 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.widgets;
15
16
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.internal.*;
19 import org.eclipse.swt.internal.win32.*;
20
21 /**
22  * Instances of this class allow the user to navigate
23  * the file system and select or enter a file name.
24  * <dl>
25  * <dt><b>Styles:</b></dt>
26  * <dd>SAVE, OPEN, MULTI</dd>
27  * <dt><b>Events:</b></dt>
28  * <dd>(none)</dd>
29  * </dl>
30  * <p>
31  * Note: Only one of the styles SAVE and OPEN may be specified.
32  * </p><p>
33  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
34  * </p>
35  *
36  * @see <a href="http://www.eclipse.org/swt/snippets/#filedialog">FileDialog snippets</a>
37  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Dialog tab</a>
38  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
39  * @noextend This class is not intended to be subclassed by clients.
40  */
41 public class FileDialog extends Dialog {
42         String [] filterNames = new String [0];
43         String [] filterExtensions = new String [0];
44         String [] fileNames = new String [0];
45         String filterPath = "", fileName = "";
46         int filterIndex = 0;
47         boolean overwrite = false;
48         static final String FILTER = "*.*";
49         static int BUFFER_SIZE = 1024 * 32;
50         static boolean USE_HOOK = true;
51         static {
52                 /*
53                 *  Feature in Vista.  When OFN_ENABLEHOOK is set in the
54                 *  save or open file dialog,  Vista uses the old XP look
55                 *  and feel.  OFN_ENABLEHOOK is used to grow the file
56                 *  name buffer in a multi-select file dialog.  The fix
57                 *  is to only use OFN_ENABLEHOOK when the buffer has
58                 *  overrun.
59                 */
60                 USE_HOOK = false;
61         }
62
63 /**
64  * Constructs a new instance of this class given only its parent.
65  *
66  * @param parent a shell which will be the parent of the new instance
67  *
68  * @exception IllegalArgumentException <ul>
69  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
70  * </ul>
71  * @exception SWTException <ul>
72  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
73  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
74  * </ul>
75  */
76 public FileDialog (Shell parent) {
77         this (parent, SWT.APPLICATION_MODAL);
78 }
79
80 /**
81  * Constructs a new instance of this class given its parent
82  * and a style value describing its behavior and appearance.
83  * <p>
84  * The style value is either one of the style constants defined in
85  * class <code>SWT</code> which is applicable to instances of this
86  * class, or must be built by <em>bitwise OR</em>'ing together
87  * (that is, using the <code>int</code> "|" operator) two or more
88  * of those <code>SWT</code> style constants. The class description
89  * lists the style constants that are applicable to the class.
90  * Style bits are also inherited from superclasses.
91  * </p>
92  *
93  * @param parent a shell which will be the parent of the new instance
94  * @param style the style of dialog to construct
95  *
96  * @exception IllegalArgumentException <ul>
97  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
98  * </ul>
99  * @exception SWTException <ul>
100  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
101  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
102  * </ul>
103  *
104  * @see SWT#SAVE
105  * @see SWT#OPEN
106  * @see SWT#MULTI
107  */
108 public FileDialog (Shell parent, int style) {
109         super (parent, checkStyle (parent, style));
110         checkSubclass ();
111 }
112
113 /**
114  * Returns the path of the first file that was
115  * selected in the dialog relative to the filter path, or an
116  * empty string if no such file has been selected.
117  *
118  * @return the relative path of the file
119  */
120 public String getFileName () {
121         return fileName;
122 }
123
124 /**
125  * Returns a (possibly empty) array with the paths of all files
126  * that were selected in the dialog relative to the filter path.
127  *
128  * @return the relative paths of the files
129  */
130 public String [] getFileNames () {
131         return fileNames;
132 }
133
134 /**
135  * Returns the file extensions which the dialog will
136  * use to filter the files it shows.
137  *
138  * @return the file extensions filter
139  */
140 public String [] getFilterExtensions () {
141         return filterExtensions;
142 }
143
144 /**
145  * Get the 0-based index of the file extension filter
146  * which was selected by the user, or -1 if no filter
147  * was selected.
148  * <p>
149  * This is an index into the FilterExtensions array and
150  * the FilterNames array.
151  * </p>
152  *
153  * @return index the file extension filter index
154  *
155  * @see #getFilterExtensions
156  * @see #getFilterNames
157  *
158  * @since 3.4
159  */
160 public int getFilterIndex () {
161         return filterIndex;
162 }
163
164 /**
165  * Returns the names that describe the filter extensions
166  * which the dialog will use to filter the files it shows.
167  *
168  * @return the list of filter names
169  */
170 public String [] getFilterNames () {
171         return filterNames;
172 }
173
174 /**
175  * Returns the directory path that the dialog will use, or an empty
176  * string if this is not set.  File names in this path will appear
177  * in the dialog, filtered according to the filter extensions.
178  *
179  * @return the directory path string
180  *
181  * @see #setFilterExtensions
182  */
183 public String getFilterPath () {
184         return filterPath;
185 }
186
187 /**
188  * Returns the flag that the dialog will use to
189  * determine whether to prompt the user for file
190  * overwrite if the selected file already exists.
191  *
192  * @return true if the dialog will prompt for file overwrite, false otherwise
193  *
194  * @since 3.4
195  */
196 public boolean getOverwrite () {
197         return overwrite;
198 }
199
200 long OFNHookProc (long hdlg, long uiMsg, long wParam, long lParam) {
201         switch ((int)uiMsg) {
202                 case OS.WM_NOTIFY:
203                         OFNOTIFY ofn = new OFNOTIFY ();
204                         OS.MoveMemory (ofn, lParam, OFNOTIFY.sizeof);
205                         if (ofn.code == OS.CDN_SELCHANGE) {
206                                 int lResult = (int)OS.SendMessage (ofn.hwndFrom, OS.CDM_GETSPEC, 0, 0);
207                                 if (lResult > 0) {
208                                         lResult += OS.MAX_PATH;
209                                         OPENFILENAME lpofn = new OPENFILENAME ();
210                                         OS.MoveMemory (lpofn, ofn.lpOFN, OPENFILENAME.sizeof);
211                                         if (lpofn.nMaxFile < lResult) {
212                                                 long hHeap = OS.GetProcessHeap ();
213                                                 long lpstrFile = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, lResult * TCHAR.sizeof);
214                                                 if (lpstrFile != 0) {
215                                                         if (lpofn.lpstrFile != 0) OS.HeapFree (hHeap, 0, lpofn.lpstrFile);
216                                                         lpofn.lpstrFile = lpstrFile;
217                                                         lpofn.nMaxFile = lResult;
218                                                         OS.MoveMemory (ofn.lpOFN, lpofn, OPENFILENAME.sizeof);
219                                                 }
220                                         }
221                                 }
222                         }
223                 break;
224         }
225         return 0;
226 }
227
228 /**
229  * Makes the dialog visible and brings it to the front
230  * of the display.
231  *
232  * @return a string describing the absolute path of the first selected file,
233  *         or null if the dialog was cancelled or an error occurred
234  *
235  * @exception SWTException <ul>
236  *    <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li>
237  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li>
238  * </ul>
239  */
240 public String open () {
241         long hHeap = OS.GetProcessHeap ();
242
243         /* Get the owner HWND for the dialog */
244         long hwndOwner = parent.handle;
245         long hwndParent = parent.handle;
246
247         /*
248         * Feature in Windows.  There is no API to set the orientation of a
249         * file dialog.  It is always inherited from the parent.  The fix is
250         * to create a hidden parent and set the orientation in the hidden
251         * parent for the dialog to inherit.
252         */
253         boolean enabled = false;
254         int dialogOrientation = style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
255         int parentOrientation = parent.style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
256         if (dialogOrientation != parentOrientation) {
257                 int exStyle = OS.WS_EX_NOINHERITLAYOUT;
258                 if (dialogOrientation == SWT.RIGHT_TO_LEFT) exStyle |= OS.WS_EX_LAYOUTRTL;
259                 hwndOwner = OS.CreateWindowEx (
260                         exStyle,
261                         Shell.DialogClass,
262                         null,
263                         0,
264                         OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
265                         hwndParent,
266                         0,
267                         OS.GetModuleHandle (null),
268                         null);
269                 enabled = OS.IsWindowEnabled (hwndParent);
270                 if (enabled) OS.EnableWindow (hwndParent, false);
271         }
272
273         /* Convert the title and copy it into lpstrTitle */
274         if (title == null) title = "";
275         /* Use the character encoding for the default locale */
276         TCHAR buffer3 = new TCHAR (0, title, true);
277         int byteCount3 = buffer3.length () * TCHAR.sizeof;
278         long lpstrTitle = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount3);
279         OS.MoveMemory (lpstrTitle, buffer3, byteCount3);
280
281         /* Compute filters and copy into lpstrFilter */
282         String strFilter = "";
283         if (filterNames == null) filterNames = new String [0];
284         if (filterExtensions == null) filterExtensions = new String [0];
285         for (int i=0; i<filterExtensions.length; i++) {
286                 String filterName = filterExtensions [i];
287                 if (i < filterNames.length) filterName = filterNames [i];
288                 strFilter = strFilter + filterName + '\0' + filterExtensions [i] + '\0';
289         }
290         if (filterExtensions.length == 0) {
291                 strFilter = strFilter + FILTER + '\0' + FILTER + '\0';
292         }
293         /* Use the character encoding for the default locale */
294         TCHAR buffer4 = new TCHAR (0, strFilter, true);
295         int byteCount4 = buffer4.length () * TCHAR.sizeof;
296         long lpstrFilter = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount4);
297         OS.MoveMemory (lpstrFilter, buffer4, byteCount4);
298
299         /* Convert the fileName and filterName to C strings */
300         if (fileName == null) fileName = "";
301         /* Use the character encoding for the default locale */
302         TCHAR name = new TCHAR (0, fileName, true);
303
304         /*
305         * Copy the name into lpstrFile and ensure that the
306         * last byte is NULL and the buffer does not overrun.
307         */
308         int nMaxFile = OS.MAX_PATH;
309         if ((style & SWT.MULTI) != 0) nMaxFile = Math.max (nMaxFile, BUFFER_SIZE);
310         int byteCount = nMaxFile * TCHAR.sizeof;
311         long lpstrFile = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
312         int byteCountFile = Math.min (name.length () * TCHAR.sizeof, byteCount - TCHAR.sizeof);
313         OS.MoveMemory (lpstrFile, name, byteCountFile);
314
315         /*
316         * Copy the path into lpstrInitialDir and ensure that
317         * the last byte is NULL and the buffer does not overrun.
318         */
319         if (filterPath == null) filterPath = "";
320         /* Use the character encoding for the default locale */
321         TCHAR path = new TCHAR (0, filterPath.replace ('/', '\\'), true);
322         int byteCount5 = OS.MAX_PATH * TCHAR.sizeof;
323         long lpstrInitialDir = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount5);
324         int byteCountDir = Math.min (path.length () * TCHAR.sizeof, byteCount5 - TCHAR.sizeof);
325         OS.MoveMemory (lpstrInitialDir, path, byteCountDir);
326
327         /* Create the file dialog struct */
328         OPENFILENAME struct = new OPENFILENAME ();
329         struct.lStructSize = OPENFILENAME.sizeof;
330         struct.Flags = OS.OFN_HIDEREADONLY | OS.OFN_NOCHANGEDIR;
331         boolean save = (style & SWT.SAVE) != 0;
332         if (save && overwrite) struct.Flags |= OS.OFN_OVERWRITEPROMPT;
333         Callback callback = null;
334         if ((style & SWT.MULTI) != 0) {
335                 struct.Flags |= OS.OFN_ALLOWMULTISELECT | OS.OFN_EXPLORER | OS.OFN_ENABLESIZING;
336                 if (USE_HOOK) {
337                         callback = new Callback (this, "OFNHookProc", 4); //$NON-NLS-1$
338                         long lpfnHook = callback.getAddress ();
339                         if (lpfnHook == 0) error (SWT.ERROR_NO_MORE_CALLBACKS);
340                         struct.lpfnHook = lpfnHook;
341                         struct.Flags |= OS.OFN_ENABLEHOOK;
342                 }
343         }
344         struct.hwndOwner = hwndOwner;
345         struct.lpstrTitle = lpstrTitle;
346         struct.lpstrFile = lpstrFile;
347         struct.nMaxFile = nMaxFile;
348         struct.lpstrInitialDir = lpstrInitialDir;
349         struct.lpstrFilter = lpstrFilter;
350         struct.nFilterIndex = filterIndex == 0 ? filterIndex : filterIndex + 1;
351
352         /*
353         * Set the default extension to an empty string.  If the
354         * user fails to type an extension and this extension is
355         * empty, Windows uses the current value of the filter
356         * extension at the time that the dialog is closed.
357         */
358         long lpstrDefExt = 0;
359         if (save) {
360                 lpstrDefExt = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
361                 struct.lpstrDefExt = lpstrDefExt;
362         }
363
364         /* Make the parent shell be temporary modal */
365         Dialog oldModal = null;
366         Display display = parent.getDisplay ();
367         if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) {
368                 oldModal = display.getModalDialog ();
369                 display.setModalDialog (this);
370         }
371
372         /*
373         * Feature in Windows.  For some reason, the WH_MSGFILTER filter
374         * does not run for GetSaveFileName() or GetOpenFileName().  The
375         * fix is to allow async messages to run in the WH_FOREGROUNDIDLE
376         * hook instead.
377         */
378         boolean oldRunMessagesInIdle = display.runMessagesInIdle;
379         display.runMessagesInIdle = true;
380         display.externalEventLoop = true;
381         display.sendPreExternalEventDispatchEvent ();
382         /*
383         * Open the dialog.  If the open fails due to an invalid
384         * file name, use an empty file name and open it again.
385         */
386         boolean success = (save) ? OS.GetSaveFileName (struct) : OS.GetOpenFileName (struct);
387         display.externalEventLoop = false;
388         display.sendPostExternalEventDispatchEvent ();
389         switch (OS.CommDlgExtendedError ()) {
390                 case OS.FNERR_INVALIDFILENAME:
391                         OS.MoveMemory (lpstrFile, new char [1], TCHAR.sizeof);
392                         display.externalEventLoop = true;
393                         display.sendPreExternalEventDispatchEvent ();
394                         success = (save) ? OS.GetSaveFileName (struct) : OS.GetOpenFileName (struct);
395                         display.externalEventLoop = false;
396                         display.sendPostExternalEventDispatchEvent ();
397                         break;
398                 case OS.FNERR_BUFFERTOOSMALL:
399                         USE_HOOK = true;
400                         break;
401         }
402         display.runMessagesInIdle = oldRunMessagesInIdle;
403
404         /* Clear the temporary dialog modal parent */
405         if ((style & (SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL)) != 0) {
406                 display.setModalDialog (oldModal);
407         }
408
409         /* Dispose the callback and reassign the buffer */
410         if (callback != null) callback.dispose ();
411         lpstrFile = struct.lpstrFile;
412
413         /* Set the new path, file name and filter */
414         fileNames = new String [0];
415         String fullPath = null;
416         if (success) {
417                 char [] buffer = new char [struct.nMaxFile];
418                 OS.MoveMemory (buffer, lpstrFile, buffer.length * TCHAR.sizeof);
419                 int nFileOffset = struct.nFileOffset;
420                 if (nFileOffset > 0) {
421                         filterPath = new String (buffer, 0, nFileOffset - 1);
422
423                         /*
424                         * Get each file from the buffer.  Files are delimited
425                         * by a NULL character with 2 NULL characters at the end.
426                         */
427                         int count = 0;
428                         fileNames = new String [(style & SWT.MULTI) != 0 ? 4 : 1];
429                         int start = nFileOffset;
430                         do {
431                                 int end = start;
432                                 while (end < buffer.length && buffer [end] != 0) end++;
433                                 String string = new String (buffer, start, end - start);
434                                 start = end + 1;
435                                 if (count == fileNames.length) {
436                                         String [] newFileNames = new String [fileNames.length + 4];
437                                         System.arraycopy (fileNames, 0, newFileNames, 0, fileNames.length);
438                                         fileNames = newFileNames;
439                                 }
440                                 fileNames [count++] = string;
441                                 if ((style & SWT.MULTI) == 0) break;
442                         } while (start < buffer.length && buffer[start] != 0);
443
444                         if (fileNames.length > 0) fileName = fileNames  [0];
445                         String separator = "";
446                         int length = filterPath.length ();
447                         if (length > 0 && filterPath.charAt (length - 1) != '\\') {
448                                 separator = "\\";
449                         }
450                         fullPath = filterPath + separator + fileName;
451                         if (count < fileNames.length) {
452                                 String [] newFileNames = new String [count];
453                                 System.arraycopy (fileNames, 0, newFileNames, 0, count);
454                                 fileNames = newFileNames;
455                         }
456                 }
457                 filterIndex = struct.nFilterIndex - 1;
458         }
459
460         /* Free the memory that was allocated. */
461         OS.HeapFree (hHeap, 0, lpstrFile);
462         OS.HeapFree (hHeap, 0, lpstrFilter);
463         OS.HeapFree (hHeap, 0, lpstrInitialDir);
464         OS.HeapFree (hHeap, 0, lpstrTitle);
465         if (lpstrDefExt != 0) OS.HeapFree (hHeap, 0, lpstrDefExt);
466
467         /* Destroy the BIDI orientation window */
468         if (hwndParent != hwndOwner) {
469                 if (enabled) OS.EnableWindow (hwndParent, true);
470                 OS.SetActiveWindow (hwndParent);
471                 OS.DestroyWindow (hwndOwner);
472         }
473
474         /*
475         * This code is intentionally commented.  On some
476         * platforms, the owner window is repainted right
477         * away when a dialog window exits.  This behavior
478         * is currently unspecified.
479         */
480 //      if (hwndOwner != 0) OS.UpdateWindow (hwndOwner);
481
482         /* Answer the full path or null */
483         return fullPath;
484 }
485
486 /**
487  * Set the initial filename which the dialog will
488  * select by default when opened to the argument,
489  * which may be null.  The name will be prefixed with
490  * the filter path when one is supplied.
491  *
492  * @param string the file name
493  */
494 public void setFileName (String string) {
495         fileName = string;
496 }
497
498 /**
499  * Set the file extensions which the dialog will
500  * use to filter the files it shows to the argument,
501  * which may be null.
502  * <p>
503  * The strings are platform specific. For example, on
504  * some platforms, an extension filter string is typically
505  * of the form "*.extension", where "*.*" matches all files.
506  * For filters with multiple extensions, use semicolon as
507  * a separator, e.g. "*.jpg;*.png".
508  * </p>
509  * <p>
510  * Note: On Mac, setting the file extension filter affects how
511  * app bundles are treated by the dialog. When a filter extension
512  * having the app extension (.app) is selected, bundles are treated
513  * as files. For all other extension filters, bundles are treated
514  * as directories. When no filter extension is set, bundles are
515  * treated as files.
516  * </p>
517  *
518  * @param extensions the file extension filter
519  *
520  * @see #setFilterNames to specify the user-friendly
521  * names corresponding to the extensions
522  */
523 public void setFilterExtensions (String [] extensions) {
524         filterExtensions = extensions;
525 }
526
527 /**
528  * Set the 0-based index of the file extension filter
529  * which the dialog will use initially to filter the files
530  * it shows to the argument.
531  * <p>
532  * This is an index into the FilterExtensions array and
533  * the FilterNames array.
534  * </p>
535  *
536  * @param index the file extension filter index
537  *
538  * @see #setFilterExtensions
539  * @see #setFilterNames
540  *
541  * @since 3.4
542  */
543 public void setFilterIndex (int index) {
544         filterIndex = index;
545 }
546
547 /**
548  * Sets the names that describe the filter extensions
549  * which the dialog will use to filter the files it shows
550  * to the argument, which may be null.
551  * <p>
552  * Each name is a user-friendly short description shown for
553  * its corresponding filter. The <code>names</code> array must
554  * be the same length as the <code>extensions</code> array.
555  * </p>
556  *
557  * @param names the list of filter names, or null for no filter names
558  *
559  * @see #setFilterExtensions
560  */
561 public void setFilterNames (String [] names) {
562         filterNames = names;
563 }
564
565 /**
566  * Sets the directory path that the dialog will use
567  * to the argument, which may be null. File names in this
568  * path will appear in the dialog, filtered according
569  * to the filter extensions. If the string is null,
570  * then the operating system's default filter path
571  * will be used.
572  * <p>
573  * Note that the path string is platform dependent.
574  * For convenience, either '/' or '\' can be used
575  * as a path separator.
576  * </p>
577  *
578  * @param string the directory path
579  *
580  * @see #setFilterExtensions
581  */
582 public void setFilterPath (String string) {
583         filterPath = string;
584 }
585
586 /**
587  * Sets the flag that the dialog will use to
588  * determine whether to prompt the user for file
589  * overwrite if the selected file already exists.
590  *
591  * @param overwrite true if the dialog will prompt for file overwrite, false otherwise
592  *
593  * @since 3.4
594  */
595 public void setOverwrite (boolean overwrite) {
596         this.overwrite = overwrite;
597 }
598 }