Move the custom draw notifications into the Draw.*Item functions.
[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 LRESULT WINAPI FileOpenDlgProc16(HWND16 hWnd, UINT16 wMsg, WPARAM16 wParam,
93                                  LPARAM lParam);
94 LRESULT WINAPI FileSaveDlgProc16(HWND16 hWnd, UINT16 wMsg, WPARAM16 wParam,
95                                  LPARAM lParam);
96
97 static LRESULT WINAPI 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, ofnW->nMaxFile, NULL, NULL ))
673             tmp[ofnW->nMaxFile-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 (!WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFile, -1,
689                                   lfs->ofnA->lpstrFile, ofnW->nMaxFile, NULL, NULL ))
690             lfs->ofnA->lpstrFile[ofnW->nMaxFile-1] = 0;
691         lfs->ofnA->nFileOffset = ofnW->nFileOffset;
692         lfs->ofnA->nFileExtension = ofnW->nFileExtension;
693     }
694 }
695
696
697 /***********************************************************************
698  *                              FILEDLG_UpdateFileTitle         [internal]
699  *      update the displayed file name (without path)
700  */
701 void FILEDLG_UpdateFileTitle(LFSPRIVATE lfs)
702 {
703   LONG lRet;
704   LPOPENFILENAMEW ofnW = lfs->ofnW;
705   if (ofnW->lpstrFileTitle != NULL)
706   {
707     lRet = SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETCURSEL, 0, 0);
708     SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETTEXT, lRet,
709                              (LPARAM)ofnW->lpstrFileTitle );
710     if (lfs->ofn16)
711     {
712         char *dest = MapSL(lfs->ofn16->lpstrFileTitle);
713         if (!WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFileTitle, -1,
714                                   dest, ofnW->nMaxFileTitle, NULL, NULL ))
715             dest[ofnW->nMaxFileTitle-1] = 0;
716     }
717     if (lfs->ofnA)
718     {
719         if (!WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFileTitle, -1,
720                                   lfs->ofnA->lpstrFileTitle, ofnW->nMaxFileTitle, NULL, NULL ))
721             lfs->ofnA->lpstrFileTitle[ofnW->nMaxFileTitle-1] = 0;
722     }
723   }
724 }
725
726
727
728 /***********************************************************************
729  *                              FILEDLG_DirListDblClick         [internal]
730  */
731 static LRESULT FILEDLG_DirListDblClick( LFSPRIVATE lfs )
732 {
733   LONG lRet;
734   HWND hWnd = lfs->hwnd;
735   LPWSTR pstr;
736   WCHAR tmpstr[BUFFILE];
737
738   /* get the raw string (with brackets) */
739   lRet = SendDlgItemMessageW(hWnd, lst2, LB_GETCURSEL, 0, 0);
740   if (lRet == LB_ERR) return TRUE;
741   pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
742   SendDlgItemMessageW(hWnd, lst2, LB_GETTEXT, lRet,
743                      (LPARAM)pstr);
744   strcpyW( tmpstr, pstr );
745   HeapFree(GetProcessHeap(), 0, pstr);
746   /* get the selected directory in tmpstr */
747   if (tmpstr[0] == '[')
748     {
749       tmpstr[lstrlenW(tmpstr) - 1] = 0;
750       strcpyW(tmpstr,tmpstr+1);
751     }
752   strcatW(tmpstr, FILE_bslash);
753
754   FILEDLG_ScanDir(hWnd, tmpstr);
755   /* notify the app */
756   if (lfs->hook)
757     {
758       if (FILEDLG_CallWindowProc(lfs, lfs->lbselchstring, lst2,
759               MAKELONG(lRet,CD_LBSELCHANGE)))
760         return TRUE;
761     }
762   return TRUE;
763 }
764
765
766 /***********************************************************************
767  *                              FILEDLG_FileListSelect         [internal]
768  *    called when a new item is picked in the file list
769  */
770 static LRESULT FILEDLG_FileListSelect( LFSPRIVATE lfs )
771 {
772     LONG lRet;
773     HWND hWnd = lfs->hwnd;
774     LPWSTR pstr;
775
776     lRet = SendDlgItemMessageW(hWnd, lst1, LB_GETCURSEL16, 0, 0);
777     if (lRet == LB_ERR)
778         return TRUE;
779
780     /* set the edit control to the choosen file */
781     if ((pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
782     {
783         SendDlgItemMessageW(hWnd, lst1, LB_GETTEXT, lRet,
784                        (LPARAM)pstr);
785         SetDlgItemTextW( hWnd, edt1, pstr );
786         HeapFree(GetProcessHeap(), 0, pstr);
787     }
788     if (lfs->hook)
789     {
790         FILEDLG_CallWindowProc(lfs, lfs->lbselchstring, lst1,
791                            MAKELONG(lRet,CD_LBSELCHANGE));
792     }
793     /* FIXME: for OFN_ALLOWMULTISELECT we need CD_LBSELSUB, CD_SELADD,
794            CD_LBSELNOITEMS */
795     return TRUE;
796 }
797
798 /***********************************************************************
799  *                              FILEDLG_TestPath      [internal]
800  *      before accepting the file name, test if it includes wild cards
801  *      tries to scan the directory and returns TRUE if no error.
802  */
803 static LRESULT FILEDLG_TestPath( LFSPRIVATE lfs, LPWSTR path )
804 {
805     HWND hWnd = lfs->hwnd;
806     LPWSTR pBeginFileName, pstr2;
807     WCHAR tmpstr2[BUFFILE];
808
809     pBeginFileName = strrchrW(path, '\\');
810     if (pBeginFileName == NULL)
811         pBeginFileName = strrchrW(path, ':');
812
813     if (strchrW(path,'*') != NULL || strchrW(path,'?') != NULL)
814     {
815         /* edit control contains wildcards */
816         if (pBeginFileName != NULL)
817         {
818             lstrcpynW(tmpstr2, pBeginFileName + 1, BUFFILE);
819             *(pBeginFileName + 1) = 0;
820         }
821         else
822         {
823             strcpyW(tmpstr2, path);
824             *path = 0;
825         }
826
827         TRACE("path=%s, tmpstr2=%s\n", debugstr_w(path), debugstr_w(tmpstr2));
828         SetDlgItemTextW( hWnd, edt1, tmpstr2 );
829         FILEDLG_ScanDir(hWnd, path);
830         return FALSE;
831     }
832
833     /* no wildcards, we might have a directory or a filename */
834     /* try appending a wildcard and reading the directory */
835
836     pstr2 = path + lstrlenW(path);
837     if (pBeginFileName == NULL || *(pBeginFileName + 1) != 0)
838         strcatW(path, FILE_bslash);
839
840     /* if ScanDir succeeds, we have changed the directory */
841     if (FILEDLG_ScanDir(hWnd, path))
842         return TRUE;
843
844     /* if not, this must be a filename */
845
846     *pstr2 = 0; /* remove the wildcard added before */
847
848     if (pBeginFileName != NULL)
849     {
850         /* strip off the pathname */
851         *pBeginFileName = 0;
852         SetDlgItemTextW( hWnd, edt1, pBeginFileName + 1 );
853
854         lstrcpynW(tmpstr2, pBeginFileName + 1, sizeof(tmpstr2)/sizeof(WCHAR) );
855         /* Should we MessageBox() if this fails? */
856         if (!FILEDLG_ScanDir(hWnd, path))
857         {
858             return FALSE;
859         }
860         strcpyW(path, tmpstr2);
861     }
862     else
863         SetDlgItemTextW( hWnd, edt1, path );
864     return TRUE;
865 }
866
867 /***********************************************************************
868  *                              FILEDLG_Validate               [internal]
869  *   called on: click Ok button, Enter in edit, DoubleClick in file list
870  */
871 static LRESULT FILEDLG_Validate( LFSPRIVATE lfs, LPWSTR path, UINT control, INT itemIndex,
872                                  BOOL internalUse )
873 {
874     LONG lRet;
875     HWND hWnd = lfs->hwnd;
876     OPENFILENAMEW ofnsav;
877     LPOPENFILENAMEW ofnW = lfs->ofnW;
878     WCHAR filename[BUFFILE];
879
880     ofnsav = *ofnW; /* for later restoring */
881
882     /* get current file name */
883     if (path)
884         lstrcpynW(filename, path, sizeof(filename)/sizeof(WCHAR));
885     else
886         GetDlgItemTextW( hWnd, edt1, filename, sizeof(filename)/sizeof(WCHAR));
887
888     /* if we did not click in file list to get there */
889     if (control != lst1)
890     {
891         if (!FILEDLG_TestPath( lfs, filename) )
892            return FALSE;
893     }
894     FILEDLG_UpdateResult(lfs, filename);
895
896     if (internalUse)
897     { /* called internally after a change in a combo */
898         if (lfs->hook)
899         {
900              FILEDLG_CallWindowProc(lfs, lfs->lbselchstring, control,
901                              MAKELONG(itemIndex,CD_LBSELCHANGE));
902         }
903         return TRUE;
904     }
905
906     FILEDLG_UpdateFileTitle(lfs);
907     if (lfs->hook)
908     {
909         lRet = (BOOL)FILEDLG_CallWindowProc(lfs, lfs->fileokstring,
910                   0, lfs->lParam );
911         if (lRet)
912         {
913             *ofnW = ofnsav; /* restore old state */
914             return FALSE;
915         }
916     }
917     if ((ofnW->Flags & OFN_ALLOWMULTISELECT) && (ofnW->Flags & OFN_EXPLORER))
918     {
919         if (ofnW->lpstrFile)
920         {
921             LPWSTR str = (LPWSTR)ofnW->lpstrFile;
922             LPWSTR ptr = strrchrW(str, '\\');
923             str[lstrlenW(str) + 1] = '\0';
924             *ptr = 0;
925         }
926     }
927     return TRUE;
928 }
929
930 /***********************************************************************
931  *                              FILEDLG_DiskChange             [internal]
932  *    called when a new item is picked in the disk selection combo
933  */
934 static LRESULT FILEDLG_DiskChange( LFSPRIVATE lfs )
935 {
936     LONG lRet;
937     HWND hWnd = lfs->hwnd;
938     LPWSTR pstr;
939     WCHAR diskname[BUFFILE];
940
941     FILEDLG_StripEditControl(hWnd);
942     lRet = SendDlgItemMessageW(hWnd, cmb2, CB_GETCURSEL, 0, 0L);
943     if (lRet == LB_ERR)
944         return 0;
945     pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
946     SendDlgItemMessageW(hWnd, cmb2, CB_GETLBTEXT, lRet,
947                          (LPARAM)pstr);
948     wsprintfW(diskname, FILE_specc, pstr[2]);
949     HeapFree(GetProcessHeap(), 0, pstr);
950
951     return FILEDLG_Validate( lfs, diskname, cmb2, lRet, TRUE );
952 }
953
954
955 /***********************************************************************
956  *                              FILEDLG_FileTypeChange         [internal]
957  *    called when a new item is picked in the file type combo
958  */
959 static LRESULT FILEDLG_FileTypeChange( LFSPRIVATE lfs )
960 {
961     LONG lRet;
962     WCHAR diskname[BUFFILE];
963     LPWSTR pstr;
964
965     diskname[0] = 0;
966
967     lRet = SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETCURSEL, 0, 0);
968     if (lRet == LB_ERR)
969         return TRUE;
970     pstr = (LPWSTR)SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETITEMDATA, lRet, 0);
971     TRACE("Selected filter : %s\n", debugstr_w(pstr));
972     SetDlgItemTextW( lfs->hwnd, edt1, pstr );
973
974     return FILEDLG_Validate( lfs, NULL, cmb1, lRet, TRUE );
975 }
976
977 /***********************************************************************
978  *                              FILEDLG_WMCommand               [internal]
979  */
980 static LRESULT FILEDLG_WMCommand(HWND hWnd, LPARAM lParam, UINT notification,
981        UINT control, LFSPRIVATE lfs )
982 {
983     switch (control)
984     {
985         case lst1: /* file list */
986         FILEDLG_StripEditControl(hWnd);
987         if (notification == LBN_DBLCLK)
988         {
989             if (FILEDLG_Validate( lfs, NULL, control, 0, FALSE ))
990                 EndDialog(hWnd, TRUE);
991             return TRUE;
992         }
993         else if (notification == LBN_SELCHANGE)
994             return FILEDLG_FileListSelect( lfs );
995         break;
996
997         case lst2: /* directory list */
998         FILEDLG_StripEditControl(hWnd);
999         if (notification == LBN_DBLCLK)
1000             return FILEDLG_DirListDblClick( lfs );
1001         break;
1002
1003         case cmb1: /* file type drop list */
1004         if (notification == CBN_SELCHANGE)
1005             return FILEDLG_FileTypeChange( lfs );
1006         break;
1007
1008         case chx1:
1009         break;
1010
1011         case pshHelp:
1012         break;
1013
1014         case cmb2: /* disk dropdown combo */
1015         if (notification == CBN_SELCHANGE)
1016             return FILEDLG_DiskChange( lfs );
1017         break;
1018
1019         case IDOK:
1020         if (FILEDLG_Validate( lfs, NULL, control, 0, FALSE ))
1021             EndDialog(hWnd, TRUE);
1022         return TRUE;
1023
1024         case IDCANCEL:
1025         EndDialog(hWnd, FALSE);
1026         return TRUE;
1027
1028         case IDABORT: /* can be sent by the hook procedure */
1029         EndDialog(hWnd, TRUE);
1030         return TRUE;
1031     }
1032     return FALSE;
1033 }
1034
1035 /***********************************************************************
1036  *                              FILEDLG_MapDrawItemStruct       [internal]
1037  *      map a 16 bits drawitem struct to 32
1038  */
1039 void FILEDLG_MapDrawItemStruct(LPDRAWITEMSTRUCT16 lpdis16, LPDRAWITEMSTRUCT lpdis)
1040 {
1041     lpdis->CtlType = lpdis16->CtlType;
1042     lpdis->CtlID = lpdis16->CtlID;
1043     lpdis->itemID = lpdis16->itemID;
1044     lpdis->itemAction = lpdis16->itemAction;
1045     lpdis->itemState = lpdis16->itemState;
1046     lpdis->hwndItem = HWND_32(lpdis16->hwndItem);
1047     lpdis->hDC = lpdis16->hDC;
1048     lpdis->rcItem.right = lpdis16->rcItem.right;
1049     lpdis->rcItem.left = lpdis16->rcItem.left;
1050     lpdis->rcItem.top = lpdis16->rcItem.top;
1051     lpdis->rcItem.bottom = lpdis16->rcItem.bottom;
1052     lpdis->itemData = lpdis16->itemData;
1053 }
1054
1055 /************************************************************************
1056  *                              FILEDLG_MapStringPairsToW       [internal]
1057  *      map string pairs to Unicode
1058  */
1059 static LPWSTR FILEDLG_MapStringPairsToW(LPCSTR strA, UINT size)
1060 {
1061     LPCSTR s;
1062     LPWSTR x;
1063     int n, len;
1064
1065     s = strA;
1066     while (*s)
1067         s = s+strlen(s)+1;
1068     s++;
1069     n = s + 1 - strA; /* Don't forget the other \0 */
1070     if (n < size) n = size;
1071
1072     len = MultiByteToWideChar( CP_ACP, 0, strA, n, NULL, 0 );
1073     x = HeapAlloc(GetProcessHeap(),0, len * sizeof(WCHAR));
1074     MultiByteToWideChar( CP_ACP, 0, strA, n, x, len );
1075     return x;
1076 }
1077
1078
1079 /************************************************************************
1080  *                              FILEDLG_DupToW                  [internal]
1081  *      duplicates an Ansi string to unicode, with a buffer size
1082  */
1083 LPWSTR FILEDLG_DupToW(LPCSTR str, DWORD size)
1084 {
1085     LPWSTR strW = NULL;
1086     if (str && (size > 0))
1087     {
1088         strW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1089         if (strW) MultiByteToWideChar( CP_ACP, 0, str, -1, strW, size );
1090     }
1091     return strW;
1092 }
1093
1094
1095 /************************************************************************
1096  *                              FILEDLG_MapOfnStructA          [internal]
1097  *      map a 32 bits Ansi structure to an Unicode one
1098  */
1099 void FILEDLG_MapOfnStructA(LPOPENFILENAMEA ofnA, LPOPENFILENAMEW ofnW, BOOL open)
1100 {
1101     LPCSTR str;
1102
1103     ofnW->lStructSize = sizeof(OPENFILENAMEW);
1104     ofnW->hwndOwner = ofnA->hwndOwner;
1105     ofnW->hInstance = ofnA->hInstance;
1106     if (ofnA->lpstrFilter)
1107         ofnW->lpstrFilter = FILEDLG_MapStringPairsToW(ofnA->lpstrFilter, 0);
1108
1109     if ((ofnA->lpstrCustomFilter) && (*(ofnA->lpstrCustomFilter)))
1110         ofnW->lpstrCustomFilter = FILEDLG_MapStringPairsToW(ofnA->lpstrCustomFilter, ofnA->nMaxCustFilter);
1111     ofnW->nMaxCustFilter = ofnA->nMaxCustFilter;
1112     ofnW->nFilterIndex = ofnA->nFilterIndex;
1113     ofnW->nMaxFile = ofnA->nMaxFile;
1114     ofnW->lpstrFile = FILEDLG_DupToW(ofnA->lpstrFile, ofnW->nMaxFile);
1115     ofnW->nMaxFileTitle = ofnA->nMaxFileTitle;
1116     ofnW->lpstrFileTitle = FILEDLG_DupToW(ofnA->lpstrFileTitle, ofnW->nMaxFileTitle);
1117     if (ofnA->lpstrInitialDir)
1118         ofnW->lpstrInitialDir = HEAP_strdupAtoW(GetProcessHeap(),0,ofnA->lpstrInitialDir);
1119     if (ofnA->lpstrTitle)
1120         str = ofnA->lpstrTitle;
1121     else
1122         /* Allocates default title (FIXME : get it from resource) */
1123         str = open ? defaultopen:defaultsave;
1124     ofnW->lpstrTitle = HEAP_strdupAtoW(GetProcessHeap(),0, str);
1125     ofnW->Flags = ofnA->Flags;
1126     ofnW->nFileOffset = ofnA->nFileOffset;
1127     ofnW->nFileExtension = ofnA->nFileExtension;
1128     ofnW->lpstrDefExt = FILEDLG_DupToW(ofnA->lpstrDefExt, 3);
1129     if ((ofnA->Flags & OFN_ENABLETEMPLATE) && (ofnA->lpTemplateName))
1130     {
1131         if (HIWORD(ofnA->lpTemplateName))
1132             ofnW->lpTemplateName = HEAP_strdupAtoW(GetProcessHeap(), 0, ofnA->lpTemplateName);
1133         else /* numbered resource */
1134             ofnW->lpTemplateName = (LPWSTR) ofnA->lpTemplateName;
1135     }
1136 }
1137
1138
1139 /************************************************************************
1140  *                              FILEDLG_MapOfnStruct16          [internal]
1141  *      map a 16 bits structure to an Unicode one
1142  */
1143 void FILEDLG_MapOfnStruct16(LPOPENFILENAME16 ofn16, LPOPENFILENAMEW ofnW, BOOL open)
1144 {
1145     OPENFILENAMEA ofnA;
1146     /* first convert to linear pointers */
1147     memset(&ofnA, 0, sizeof(OPENFILENAMEA));
1148     ofnA.lStructSize = sizeof(OPENFILENAMEA);
1149     ofnA.hwndOwner = HWND_32(ofn16->hwndOwner);
1150     ofnA.hInstance = ofn16->hInstance;
1151     if (ofn16->lpstrFilter)
1152         ofnA.lpstrFilter = MapSL(ofn16->lpstrFilter);
1153     if (ofn16->lpstrCustomFilter)
1154         ofnA.lpstrCustomFilter = MapSL(ofn16->lpstrCustomFilter);
1155     ofnA.nMaxCustFilter = ofn16->nMaxCustFilter;
1156     ofnA.nFilterIndex = ofn16->nFilterIndex;
1157     ofnA.lpstrFile = MapSL(ofn16->lpstrFile);
1158     ofnA.nMaxFile = ofn16->nMaxFile;
1159     ofnA.lpstrFileTitle = MapSL(ofn16->lpstrFileTitle);
1160     ofnA.nMaxFileTitle = ofn16->nMaxFileTitle;
1161     ofnA.lpstrInitialDir = MapSL(ofn16->lpstrInitialDir);
1162     ofnA.lpstrTitle = MapSL(ofn16->lpstrTitle);
1163     ofnA.Flags = ofn16->Flags;
1164     ofnA.nFileOffset = ofn16->nFileOffset;
1165     ofnA.nFileExtension = ofn16->nFileExtension;
1166     ofnA.lpstrDefExt = MapSL(ofn16->lpstrDefExt);
1167     if (HIWORD(ofn16->lpTemplateName))
1168         ofnA.lpTemplateName = MapSL(ofn16->lpTemplateName);
1169     else
1170         ofnA.lpTemplateName = (LPSTR) ofn16->lpTemplateName; /* ressource number */
1171     /* now calls the 32 bits Ansi to Unicode version to complete the job */
1172     FILEDLG_MapOfnStructA(&ofnA, ofnW, open);
1173 }
1174
1175
1176 /************************************************************************
1177  *                              FILEDLG_DestroyPrivate            [internal]
1178  *      destroys the private object
1179  */
1180 void FILEDLG_DestroyPrivate(LFSPRIVATE lfs)
1181 {
1182     HWND hwnd;
1183     if (!lfs) return;
1184     hwnd = lfs->hwnd;
1185     /* free resources for a 16 bits dialog */
1186     if (lfs->hResource16) FreeResource16(lfs->hResource16);
1187     if (lfs->hGlobal16)
1188     {
1189         GlobalUnlock16(lfs->hGlobal16);
1190         GlobalFree16(lfs->hGlobal16);
1191     }
1192     /* if ofnW has been allocated, have to free everything in it */
1193     if (lfs->ofn16 || lfs->ofnA)
1194     {
1195        LPOPENFILENAMEW ofnW = lfs->ofnW;
1196        if (ofnW->lpstrFilter) HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrFilter);
1197        if (ofnW->lpstrCustomFilter) HeapFree(GetProcessHeap(), 0, ofnW->lpstrCustomFilter);
1198        if (ofnW->lpstrFile) HeapFree(GetProcessHeap(), 0, ofnW->lpstrFile);
1199        if (ofnW->lpstrFileTitle) HeapFree(GetProcessHeap(), 0, ofnW->lpstrFileTitle);
1200        if (ofnW->lpstrInitialDir) HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrInitialDir);
1201        if (ofnW->lpstrTitle) HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrTitle);
1202        if ((ofnW->lpTemplateName) && (HIWORD(ofnW->lpTemplateName)))
1203            HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpTemplateName);
1204        HeapFree(GetProcessHeap(), 0, ofnW);
1205     }
1206     TRACE("destroying private allocation %p\n", lfs);
1207     HeapFree(GetProcessHeap(), 0, lfs);
1208     RemovePropA(hwnd, OFN_PROP);
1209 }
1210
1211 /************************************************************************
1212  *                              FILEDLG_AllocPrivate            [internal]
1213  *      allocate a private object to hold 32 bits Unicode
1214  *      structure that will be used throughtout the calls, while
1215  *      keeping available the original structures and a few variables
1216  *      On entry : type = dialog procedure type (16,32A,32W)
1217  *                 dlgType = dialog type (open or save)
1218  */
1219 LFSPRIVATE FILEDLG_AllocPrivate(LPARAM lParam, int type, UINT dlgType)
1220 {
1221     LFSPRIVATE lfs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct FSPRIVATE));
1222     LFSPRIVATE ret;
1223     TRACE("alloc private buf %p\n", lfs);
1224     if (!lfs) return NULL;
1225     lfs->hook = FALSE;
1226     lfs->lParam = lParam;
1227     if (dlgType == OPEN_DIALOG)
1228         lfs->open = TRUE;
1229     else
1230         lfs->open = FALSE;
1231     lfs->lbselchstring = RegisterWindowMessageA(LBSELCHSTRINGA);
1232     lfs->fileokstring = RegisterWindowMessageA(FILEOKSTRINGA);
1233     switch(type)
1234     {
1235         case LFS16:
1236         lfs->ofn16 = MapSL(lParam);
1237         if (lfs->ofn16->Flags & OFN_ENABLEHOOK)
1238             if (lfs->ofn16->lpfnHook)
1239                 lfs->hook = TRUE;
1240
1241         break;
1242
1243         case LFS32A:
1244         lfs->ofnA = (LPOPENFILENAMEA) lParam;
1245         if (lfs->ofnA->Flags & OFN_ENABLEHOOK)
1246             if (lfs->ofnA->lpfnHook)
1247                 lfs->hook = TRUE;
1248         break;
1249
1250         case LFS32W:
1251         lfs->ofnW = (LPOPENFILENAMEW) lParam;
1252         if (lfs->ofnW->Flags & OFN_ENABLEHOOK)
1253             if (lfs->ofnW->lpfnHook)
1254                 lfs->hook = TRUE;
1255         break;
1256     }
1257     ret = lfs;
1258     if (!lfs->ofnW)
1259     { /* this structure is needed internally, so create it */
1260         lfs->ofnW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(OPENFILENAMEW));
1261         if (lfs->ofnW)
1262         {
1263             if (lfs->ofn16)
1264                 FILEDLG_MapOfnStruct16(lfs->ofn16, lfs->ofnW, lfs->open);
1265             if (lfs->ofnA)
1266                 FILEDLG_MapOfnStructA(lfs->ofnA, lfs->ofnW, lfs->open);
1267         }
1268         else
1269             ret = NULL;
1270     }
1271     if (lfs->ofn16)
1272     {
1273         if (!Get16BitsTemplate(lfs)) ret = NULL;
1274     }
1275     else
1276         if (!Get32BitsTemplate(lfs)) ret = NULL;
1277     if (!ret) FILEDLG_DestroyPrivate(lfs);
1278     return ret;
1279 }
1280
1281
1282 /***********************************************************************
1283  *           GetFileName31A                                 [internal]
1284  *
1285  * Creates a win31 style dialog box for the user to select a file to open/save.
1286  */
1287 BOOL WINAPI GetFileName31A(
1288                            LPOPENFILENAMEA lpofn, /* addess of structure with data*/
1289                            UINT dlgType /* type dialogue : open/save */
1290                           )
1291 {
1292     HINSTANCE hInst;
1293     BOOL bRet = FALSE;
1294     LFSPRIVATE lfs;
1295
1296     if (!lpofn || !FileDlg_Init()) return FALSE;
1297
1298     lfs = FILEDLG_AllocPrivate((LPARAM) lpofn, LFS32A, dlgType);
1299     if (lfs)
1300     {
1301         hInst = GetWindowLongA( lpofn->hwndOwner, GWL_HINSTANCE );
1302         bRet = DialogBoxIndirectParamA( hInst, lfs->template, lpofn->hwndOwner,
1303              (DLGPROC) FileOpenDlgProc, (DWORD) lfs);
1304         FILEDLG_DestroyPrivate(lfs);
1305     }
1306
1307     TRACE("return lpstrFile='%s' !\n", lpofn->lpstrFile);
1308     return bRet;
1309 }
1310
1311
1312 /***********************************************************************
1313  *           GetFileName31W                                 [internal]
1314  *
1315  * Creates a win31 style dialog box for the user to select a file to open/save
1316  */
1317 BOOL WINAPI GetFileName31W(
1318                            LPOPENFILENAMEW lpofn, /* addess of structure with data*/
1319                            UINT dlgType /* type dialogue : open/save */
1320                           )
1321 {
1322     HINSTANCE hInst;
1323     BOOL bRet = FALSE;
1324     LFSPRIVATE lfs;
1325
1326     if (!lpofn || !FileDlg_Init()) return FALSE;
1327
1328     lfs = FILEDLG_AllocPrivate((LPARAM) lpofn, LFS32W, dlgType);
1329     if (lfs)
1330     {
1331         hInst = GetWindowLongA( lpofn->hwndOwner, GWL_HINSTANCE );
1332         bRet = DialogBoxIndirectParamW( hInst, lfs->template, lpofn->hwndOwner,
1333              (DLGPROC) FileOpenDlgProc, (DWORD) lfs);
1334         FILEDLG_DestroyPrivate(lfs);
1335     }
1336
1337     TRACE("return lpstrFile=%s !\n", debugstr_w(lpofn->lpstrFile));
1338     return bRet;
1339 }
1340
1341
1342 /* ------------------ Dialog procedures ---------------------- */
1343
1344 /***********************************************************************
1345  *           FileOpenDlgProc   (COMMDLG.6)
1346  */
1347 LRESULT WINAPI FileOpenDlgProc16(HWND16 hWnd16, UINT16 wMsg, WPARAM16 wParam,
1348                                LPARAM lParam)
1349 {
1350     HWND hWnd = HWND_32(hWnd16);
1351     LFSPRIVATE lfs = (LFSPRIVATE)GetPropA(hWnd,OFN_PROP);
1352     DRAWITEMSTRUCT dis;
1353
1354     TRACE("msg=%x wparam=%x lParam=%lx\n", wMsg, wParam, lParam);
1355     if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
1356         {
1357             LRESULT lRet = (BOOL16)FILEDLG_CallWindowProc(lfs, wMsg, wParam, lParam);
1358             if (lRet)
1359                 return lRet;         /* else continue message processing */
1360         }
1361     switch (wMsg)
1362     {
1363     case WM_INITDIALOG:
1364         return FILEDLG_WMInitDialog(hWnd, wParam, lParam);
1365
1366     case WM_MEASUREITEM:
1367         return FILEDLG_WMMeasureItem16(hWnd16, wParam, lParam);
1368
1369     case WM_DRAWITEM:
1370         FILEDLG_MapDrawItemStruct(MapSL(lParam), &dis);
1371         return FILEDLG_WMDrawItem(hWnd, wParam, lParam, FALSE, &dis);
1372
1373     case WM_COMMAND:
1374         return FILEDLG_WMCommand(hWnd, lParam, HIWORD(lParam),wParam, lfs);
1375 #if 0
1376     case WM_CTLCOLOR:
1377          SetBkColor((HDC16)wParam, 0x00C0C0C0);
1378          switch (HIWORD(lParam))
1379          {
1380          case CTLCOLOR_BTN:
1381              SetTextColor((HDC16)wParam, 0x00000000);
1382              return hGRAYBrush;
1383         case CTLCOLOR_STATIC:
1384              SetTextColor((HDC16)wParam, 0x00000000);
1385              return hGRAYBrush;
1386         }
1387       break;
1388 #endif
1389     }
1390     return FALSE;
1391 }
1392
1393 /***********************************************************************
1394  *           FileSaveDlgProc   (COMMDLG.7)
1395  */
1396 LRESULT WINAPI FileSaveDlgProc16(HWND16 hWnd16, UINT16 wMsg, WPARAM16 wParam,
1397                                LPARAM lParam)
1398 {
1399  HWND hWnd = HWND_32(hWnd16);
1400  LFSPRIVATE lfs = (LFSPRIVATE)GetPropA(hWnd,OFN_PROP);
1401  DRAWITEMSTRUCT dis;
1402
1403  TRACE("msg=%x wparam=%x lParam=%lx\n", wMsg, wParam, lParam);
1404  if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
1405   {
1406    LRESULT  lRet;
1407    lRet = (BOOL16)FILEDLG_CallWindowProc(lfs, wMsg, wParam, lParam);
1408    if (lRet)
1409     return lRet;         /* else continue message processing */
1410   }
1411   switch (wMsg) {
1412    case WM_INITDIALOG:
1413       return FILEDLG_WMInitDialog(hWnd, wParam, lParam);
1414
1415    case WM_MEASUREITEM:
1416       return FILEDLG_WMMeasureItem16(hWnd16, wParam, lParam);
1417
1418    case WM_DRAWITEM:
1419       FILEDLG_MapDrawItemStruct(MapSL(lParam), &dis);
1420       return FILEDLG_WMDrawItem(hWnd, wParam, lParam, TRUE, &dis);
1421
1422    case WM_COMMAND:
1423       return FILEDLG_WMCommand(hWnd, lParam, HIWORD(lParam), wParam, lfs);
1424   }
1425
1426   /*
1427   case WM_CTLCOLOR:
1428    SetBkColor((HDC16)wParam, 0x00C0C0C0);
1429    switch (HIWORD(lParam))
1430    {
1431     case CTLCOLOR_BTN:
1432      SetTextColor((HDC16)wParam, 0x00000000);
1433      return hGRAYBrush;
1434     case CTLCOLOR_STATIC:
1435      SetTextColor((HDC16)wParam, 0x00000000);
1436      return hGRAYBrush;
1437    }
1438    return FALSE;
1439
1440    */
1441   return FALSE;
1442 }
1443
1444 /***********************************************************************
1445  *           FileOpenDlgProc                                    [internal]
1446  *      Used for open and save, in fact.
1447  */
1448 static LRESULT WINAPI FileOpenDlgProc(HWND hWnd, UINT wMsg,
1449                                       WPARAM wParam, LPARAM lParam)
1450 {
1451     LFSPRIVATE lfs = (LFSPRIVATE)GetPropA(hWnd,OFN_PROP);
1452
1453     TRACE("msg=%x wparam=%x lParam=%lx\n", wMsg, wParam, lParam);
1454     if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
1455         {
1456             LRESULT lRet;
1457             lRet  = (BOOL)FILEDLG_CallWindowProc(lfs, wMsg, wParam, lParam);
1458             if (lRet)
1459                 return lRet;         /* else continue message processing */
1460         }
1461     switch (wMsg)
1462     {
1463     case WM_INITDIALOG:
1464         return FILEDLG_WMInitDialog(hWnd, wParam, lParam);
1465
1466     case WM_MEASUREITEM:
1467         return FILEDLG_WMMeasureItem(hWnd, wParam, lParam);
1468
1469     case WM_DRAWITEM:
1470         return FILEDLG_WMDrawItem(hWnd, wParam, lParam, !lfs->open, (DRAWITEMSTRUCT *)lParam);
1471
1472     case WM_COMMAND:
1473         return FILEDLG_WMCommand(hWnd, lParam, HIWORD(wParam), LOWORD(wParam), lfs);
1474 #if 0
1475     case WM_CTLCOLOR:
1476          SetBkColor((HDC16)wParam, 0x00C0C0C0);
1477          switch (HIWORD(lParam))
1478          {
1479          case CTLCOLOR_BTN:
1480              SetTextColor((HDC16)wParam, 0x00000000);
1481              return hGRAYBrush;
1482         case CTLCOLOR_STATIC:
1483              SetTextColor((HDC16)wParam, 0x00000000);
1484              return hGRAYBrush;
1485         }
1486       break;
1487 #endif
1488     }
1489     return FALSE;
1490 }
1491
1492 /* ------------------ APIs ---------------------- */
1493
1494 /***********************************************************************
1495  *           GetOpenFileName   (COMMDLG.1)
1496  *
1497  * Creates a dialog box for the user to select a file to open.
1498  *
1499  * RETURNS
1500  *    TRUE on success: user selected a valid file
1501  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1502  *
1503  * BUGS
1504  *    unknown, there are some FIXME's left.
1505  */
1506 BOOL16 WINAPI GetOpenFileName16(
1507                                 SEGPTR ofn /* [in/out] address of structure with data*/
1508                                 )
1509 {
1510     HINSTANCE hInst;
1511     BOOL bRet = FALSE;
1512     LPOPENFILENAME16 lpofn = MapSL(ofn);
1513     LFSPRIVATE lfs;
1514     FARPROC16 ptr;
1515
1516     if (!lpofn || !FileDlg_Init()) return FALSE;
1517
1518     lfs = FILEDLG_AllocPrivate((LPARAM) ofn, LFS16, OPEN_DIALOG);
1519     if (lfs)
1520     {
1521         hInst = GetWindowLongA( HWND_32(lpofn->hwndOwner), GWL_HINSTANCE );
1522         ptr = GetProcAddress16(GetModuleHandle16("COMMDLG"), (LPCSTR) 6);
1523         bRet = DialogBoxIndirectParam16( hInst, lfs->hDlgTmpl16, lpofn->hwndOwner,
1524              (DLGPROC16) ptr, (DWORD) lfs);
1525         FILEDLG_DestroyPrivate(lfs);
1526     }
1527
1528     TRACE("return lpstrFile='%s' !\n", (char *)MapSL(lpofn->lpstrFile));
1529     return bRet;
1530 }
1531
1532 /***********************************************************************
1533  *           GetSaveFileName   (COMMDLG.2)
1534  *
1535  * Creates a dialog box for the user to select a file to save.
1536  *
1537  * RETURNS
1538  *    TRUE on success: user enters a valid file
1539  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1540  *
1541  * BUGS
1542  *    unknown. There are some FIXME's left.
1543  */
1544 BOOL16 WINAPI GetSaveFileName16(
1545                                 SEGPTR ofn /* [in/out] addess of structure with data*/
1546                                 )
1547 {
1548     HINSTANCE hInst;
1549     BOOL bRet = FALSE;
1550     LPOPENFILENAME16 lpofn = MapSL(ofn);
1551     LFSPRIVATE lfs;
1552     FARPROC16 ptr;
1553
1554     if (!lpofn || !FileDlg_Init()) return FALSE;
1555
1556     lfs = FILEDLG_AllocPrivate((LPARAM) ofn, LFS16, SAVE_DIALOG);
1557     if (lfs)
1558     {
1559         hInst = GetWindowLongA( HWND_32(lpofn->hwndOwner), GWL_HINSTANCE );
1560         ptr = GetProcAddress16(GetModuleHandle16("COMMDLG"), (LPCSTR) 7);
1561         bRet = DialogBoxIndirectParam16( hInst, lfs->hDlgTmpl16, lpofn->hwndOwner,
1562              (DLGPROC16) ptr, (DWORD) lfs);
1563         FILEDLG_DestroyPrivate(lfs);
1564     }
1565
1566     TRACE("return lpstrFile='%s' !\n", (char *)MapSL(lpofn->lpstrFile));
1567     return bRet;
1568 }
1569
1570 /***********************************************************************
1571  *            GetOpenFileNameA  (COMDLG32.@)
1572  *
1573  * Creates a dialog box for the user to select a file to open.
1574  *
1575  * RETURNS
1576  *    TRUE on success: user enters a valid file
1577  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1578  *
1579  */
1580 BOOL WINAPI GetOpenFileNameA(
1581         LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
1582 {
1583     BOOL  newlook = TRUE; /* FIXME: TWEAK_WineLook */
1584     COMDLG32_SetCommDlgExtendedError(0);
1585     /* some flags don't allow to match the TWEAK_WineLook */
1586     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
1587     {
1588       newlook = (ofn->Flags & OFN_EXPLORER) ? TRUE : FALSE;
1589     }
1590
1591     if (newlook)
1592     {
1593         return GetFileDialog95A(ofn, OPEN_DIALOG);
1594     }
1595     else
1596     {
1597         return GetFileName31A(ofn, OPEN_DIALOG);
1598     }
1599 }
1600
1601 /***********************************************************************
1602  *            GetOpenFileNameW (COMDLG32.@)
1603  *
1604  * Creates a dialog box for the user to select a file to open.
1605  *
1606  * RETURNS
1607  *    TRUE on success: user enters a valid file
1608  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1609  *
1610  */
1611 BOOL WINAPI GetOpenFileNameW(
1612         LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
1613 {
1614     BOOL  newlook = TRUE; /* FIXME: TWEAK_WineLook */
1615     COMDLG32_SetCommDlgExtendedError(0);
1616     /* some flags don't allow to match the TWEAK_WineLook */
1617     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
1618     {
1619       newlook = (ofn->Flags & OFN_EXPLORER) ? TRUE : FALSE;
1620     }
1621
1622     if (newlook)
1623     {
1624         return GetFileDialog95W(ofn, OPEN_DIALOG);
1625     }
1626     else
1627     {
1628         return GetFileName31W(ofn, OPEN_DIALOG);
1629     }
1630 }
1631
1632 /***********************************************************************
1633  *            GetSaveFileNameA  (COMDLG32.@)
1634  *
1635  * Creates a dialog box for the user to select a file to save.
1636  *
1637  * RETURNS
1638  *    TRUE on success: user enters a valid file
1639  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1640  *
1641  */
1642 BOOL WINAPI GetSaveFileNameA(
1643         LPOPENFILENAMEA ofn) /* [in/out] address of init structure */
1644 {
1645     BOOL  newlook = TRUE; /* FIXME: TWEAK_WineLook */
1646     COMDLG32_SetCommDlgExtendedError(0);
1647     /* some flags don't allow to match the TWEAK_WineLook */
1648     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
1649     {
1650       newlook = (ofn->Flags & OFN_EXPLORER) ? TRUE : FALSE;
1651     }
1652
1653     if (newlook)
1654     {
1655         return GetFileDialog95A(ofn, SAVE_DIALOG);
1656     }
1657     else
1658     {
1659         return GetFileName31A(ofn, SAVE_DIALOG);
1660     }
1661 }
1662
1663 /***********************************************************************
1664  *            GetSaveFileNameW  (COMDLG32.@)
1665  *
1666  * Creates a dialog box for the user to select a file to save.
1667  *
1668  * RETURNS
1669  *    TRUE on success: user enters a valid file
1670  *    FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
1671  *
1672  */
1673 BOOL WINAPI GetSaveFileNameW(
1674         LPOPENFILENAMEW ofn) /* [in/out] address of init structure */
1675 {
1676     BOOL  newlook = TRUE; /* FIXME: TWEAK_WineLook */
1677     COMDLG32_SetCommDlgExtendedError(0);
1678     /* some flags don't allow to match the TWEAK_WineLook */
1679     if (ofn->Flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE))
1680     {
1681       newlook = (ofn->Flags & OFN_EXPLORER) ? TRUE : FALSE;
1682     }
1683
1684     if (newlook)
1685     {
1686         return GetFileDialog95W(ofn, SAVE_DIALOG);
1687     }
1688     else
1689     {
1690         return GetFileName31W(ofn, SAVE_DIALOG);
1691     }
1692 }