mshtml: Don't crash in QueryInterface if uri is NULL.
[wine] / dlls / comdlg32 / filedlg31.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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/unicode.h"
32 #include "wine/debug.h"
33 #include "cderr.h"
34 #include "winreg.h"
35 #include "winternl.h"
36 #include "commdlg.h"
37 #include "shlwapi.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
40
41 #include "cdlg.h"
42 #include "filedlg31.h"
43
44 #define BUFFILE 512
45 #define BUFFILEALLOC 512 * sizeof(WCHAR)
46
47 static const WCHAR FILE_star[] = {'*','.','*', 0};
48 static const WCHAR FILE_bslash[] = {'\\', 0};
49 static const WCHAR FILE_specc[] = {'%','c',':', 0};
50 static const int fldrHeight = 16;
51 static const int fldrWidth = 20;
52
53 static HICON hFolder = 0;
54 static HICON hFolder2 = 0;
55 static HICON hFloppy = 0;
56 static HICON hHDisk = 0;
57 static HICON hCDRom = 0;
58 static HICON hNet = 0;
59
60 /***********************************************************************
61  *                              FD31_Init                       [internal]
62  */
63 BOOL FD31_Init(void)
64 {
65     static BOOL initialized = 0;
66
67     if (!initialized) {
68         hFolder  = LoadImageA( COMDLG32_hInstance, "FOLDER", IMAGE_ICON, 16, 16, LR_SHARED );
69         hFolder2 = LoadImageA( COMDLG32_hInstance, "FOLDER2", IMAGE_ICON, 16, 16, LR_SHARED );
70         hFloppy  = LoadImageA( COMDLG32_hInstance, "FLOPPY", IMAGE_ICON, 16, 16, LR_SHARED );
71         hHDisk   = LoadImageA( COMDLG32_hInstance, "HDISK", IMAGE_ICON, 16, 16, LR_SHARED );
72         hCDRom   = LoadImageA( COMDLG32_hInstance, "CDROM", IMAGE_ICON, 16, 16, LR_SHARED );
73         hNet     = LoadImageA( COMDLG32_hInstance, "NETWORK", IMAGE_ICON, 16, 16, LR_SHARED );
74         if (hFolder == 0 || hFolder2 == 0 || hFloppy == 0 ||
75             hHDisk == 0 || hCDRom == 0 || hNet == 0)
76         {
77             ERR("Error loading icons !\n");
78             return FALSE;
79         }
80         initialized = TRUE;
81     }
82     return TRUE;
83 }
84
85 /***********************************************************************
86  *                              FD31_StripEditControl        [internal]
87  * Strip pathnames off the contents of the edit control.
88  */
89 static void FD31_StripEditControl(HWND hwnd)
90 {
91     WCHAR temp[BUFFILE], *cp;
92
93     GetDlgItemTextW( hwnd, edt1, temp, sizeof(temp)/sizeof(WCHAR));
94     cp = strrchrW(temp, '\\');
95     if (cp != NULL) {
96         strcpyW(temp, cp+1);
97     }
98     cp = strrchrW(temp, ':');
99     if (cp != NULL) {
100         strcpyW(temp, cp+1);
101     }
102     /* FIXME: shouldn't we do something with the result here? ;-) */
103 }
104
105 /***********************************************************************
106  *                              FD31_CallWindowProc          [internal]
107  *
108  *      Call the appropriate hook
109  */
110 BOOL FD31_CallWindowProc(PFD31_DATA lfs, UINT wMsg, WPARAM wParam,
111                          LPARAM lParam)
112 {
113     return lfs->callbacks->CWP(lfs, wMsg, wParam, lParam);
114 }
115
116 /***********************************************************************
117  *                              FD31_ScanDir                 [internal]
118  */
119 static BOOL FD31_ScanDir(HWND hWnd, LPWSTR newPath)
120 {
121     WCHAR               buffer[BUFFILE];
122     HWND                hdlg, hdlgDir;
123     LRESULT             lRet = TRUE;
124     HCURSOR             hCursorWait, oldCursor;
125
126     TRACE("Trying to change to %s\n", debugstr_w(newPath));
127     if  ( newPath[0] && !SetCurrentDirectoryW( newPath ))
128         return FALSE;
129     lstrcpynW(buffer, newPath, sizeof(buffer)/sizeof(WCHAR));
130
131     /* get the list of spec files */
132     GetDlgItemTextW(hWnd, edt1, buffer, sizeof(buffer)/sizeof(WCHAR));
133
134     hCursorWait = LoadCursorA(0, (LPSTR)IDC_WAIT);
135     oldCursor = SetCursor(hCursorWait);
136
137     /* list of files */
138     if ((hdlg = GetDlgItem(hWnd, lst1)) != 0) {
139         WCHAR*  scptr; /* ptr on semi-colon */
140         WCHAR*  filter = buffer;
141
142         TRACE("Using filter %s\n", debugstr_w(filter));
143         SendMessageW(hdlg, LB_RESETCONTENT, 0, 0);
144         while (filter) {
145             scptr = strchrW(filter, ';');
146             if (scptr)  *scptr = 0;
147             while (*filter == ' ') filter++;
148             TRACE("Using file spec %s\n", debugstr_w(filter));
149             if (SendMessageW(hdlg, LB_DIR, 0, (LPARAM)filter) == LB_ERR)
150                 return FALSE;
151             if (scptr) *scptr = ';';
152                 filter = (scptr) ? (scptr + 1) : 0;
153          }
154     }
155
156     /* list of directories */
157     strcpyW(buffer, FILE_star);
158
159     if ((hdlgDir = GetDlgItem(hWnd, lst2)) != 0) {
160         lRet = DlgDirListW(hWnd, buffer, lst2, stc1, DDL_EXCLUSIVE | DDL_DIRECTORY);
161     }
162     SetCursor(oldCursor);
163     return lRet;
164 }
165
166 /***********************************************************************
167  *                              FD31_GetFileType                [internal]
168  */
169
170 static LPWSTR FD31_GetFileType(LPWSTR cfptr, LPWSTR fptr, WORD index)
171 {
172   int n, i;
173   i = 0;
174   if (cfptr)
175     for ( ;(n = lstrlenW(cfptr)) != 0; i++)
176       {
177         cfptr += n + 1;
178         if (i == index)
179           return cfptr;
180         cfptr += lstrlenW(cfptr) + 1;
181       }
182   if (fptr)
183     for ( ;(n = lstrlenW(fptr)) != 0; i++)
184       {
185         fptr += n + 1;
186         if (i == index)
187           return fptr;
188         fptr += lstrlenW(fptr) + 1;
189     }
190   return (LPWSTR) FILE_star; /* FIXME */
191 }
192
193 /***********************************************************************
194  *                              FD31_WMDrawItem              [internal]
195  */
196 LONG FD31_WMDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam,
197        int savedlg, LPDRAWITEMSTRUCT lpdis)
198 {
199     WCHAR *str;
200     HICON hIcon;
201     COLORREF oldText = 0, oldBk = 0;
202
203     if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst1)
204     {
205         if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC))) return FALSE;
206         SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
207                       (LPARAM)str);
208
209         if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
210         {
211             oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
212             oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
213         }
214         if (savedlg)
215             SetTextColor(lpdis->hDC,GetSysColor(COLOR_GRAYTEXT) );
216
217         ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + 1,
218                   lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
219                   &(lpdis->rcItem), str, lstrlenW(str), NULL);
220
221         if (lpdis->itemState & ODS_SELECTED)
222             DrawFocusRect( lpdis->hDC, &(lpdis->rcItem) );
223
224         if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
225         {
226             SetBkColor( lpdis->hDC, oldBk );
227             SetTextColor( lpdis->hDC, oldText );
228         }
229         HeapFree(GetProcessHeap(), 0, str);
230         return TRUE;
231     }
232
233     if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst2)
234     {
235         if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
236             return FALSE;
237         SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
238                       (LPARAM)str);
239
240         if (lpdis->itemState & ODS_SELECTED)
241         {
242             oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
243             oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
244         }
245         ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + fldrWidth,
246                   lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
247                   &(lpdis->rcItem), str, lstrlenW(str), NULL);
248
249         if (lpdis->itemState & ODS_SELECTED)
250             DrawFocusRect( lpdis->hDC, &(lpdis->rcItem) );
251
252         if (lpdis->itemState & ODS_SELECTED)
253         {
254             SetBkColor( lpdis->hDC, oldBk );
255             SetTextColor( lpdis->hDC, oldText );
256         }
257         DrawIcon(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, hFolder);
258         HeapFree(GetProcessHeap(), 0, str);
259         return TRUE;
260     }
261     if (lpdis->CtlType == ODT_COMBOBOX && lpdis->CtlID == cmb2)
262     {
263         char root[] = "a:";
264         if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
265             return FALSE;
266         SendMessageW(lpdis->hwndItem, CB_GETLBTEXT, lpdis->itemID,
267                       (LPARAM)str);
268         root[0] += str[2] - 'a';
269         switch(GetDriveTypeA(root))
270         {
271         case DRIVE_REMOVABLE: hIcon = hFloppy; break;
272         case DRIVE_CDROM:     hIcon = hCDRom; break;
273         case DRIVE_REMOTE:    hIcon = hNet; break;
274         case DRIVE_FIXED:
275         default:           hIcon = hHDisk; break;
276         }
277         if (lpdis->itemState & ODS_SELECTED)
278         {
279             oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
280             oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
281         }
282         ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + fldrWidth,
283                   lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
284                   &(lpdis->rcItem), str, lstrlenW(str), NULL);
285
286         if (lpdis->itemState & ODS_SELECTED)
287         {
288             SetBkColor( lpdis->hDC, oldBk );
289             SetTextColor( lpdis->hDC, oldText );
290         }
291         DrawIcon(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, hIcon);
292         HeapFree(GetProcessHeap(), 0, str);
293         return TRUE;
294     }
295     return FALSE;
296 }
297
298 /***********************************************************************
299  *                              FD31_UpdateResult            [internal]
300  *      update the displayed file name (with path)
301  */
302 static void FD31_UpdateResult(PFD31_DATA lfs, WCHAR *tmpstr)
303 {
304     int lenstr2;
305     LPOPENFILENAMEW ofnW = lfs->ofnW;
306     WCHAR tmpstr2[BUFFILE];
307     WCHAR *p;
308
309     TRACE("%s\n", debugstr_w(tmpstr));
310     if(ofnW->Flags & OFN_NOVALIDATE)
311         tmpstr2[0] = '\0';
312     else
313         GetCurrentDirectoryW(BUFFILE, tmpstr2);
314     lenstr2 = strlenW(tmpstr2);
315     if (lenstr2 > 3)
316         tmpstr2[lenstr2++]='\\';
317     lstrcpynW(tmpstr2+lenstr2, tmpstr, BUFFILE-lenstr2);
318     if (ofnW->lpstrFile)
319         lstrcpynW(ofnW->lpstrFile, tmpstr2, ofnW->nMaxFile);
320
321     /* set filename offset */
322     p = PathFindFileNameW(ofnW->lpstrFile);
323     ofnW->nFileOffset = (p - ofnW->lpstrFile);
324
325     /* set extension offset */
326     p = PathFindExtensionW(ofnW->lpstrFile);
327     ofnW->nFileExtension = (*p) ? (p - ofnW->lpstrFile) + 1 : 0;
328
329     TRACE("file %s, file offset %d, ext offset %d\n",
330           debugstr_w(ofnW->lpstrFile), ofnW->nFileOffset, ofnW->nFileExtension);
331
332     /* update the real client structures if any */
333     lfs->callbacks->UpdateResult(lfs);
334 }
335
336 /***********************************************************************
337  *                              FD31_UpdateFileTitle         [internal]
338  *      update the displayed file name (without path)
339  */
340 static void FD31_UpdateFileTitle(PFD31_DATA lfs)
341 {
342   LONG lRet;
343   LPOPENFILENAMEW ofnW = lfs->ofnW;
344   if (ofnW->lpstrFileTitle != NULL)
345   {
346     lRet = SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETCURSEL, 0, 0);
347     SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETTEXT, lRet,
348                              (LPARAM)ofnW->lpstrFileTitle );
349     lfs->callbacks->UpdateFileTitle(lfs);
350   }
351 }
352
353 /***********************************************************************
354  *                              FD31_DirListDblClick         [internal]
355  */
356 static LRESULT FD31_DirListDblClick( PFD31_DATA lfs )
357 {
358   LONG lRet;
359   HWND hWnd = lfs->hwnd;
360   LPWSTR pstr;
361   WCHAR tmpstr[BUFFILE];
362
363   /* get the raw string (with brackets) */
364   lRet = SendDlgItemMessageW(hWnd, lst2, LB_GETCURSEL, 0, 0);
365   if (lRet == LB_ERR) return TRUE;
366   pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
367   SendDlgItemMessageW(hWnd, lst2, LB_GETTEXT, lRet,
368                      (LPARAM)pstr);
369   strcpyW( tmpstr, pstr );
370   HeapFree(GetProcessHeap(), 0, pstr);
371   /* get the selected directory in tmpstr */
372   if (tmpstr[0] == '[')
373     {
374       tmpstr[lstrlenW(tmpstr) - 1] = 0;
375       strcpyW(tmpstr,tmpstr+1);
376     }
377   strcatW(tmpstr, FILE_bslash);
378
379   FD31_ScanDir(hWnd, tmpstr);
380   /* notify the app */
381   if (lfs->hook)
382     {
383       if (FD31_CallWindowProc(lfs, lfs->lbselchstring, lst2,
384               MAKELONG(lRet,CD_LBSELCHANGE)))
385         return TRUE;
386     }
387   return TRUE;
388 }
389
390 /***********************************************************************
391  *                              FD31_FileListSelect         [internal]
392  *    called when a new item is picked in the file list
393  */
394 static LRESULT FD31_FileListSelect( PFD31_DATA lfs )
395 {
396     LONG lRet;
397     HWND hWnd = lfs->hwnd;
398     LPWSTR pstr;
399
400     lRet = lfs->callbacks->SendLbGetCurSel(lfs);
401     if (lRet == LB_ERR)
402         return TRUE;
403
404     /* set the edit control to the choosen file */
405     if ((pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
406     {
407         SendDlgItemMessageW(hWnd, lst1, LB_GETTEXT, lRet,
408                        (LPARAM)pstr);
409         SetDlgItemTextW( hWnd, edt1, pstr );
410         HeapFree(GetProcessHeap(), 0, pstr);
411     }
412     if (lfs->hook)
413     {
414         FD31_CallWindowProc(lfs, lfs->lbselchstring, lst1,
415                            MAKELONG(lRet,CD_LBSELCHANGE));
416     }
417     /* FIXME: for OFN_ALLOWMULTISELECT we need CD_LBSELSUB, CD_SELADD,
418            CD_LBSELNOITEMS */
419     return TRUE;
420 }
421
422 /***********************************************************************
423  *                              FD31_TestPath      [internal]
424  *      before accepting the file name, test if it includes wild cards
425  *      tries to scan the directory and returns TRUE if no error.
426  */
427 static LRESULT FD31_TestPath( PFD31_DATA lfs, LPWSTR path )
428 {
429     HWND hWnd = lfs->hwnd;
430     LPWSTR pBeginFileName, pstr2;
431     WCHAR tmpstr2[BUFFILE];
432
433     pBeginFileName = strrchrW(path, '\\');
434     if (pBeginFileName == NULL)
435         pBeginFileName = strrchrW(path, ':');
436
437     if (strchrW(path,'*') != NULL || strchrW(path,'?') != NULL)
438     {
439         /* edit control contains wildcards */
440         if (pBeginFileName != NULL)
441         {
442             lstrcpynW(tmpstr2, pBeginFileName + 1, BUFFILE);
443             *(pBeginFileName + 1) = 0;
444         }
445         else
446         {
447             strcpyW(tmpstr2, path);
448             if(!(lfs->ofnW->Flags & OFN_NOVALIDATE))
449                 *path = 0;
450         }
451
452         TRACE("path=%s, tmpstr2=%s\n", debugstr_w(path), debugstr_w(tmpstr2));
453         SetDlgItemTextW( hWnd, edt1, tmpstr2 );
454         FD31_ScanDir(hWnd, path);
455         return (lfs->ofnW->Flags & OFN_NOVALIDATE) ? TRUE : FALSE;
456     }
457
458     /* no wildcards, we might have a directory or a filename */
459     /* try appending a wildcard and reading the directory */
460
461     pstr2 = path + lstrlenW(path);
462     if (pBeginFileName == NULL || *(pBeginFileName + 1) != 0)
463         strcatW(path, FILE_bslash);
464
465     /* if ScanDir succeeds, we have changed the directory */
466     if (FD31_ScanDir(hWnd, path))
467         return FALSE; /* and path is not a valid file name */
468
469     /* if not, this must be a filename */
470
471     *pstr2 = 0; /* remove the wildcard added before */
472
473     if (pBeginFileName != NULL)
474     {
475         /* strip off the pathname */
476         *pBeginFileName = 0;
477         SetDlgItemTextW( hWnd, edt1, pBeginFileName + 1 );
478
479         lstrcpynW(tmpstr2, pBeginFileName + 1, sizeof(tmpstr2)/sizeof(WCHAR) );
480         /* Should we MessageBox() if this fails? */
481         if (!FD31_ScanDir(hWnd, path))
482         {
483             return FALSE;
484         }
485         strcpyW(path, tmpstr2);
486     }
487     else
488         SetDlgItemTextW( hWnd, edt1, path );
489     return TRUE;
490 }
491
492 /***********************************************************************
493  *                              FD31_Validate               [internal]
494  *   called on: click Ok button, Enter in edit, DoubleClick in file list
495  */
496 static LRESULT FD31_Validate( PFD31_DATA lfs, LPWSTR path, UINT control, INT itemIndex,
497                                  BOOL internalUse )
498 {
499     LONG lRet;
500     HWND hWnd = lfs->hwnd;
501     OPENFILENAMEW ofnsav;
502     LPOPENFILENAMEW ofnW = lfs->ofnW;
503     WCHAR filename[BUFFILE];
504
505     ofnsav = *ofnW; /* for later restoring */
506
507     /* get current file name */
508     if (path)
509         lstrcpynW(filename, path, sizeof(filename)/sizeof(WCHAR));
510     else
511         GetDlgItemTextW( hWnd, edt1, filename, sizeof(filename)/sizeof(WCHAR));
512
513     TRACE("got filename = %s\n", debugstr_w(filename));
514     /* if we did not click in file list to get there */
515     if (control != lst1)
516     {
517         if (!FD31_TestPath( lfs, filename) )
518            return FALSE;
519     }
520     FD31_UpdateResult(lfs, filename);
521
522     if (internalUse)
523     { /* called internally after a change in a combo */
524         if (lfs->hook)
525         {
526              FD31_CallWindowProc(lfs, lfs->lbselchstring, control,
527                              MAKELONG(itemIndex,CD_LBSELCHANGE));
528         }
529         return TRUE;
530     }
531
532     FD31_UpdateFileTitle(lfs);
533     if (lfs->hook)
534     {
535         lRet = (BOOL)FD31_CallWindowProc(lfs, lfs->fileokstring,
536                   0, lfs->lParam );
537         if (lRet)
538         {
539             *ofnW = ofnsav; /* restore old state */
540             return FALSE;
541         }
542     }
543     if ((ofnW->Flags & OFN_ALLOWMULTISELECT) && (ofnW->Flags & OFN_EXPLORER))
544     {
545         if (ofnW->lpstrFile)
546         {
547             LPWSTR str = (LPWSTR)ofnW->lpstrFile;
548             LPWSTR ptr = strrchrW(str, '\\');
549             str[lstrlenW(str) + 1] = '\0';
550             *ptr = 0;
551         }
552     }
553     return TRUE;
554 }
555
556 /***********************************************************************
557  *                              FD31_DiskChange             [internal]
558  *    called when a new item is picked in the disk selection combo
559  */
560 static LRESULT FD31_DiskChange( PFD31_DATA lfs )
561 {
562     LONG lRet;
563     HWND hWnd = lfs->hwnd;
564     LPWSTR pstr;
565     WCHAR diskname[BUFFILE];
566
567     FD31_StripEditControl(hWnd);
568     lRet = SendDlgItemMessageW(hWnd, cmb2, CB_GETCURSEL, 0, 0L);
569     if (lRet == LB_ERR)
570         return 0;
571     pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
572     SendDlgItemMessageW(hWnd, cmb2, CB_GETLBTEXT, lRet,
573                          (LPARAM)pstr);
574     wsprintfW(diskname, FILE_specc, pstr[2]);
575     HeapFree(GetProcessHeap(), 0, pstr);
576
577     return FD31_Validate( lfs, diskname, cmb2, lRet, TRUE );
578 }
579
580 /***********************************************************************
581  *                              FD31_FileTypeChange         [internal]
582  *    called when a new item is picked in the file type combo
583  */
584 static LRESULT FD31_FileTypeChange( PFD31_DATA lfs )
585 {
586     LONG lRet;
587     LPWSTR pstr;
588
589     lRet = SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETCURSEL, 0, 0);
590     if (lRet == LB_ERR)
591         return TRUE;
592     pstr = (LPWSTR)SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETITEMDATA, lRet, 0);
593     TRACE("Selected filter : %s\n", debugstr_w(pstr));
594     SetDlgItemTextW( lfs->hwnd, edt1, pstr );
595
596     return FD31_Validate( lfs, NULL, cmb1, lRet, TRUE );
597 }
598
599 /***********************************************************************
600  *                              FD31_WMCommand               [internal]
601  */
602 LRESULT FD31_WMCommand(HWND hWnd, LPARAM lParam, UINT notification,
603        UINT control, PFD31_DATA lfs )
604 {
605     switch (control)
606     {
607         case lst1: /* file list */
608         FD31_StripEditControl(hWnd);
609         if (notification == LBN_DBLCLK)
610         {
611             return SendMessageW(hWnd, WM_COMMAND, IDOK, 0);
612         }
613         else if (notification == LBN_SELCHANGE)
614             return FD31_FileListSelect( lfs );
615         break;
616
617         case lst2: /* directory list */
618         FD31_StripEditControl(hWnd);
619         if (notification == LBN_DBLCLK)
620             return FD31_DirListDblClick( lfs );
621         break;
622
623         case cmb1: /* file type drop list */
624         if (notification == CBN_SELCHANGE)
625             return FD31_FileTypeChange( lfs );
626         break;
627
628         case chx1:
629         break;
630
631         case pshHelp:
632         break;
633
634         case cmb2: /* disk dropdown combo */
635         if (notification == CBN_SELCHANGE)
636             return FD31_DiskChange( lfs );
637         break;
638
639         case IDOK:
640         TRACE("OK pressed\n");
641         if (FD31_Validate( lfs, NULL, control, 0, FALSE ))
642             EndDialog(hWnd, TRUE);
643         return TRUE;
644
645         case IDCANCEL:
646         EndDialog(hWnd, FALSE);
647         return TRUE;
648
649         case IDABORT: /* can be sent by the hook procedure */
650         EndDialog(hWnd, TRUE);
651         return TRUE;
652     }
653     return FALSE;
654 }
655
656 /************************************************************************
657  *                              FD31_MapStringPairsToW       [internal]
658  *      map string pairs to Unicode
659  */
660 static LPWSTR FD31_MapStringPairsToW(LPCSTR strA, UINT size)
661 {
662     LPCSTR s;
663     LPWSTR x;
664     unsigned int n, len;
665
666     s = strA;
667     while (*s)
668         s = s+strlen(s)+1;
669     s++;
670     n = s + 1 - strA; /* Don't forget the other \0 */
671     if (n < size) n = size;
672
673     len = MultiByteToWideChar( CP_ACP, 0, strA, n, NULL, 0 );
674     x = HeapAlloc(GetProcessHeap(),0, len * sizeof(WCHAR));
675     MultiByteToWideChar( CP_ACP, 0, strA, n, x, len );
676     return x;
677 }
678
679
680 /************************************************************************
681  *                              FD31_DupToW                  [internal]
682  *      duplicates an Ansi string to unicode, with a buffer size
683  */
684 static LPWSTR FD31_DupToW(LPCSTR str, DWORD size)
685 {
686     LPWSTR strW = NULL;
687     if (str && (size > 0))
688     {
689         strW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
690         if (strW) MultiByteToWideChar( CP_ACP, 0, str, -1, strW, size );
691     }
692     return strW;
693 }
694
695 /************************************************************************
696  *                              FD31_MapOfnStructA          [internal]
697  *      map a 32 bits Ansi structure to an Unicode one
698  */
699 void FD31_MapOfnStructA(const LPOPENFILENAMEA ofnA, LPOPENFILENAMEW ofnW, BOOL open)
700 {
701     UNICODE_STRING usBuffer;
702
703     ofnW->lStructSize = sizeof(OPENFILENAMEW);
704     ofnW->hwndOwner = ofnA->hwndOwner;
705     ofnW->hInstance = ofnA->hInstance;
706     if (ofnA->lpstrFilter)
707         ofnW->lpstrFilter = FD31_MapStringPairsToW(ofnA->lpstrFilter, 0);
708
709     if ((ofnA->lpstrCustomFilter) && (*(ofnA->lpstrCustomFilter)))
710         ofnW->lpstrCustomFilter = FD31_MapStringPairsToW(ofnA->lpstrCustomFilter, ofnA->nMaxCustFilter);
711     ofnW->nMaxCustFilter = ofnA->nMaxCustFilter;
712     ofnW->nFilterIndex = ofnA->nFilterIndex;
713     ofnW->nMaxFile = ofnA->nMaxFile;
714     ofnW->lpstrFile = FD31_DupToW(ofnA->lpstrFile, ofnW->nMaxFile);
715     ofnW->nMaxFileTitle = ofnA->nMaxFileTitle;
716     ofnW->lpstrFileTitle = FD31_DupToW(ofnA->lpstrFileTitle, ofnW->nMaxFileTitle);
717     if (ofnA->lpstrInitialDir)
718     {
719         RtlCreateUnicodeStringFromAsciiz (&usBuffer,ofnA->lpstrInitialDir);
720         ofnW->lpstrInitialDir = usBuffer.Buffer;
721     }
722     if (ofnA->lpstrTitle) {
723         RtlCreateUnicodeStringFromAsciiz (&usBuffer, ofnA->lpstrTitle);
724         ofnW->lpstrTitle = usBuffer.Buffer;
725     } else {
726         WCHAR buf[16];
727         int len;
728         LoadStringW(COMDLG32_hInstance, open ? IDS_OPEN_FILE : IDS_SAVE_AS,
729                     buf, sizeof(buf)/sizeof(WCHAR));
730         len = lstrlenW(buf)+1;
731         ofnW->lpstrTitle = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
732         memcpy((void*)ofnW->lpstrTitle, buf, len*sizeof(WCHAR));
733     }
734     ofnW->Flags = ofnA->Flags;
735     ofnW->nFileOffset = ofnA->nFileOffset;
736     ofnW->nFileExtension = ofnA->nFileExtension;
737     ofnW->lpstrDefExt = FD31_DupToW(ofnA->lpstrDefExt, 3);
738     if ((ofnA->Flags & OFN_ENABLETEMPLATE) && (ofnA->lpTemplateName))
739     {
740         if (HIWORD(ofnA->lpTemplateName))
741         {
742             RtlCreateUnicodeStringFromAsciiz (&usBuffer,ofnA->lpTemplateName);
743             ofnW->lpTemplateName = usBuffer.Buffer;
744         }
745         else /* numbered resource */
746             ofnW->lpTemplateName = (LPWSTR) ofnA->lpTemplateName;
747     }
748 }
749
750
751 /************************************************************************
752  *                              FD31_FreeOfnW          [internal]
753  *      Undo all allocations done by FD31_MapOfnStructA
754  */
755 void FD31_FreeOfnW(LPOPENFILENAMEW ofnW)
756 {
757    HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrFilter);
758    HeapFree(GetProcessHeap(), 0, ofnW->lpstrCustomFilter);
759    HeapFree(GetProcessHeap(), 0, ofnW->lpstrFile);
760    HeapFree(GetProcessHeap(), 0, ofnW->lpstrFileTitle);
761    HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrInitialDir);
762    HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrTitle);
763    if (HIWORD(ofnW->lpTemplateName))
764        HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpTemplateName);
765 }
766
767 /************************************************************************
768  *                              FD31_DestroyPrivate            [internal]
769  *      destroys the private object
770  */
771 void FD31_DestroyPrivate(PFD31_DATA lfs)
772 {
773     HWND hwnd;
774     if (!lfs) return;
775     hwnd = lfs->hwnd;
776     TRACE("destroying private allocation %p\n", lfs);
777     lfs->callbacks->Destroy(lfs);
778     HeapFree(GetProcessHeap(), 0, lfs);
779     RemovePropA(hwnd, FD31_OFN_PROP);
780 }
781
782 /************************************************************************
783  *                              FD31_AllocPrivate            [internal]
784  *      allocate a private object to hold 32 bits Unicode
785  *      structure that will be used throughtout the calls, while
786  *      keeping available the original structures and a few variables
787  *      On entry : type = dialog procedure type (16,32A,32W)
788  *                 dlgType = dialog type (open or save)
789  */
790 PFD31_DATA FD31_AllocPrivate(LPARAM lParam, UINT dlgType,
791                              PFD31_CALLBACKS callbacks, DWORD data)
792 {
793     PFD31_DATA lfs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FD31_DATA));
794
795     TRACE("alloc private buf %p\n", lfs);
796     if (!lfs) return NULL;
797     lfs->hook = FALSE;
798     lfs->lParam = lParam;
799     lfs->open = (dlgType == OPEN_DIALOG);
800     lfs->callbacks = callbacks;
801     if (! lfs->callbacks->Init(lParam, lfs, data))
802     {
803         FD31_DestroyPrivate(lfs);
804         return NULL;
805     }
806     lfs->lbselchstring = RegisterWindowMessageA(LBSELCHSTRINGA);
807     lfs->fileokstring = RegisterWindowMessageA(FILEOKSTRINGA);
808
809     return lfs;
810 }
811
812 /***********************************************************************
813  *                              FD31_WMInitDialog            [internal]
814  */
815
816 LONG FD31_WMInitDialog(HWND hWnd, WPARAM wParam, LPARAM lParam)
817 {
818   int i, n;
819   WCHAR tmpstr[BUFFILE];
820   LPWSTR pstr, old_pstr;
821   LPOPENFILENAMEW ofn;
822   PFD31_DATA lfs = (PFD31_DATA) lParam;
823
824   if (!lfs) return FALSE;
825   SetPropA(hWnd, FD31_OFN_PROP, (HANDLE)lfs);
826   lfs->hwnd = hWnd;
827   ofn = lfs->ofnW;
828
829   TRACE("flags=%lx initialdir=%s\n", ofn->Flags, debugstr_w(ofn->lpstrInitialDir));
830
831   SetWindowTextW( hWnd, ofn->lpstrTitle );
832   /* read custom filter information */
833   if (ofn->lpstrCustomFilter)
834     {
835       pstr = ofn->lpstrCustomFilter;
836       n = 0;
837       TRACE("lpstrCustomFilter = %p\n", pstr);
838       while(*pstr)
839         {
840           old_pstr = pstr;
841           i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
842                                    (LPARAM)(ofn->lpstrCustomFilter) + n );
843           n += lstrlenW(pstr) + 1;
844           pstr += lstrlenW(pstr) + 1;
845           TRACE("add str=%s associated to %s\n",
846                 debugstr_w(old_pstr), debugstr_w(pstr));
847           SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
848           n += lstrlenW(pstr) + 1;
849           pstr += lstrlenW(pstr) + 1;
850         }
851     }
852   /* read filter information */
853   if (ofn->lpstrFilter) {
854         pstr = (LPWSTR) ofn->lpstrFilter;
855         n = 0;
856         while(*pstr) {
857           old_pstr = pstr;
858           i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
859                                        (LPARAM)(ofn->lpstrFilter + n) );
860           n += lstrlenW(pstr) + 1;
861           pstr += lstrlenW(pstr) + 1;
862           TRACE("add str=%s associated to %s\n",
863                 debugstr_w(old_pstr), debugstr_w(pstr));
864           SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
865           n += lstrlenW(pstr) + 1;
866           pstr += lstrlenW(pstr) + 1;
867         }
868   }
869   /* set default filter */
870   if (ofn->nFilterIndex == 0 && ofn->lpstrCustomFilter == NULL)
871         ofn->nFilterIndex = 1;
872   SendDlgItemMessageW(hWnd, cmb1, CB_SETCURSEL, ofn->nFilterIndex - 1, 0);
873   lstrcpynW(tmpstr, FD31_GetFileType(ofn->lpstrCustomFilter,
874              (LPWSTR)ofn->lpstrFilter, ofn->nFilterIndex - 1),BUFFILE);
875   TRACE("nFilterIndex = %ld, SetText of edt1 to %s\n",
876                         ofn->nFilterIndex, debugstr_w(tmpstr));
877   SetDlgItemTextW( hWnd, edt1, tmpstr );
878   /* get drive list */
879   *tmpstr = 0;
880   DlgDirListComboBoxW(hWnd, tmpstr, cmb2, 0, DDL_DRIVES | DDL_EXCLUSIVE);
881   /* read initial directory */
882   /* FIXME: Note that this is now very version-specific (See MSDN description of
883    * the OPENFILENAME structure).  For example under 2000/XP any path in the
884    * lpstrFile overrides the lpstrInitialDir, but not under 95/98/ME
885    */
886   if (ofn->lpstrInitialDir != NULL)
887     {
888       int len;
889       lstrcpynW(tmpstr, ofn->lpstrInitialDir, 511);
890       len = lstrlenW(tmpstr);
891       if (len > 0 && tmpstr[len-1] != '\\'  && tmpstr[len-1] != ':') {
892         tmpstr[len]='\\';
893         tmpstr[len+1]='\0';
894       }
895     }
896   else
897     *tmpstr = 0;
898   if (!FD31_ScanDir(hWnd, tmpstr)) {
899     *tmpstr = 0;
900     if (!FD31_ScanDir(hWnd, tmpstr))
901       WARN("Couldn't read initial directory %s!\n", debugstr_w(tmpstr));
902   }
903   /* select current drive in combo 2, omit missing drives */
904   {
905       char dir[MAX_PATH];
906       char str[4] = "a:\\";
907       GetCurrentDirectoryA( sizeof(dir), dir );
908       for(i = 0, n = -1; i < 26; i++)
909       {
910           str[0] = 'a' + i;
911           if (GetDriveTypeA(str) > DRIVE_NO_ROOT_DIR) n++;
912           if (toupper(str[0]) == toupper(dir[0])) break;
913       }
914   }
915   SendDlgItemMessageW(hWnd, cmb2, CB_SETCURSEL, n, 0);
916   if (!(ofn->Flags & OFN_SHOWHELP))
917     ShowWindow(GetDlgItem(hWnd, pshHelp), SW_HIDE);
918   if (ofn->Flags & OFN_HIDEREADONLY)
919     ShowWindow(GetDlgItem(hWnd, chx1), SW_HIDE);
920   if (lfs->hook)
921       return (BOOL) FD31_CallWindowProc(lfs, WM_INITDIALOG, wParam, lfs->lParam);
922   return TRUE;
923 }
924
925 int FD31_GetFldrHeight(void)
926 {
927   return fldrHeight;
928 }