001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Dmitry A. Durnev
019: * @version $Revision$
020: */package org.apache.harmony.awt.theme.windows;
021:
022: import java.awt.AWTEvent;
023: import java.awt.EventQueue;
024: import java.awt.FileDialog;
025: import java.awt.Toolkit;
026: import java.awt.Window;
027: import java.awt.event.ComponentEvent;
028: import java.io.File;
029: import java.io.FilenameFilter;
030: import java.util.HashMap;
031: import java.util.Map;
032:
033: import org.apache.harmony.awt.ComponentInternals;
034: import org.apache.harmony.awt.internal.nls.Messages;
035: import org.apache.harmony.awt.nativebridge.Int16Pointer;
036: import org.apache.harmony.awt.nativebridge.NativeBridge;
037: import org.apache.harmony.awt.nativebridge.PointerPointer;
038: import org.apache.harmony.awt.nativebridge.VoidPointer;
039: import org.apache.harmony.awt.nativebridge.windows.Callback;
040: import org.apache.harmony.awt.nativebridge.windows.Win32;
041: import org.apache.harmony.awt.nativebridge.windows.WindowsConsts;
042: import org.apache.harmony.awt.nativebridge.windows.Callback.Handler;
043:
044: public class WinFileDialog extends WinStyle {
045:
046: // windows defs from CommDlg.h
047: private static final int CDN_FIRST = -601;
048: private static final int CDN_INCLUDEITEM = CDN_FIRST - 0x0007;
049:
050: private static class OFNHookHandler implements Handler {
051:
052: public long windowProc(long hwnd, int msg, long wParam,
053: long lParam) {
054: FileDialog fd = thread2fd.get(Thread.currentThread());
055: if (fd == null) {
056: return 0l;
057: }
058: FilenameFilter ff = fd.getFilenameFilter();
059:
060: if (msg == WM_INITDIALOG) {
061: WinFileDialog.getInstance(fd).hwnd = win32
062: .GetParent(hwnd);
063: }
064: if (msg != WM_NOTIFY) {
065: return 0l;
066: }
067: if (win32.createNMHDR(lParam).get_code() != CDN_INCLUDEITEM) {
068: return 0l;
069: }
070:
071: if (ff == null) {
072: return 1l;
073: }
074: Win32.OFNOTIFYEXW ofnotify = win32
075: .createOFNOTIFYEXW(lParam);
076: Win32.IShellFolder psf = getIShellFolder(ofnotify.get_psf());
077:
078: int flags = (WindowsConsts.SHGDN_FORPARSING);
079: Win32.ITEMIDLIST item = win32.createITEMIDLIST(ofnotify
080: .get_pidl());
081: String fullName = getDisplayNameOf(item, psf, flags);
082: File file = new File(fullName);
083: String fName = file.getName();
084: boolean res = true;
085: if (!"".equals(fName)) { //$NON-NLS-1$
086: res = ff.accept(file.getParentFile(), fName);
087: }
088: return (res ? 1 : 0);
089: }
090:
091: private Win32.IShellFolder getIShellFolder(VoidPointer ptr) {
092: PointerPointer ptrPtr = nb.createPointerPointer(ptr, false);
093: return win32.createIShellFolder(ptrPtr.getAddress(0));
094: }
095:
096: private String getDisplayNameOf(Win32.ITEMIDLIST item,
097: Win32.IShellFolder parent, int flags) {
098:
099: Win32.STRRET strret = win32.createSTRRET(false);
100: parent.GetDisplayNameOf(item, flags, strret);
101: int bufLen = 2048;
102: Int16Pointer bufPtr = nb.createInt16Pointer(bufLen, false);
103: win32.StrRetToBufW(strret, null, bufPtr, bufLen);
104: return bufPtr.getString();
105: }
106:
107: }
108:
109: private static final Win32 win32 = Win32.getInstance();
110: private static final NativeBridge nb = NativeBridge.getInstance();
111: private static final ComponentInternals ci = ComponentInternals
112: .getComponentInternals();
113: private static final Map<Thread, FileDialog> thread2fd = new HashMap<Thread, FileDialog>();
114: private static final Map<FileDialog, WinFileDialog> fd2win = new HashMap<FileDialog, WinFileDialog>();
115: private static final OFNHookHandler handler = new OFNHookHandler();
116: private static final long ofnHookPtr = Callback
117: .registerCallbackOFN(handler);
118: private final FileDialog fileDialog;
119: private final boolean modal;
120: private final Win32.OPENFILENAMEW ofn;
121: private long hwnd;
122:
123: public static WinFileDialog getInstance(FileDialog fd) {
124: return fd2win.get(fd);
125: }
126:
127: public WinFileDialog(FileDialog fd) {
128: fileDialog = fd;
129: fd2win.put(fd, this );
130: modal = fd.isModal();
131: ofn = win32.createOPENFILENAMEW(false);
132: ofn.set_lStructSize(ofn.size());
133: }
134:
135: private void show(int mode) {
136: synchronized (handler) {
137: if (!fileDialog.isDisplayable()) {
138: // make displayable together with owner
139: fileDialog.addNotify();
140: }
141: ci.setVisibleFlag(fileDialog, true);
142: postEvent(new ComponentEvent(fileDialog,
143: ComponentEvent.COMPONENT_SHOWN));
144: initOFN();
145: boolean ok = false;
146: Thread thread = Thread.currentThread();
147: thread2fd.put(thread, fileDialog);
148:
149: switch (mode) {
150: case FileDialog.LOAD:
151: ok = (win32.GetOpenFileNameW(ofn) != 0);
152: break;
153: case FileDialog.SAVE:
154: ok = (win32.GetSaveFileNameW(ofn) != 0);
155: break;
156: default:
157: return;
158:
159: }
160: setValues(ok);
161: thread2fd.remove(thread);
162: fd2win.remove(fileDialog);
163: ci.setVisibleFlag(fileDialog, false);
164: postEvent(new ComponentEvent(fileDialog,
165: ComponentEvent.COMPONENT_HIDDEN));
166: }
167: }
168:
169: private void setValues(boolean ok) {
170: String error = null;
171: if (ok) {
172: String fullName = ofn.get_lpstrFile().getString();
173: File file = new File(fullName);
174: fileDialog.setFile(ofn.get_lpstrFileTitle().getString());
175: fileDialog.setDirectory(file.getParent() + File.separator);
176:
177: } else {
178: fileDialog.setFile(null);
179: fileDialog.setDirectory(null);
180: int code = win32.CommDlgExtendedError();
181: if (code != 0) {
182: error = getExtendedError(code);
183: }
184: }
185: if (error != null) {
186: // awt.err.00=file dialog {0} error!
187: System.err.println(Messages.getString("awt.err.00", error)); //$NON-NLS-1$
188: }
189: }
190:
191: private void initOFN() {
192: setOwner(fileDialog.getOwner());
193:
194: setFile(fileDialog.getFile());
195: setDir(fileDialog.getDirectory());
196: setTitle(fileDialog.getTitle());
197: setFilter("All Files (*.*)"); //$NON-NLS-1$
198:
199: ofn.set_Flags(OFN_ENABLEHOOK | OFN_ENABLEINCLUDENOTIFY
200: | OFN_EXPLORER | OFN_ENABLESIZING);
201: ofn.set_lpfnHook(ofnHookPtr);
202: }
203:
204: private void setOwner(Window w) {
205: if ((w == null) || !w.isDisplayable()) {
206: return;
207: }
208: // this also makes file dialog modal:
209: ofn.set_hwndOwner(ci.getNativeWindow(w).getId());
210: }
211:
212: private void setFilter(String filter) {
213: if (filter == null) {
214: return;
215: }
216: ofn.set_lpstrFilter(nb.createInt16Pointer(filter, false));
217: }
218:
219: private void setTitle(String title) {
220: if (title == null) {
221: return;
222: }
223: ofn.set_lpstrTitle(nb.createInt16Pointer(title, false));
224: }
225:
226: private void setDir(String dirName) {
227: if (dirName == null) {
228: dirName = System.getProperty("user.dir"); //$NON-NLS-1$
229: }
230: ofn.set_lpstrInitialDir(nb.createInt16Pointer(dirName, false));
231: }
232:
233: private void setFile(String fileName) {
234: int bufSize = 255;
235: Int16Pointer bufferPtr = nb.createInt16Pointer(bufSize, false);
236: if (fileName != null) {
237: bufferPtr.setString(fileName);
238: }
239: ofn.set_nMaxFileTitle(bufSize);
240: ofn.set_lpstrFileTitle(nb.createInt16Pointer(bufSize, false));
241: ofn.set_lpstrFile(bufferPtr);
242: ofn.set_nMaxFile(bufSize); // mandatory!
243: }
244:
245: public boolean show() {
246: if (modal) {
247: if (EventQueue.isDispatchThread()) {
248: // need to continue dispatching events
249: // so start inner modal loop:
250: new Thread() {
251: @Override
252: public void run() {
253: show(fileDialog.getMode());
254: ci.endModalLoop(fileDialog);
255: }
256: }.start();
257: ci.runModalLoop(fileDialog);
258: } else {
259: // just block the calling thread:
260: show(fileDialog.getMode());
261: }
262: } else {
263: // start new thread here and
264: // return immediately(return value is useless)
265: new Thread() {
266: @Override
267: public void run() {
268: show(fileDialog.getMode());
269: }
270: }.start();
271: }
272:
273: return false; // don't call super(Dialog).show()
274: }
275:
276: private String getExtendedError(int code) {
277: switch (code) {
278: case CDERR_GENERALCODES:
279: return "general"; //$NON-NLS-1$
280: case CDERR_STRUCTSIZE:
281: return "structure size"; //$NON-NLS-1$
282: case CDERR_INITIALIZATION:
283: return "init"; //$NON-NLS-1$
284: case CDERR_NOTEMPLATE:
285: return "no template"; //$NON-NLS-1$
286: case CDERR_NOHINSTANCE:
287: return "no hInstance"; //$NON-NLS-1$
288: case CDERR_LOADSTRFAILURE:
289: return "load string failure"; //$NON-NLS-1$
290: case CDERR_FINDRESFAILURE:
291: return "find resource failure"; //$NON-NLS-1$
292: case CDERR_LOADRESFAILURE:
293: return "load resource failure"; //$NON-NLS-1$
294: case CDERR_LOCKRESFAILURE:
295: return "lock resource failure"; //$NON-NLS-1$
296: case CDERR_MEMALLOCFAILURE:
297: return "mem alloc failure"; //$NON-NLS-1$
298: case CDERR_MEMLOCKFAILURE:
299: return "mem lock failure"; //$NON-NLS-1$
300: case CDERR_NOHOOK:
301: return "no hook"; //$NON-NLS-1$
302: }
303: return "unknown"; //$NON-NLS-1$
304: }
305:
306: private void postEvent(AWTEvent e) {
307: Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(e);
308: }
309:
310: public long close() {
311: ci.setVisibleFlag(fileDialog, false);
312: fd2win.remove(fileDialog);
313: // should post IDABORT, but it doesn't work for some reason
314: // so use IDCANCEL as a workaround
315: long res = win32.PostMessageW(hwnd, WM_COMMAND, IDCANCEL, 0);
316:
317: if (res == 0) {
318: // awt.err.01=error: {0}
319: System.err.println(Messages.getString(
320: "awt.err.01", win32.GetLastError())); //$NON-NLS-1$
321: }
322: return res;
323: }
324: }
|