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