Make commdlg compile with -DSTRICT.
[wine] / dlls / commdlg / filedlg.c
1 /*
2  * COMMDLG - File Dialogs
3  *
4  * Copyright 1994 Martin Ayotte
5  * Copyright 1996 Albrecht Kleine
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "windef.h"
26 #include "winnls.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "wine/winbase16.h"
30 #include "wine/winuser16.h"
31 #include "wine/unicode.h"
32 #include "heap.h"
33 #include "commdlg.h"
34 #include "wine/debug.h"
35 #include "cderr.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
38
39 #include "cdlg.h"
40
41 #define BUFFILE 512
42 #define BUFFILEALLOC 512 * sizeof(WCHAR)
43
44 struct FSPRIVATE
45 {
46     HWND hwnd; /* file dialog window handle */
47     BOOL hook; /* TRUE if the dialog is hooked */
48     UINT lbselchstring; /* registered message id */
49     UINT fileokstring; /* registered message id */
50     LPARAM lParam; /* save original lparam */
51     HANDLE16 hDlgTmpl16; /* handle for resource 16 */
52     HANDLE16 hResource16; /* handle for allocated resource 16 */
53     HANDLE16 hGlobal16; /* 16 bits mem block (resources) */
54     LPCVOID template; /* template for 32 bits resource */
55     BOOL open; /* TRUE if open dialog, FALSE if save dialog */
56     OPENFILENAMEW *ofnW; /* original structure or work struct */
57     OPENFILENAMEA *ofnA; /* original structure if 32bits ansi dialog */
58     OPENFILENAME16 *ofn16; /* original structure if 16 bits dialog */
59 };
60
61
62 #define LFSPRIVATE struct FSPRIVATE *
63
64 #define LFS16 1
65 #define LFS32A 2
66 #define LFS32W 3
67
68 static const WCHAR FILE_star[] = {'*','.','*', 0};
69 static const WCHAR FILE_bslash[] = {'\\', 0};
70 static const WCHAR FILE_specc[] = {'%','c',':', 0};
71
72 static HICON hFolder = 0;
73 static HICON hFolder2 = 0;
74 static HICON hFloppy = 0;
75 static HICON hHDisk = 0;
76 static HICON hCDRom = 0;
77 static HICON hNet = 0;
78 static const int fldrHeight = 16;
79 static const int fldrWidth = 20;
80
81 #define OFN_PROP "FILEDLG_OFN"
82
83 static char defaultopen[]="Open File";
84 static char defaultsave[]="Save as";
85
86 /***********************************************************************
87  *
88  * Windows 3.1 style OpenFileName/SaveFileName dialog
89  *
90  */
91
92 BOOL16 CALLBACK FileOpenDlgProc16(HWND16 hWnd, UINT16 wMsg, WPARAM16 wParam,
93                                  LPARAM lParam);
94 BOOL16 CALLBACK FileSaveDlgProc16(HWND16 hWnd, UINT16 wMsg, WPARAM16 wParam,
95                                  LPARAM lParam);
96
97 static INT_PTR CALLBACK FileOpenDlgProc(HWND hDlg, UINT msg,
98                                       WPARAM wParam, LPARAM lParam);
99
100 /***********************************************************************
101  *                              FileDlg_Init                    [internal]
102  */
103 static BOOL FileDlg_Init(void)
104 {
105     static BOOL initialized = 0;
106
107     if (!initialized) {
108         HINSTANCE inst = GetModuleHandleA( "comdlg32.dll" );
109         if (!inst)
110         {
111             ERR( "cannot get comdlg32.dll instance\n" );
112             return FALSE;
113         }
114         hFolder  = LoadImageA( inst, "FOLDER", IMAGE_ICON, 16, 16, LR_SHARED );
115         hFolder2 = LoadImageA( inst, "FOLDER2", IMAGE_ICON, 16, 16, LR_SHARED );
116         hFloppy  = LoadImageA( inst, "FLOPPY", IMAGE_ICON, 16, 16, LR_SHARED );
117         hHDisk   = LoadImageA( inst, "HDISK", IMAGE_ICON, 16, 16, LR_SHARED );
118         hCDRom   = LoadImageA( inst, "CDROM", IMAGE_ICON, 16, 16, LR_SHARED );
119         hNet     = LoadImageA( inst, "NETWORK", IMAGE_ICON, 16, 16, LR_SHARED );
120         if (hFolder == 0 || hFolder2 == 0 || hFloppy == 0 ||
121             hHDisk == 0 || hCDRom == 0 || hNet == 0)
122         {
123             ERR("Error loading icons !\n");
124             return FALSE;
125         }
126         initialized = TRUE;
127     }
128     return TRUE;
129 }
130
131
132 /***********************************************************************
133  *           Get32BitsTemplate                                  [internal]
134  *
135  * Get a template (or FALSE if failure) when 16 bits dialogs are used
136  * by a 32 bits application
137  *
138  */
139 BOOL Get32BitsTemplate(LFSPRIVATE lfs)
140 {
141     LPOPENFILENAMEW ofnW = lfs->ofnW;
142     HANDLE hDlgTmpl;
143
144     if (ofnW->Flags & OFN_ENABLETEMPLATEHANDLE)
145     {
146         if (!(lfs->template = LockResource( ofnW->hInstance )))
147         {
148             COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
149             return FALSE;
150         }
151     }
152     else if (ofnW->Flags & OFN_ENABLETEMPLATE)
153     {
154         HRSRC hResInfo;
155         if (lfs->ofnA)
156             hResInfo = FindResourceA(lfs->ofnA->hInstance,
157                                  lfs->ofnA->lpTemplateName,
158                                  RT_DIALOGA);
159         else
160             hResInfo = FindResourceW(ofnW->hInstance,
161                                  ofnW->lpTemplateName,
162                                  RT_DIALOGW);
163         if (!hResInfo)
164         {
165             COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
166             return FALSE;
167         }
168         if (!(hDlgTmpl = LoadResource(ofnW->hInstance,
169                                 hResInfo)) ||
170                     !(lfs->template = LockResource(hDlgTmpl)))
171         {
172             COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
173             return FALSE;
174         }
175     } else { /* get it from internal Wine resource */
176         HRSRC hResInfo;
177         if (!(hResInfo = FindResourceA(COMMDLG_hInstance32,
178              lfs->open? "OPEN_FILE":"SAVE_FILE", RT_DIALOGA)))
179         {
180             COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
181             return FALSE;
182         }
183         if (!(hDlgTmpl = LoadResource(COMMDLG_hInstance32, hResInfo )) ||
184                 !(lfs->template = LockResource( hDlgTmpl )))
185         {
186             COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
187             return FALSE;
188         }
189     }
190     return TRUE;
191 }
192
193
194 /***********************************************************************
195  *           Get16BitsTemplate                                [internal]
196  *
197  * Get a template (FALSE if failure) when 16 bits dialogs are used
198  * by a 16 bits application
199  *
200  */
201 BOOL Get16BitsTemplate(LFSPRIVATE lfs)
202 {
203     LPOPENFILENAME16 ofn16 = lfs->ofn16;
204     LPCVOID template;
205     HGLOBAL16 hGlobal16 = 0;
206
207     if (ofn16->Flags & OFN_ENABLETEMPLATEHANDLE)
208         lfs->hDlgTmpl16 = ofn16->hInstance;
209     else if (ofn16->Flags & OFN_ENABLETEMPLATE)
210     {
211         HANDLE16 hResInfo;
212         if (!(hResInfo = FindResource16(ofn16->hInstance,
213                                         MapSL(ofn16->lpTemplateName),
214                                         RT_DIALOGA)))
215         {
216             COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
217             return FALSE;
218         }
219         if (!(lfs->hDlgTmpl16 = LoadResource16( ofn16->hInstance, hResInfo )))
220         {
221             COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
222             return FALSE;
223         }
224         lfs->hResource16 = lfs->hDlgTmpl16;
225     }
226     else
227     { /* get resource from (32 bits) own Wine resource; convert it to 16 */
228         HRSRC hResInfo;
229         HGLOBAL hDlgTmpl32;
230         LPCVOID template32;
231         DWORD size;
232
233         if (!(hResInfo = FindResourceA(COMMDLG_hInstance32,
234                lfs->open ? "OPEN_FILE":"SAVE_FILE", RT_DIALOGA)))
235         {
236             COMDLG32_SetCommDlgExtendedError(CDERR_FINDRESFAILURE);
237             return FALSE;
238         }
239         if (!(hDlgTmpl32 = LoadResource(COMMDLG_hInstance32, hResInfo )) ||
240             !(template32 = LockResource( hDlgTmpl32 )))
241         {
242             COMDLG32_SetCommDlgExtendedError(CDERR_LOADRESFAILURE);
243             return FALSE;
244         }
245         size = SizeofResource(GetModuleHandleA("COMDLG32"), hResInfo);
246         hGlobal16 = GlobalAlloc16(0, size);
247         if (!hGlobal16)
248         {
249             COMDLG32_SetCommDlgExtendedError(CDERR_MEMALLOCFAILURE);
250             ERR("alloc failure for %ld bytes\n", size);
251             return FALSE;
252         }
253         template = GlobalLock16(hGlobal16);
254         if (!template)
255         {
256             COMDLG32_SetCommDlgExtendedError(CDERR_MEMLOCKFAILURE);
257             ERR("global lock failure for %x handle\n", hGlobal16);
258             GlobalFree16(hGlobal16);
259             return FALSE;
260         }
261         ConvertDialog32To16((LPVOID)template32, size, (LPVOID)template);
262         lfs->hDlgTmpl16 = hGlobal16;
263         lfs->hGlobal16 = hGlobal16;
264     }
265     return TRUE;
266 }
267
268 /***********************************************************************
269  *                              FILEDLG_StripEditControl        [internal]
270  * Strip pathnames off the contents of the edit control.
271  */
272 static void FILEDLG_StripEditControl(HWND hwnd)
273 {
274     WCHAR temp[BUFFILE], *cp;
275
276     GetDlgItemTextW( hwnd, edt1, temp, sizeof(temp)/sizeof(WCHAR));
277     cp = strrchrW(temp, '\\');
278     if (cp != NULL) {
279         strcpyW(temp, cp+1);
280     }
281     cp = strrchrW(temp, ':');
282     if (cp != NULL) {
283         strcpyW(temp, cp+1);
284     }
285     /* FIXME: shouldn't we do something with the result here? ;-) */
286 }
287
288
289
290 /***********************************************************************
291  *                              FILEDLG_CallWindowProc          [internal]
292  *
293  *      Call the appropriate hook
294  */
295 static BOOL FILEDLG_CallWindowProc(LFSPRIVATE lfs, UINT wMsg, WPARAM wParam,
296                                    LPARAM lParam)
297 {
298     if (lfs->ofn16)
299     {
300         return (BOOL16) CallWindowProc16(
301           (WNDPROC16)lfs->ofn16->lpfnHook, HWND_16(lfs->hwnd),
302           (UINT16)wMsg, (WPARAM16)wParam, lParam);
303     }
304     if (lfs->ofnA)
305     {
306         return (BOOL) CallWindowProcA(
307           (WNDPROC)lfs->ofnA->lpfnHook, lfs->hwnd,
308           wMsg, wParam, lParam);
309     }
310
311     if (lfs->ofnW)
312     {
313         return (BOOL) CallWindowProcW(
314           (WNDPROC)lfs->ofnW->lpfnHook, lfs->hwnd,
315           wMsg, wParam, lParam);
316     }
317     return FALSE;
318 }
319
320
321 /***********************************************************************
322  *                              FILEDLG_ScanDir                 [internal]
323  */
324 static BOOL FILEDLG_ScanDir(HWND hWnd, LPWSTR newPath)
325 {
326     WCHAR               buffer[BUFFILE];
327     HWND                hdlg, hdlgDir;
328     LRESULT             lRet = TRUE;
329     HCURSOR             hCursorWait, oldCursor;
330
331     if  ( !SetCurrentDirectoryW( newPath ))
332         return FALSE;
333     lstrcpynW(buffer, newPath, sizeof(buffer)/sizeof(WCHAR));
334
335     /* get the list of spec files */
336     GetDlgItemTextW(hWnd, edt1, buffer, sizeof(buffer)/sizeof(WCHAR));
337
338     hCursorWait = LoadCursorA(0, IDC_WAITA);
339     oldCursor = SetCursor(hCursorWait);
340
341     /* list of files */
342     if ((hdlg = GetDlgItem(hWnd, lst1)) != 0) {
343         WCHAR*  scptr; /* ptr on semi-colon */
344         WCHAR*  filter = buffer;
345
346         TRACE("Using filter %s\n", debugstr_w(filter));
347         SendMessageW(hdlg, LB_RESETCONTENT, 0, 0);
348         while (filter) {
349             scptr = strchrW(filter, ';');
350             if (scptr)  *scptr = 0;
351             while (*filter == ' ') filter++;
352             TRACE("Using file spec %s\n", debugstr_w(filter));
353             if (SendMessageW(hdlg, LB_DIR, 0, (LPARAM)filter) == LB_ERR)
354                 return FALSE;
355             if (scptr) *scptr = ';';
356                 filter = (scptr) ? (scptr + 1) : 0;
357          }
358     }
359
360     /* list of directories */
361     strcpyW(buffer, FILE_star);
362
363     if ((hdlgDir = GetDlgItem(hWnd, lst2)) != 0) {
364         lRet = DlgDirListW(hWnd, buffer, lst2, stc1, DDL_EXCLUSIVE | DDL_DIRECTORY);
365     }
366     SetCursor(oldCursor);
367     return lRet;
368 }
369
370
371 /***********************************************************************
372  *                              FILEDLG_GetFileType             [internal]
373  */
374
375 static LPWSTR FILEDLG_GetFileType(LPWSTR cfptr, LPWSTR fptr, WORD index)
376 {
377   int n, i;
378   i = 0;
379   if (cfptr)
380     for ( ;(n = lstrlenW(cfptr)) != 0; i++)
381       {
382         cfptr += n + 1;
383         if (i == index)
384           return cfptr;
385         cfptr += lstrlenW(cfptr) + 1;
386       }
387   if (fptr)
388     for ( ;(n = lstrlenW(fptr)) != 0; i++)
389       {
390         fptr += n + 1;
391         if (i == index)
392           return fptr;
393         fptr += lstrlenW(fptr) + 1;
394     }
395   return (LPWSTR) FILE_star; /* FIXME */
396 }
397
398 /***********************************************************************
399  *                              FILEDLG_WMDrawItem              [internal]
400  */
401 static LONG FILEDLG_WMDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam,
402        int savedlg, LPDRAWITEMSTRUCT lpdis)
403 {
404     WCHAR *str;
405     HICON hIcon;
406     COLORREF oldText = 0, oldBk = 0;
407
408     if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst1)
409     {
410         if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC))) return FALSE;
411         SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
412                       (LPARAM)str);
413
414         if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
415         {
416             oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
417             oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
418         }
419         if (savedlg)
420             SetTextColor(lpdis->hDC,GetSysColor(COLOR_GRAYTEXT) );
421
422         ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + 1,
423                   lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
424                   &(lpdis->rcItem), str, lstrlenW(str), NULL);
425
426         if (lpdis->itemState & ODS_SELECTED)
427             DrawFocusRect( lpdis->hDC, &(lpdis->rcItem) );
428
429         if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
430         {
431             SetBkColor( lpdis->hDC, oldBk );
432             SetTextColor( lpdis->hDC, oldText );
433         }
434         HeapFree(GetProcessHeap(), 0, str);
435         return TRUE;
436     }
437
438     if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst2)
439     {
440         if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
441             return FALSE;
442         SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
443                       (LPARAM)str);
444
445         if (lpdis->itemState & ODS_SELECTED)
446         {
447             oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
448             oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
449         }
450         ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + fldrWidth,
451                   lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
452                   &(lpdis->rcItem), str, lstrlenW(str), NULL);
453
454         if (lpdis->itemState & ODS_SELECTED)
455             DrawFocusRect( lpdis->hDC, &(lpdis->rcItem) );
456
457         if (lpdis->itemState & ODS_SELECTED)
458         {
459             SetBkColor( lpdis->hDC, oldBk );
460             SetTextColor( lpdis->hDC, oldText );
461         }
462         DrawIcon(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, hFolder);
463         HeapFree(GetProcessHeap(), 0, str);
464         return TRUE;
465     }
466     if (lpdis->CtlType == ODT_COMBOBOX && lpdis->CtlID == cmb2)
467     {
468         char root[] = "a:";
469         if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
470             return FALSE;
471         SendMessageW(lpdis->hwndItem, CB_GETLBTEXT, lpdis->itemID,
472                       (LPARAM)str);
473         root[0] += str[2] - 'a';
474         switch(GetDriveTypeA(root))
475         {
476         case DRIVE_REMOVABLE: hIcon = hFloppy; break;
477         case DRIVE_CDROM:     hIcon = hCDRom; break;
478         case DRIVE_REMOTE:    hIcon = hNet; break;
479         case DRIVE_FIXED:
480         default:           hIcon = hHDisk; break;
481         }
482         if (lpdis->itemState & ODS_SELECTED)
483         {
484             oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
485             oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
486         }
487         ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + fldrWidth,
488                   lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
489                   &(lpdis->rcItem), str, lstrlenW(str), NULL);
490
491         if (lpdis->itemState & ODS_SELECTED)
492         {
493             SetBkColor( lpdis->hDC, oldBk );
494             SetTextColor( lpdis->hDC, oldText );
495         }
496         DrawIcon(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, hIcon);
497         HeapFree(GetProcessHeap(), 0, str);
498         return TRUE;
499     }
500     return FALSE;
501 }
502
503 /***********************************************************************
504  *                              FILEDLG_WMMeasureItem           [internal]
505  */
506 static LONG FILEDLG_WMMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
507 {
508     LPMEASUREITEMSTRUCT lpmeasure;
509
510     lpmeasure = (LPMEASUREITEMSTRUCT)lParam;
511     lpmeasure->itemHeight = fldrHeight;
512     return TRUE;
513 }
514
515 /***********************************************************************
516  *                              FILEDLG_WMMeasureItem16         [internal]
517  */
518 static LONG FILEDLG_WMMeasureItem16(HWND16 hWnd, WPARAM16 wParam, LPARAM lParam)
519 {
520     LPMEASUREITEMSTRUCT16 lpmeasure;
521
522     lpmeasure = MapSL(lParam);
523     lpmeasure->itemHeight = fldrHeight;
524     return TRUE;
525 }
526
527 /***********************************************************************
528  *                              FILEDLG_WMInitDialog            [internal]
529  */
530
531 static LONG FILEDLG_WMInitDialog(HWND hWnd, WPARAM wParam, LPARAM lParam)
532 {
533   int i, n;
534   WCHAR tmpstr[BUFFILE];
535   LPWSTR pstr, old_pstr;
536   LPOPENFILENAMEW ofn;
537   LFSPRIVATE lfs = (LFSPRIVATE) lParam;
538
539   if (!lfs) return FALSE;
540   SetPropA(hWnd, OFN_PROP, (HANDLE)lfs);
541   lfs->hwnd = hWnd;
542   ofn = lfs->ofnW;
543
544   TRACE("flags=%lx initialdir=%s\n", ofn->Flags, debugstr_w(ofn->lpstrInitialDir));
545
546   SetWindowTextW( hWnd, ofn->lpstrTitle );
547   /* read custom filter information */
548   if (ofn->lpstrCustomFilter)
549     {
550       pstr = ofn->lpstrCustomFilter;
551       n = 0;
552       TRACE("lpstrCustomFilter = %p\n", pstr);
553       while(*pstr)
554         {
555           old_pstr = pstr;
556           i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
557                                    (LPARAM)(ofn->lpstrCustomFilter) + n );
558           n += lstrlenW(pstr) + 1;
559           pstr += lstrlenW(pstr) + 1;
560           TRACE("add str=%s associated to %s\n",
561                 debugstr_w(old_pstr), debugstr_w(pstr));
562           SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
563           n += lstrlenW(pstr) + 1;
564           pstr += lstrlenW(pstr) + 1;
565         }
566     }
567   /* read filter information */
568   if (ofn->lpstrFilter) {
569         pstr = (LPWSTR) ofn->lpstrFilter;
570         n = 0;
571         while(*pstr) {
572           old_pstr = pstr;
573           i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
574                                        (LPARAM)(ofn->lpstrFilter + n) );
575           n += lstrlenW(pstr) + 1;
576           pstr += lstrlenW(pstr) + 1;
577           TRACE("add str=%s associated to %s\n",
578                 debugstr_w(old_pstr), debugstr_w(pstr));
579           SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
580           n += lstrlenW(pstr) + 1;
581           pstr += lstrlenW(pstr) + 1;
582         }
583   }
584   /* set default filter */
585   if (ofn->nFilterIndex == 0 && ofn->lpstrCustomFilter == NULL)
586         ofn->nFilterIndex = 1;
587   SendDlgItemMessageW(hWnd, cmb1, CB_SETCURSEL, ofn->nFilterIndex - 1, 0);
588   lstrcpynW(tmpstr, FILEDLG_GetFileType(ofn->lpstrCustomFilter,
589              (LPWSTR)ofn->lpstrFilter, ofn->nFilterIndex - 1),BUFFILE);
590   TRACE("nFilterIndex = %ld, SetText of edt1 to %s\n",
591                         ofn->nFilterIndex, debugstr_w(tmpstr));
592   SetDlgItemTextW( hWnd, edt1, tmpstr );
593   /* get drive list */
594   *tmpstr = 0;
595   DlgDirListComboBoxW(hWnd, tmpstr, cmb2, 0, DDL_DRIVES | DDL_EXCLUSIVE);
596   /* read initial directory */
597   /* FIXME: Note that this is now very version-specific (See MSDN description of
598    * the OPENFILENAME structure).  For example under 2000/XP any path in the
599    * lpstrFile overrides the lpstrInitialDir, but not under 95/98/ME
600    */
601   if (ofn->lpstrInitialDir != NULL)
602     {
603       int len;
604       lstrcpynW(tmpstr, ofn->lpstrInitialDir, 511);
605       len = lstrlenW(tmpstr);
606       if (len > 0 && tmpstr[len-1] != '\\'  && tmpstr[len-1] != ':') {
607         tmpstr[len]='\\';
608         tmpstr[len+1]='\0';
609       }
610     }
611   else
612     *tmpstr = 0;
613   if (!FILEDLG_ScanDir(hWnd, tmpstr)) {
614     *tmpstr = 0;
615     if (!FILEDLG_ScanDir(hWnd, tmpstr))
616       WARN("Couldn't read initial directory %s!\n", debugstr_w(tmpstr));
617   }
618   /* select current drive in combo 2, omit missing drives */
619   {
620       char dir[MAX_PATH];
621       char str[4] = "a:\\";
622       GetCurrentDirectoryA( sizeof(dir), dir );
623       for(i = 0, n = -1; i < 26; i++)
624       {
625           str[0] = 'a' + i;
626           if (GetDriveTypeA(str) > DRIVE_NO_ROOT_DIR) n++;
627           if (toupper(str[0]) == toupper(dir[0])) break;
628       }
629   }
630   SendDlgItemMessageW(hWnd, cmb2, CB_SETCURSEL, n, 0);
631   if (!(ofn->Flags & OFN_SHOWHELP))
632     ShowWindow(GetDlgItem(hWnd, pshHelp), SW_HIDE);
633   if (ofn->Flags & OFN_HIDEREADONLY)
634     ShowWindow(GetDlgItem(hWnd, chx1), SW_HIDE);
635   if (lfs->hook)
636       return (BOOL) FILEDLG_CallWindowProc(lfs, WM_INITDIALOG, wParam, lfs->lParam);
637   return TRUE;
638 }
639
640 /***********************************************************************
641  *                              FILEDLG_UpdateResult            [internal]
642  *      update the displayed file name (with path)
643  */
644 void FILEDLG_UpdateResult(LFSPRIVATE lfs, WCHAR *tmpstr)
645 {
646     int lenstr2;
647     LPOPENFILENAMEW ofnW = lfs->ofnW;
648     WCHAR tmpstr2[BUFFILE];
649
650     GetCurrentDirectoryW(BUFFILE, tmpstr2);
651     lenstr2 = strlenW(tmpstr2);
652     if (lenstr2 > 3)
653         tmpstr2[lenstr2++]='\\';
654     lstrcpynW(tmpstr2+lenstr2, tmpstr, BUFFILE-lenstr2);
655     if (ofnW->lpstrFile)
656         lstrcpynW(ofnW->lpstrFile, tmpstr2, ofnW->nMaxFile);
657     ofnW->nFileOffset = strrchrW(tmpstr2,'\\') - tmpstr2 +1;
658     ofnW->nFileExtension = 0;
659     while(tmpstr2[ofnW->nFileExtension] != '.' && tmpstr2[ofnW->nFileExtension] != '\0')
660         ofnW->nFileExtension++;
661     if (tmpstr2[ofnW->nFileExtension] == '\0')
662         ofnW->nFileExtension = 0;
663     else
664         ofnW->nFileExtension++;
665     /* update the real client structures if any */
666     if (lfs->ofn16)
667     { /* we have to convert to short (8.3) path */
668         char tmp[1024]; /* MAX_PATHNAME_LEN */
669         LPOPENFILENAME16 ofn16 = lfs->ofn16;
670         char *dest = MapSL(ofn16->lpstrFile);
671         if (!WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFile, -1,
672                                   tmp, sizeof(tmp), NULL, NULL ))
673             tmp[sizeof(tmp)-1] = 0;
674         GetShortPathNameA(tmp, dest, ofn16->nMaxFile);
675
676         /* the same procedure as every year... */
677         ofn16->nFileOffset = strrchr(dest,'\\') - dest +1;
678         ofn16->nFileExtension = 0;
679         while(dest[ofn16->nFileExtension] != '.' && dest[ofn16->nFileExtension] != '\0')
680             ofn16->nFileExtension++;
681         if (dest[ofn16->nFileExtension] == '\0')
682             ofn16->nFileExtension = 0;
683         else
684             ofn16->nFileExtension++;
685     }
686     if (lfs->ofnA)
687     {
688         if (ofnW->nMaxFile &&
689             !WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFile, -1,
690                                   lfs->ofnA->lpstrFile, ofnW->nMaxFile, NULL, NULL ))
691             lfs->ofnA->lpstrFile[ofnW->nMaxFile-1] = 0;
692         lfs->ofnA->nFileOffset = ofnW->nFileOffset;
693         lfs->ofnA->nFileExtension = ofnW->nFileExtension;
694     }
695 }
696
697
698 /***********************************************************************
699  *                              FILEDLG_UpdateFileTitle         [internal]
700  *      update the displayed file name (without path)
701  */
702 void FILEDLG_UpdateFileTitle(LFSPRIVATE lfs)
703 {
704   LONG lRet;
705   LPOPENFILENAMEW ofnW = lfs->ofnW;
706   if (ofnW->lpstrFileTitle != NULL)
707   {
708     lRet = SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETCURSEL, 0, 0);
709     SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETTEXT, lRet,
710                              (LPARAM)ofnW->lpstrFileTitle );
711     if (lfs->ofn16)
712     {
713         char *dest = MapSL(lfs->ofn16->lpstrFileTitle);
714         if (!WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFileTitle, -1,
715                                   dest, ofnW->nMaxFileTitle, NULL, NULL ))
716             dest[ofnW->nMaxFileTitle-1] = 0;
717     }
718     if (lfs->ofnA)
719     {
720         if (!WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFileTitle, -1,
721                                   lfs->ofnA->lpstrFileTitle, ofnW->nMaxFileTitle, NULL, NULL ))
722             lfs->ofnA->lpstrFileTitle[ofnW->nMaxFileTitle-1] = 0;
723     }
724   }
725 }
726
727
728
729 /***********************************************************************
730  *                              FILEDLG_DirListDblClick         [internal]
731  */
732 static LRESULT FILEDLG_DirListDblClick( LFSPRIVATE lfs )
733 {
734   LONG lRet;
735   HWND hWnd = lfs->hwnd;
736   LPWSTR pstr;
737   WCHAR tmpstr[BUFFILE];
738
739   /* get the raw string (with brackets) */
740   lRet = SendDlgItemMessageW(hWnd, lst2, LB_GETCURSEL, 0, 0);
741   if (lRet == LB_ERR) return TRUE;
742   pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
743   SendDlgItemMessageW(hWnd, lst2, LB_GETTEXT, lRet,
744                      (LPARAM)pstr);
745   strcpyW( tmpstr, pstr );
746   HeapFree(GetProcessHeap(), 0, pstr);
747   /* get the selected directory in tmpstr */
748   if (tmpstr[0] == '[')
749     {
750       tmpstr[lstrlenW(tmpstr) - 1] = 0;
751       strcpyW(tmpstr,tmpstr+1);
752     }
753   strcatW(tmpstr, FILE_bslash);
754
755   FILEDLG_ScanDir(hWnd, tmpstr);
756   /* notify the app */
757   if (lfs->hook)
758     {
759       if (FILEDLG_CallWindowProc(lfs, lfs->lbselchstring, lst2,
760               MAKELONG(lRet,CD_LBSELCHANGE)))
761         return TRUE;
762     }
763   return TRUE;
764 }
765
766
767 /***********************************************************************
768  *                              FILEDLG_FileListSelect         [internal]
769  *    called when a new item is picked in the file list
770  */
771 static LRESULT FILEDLG_FileListSelect( LFSPRIVATE lfs )
772 {
773     LONG lRet;
774     HWND hWnd = lfs->hwnd;
775     LPWSTR pstr;
776
777     lRet = SendDlgItemMessageW(hWnd, lst1, LB_GETCURSEL16, 0, 0);
778     if (lRet == LB_ERR)
779         return TRUE;
780
781     /* set the edit control to the choosen file */
782     if ((pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
783     {
784         SendDlgItemMessageW(hWnd, lst1, LB_GETTEXT, lRet,
785                        (LPARAM)pstr);
786         SetDlgItemTextW( hWnd, edt1, pstr );
787         HeapFree(GetProcessHeap(), 0, pstr);
788     }
789     if (lfs->hook)
790     {
791         FILEDLG_CallWindowProc(lfs, lfs->lbselchstring, lst1,
792                            MAKELONG(lRet,CD_LBSELCHANGE));
793     }
794     /* FIXME: for OFN_ALLOWMULTISELECT we need CD_LBSELSUB, CD_SELADD,
795            CD_LBSELNOITEMS */
796     return TRUE;
797 }
798
799 /***********************************************************************
800  *                              FILEDLG_TestPath      [internal]
801  *      before accepting the file name, test if it includes wild cards
802  *      tries to scan the directory and returns TRUE if no error.
803  */
804 static LRESULT FILEDLG_TestPath( LFSPRIVATE lfs, LPWSTR path )
805 {
806     HWND hWnd = lfs->hwnd;
807     LPWSTR pBeginFileName, pstr2;
808     WCHAR tmpstr2[BUFFILE];
809
810     pBeginFileName = strrchrW(path, '\\');
811     if (pBeginFileName == NULL)
812         pBeginFileName = strrchrW(path, ':');
813
814     if (strchrW(path,'*') != NULL || strchrW(path,'?') != NULL)
815     {
816         /* edit control contains wildcards */
817         if (pBeginFileName != NULL)
818         {
819             lstrcpynW(tmpstr2, pBeginFileName + 1, BUFFILE);
820             *(pBeginFileName + 1) = 0;
821         }
822         else
823         {
824             strcpyW(tmpstr2, path);
825             *path = 0;
826         }
827
828         TRACE("path=%s, tmpstr2=%s\n", debugstr_w(path), debugstr_w(tmpstr2));
829         SetDlgItemTextW( hWnd, edt1, tmpstr2 );
830         FILEDLG_ScanDir(hWnd, path);
831         return FALSE;
832     }
833
834     /* no wildcards, we might have a directory or a filename */
835     /* try appending a wildcard and reading the directory */
836
837     pstr2 = path + lstrlenW(path);
838     if (pBeginFileName == NULL || *(pBeginFileName + 1) != 0)
839         strcatW(path, FILE_bslash);
840
841     /* if ScanDir succeeds, we have changed the directory */
842     if (FILEDLG_ScanDir(hWnd, path))
843         return TRUE;
844
845     /* if not, this must be a filename */
846
847     *pstr2 = 0; /* remove the wildcard added before */
848
849     if (pBeginFileName != NULL)
850     {
851         /* strip off the pathname */
852         *pBeginFileName = 0;
853         SetDlgItemTextW( hWnd, edt1, pBeginFileName + 1 );
854
855         lstrcpynW(tmpstr2, pBeginFileName + 1, sizeof(tmpstr2)/sizeof(WCHAR) );
856         /* Should we MessageBox() if this fails? */
857         if (!FILEDLG_ScanDir(hWnd, path))
858         {
859             return FALSE;
860         }
861         strcpyW(path, tmpstr2);
862     }
863     else
864         SetDlgItemTextW( hWnd, edt1, path );
865     return TRUE;
866 }
867
868 /***********************************************************************
869  *                              FILEDLG_Validate               [internal]
870  *   called on: click Ok button, Enter in edit, DoubleClick in file list
871  */
872 static LRESULT FILEDLG_Validate( LFSPRIVATE lfs, LPWSTR path, UINT control, INT itemIndex,
873                                  BOOL internalUse )
874 {
875     LONG lRet;
876     HWND hWnd = lfs->hwnd;
877     OPENFILENAMEW ofnsav;
878     LPOPENFILENAMEW ofnW = lfs->ofnW;
879     WCHAR filename[BUFFILE];
880
881     ofnsav = *ofnW; /* for later restoring */
882
883     /* get current file name */
884     if (path)
885         lstrcpynW(filename, path, sizeof(filename)/sizeof(WCHAR));
886     else
887         GetDlgItemTextW( hWnd, edt1, filename, sizeof(filename)/sizeof(WCHAR));
888
889     /* if we did not click in file list to get there */
890     if (control != lst1)
891     {
892         if (!FILEDLG_TestPath( lfs, filename) )
893            return FALSE;
894     }
895     FILEDLG_UpdateResult(lfs, filename);
896
897     if (internalUse)
898     { /* called internally after a change in a combo */
899         if (lfs->hook)
900         {
901              FILEDLG_CallWindowProc(lfs, lfs->lbselchstring, control,
902                              MAKELONG(itemIndex,CD_LBSELCHANGE));
903         }
904         return TRUE;
905     }
906
907     FILEDLG_UpdateFileTitle(lfs);
908     if (lfs->hook)
909     {
910         lRet = (BOOL)FILEDLG_CallWindowProc(lfs, lfs->fileokstring,
911                   0, lfs->lParam );
912         if (lRet)
913         {
914             *ofnW = ofnsav; /* restore old state */
915             return FALSE;
916         }
917     }
918     if ((ofnW->Flags & OFN_ALLOWMULTISELECT) && (ofnW->Flags & OFN_EXPLORER))
919     {
920         if (ofnW->lpstrFile)
921         {
922             LPWSTR str = (LPWSTR)ofnW->lpstrFile;
923             LPWSTR ptr = strrchrW(str, '\\');
924             str[lstrlenW(str) + 1] = '\0';
925             *ptr = 0;
926         }
927     }
928     return TRUE;
929 }
930
931 /***********************************************************************
932  *                              FILEDLG_DiskChange             [internal]
933  *    called when a new item is picked in the disk selection combo
934  */
935 static LRESULT FILEDLG_DiskChange( LFSPRIVATE lfs )
936 {
937     LONG lRet;
938     HWND hWnd = lfs->hwnd;
939     LPWSTR pstr;
940     WCHAR diskname[BUFFILE];
941
942     FILEDLG_StripEditControl(hWnd);
943     lRet = SendDlgItemMessageW(hWnd, cmb2, CB_GETCURSEL, 0, 0L);
944     if (lRet == LB_ERR)
945         return 0;
946     pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
947     SendDlgItemMessageW(hWnd, cmb2, CB_GETLBTEXT, lRet,
948                          (LPARAM)pstr);
949     wsprintfW(diskname, FILE_specc, pstr[2]);
950     HeapFree(GetProcessHeap(), 0, pstr);
951
952     return FILEDLG_Validate( lfs, diskname, cmb2, lRet, TRUE );
953 }
954
955
956 /***********************************************************************
957  *                              FILEDLG_FileTypeChange         [internal]
958  *    called when a new item is picked in the file type combo
959  */
960 static LRESULT FILEDLG_FileTypeChange( LFSPRIVATE lfs )
961 {
962     LONG lRet;
963     WCHAR diskname[BUFFILE];
964     LPWSTR pstr;
965
966     diskname[0] = 0;
967
968     lRet = SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETCURSEL, 0, 0);
969     if (lRet == LB_ERR)
970         return TRUE;
971     pstr = (LPWSTR)SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETITEMDATA, lRet, 0);
972     TRACE("Selected filter : %s\n", debugstr_w(pstr));
973     SetDlgItemTextW( lfs->hwnd, edt1, pstr );
974
975     return FILEDLG_Validate( lfs, NULL, cmb1, lRet, TRUE );
976 }
977
978 /***********************************************************************
979  *                              FILEDLG_WMCommand               [internal]
980  */
981 static LRESULT FILEDLG_WMCommand(HWND hWnd, LPARAM lParam, UINT notification,
982        UINT control, LFSPRIVATE lfs )
983 {
984     switch (control)
985     {
986         case lst1: /* file list */
987         FILEDLG_StripEditControl(hWnd);
988         if (notification == LBN_DBLCLK)
989         {
990             if (FILEDLG_Validate( lfs, NULL, control, 0, FALSE ))
991                 EndDialog(hWnd, TRUE);
992             return TRUE;
993         }
994         else if (notification == LBN_SELCHANGE)
995             return FILEDLG_FileListSelect( lfs );
996         break;
997
998         case lst2: /* directory list */
999         FILEDLG_StripEditControl(hWnd);
1000         if (notification == LBN_DBLCLK)
1001             return FILEDLG_DirListDblClick( lfs );
1002         break;
1003
1004         case cmb1: /* file type drop list */
1005         if (notification == CBN_SELCHANGE)
1006             return FILEDLG_FileTypeChange( lfs );
1007         break;
1008
1009         case chx1:
1010         break;
1011
1012         case pshHelp:
1013         break;
1014
1015         case cmb2: /* disk dropdown combo */
1016         if (notification == CBN_SELCHANGE)
1017             return FILEDLG_DiskChange( lfs );
1018         break;
1019
1020         case IDOK:
1021         if (FILEDLG_Validate( lfs, NULL, control, 0, FALSE ))
1022             EndDialog(hWnd, TRUE);
1023         return TRUE;
1024
1025         case IDCANCEL:
1026         EndDialog(hWnd, FALSE);
1027         return TRUE;
1028
1029         case IDABORT: /* can be sent by the hook procedure */
1030         EndDialog(hWnd, TRUE);
1031         return TRUE;
1032     }
1033     return FALSE;
1034 }
1035
1036 /***********************************************************************
1037  *                              FILEDLG_MapDrawItemStruct       [internal]
1038  *      map a 16 bits drawitem struct to 32
1039  */
1040 void FILEDLG_MapDrawItemStruct(LPDRAWITEMSTRUCT16 lpdis16, LPDRAWITEMSTRUCT lpdis)
1041 {
1042     lpdis->CtlType = lpdis16->CtlType;
1043     lpdis->CtlID = lpdis16->CtlID;
1044     lpdis->itemID = lpdis16->itemID;
1045     lpdis->itemAction = lpdis16->itemAction;
1046     lpdis->itemState = lpdis16->itemState;
1047     lpdis->hwndItem = HWND_32(lpdis16->hwndItem);
1048     lpdis->hDC = HDC_32(lpdis16->hDC);
1049     lpdis->rcItem.right = lpdis16->rcItem.right;
1050     lpdis->rcItem.left = lpdis16->rcItem.left;
1051     lpdis->rcItem.top = lpdis16->rcItem.top;
1052     lpdis->rcItem.bottom = lpdis16->rcItem.bottom;
1053     lpdis->itemData = lpdis16->itemData;
1054 }
1055
1056 /************************************************************************
1057  *                              FILEDLG_MapStringPairsToW       [internal]
1058  *      map string pairs to Unicode
1059  */
1060 static LPWSTR FILEDLG_MapStringPairsToW(LPCSTR strA, UINT size)
1061 {
1062     LPCSTR s;
1063     LPWSTR x;
1064     int n, len;
1065
1066     s = strA;
1067     while (*s)
1068         s = s+strlen(s)+1;
1069     s++;
1070     n = s + 1 - strA; /* Don't forget the other \0 */
1071     if (n < size) n = size;
1072
1073     len = MultiByteToWideChar( CP_ACP, 0, strA, n, NULL, 0 );
1074     x = HeapAlloc(GetProcessHeap(),0, len * sizeof(WCHAR));
1075     MultiByteToWideChar( CP_ACP, 0, strA, n, x, len );
1076     return x;
1077 }
1078
1079
1080 /************************************************************************
1081  *                              FILEDLG_DupToW                  [internal]
1082  *      duplicates an Ansi string to unicode, with a buffer size
1083  */
1084 LPWSTR FILEDLG_DupToW(LPCSTR str, DWORD size)
1085 {
1086     LPWSTR strW = NULL;
1087     if (str && (size > 0))
1088     {
1089         strW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1090         if (strW) MultiByteToWideChar( CP_ACP, 0, str, -1, strW, size );
1091     }
1092     return strW;
1093 }
1094
1095
1096 /************************************************************************
1097  *                              FILEDLG_MapOfnStructA          [internal]
1098  *      map a 32 bits Ansi structure to an Unicode one
1099  */
1100 void FILEDLG_MapOfnStructA(LPOPENFILENAMEA ofnA, LPOPENFILENAMEW ofnW, BOOL open)
1101 {
1102     LPCSTR str;
1103
1104     ofnW->lStructSize = sizeof(OPENFILENAMEW);
1105     ofnW->hwndOwner = ofnA->hwndOwner;
1106     ofnW->hInstance = ofnA->hInstance;
1107     if (ofnA->lpstrFilter)
1108         ofnW->lpstrFilter = FILEDLG_MapStringPairsToW(ofnA->lpstrFilter, 0);
1109
1110     if ((ofnA->lpstrCustomFilter) && (*(ofnA->lpstrCustomFilter)))
1111         ofnW->lpstrCustomFilter = FILEDLG_MapStringPairsToW(ofnA->lpstrCustomFilter, ofnA->nMaxCustFilter);
1112     ofnW->nMaxCustFilter = ofnA->nMaxCustFilter;
1113     ofnW->nFilterIndex = ofnA->nFilterIndex;
1114     ofnW->nMaxFile = ofnA->nMaxFile;
1115     ofnW->lpstrFile = FILEDLG_DupToW(ofnA->lpstrFile, ofnW->nMaxFile);
1116     ofnW->nMaxFileTitle = ofnA->nMaxFileTitle;
1117     ofnW->lpstrFileTitle = FILEDLG_DupToW(ofnA->lpstrFileTitle, ofnW->nMaxFileTitle);
1118     if (ofnA->lpstrInitialDir)
1119         ofnW->lpstrInitialDir = HEAP_strdupAtoW(GetProcessHeap(),0,ofnA->lpstrInitialDir);
1120     if (ofnA->lpstrTitle)
1121         str = ofnA->lpstrTitle;
1122     else
1123         /* Allocates default title (FIXME : get it from resource) */
1124         str = open ? defaultopen:defaultsave;
1125     ofnW->lpstrTitle = HEAP_strdupAtoW(GetProcessHeap(),0, str);
1126     ofnW->Flags = ofnA->Flags;
1127     ofnW->nFileOffset = ofnA->nFileOffset;
1128     ofnW->nFileExtension = ofnA->nFileExtension;
1129     ofnW->lpstrDefExt = FILEDLG_DupToW(ofnA->lpstrDefExt, 3);
1130     if ((ofnA->Flags & OFN_ENABLETEMPLATE) && (ofnA->lpTemplateName))
1131     {
1132         if (HIWORD(ofnA->lpTemplateName))
1133             ofnW->lpTemplateName = HEAP_strdupAtoW(GetProcessHeap(), 0, ofnA->lpTemplateName);
1134         else /* numbered resource */
1135             ofnW->lpTemplateName = (LPWSTR) ofnA->lpTemplateName;
1136     }
1137 }
1138
1139
1140 /************************************************************************
1141  *                              FILEDLG_MapOfnStruct16          [internal]
1142  *      map a 16 bits structure to an Unicode one
1143  */
1144 void FILEDLG_MapOfnStruct16(LPOPENFILENAME16 ofn16, LPOPENFILENAMEW ofnW, BOOL open)
1145 {
1146     OPENFILENAMEA ofnA;
1147     /* first convert to linear pointers */
1148     memset(&ofnA, 0, sizeof(OPENFILENAMEA));
1149     ofnA.lStructSize = sizeof(OPENFILENAMEA);
1150     ofnA.hwndOwner = HWND_32(ofn16->hwndOwner);
1151     ofnA.hInstance = HINSTANCE_32(ofn16->hInstance);
1152     if (ofn16->lpstrFilter)
1153         ofnA.lpstrFilter = MapSL(ofn16->lpstrFilter);
1154     if (ofn16->lpstrCustomFilter)
1155         ofnA.lpstrCustomFilter = MapSL(ofn16->lpstrCustomFilter);
1156     ofnA.nMaxCustFilter = ofn16->nMaxCustFilter;
1157     ofnA.nFilterIndex = ofn16->nFilterIndex;
1158     ofnA.lpstrFile = MapSL(ofn16->lpstrFile);
1159     ofnA.nMaxFile = ofn16->nMaxFile;
1160     ofnA.lpstrFileTitle = MapSL(ofn16->lpstrFileTitle);
1161     ofnA.nMaxFileTitle = ofn16->nMaxFileTitle;
1162     ofnA.lpstrInitialDir = MapSL(ofn16->lpstrInitialDir);
1163     ofnA.lpstrTitle = MapSL(ofn16->lpstrTitle);
1164     ofnA.Flags = ofn16->Flags;
1165     ofnA.nFileOffset = ofn16->nFileOffset;
1166     ofnA.nFileExtension = ofn16->nFileExtension;
1167     ofnA.lpstrDefExt = MapSL(ofn16->lpstrDefExt);
1168     if (HIWORD(ofn16->lpTemplateName))
1169         ofnA.lpTemplateName = MapSL(ofn16->lpTemplateName);
1170     else
1171         ofnA.lpTemplateName = (LPSTR) ofn16->lpTemplateName; /* ressource number */
1172     /* now calls the 32 bits Ansi to Unicode version to complete the job */
1173     FILEDLG_MapOfnStructA(&ofnA, ofnW, open);
1174 }
1175
1176
1177 /************************************************************************
1178  *                              FILEDLG_DestroyPrivate            [internal]
1179  *      destroys the private object
1180  */
1181 void FILEDLG_DestroyPrivate(LFSPRIVATE lfs)
1182 {
1183     HWND hwnd;
1184     if (!lfs) return;
1185     hwnd = lfs->hwnd;
1186     /* free resources for a 16 bits dialog */
1187     if (lfs->hResource16) FreeResource16(lfs->hResource16);
1188     if (lfs->hGlobal16)
1189     {
1190         GlobalUnlock16(lfs->hGlobal16);
1191         GlobalFree16(lfs->hGlobal16);
1192     }
1193     /* if ofnW has been allocated, have to free everything in it */
1194     if (lfs->ofn16 || lfs->ofnA)
1195     {
1196        LPOPENFILENAMEW ofnW = lfs->ofnW;
1197        if (ofnW->lpstrFilter) HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrFilter);
1198        if (ofnW->lpstrCustomFilter) HeapFree(GetProcessHeap(), 0, ofnW->lpstrCustomFilter);
1199        if (ofnW->lpstrFile) HeapFree(GetProcessHeap(), 0, ofnW->lpstrFile);
1200        if (ofnW->lpstrFileTitle) HeapFree(GetProcessHeap(), 0, ofnW->lpstrFileTitle);
1201        if (ofnW->lpstrInitialDir) HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrInitialDir);
1202        if (ofnW->lpstrTitle) HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrTitle);
1203        if ((ofnW->lpTemplateName) && (HIWORD(ofnW->lpTemplateName)))
1204            HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpTemplateName);
1205        HeapFree(GetProcessHeap(), 0, ofnW);
1206     }
1207     TRACE("destroying private allocation %p\n", lfs);
1208     HeapFree(GetProcessHeap(), 0, lfs);
1209     RemovePropA(hwnd, OFN_PROP);
1210 }
1211
1212 /************************************************************************
1213  *                              FILEDLG_AllocPrivate            [internal]
1214  *      allocate a private object to hold 32 bits Unicode
1215  *      structure that will be used throughtout the calls, while
1216  *      keeping available the original structures and a few variables
1217  *      On entry : type = dialog procedure type (16,32A,32W)
1218  *                 dlgType = dialog type (open or save)
1219  */
1220 LFSPRIVATE FILEDLG_AllocPrivate(LPARAM lParam, int type, UINT dlgType)
1221 {
1222     LFSPRIVATE lfs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct FSPRIVATE));
1223     LFSPRIVATE ret;
1224     TRACE("alloc private buf %p\n", lfs);
1225     if (!lfs) return NULL;
1226     lfs->hook = FALSE;
1227     lfs->lParam = lParam;
1228     if (dlgType == OPEN_DIALOG)
1229         lfs->open = TRUE;
1230     else
1231         lfs->open = FALSE;
1232     lfs->lbselchstring = RegisterWindowMessageA(LBSELCHSTRINGA);
1233     lfs->fileokstring = RegisterWindowMessageA(FILEOKSTRINGA);
1234     switch(type)
1235     {
1236         case LFS16:
1237         lfs->ofn16 = MapSL(lParam);
1238         if (lfs->ofn16->Flags & OFN_ENABLEHOOK)
1239             if (lfs->ofn16->lpfnHook)
1240                 lfs->hook = TRUE;
1241
1242         break;
1243
1244         case LFS32A:
1245         lfs->ofnA = (LPOPENFILENAMEA) lParam;
1246         if (lfs->ofnA->Flags & OFN_ENABLEHOOK)
1247             if (lfs->ofnA->lpfnHook)
1248                 lfs->hook = TRUE;
1249         break;
1250
1251         case LFS32W:
1252         lfs->ofnW = (LPOPENFILENAMEW) lParam;
1253         if (lfs->ofnW->Flags & OFN_ENABLEHOOK)
1254             if (lfs->ofnW->lpfnHook)
1255                 lfs->hook = TRUE;
1256         break;
1257     }
1258     ret = lfs;
1259     if (!lfs->ofnW)
1260     { /* this structure is needed internally, so create it */
1261         lfs->ofnW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(OPENFILENAMEW));
1262         if (lfs->ofnW)
1263         {
1264             if (lfs->ofn16)
1265                 FILEDLG_MapOfnStruct16(lfs->ofn16, lfs->ofnW, lfs->open);
1266             if (lfs->ofnA)
1267                 FILEDLG_MapOfnStructA(lfs->ofnA, lfs->ofnW, lfs->open);
1268         }
1269         else
1270             ret = NULL;
1271     }
1272     if (lfs->ofn16)
1273     {
1274         if (!Get16BitsTemplate(lfs)) ret = NULL;
1275     }
1276     else
1277         if (!Get32BitsTemplate(lfs)) ret = NULL;
1278     if (!ret) FILEDLG_DestroyPrivate(lfs);
1279     return ret;
1280 }
1281
1282
1283 /***********************************************************************
1284  *           GetFileName31A                                 [internal]
1285  *
1286  * Creates a win31 style dialog box for the user to select a file to open/save.
1287  */
1288 BOOL WINAPI GetFileName31A(
1289                            LPOPENFILENAMEA lpofn, /* addess of structure with data*/
1290                            UINT dlgType /* type dialogue : open/save */
1291                           )
1292 {
1293     HINSTANCE hInst;
1294     BOOL bRet = FALSE;
1295     LFSPRIVATE lfs;
1296
1297     if (!lpofn || !FileDlg_Init()) return FALSE;
1298
1299     lfs = FILEDLG_AllocPrivate((LPARAM) lpofn, LFS32A, dlgType);
1300     if (lfs)
1301     {
1302         hInst = (HINSTANCE)GetWindowLongA( lpofn->hwndOwner, GWL_HINSTANCE );
1303         bRet = DialogBoxIndirectParamA( hInst, lfs->template, lpofn->hwndOwner,
1304                                         FileOpenDlgProc, (LPARAM)lfs);
1305         FILEDLG_DestroyPrivate(lfs);
1306     }
1307
1308     TRACE("return lpstrFile='%s' !\n", lpofn->lpstrFile);
1309     return bRet;
1310 }
1311
1312
1313 /***********************************************************************
1314  *           GetFileName31W                                 [internal]
1315  *
1316  * Creates a win31 style dialog box for the user to select a file to open/save
1317  */
1318 BOOL WINAPI GetFileName31W(
1319                            LPOPENFILENAMEW lpofn, /* addess of structure with data*/
1320                            UINT dlgType /* type dialogue : open/save */
1321                           )
1322 {
1323     HINSTANCE hInst;
1324     BOOL bRet = FALSE;
1325     LFSPRIVATE lfs;
1326
1327     if (!lpofn || !FileDlg_Init()) return FALSE;
1328
1329     lfs = FILEDLG_AllocPrivate((LPARAM) lpofn, LFS32W, dlgType);
1330     if (lfs)
1331     {
1332         hInst = (HINSTANCE)GetWindowLongA( lpofn->hwndOwner, GWL_HINSTANCE );
1333         bRet = DialogBoxIndirectParamW( hInst, lfs->template, lpofn->hwndOwner,
1334                                         FileOpenDlgProc, (LPARAM)lfs);
1335         FILEDLG_DestroyPrivate(lfs);
1336     }
1337
1338     TRACE("return lpstrFile=%s !\n", debugstr_w(lpofn->lpstrFile));
1339     return bRet;
1340 }
1341
1342
1343 /* ------------------ Dialog procedures ---------------------- */
1344
1345 /***********************************************************************
1346  *           FileOpenDlgProc   (COMMDLG.6)
1347  */
1348 BOOL16 CALLBACK FileOpenDlgProc16(HWND16 hWnd16, UINT16 wMsg, WPARAM16 wParam,
1349                                LPARAM lParam)
1350 {
1351     HWND hWnd = HWND_32(hWnd16);
1352     LFSPRIVATE lfs = (LFSPRIVATE)GetPropA(hWnd,OFN_PROP);
1353     DRAWITEMSTRUCT dis;
1354
1355     TRACE("msg=%x wparam=%x lParam=%lx\n", wMsg, wParam, lParam);
1356     if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
1357         {
1358             LRESULT lRet = (BOOL16)FILEDLG_CallWindowProc(lfs, wMsg, wParam, lParam);
1359             if (lRet)
1360                 return lRet;         /* else continue message processing */
1361         }
1362     switch (wMsg)
1363     {
1364     case WM_INITDIALOG:
1365         return FILEDLG_WMInitDialog(hWnd, wParam, lParam);
1366
1367     case WM_MEASUREITEM:
1368         return FILEDLG_WMMeasureItem16(hWnd16, wParam, lParam);
1369
1370     case WM_DRAWITEM:
1371         FILEDLG_MapDrawItemStruct(MapSL(lParam), &dis);
1372         return FILEDLG_WMDrawItem(hWnd, wParam, lParam, FALSE, &dis);
1373
1374     case WM_COMMAND:
1375         return FILEDLG_WMCommand(hWnd, lParam, HIWORD(lParam),wParam, lfs);
1376 #if 0
1377     case WM_CTLCOLOR:
1378          SetBkColor((HDC16)wParam, 0x00C0C0C0);
1379          switch (HIWORD(lParam))
1380          {
1381          case CTLCOLOR_BTN:
1382              SetTextColor((HDC16)wParam, 0x00000000);
1383              return hGRAYBrush;
1384         case CTLCOLOR_STATIC:
1385              SetTextColor((HDC16)wParam, 0x00000000);
1386              return hGRAYBrush;
1387         }
1388       break;
1389 #endif
1390     }
1391     return FALSE;
1392 }
1393
1394 /***********************************************************************
1395  *           FileSaveDlgProc   (COMMDLG.7)
1396  */
1397 BOOL16 CALLBACK FileSaveDlgProc16(HWND16 hWnd16, UINT16 wMsg, WPARAM16 wParam,
1398                                LPARAM lParam)
1399 {
1400  HWND hWnd = HWND_32(hWnd16);
1401  LFSPRIVATE lfs = (LFSPRIVATE)GetPropA(hWnd,OFN_PROP);
1402  DRAWITEMSTRUCT dis;
1403
1404  TRACE("msg=%x wparam=%x lParam=%lx\n", wMsg, wParam, lParam);
1405  if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
1406   {
1407    LRESULT  lRet;
1408    lRet = (BOOL16)FILEDLG_CallWindowProc(lfs, wMsg, wParam, lParam);
1409    if (lRet)
1410     return lRet;         /* else continue message processing */
1411   }
1412   switch (wMsg) {
1413    case WM_INITDIALOG:
1414       return FILEDLG_WMInitDialog(hWnd, wParam, lParam);
1415
1416    case WM_MEASUREITEM:
1417       return FILEDLG_WMMeasureItem16(hWnd16, wParam, lParam);
1418
1419    case WM_DRAWITEM:
1420       FILEDLG_MapDrawItemStruct(MapSL(lParam), &dis);
1421       return FILEDLG_WMDrawItem(hWnd, wParam, lParam, TRUE, &dis);
1422
1423    case WM_COMMAND:
1424       return FILEDLG_WMCommand(hWnd, lParam, HIWORD(lParam), wParam, lfs);
1425   }
1426
1427   /*
1428   case WM_CTLCOLOR:
1429    SetBkColor((HDC16)wParam, 0x00C0C0C0);
1430    switch (HIWORD(lParam))
1431    {
1432     case CTLCOLOR_BTN:
1433      SetTextColor((HDC16)wParam, 0x00000000);
1434      return hGRAYBrush;
1435     case CTLCOLOR_STATIC:
1436      SetTextColor((HDC16)wParam, 0x00000000);
1437      return hGRAYBrush;
1438    }
1439    return FALSE;
1440
1441    */
1442   return FALSE;
1443 }
1444
1445 /***********************************************************************
1446  *           FileOpenDlgProc                                    [internal]
1447  *      Used for open and save, in fact.
1448  */
1449 static INT_PTR CALLBACK FileOpenDlgProc(HWND hWnd, UINT wMsg,
1450                                       WPARAM wParam, LPARAM lParam)
1451 {
1452     LFSPRIVATE lfs = (LFSPRIVATE)GetPropA(hWnd,OFN_PROP);
1453
1454     TRACE("msg=%x wparam=%x lParam=%lx\n", wMsg, wParam, lParam);
1455     if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
1456         {
1457             INT_PTR lRet;
1458             lRet  = (INT_PTR)FILEDLG_CallWindowProc(lfs, wMsg, wParam, lParam);
1459             if (lRet)
1460                 return lRet;         /* else continue message processing */
1461         }
1462     switch (wMsg)
1463     {
1464     case WM_INITDIALOG:
1465         return FILEDLG_WMInitDialog(hWnd, wParam, lParam);
1466
1467     case WM_MEASUREITEM:
1468         return FILEDLG_WMMeasureItem(hWnd, wParam, lParam);
1469
1470     case WM_DRAWITEM:
1471         return FILEDLG_WMDrawItem(hWnd, wParam, lParam, !lfs->open, (DRAWITEMSTRUCT *)lParam);
1472
1473     case WM_COMMAND:
1474         return FILEDLG_WMCommand(hWnd, lParam, HIWORD(wParam), LOWORD(wParam), lfs);
1475 #if 0
1476     case WM_CTLCOLOR:
1477          SetBkColor((HDC16)wParam, 0x00C0C0C0);
1478          switch (HIWORD(lParam))
1479          {
1480          case CTLCOLOR_BTN:
1481              SetTextColor((HDC16)wParam, 0x00000000);
1482              return hGRAYBrush;
1483         case CTLCOLOR_STATIC:
1484              SetTextColor((HDC16)wParam, 0x00000000);
1485              return hGRAYBrush;
1486         }
1487       break;
1488 #endif
1489     }
1490     return FALSE;
1491 }
1492
1493 /* ------------------ APIs ---------------------- */
1494
1495 /***********************************************************************
1496  *           GetOpenFileName   (COMMDLG.1)
1497  *
1498  * Creates a dialog box for the user to select a file to open.
1499  *
1500  * RETURNS
1501  *    TRUE on success: user selected a valid file
1502  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1503  *
1504  * BUGS
1505  *    unknown, there are some FIXME's left.
1506  */
1507 BOOL16 WINAPI GetOpenFileName16(
1508                                 SEGPTR ofn /* [in/out] address of structure with data*/
1509                                 )
1510 {
1511     HINSTANCE16 hInst;
1512     BOOL bRet = FALSE;
1513     LPOPENFILENAME16 lpofn = MapSL(ofn);
1514     LFSPRIVATE lfs;
1515     FARPROC16 ptr;
1516
1517     if (!lpofn || !FileDlg_Init()) return FALSE;
1518
1519     lfs = FILEDLG_AllocPrivate((LPARAM) ofn, LFS16, OPEN_DIALOG);
1520     if (lfs)
1521     {
1522         hInst = GetWindowWord( HWND_32(lpofn->hwndOwner), GWL_HINSTANCE );
1523         ptr = GetProcAddress16(GetModuleHandle16("COMMDLG"), (LPCSTR) 6);
1524         bRet = DialogBoxIndirectParam16( hInst, lfs->hDlgTmpl16, lpofn->hwndOwner,
1525                                          (DLGPROC16) ptr, (LPARAM) lfs);
1526         FILEDLG_DestroyPrivate(lfs);
1527     }
1528
1529     TRACE("return lpstrFile='%s' !\n", (char *)MapSL(lpofn->lpstrFile));
1530     return bRet;
1531 }
1532
1533 /***********************************************************************
1534  *           GetSaveFileName   (COMMDLG.2)
1535  *
1536  * Creates a dialog box for the user to select a file to save.
1537  *
1538  * RETURNS
1539  *    TRUE on success: user enters a valid file
1540  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1541  *
1542  * BUGS
1543  *    unknown. There are some FIXME's left.
1544  */
1545 BOOL16 WINAPI GetSaveFileName16(
1546                                 SEGPTR ofn /* [in/out] addess of structure with data*/
1547                                 )
1548 {
1549     HINSTANCE16 hInst;
1550     BOOL bRet = FALSE;
1551     LPOPENFILENAME16 lpofn = MapSL(ofn);
1552     LFSPRIVATE lfs;
1553     FARPROC16 ptr;
1554
1555     if (!lpofn || !FileDlg_Init()) return FALSE;
1556
1557     lfs = FILEDLG_AllocPrivate((LPARAM) ofn, LFS16, SAVE_DIALOG);
1558     if (lfs)
1559     {
1560         hInst = GetWindowWord( HWND_32(lpofn->hwndOwner), GWL_HINSTANCE );
1561         ptr = GetProcAddress16(GetModuleHandle16("COMMDLG"), (LPCSTR) 7);
1562         bRet = DialogBoxIndirectParam16( hInst, lfs->hDlgTmpl16, lpofn->hwndOwner,
1563                                          (DLGPROC16) ptr, (LPARAM) lfs);
1564         FILEDLG_DestroyPrivate(lfs);
1565     }
1566
1567     TRACE("return lpstrFile='%s' !\n", (char *)MapSL(lpofn->lpstrFile));
1568     return bRet;
1569 }
1570
1571 /***********************************************************************
1572  *            GetOpenFileNameA  (COMDLG32.@)
1573  *
1574  * Creates a dialog box for the user to select a file to open.
1575  *
1576  * RETURNS
1577  *    TRUE on success: user enters a valid file
1578  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1579  *
1580  */
1581 BOOL WINAPI GetOpenFileNameA(
1582         LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
1583 {
1584     BOOL  newlook = TRUE; /* FIXME: TWEAK_WineLook */
1585     COMDLG32_SetCommDlgExtendedError(0);
1586     /* some flags don't allow to match the TWEAK_WineLook */
1587     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
1588     {
1589       newlook = (ofn->Flags & OFN_EXPLORER) ? TRUE : FALSE;
1590     }
1591
1592     if (newlook)
1593     {
1594         return GetFileDialog95A(ofn, OPEN_DIALOG);
1595     }
1596     else
1597     {
1598         return GetFileName31A(ofn, OPEN_DIALOG);
1599     }
1600 }
1601
1602 /***********************************************************************
1603  *            GetOpenFileNameW (COMDLG32.@)
1604  *
1605  * Creates a dialog box for the user to select a file to open.
1606  *
1607  * RETURNS
1608  *    TRUE on success: user enters a valid file
1609  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1610  *
1611  */
1612 BOOL WINAPI GetOpenFileNameW(
1613         LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
1614 {
1615     BOOL  newlook = TRUE; /* FIXME: TWEAK_WineLook */
1616     COMDLG32_SetCommDlgExtendedError(0);
1617     /* some flags don't allow to match the TWEAK_WineLook */
1618     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
1619     {
1620       newlook = (ofn->Flags & OFN_EXPLORER) ? TRUE : FALSE;
1621     }
1622
1623     if (newlook)
1624     {
1625         return GetFileDialog95W(ofn, OPEN_DIALOG);
1626     }
1627     else
1628     {
1629         return GetFileName31W(ofn, OPEN_DIALOG);
1630     }
1631 }
1632
1633 /***********************************************************************
1634  *            GetSaveFileNameA  (COMDLG32.@)
1635  *
1636  * Creates a dialog box for the user to select a file to save.
1637  *
1638  * RETURNS
1639  *    TRUE on success: user enters a valid file
1640  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1641  *
1642  */
1643 BOOL WINAPI GetSaveFileNameA(
1644         LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
1645 {
1646     BOOL  newlook = TRUE; /* FIXME: TWEAK_WineLook */
1647     COMDLG32_SetCommDlgExtendedError(0);
1648     /* some flags don't allow to match the TWEAK_WineLook */
1649     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
1650     {
1651       newlook = (ofn->Flags & OFN_EXPLORER) ? TRUE : FALSE;
1652     }
1653
1654     if (newlook)
1655     {
1656         return GetFileDialog95A(ofn, SAVE_DIALOG);
1657     }
1658     else
1659     {
1660         return GetFileName31A(ofn, SAVE_DIALOG);
1661     }
1662 }
1663
1664 /***********************************************************************
1665  *            GetSaveFileNameW  (COMDLG32.@)
1666  *
1667  * Creates a dialog box for the user to select a file to save.
1668  *
1669  * RETURNS
1670  *    TRUE on success: user enters a valid file
1671  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1672  *
1673  */
1674 BOOL WINAPI GetSaveFileNameW(
1675         LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
1676 {
1677     BOOL  newlook = TRUE; /* FIXME: TWEAK_WineLook */
1678     COMDLG32_SetCommDlgExtendedError(0);
1679     /* some flags don't allow to match the TWEAK_WineLook */
1680     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
1681     {
1682       newlook = (ofn->Flags & OFN_EXPLORER) ? TRUE : FALSE;
1683     }
1684
1685     if (newlook)
1686     {
1687         return GetFileDialog95W(ofn, SAVE_DIALOG);
1688     }
1689     else
1690     {
1691         return GetFileName31W(ofn, SAVE_DIALOG);
1692     }
1693 }