2 * Win 3.1 Style File Dialogs
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1996 Albrecht Kleine
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.
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.
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
31 #include "wine/unicode.h"
32 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
44 #define BUFFILEALLOC 512 * sizeof(WCHAR)
46 static const WCHAR FILE_star[] = {'*','.','*', 0};
47 static const WCHAR FILE_bslash[] = {'\\', 0};
48 static const WCHAR FILE_specc[] = {'%','c',':', 0};
49 static const int fldrHeight = 16;
50 static const int fldrWidth = 20;
52 static HICON hFolder = 0;
53 static HICON hFolder2 = 0;
54 static HICON hFloppy = 0;
55 static HICON hHDisk = 0;
56 static HICON hCDRom = 0;
57 static HICON hNet = 0;
59 #define FD31_OFN_PROP "FILEDLG_OFN"
61 typedef struct tagFD31_DATA
63 HWND hwnd; /* file dialog window handle */
64 BOOL hook; /* TRUE if the dialog is hooked */
65 UINT lbselchstring; /* registered message id */
66 UINT fileokstring; /* registered message id */
67 LPARAM lParam; /* save original lparam */
68 LPCVOID template; /* template for 32 bits resource */
69 BOOL open; /* TRUE if open dialog, FALSE if save dialog */
70 LPOPENFILENAMEW ofnW; /* pointer either to the original structure or
71 a W copy for A/16 API */
72 LPOPENFILENAMEA ofnA; /* original structure if 32bits ansi dialog */
73 } FD31_DATA, *PFD31_DATA;
75 /***********************************************************************
76 * FD31_Init [internal]
78 static BOOL FD31_Init(void)
80 static BOOL initialized = 0;
83 hFolder = LoadImageA( COMDLG32_hInstance, "FOLDER", IMAGE_ICON, 16, 16, LR_SHARED );
84 hFolder2 = LoadImageA( COMDLG32_hInstance, "FOLDER2", IMAGE_ICON, 16, 16, LR_SHARED );
85 hFloppy = LoadImageA( COMDLG32_hInstance, "FLOPPY", IMAGE_ICON, 16, 16, LR_SHARED );
86 hHDisk = LoadImageA( COMDLG32_hInstance, "HDISK", IMAGE_ICON, 16, 16, LR_SHARED );
87 hCDRom = LoadImageA( COMDLG32_hInstance, "CDROM", IMAGE_ICON, 16, 16, LR_SHARED );
88 hNet = LoadImageA( COMDLG32_hInstance, "NETWORK", IMAGE_ICON, 16, 16, LR_SHARED );
89 if (hFolder == 0 || hFolder2 == 0 || hFloppy == 0 ||
90 hHDisk == 0 || hCDRom == 0 || hNet == 0)
92 ERR("Error loading icons !\n");
100 /***********************************************************************
101 * FD31_StripEditControl [internal]
102 * Strip pathnames off the contents of the edit control.
104 static void FD31_StripEditControl(HWND hwnd)
106 WCHAR temp[BUFFILE], *cp;
108 GetDlgItemTextW( hwnd, edt1, temp, sizeof(temp)/sizeof(WCHAR));
109 cp = strrchrW(temp, '\\');
113 cp = strrchrW(temp, ':');
117 /* FIXME: shouldn't we do something with the result here? ;-) */
120 /***********************************************************************
121 * FD31_CallWindowProc [internal]
123 * Call the appropriate hook
125 static BOOL FD31_CallWindowProc(const FD31_DATA *lfs, UINT wMsg, WPARAM wParam, LPARAM lParam)
131 TRACE("Call hookA %p (%p, %04x, %08lx, %08lx)\n",
132 lfs->ofnA->lpfnHook, lfs->hwnd, wMsg, wParam, lParam);
133 ret = lfs->ofnA->lpfnHook(lfs->hwnd, wMsg, wParam, lParam);
134 TRACE("ret hookA %p (%p, %04x, %08lx, %08lx)\n",
135 lfs->ofnA->lpfnHook, lfs->hwnd, wMsg, wParam, lParam);
139 TRACE("Call hookW %p (%p, %04x, %08lx, %08lx)\n",
140 lfs->ofnW->lpfnHook, lfs->hwnd, wMsg, wParam, lParam);
141 ret = lfs->ofnW->lpfnHook(lfs->hwnd, wMsg, wParam, lParam);
142 TRACE("Ret hookW %p (%p, %04x, %08lx, %08lx)\n",
143 lfs->ofnW->lpfnHook, lfs->hwnd, wMsg, wParam, lParam);
147 /***********************************************************************
148 * FD31_GetFileType [internal]
150 static LPCWSTR FD31_GetFileType(LPCWSTR cfptr, LPCWSTR fptr, const WORD index)
155 for ( ;(n = lstrlenW(cfptr)) != 0; i++)
160 cfptr += lstrlenW(cfptr) + 1;
163 for ( ;(n = lstrlenW(fptr)) != 0; i++)
168 fptr += lstrlenW(fptr) + 1;
170 return FILE_star; /* FIXME */
173 /***********************************************************************
174 * FD31_ScanDir [internal]
176 static BOOL FD31_ScanDir(const OPENFILENAMEW *ofn, HWND hWnd, LPCWSTR newPath)
178 WCHAR buffer[BUFFILE];
181 HCURSOR hCursorWait, oldCursor;
183 TRACE("Trying to change to %s\n", debugstr_w(newPath));
184 if ( newPath[0] && !SetCurrentDirectoryW( newPath ))
187 /* get the list of spec files */
188 lstrcpynW(buffer, FD31_GetFileType(ofn->lpstrCustomFilter,
189 ofn->lpstrFilter, ofn->nFilterIndex - 1), BUFFILE);
191 hCursorWait = LoadCursorA(0, (LPSTR)IDC_WAIT);
192 oldCursor = SetCursor(hCursorWait);
195 if ((hdlg = GetDlgItem(hWnd, lst1)) != 0) {
196 WCHAR* scptr; /* ptr on semi-colon */
197 WCHAR* filter = buffer;
199 TRACE("Using filter %s\n", debugstr_w(filter));
200 SendMessageW(hdlg, LB_RESETCONTENT, 0, 0);
202 scptr = strchrW(filter, ';');
203 if (scptr) *scptr = 0;
204 while (*filter == ' ') filter++;
205 TRACE("Using file spec %s\n", debugstr_w(filter));
206 SendMessageW(hdlg, LB_DIR, 0, (LPARAM)filter);
207 if (scptr) *scptr = ';';
208 filter = (scptr) ? (scptr + 1) : 0;
212 /* list of directories */
213 strcpyW(buffer, FILE_star);
215 if (GetDlgItem(hWnd, lst2) != 0) {
216 lRet = DlgDirListW(hWnd, buffer, lst2, stc1, DDL_EXCLUSIVE | DDL_DIRECTORY);
218 SetCursor(oldCursor);
222 /***********************************************************************
223 * FD31_WMDrawItem [internal]
225 static LONG FD31_WMDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam,
226 int savedlg, const DRAWITEMSTRUCT *lpdis)
230 COLORREF oldText = 0, oldBk = 0;
232 if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst1)
234 if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC))) return FALSE;
235 SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
238 if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
240 oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
241 oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
244 SetTextColor(lpdis->hDC,GetSysColor(COLOR_GRAYTEXT) );
246 ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + 1,
247 lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
248 &(lpdis->rcItem), str, lstrlenW(str), NULL);
250 if (lpdis->itemState & ODS_SELECTED)
251 DrawFocusRect( lpdis->hDC, &(lpdis->rcItem) );
253 if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
255 SetBkColor( lpdis->hDC, oldBk );
256 SetTextColor( lpdis->hDC, oldText );
258 HeapFree(GetProcessHeap(), 0, str);
262 if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst2)
264 if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
266 SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
269 if (lpdis->itemState & ODS_SELECTED)
271 oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
272 oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
274 ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + fldrWidth,
275 lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
276 &(lpdis->rcItem), str, lstrlenW(str), NULL);
278 if (lpdis->itemState & ODS_SELECTED)
279 DrawFocusRect( lpdis->hDC, &(lpdis->rcItem) );
281 if (lpdis->itemState & ODS_SELECTED)
283 SetBkColor( lpdis->hDC, oldBk );
284 SetTextColor( lpdis->hDC, oldText );
286 DrawIconEx( lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, hFolder, 16, 16, 0, 0, DI_NORMAL );
287 HeapFree(GetProcessHeap(), 0, str);
290 if (lpdis->CtlType == ODT_COMBOBOX && lpdis->CtlID == cmb2)
293 if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
295 SendMessageW(lpdis->hwndItem, CB_GETLBTEXT, lpdis->itemID,
297 root[0] += str[2] - 'a';
298 switch(GetDriveTypeA(root))
300 case DRIVE_REMOVABLE: hIcon = hFloppy; break;
301 case DRIVE_CDROM: hIcon = hCDRom; break;
302 case DRIVE_REMOTE: hIcon = hNet; break;
304 default: hIcon = hHDisk; break;
306 if (lpdis->itemState & ODS_SELECTED)
308 oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
309 oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
311 ExtTextOutW(lpdis->hDC, lpdis->rcItem.left + fldrWidth,
312 lpdis->rcItem.top + 1, ETO_OPAQUE | ETO_CLIPPED,
313 &(lpdis->rcItem), str, lstrlenW(str), NULL);
315 if (lpdis->itemState & ODS_SELECTED)
317 SetBkColor( lpdis->hDC, oldBk );
318 SetTextColor( lpdis->hDC, oldText );
320 DrawIconEx( lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, hIcon, 16, 16, 0, 0, DI_NORMAL );
321 HeapFree(GetProcessHeap(), 0, str);
327 /***********************************************************************
328 * FD31_UpdateResult [internal]
329 * update the displayed file name (with path)
331 static void FD31_UpdateResult(const FD31_DATA *lfs, const WCHAR *tmpstr)
334 LPOPENFILENAMEW ofnW = lfs->ofnW;
335 LPOPENFILENAMEA ofnA = lfs->ofnA;
336 WCHAR tmpstr2[BUFFILE];
339 TRACE("%s\n", debugstr_w(tmpstr));
340 if(ofnW->Flags & OFN_NOVALIDATE)
343 GetCurrentDirectoryW(BUFFILE, tmpstr2);
344 lenstr2 = strlenW(tmpstr2);
346 tmpstr2[lenstr2++]='\\';
347 lstrcpynW(tmpstr2+lenstr2, tmpstr, BUFFILE-lenstr2);
348 if (!ofnW->lpstrFile)
351 lstrcpynW(ofnW->lpstrFile, tmpstr2, ofnW->nMaxFile);
353 /* set filename offset */
354 p = PathFindFileNameW(ofnW->lpstrFile);
355 ofnW->nFileOffset = (p - ofnW->lpstrFile);
357 /* set extension offset */
358 p = PathFindExtensionW(ofnW->lpstrFile);
359 ofnW->nFileExtension = (*p) ? (p - ofnW->lpstrFile) + 1 : 0;
361 TRACE("file %s, file offset %d, ext offset %d\n",
362 debugstr_w(ofnW->lpstrFile), ofnW->nFileOffset, ofnW->nFileExtension);
364 /* update the real client structures if any */
368 if (ofnW->nMaxFile &&
369 !WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFile, -1,
370 ofnA->lpstrFile, ofnA->nMaxFile, NULL, NULL ))
371 ofnA->lpstrFile[ofnA->nMaxFile-1] = 0;
373 /* offsets are not guaranteed to be the same in WCHAR to MULTIBYTE conversion */
374 /* set filename offset */
375 lpszTemp = PathFindFileNameA(ofnA->lpstrFile);
376 ofnA->nFileOffset = (lpszTemp - ofnA->lpstrFile);
378 /* set extension offset */
379 lpszTemp = PathFindExtensionA(ofnA->lpstrFile);
380 ofnA->nFileExtension = (*lpszTemp) ? (lpszTemp - ofnA->lpstrFile) + 1 : 0;
384 /***********************************************************************
385 * FD31_UpdateFileTitle [internal]
386 * update the displayed file name (without path)
388 static void FD31_UpdateFileTitle(const FD31_DATA *lfs)
391 LPOPENFILENAMEW ofnW = lfs->ofnW;
392 LPOPENFILENAMEA ofnA = lfs->ofnA;
394 if (ofnW->lpstrFileTitle != NULL)
396 lRet = SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETCURSEL, 0, 0);
397 SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETTEXT, lRet,
398 (LPARAM)ofnW->lpstrFileTitle );
401 if (!WideCharToMultiByte( CP_ACP, 0, ofnW->lpstrFileTitle, -1,
402 ofnA->lpstrFileTitle, ofnA->nMaxFileTitle, NULL, NULL ))
403 ofnA->lpstrFileTitle[ofnA->nMaxFileTitle-1] = 0;
408 /***********************************************************************
409 * FD31_DirListDblClick [internal]
411 static LRESULT FD31_DirListDblClick( const FD31_DATA *lfs )
414 HWND hWnd = lfs->hwnd;
416 WCHAR tmpstr[BUFFILE];
418 /* get the raw string (with brackets) */
419 lRet = SendDlgItemMessageW(hWnd, lst2, LB_GETCURSEL, 0, 0);
420 if (lRet == LB_ERR) return TRUE;
421 pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
422 SendDlgItemMessageW(hWnd, lst2, LB_GETTEXT, lRet,
424 strcpyW( tmpstr, pstr );
425 HeapFree(GetProcessHeap(), 0, pstr);
426 /* get the selected directory in tmpstr */
427 if (tmpstr[0] == '[')
429 tmpstr[lstrlenW(tmpstr) - 1] = 0;
430 strcpyW(tmpstr,tmpstr+1);
432 strcatW(tmpstr, FILE_bslash);
434 FD31_ScanDir(lfs->ofnW, hWnd, tmpstr);
438 if (FD31_CallWindowProc(lfs, lfs->lbselchstring, lst2,
439 MAKELONG(lRet,CD_LBSELCHANGE)))
445 /***********************************************************************
446 * FD31_FileListSelect [internal]
447 * called when a new item is picked in the file list
449 static LRESULT FD31_FileListSelect( const FD31_DATA *lfs )
452 HWND hWnd = lfs->hwnd;
455 lRet = SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETCURSEL, 0, 0);
459 /* set the edit control to the chosen file */
460 if ((pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
462 SendDlgItemMessageW(hWnd, lst1, LB_GETTEXT, lRet,
464 SetDlgItemTextW( hWnd, edt1, pstr );
465 HeapFree(GetProcessHeap(), 0, pstr);
469 FD31_CallWindowProc(lfs, lfs->lbselchstring, lst1,
470 MAKELONG(lRet,CD_LBSELCHANGE));
472 /* FIXME: for OFN_ALLOWMULTISELECT we need CD_LBSELSUB, CD_SELADD,
477 /***********************************************************************
478 * FD31_TestPath [internal]
479 * before accepting the file name, test if it includes wild cards
480 * tries to scan the directory and returns TRUE if no error.
482 static LRESULT FD31_TestPath( const FD31_DATA *lfs, LPWSTR path )
484 HWND hWnd = lfs->hwnd;
485 LPWSTR pBeginFileName, pstr2;
486 WCHAR tmpstr2[BUFFILE];
488 pBeginFileName = strrchrW(path, '\\');
489 if (pBeginFileName == NULL)
490 pBeginFileName = strrchrW(path, ':');
492 if (strchrW(path,'*') != NULL || strchrW(path,'?') != NULL)
494 /* edit control contains wildcards */
495 if (pBeginFileName != NULL)
497 lstrcpynW(tmpstr2, pBeginFileName + 1, BUFFILE);
498 *(pBeginFileName + 1) = 0;
502 strcpyW(tmpstr2, path);
503 if(!(lfs->ofnW->Flags & OFN_NOVALIDATE))
507 TRACE("path=%s, tmpstr2=%s\n", debugstr_w(path), debugstr_w(tmpstr2));
508 SetDlgItemTextW( hWnd, edt1, tmpstr2 );
509 FD31_ScanDir(lfs->ofnW, hWnd, path);
510 return (lfs->ofnW->Flags & OFN_NOVALIDATE) != 0;
513 /* no wildcards, we might have a directory or a filename */
514 /* try appending a wildcard and reading the directory */
516 pstr2 = path + lstrlenW(path);
517 if (pBeginFileName == NULL || *(pBeginFileName + 1) != 0)
518 strcatW(path, FILE_bslash);
520 /* if ScanDir succeeds, we have changed the directory */
521 if (FD31_ScanDir(lfs->ofnW, hWnd, path))
522 return FALSE; /* and path is not a valid file name */
524 /* if not, this must be a filename */
526 *pstr2 = 0; /* remove the wildcard added before */
528 if (pBeginFileName != NULL)
530 /* strip off the pathname */
532 SetDlgItemTextW( hWnd, edt1, pBeginFileName + 1 );
534 lstrcpynW(tmpstr2, pBeginFileName + 1, sizeof(tmpstr2)/sizeof(WCHAR) );
535 /* Should we MessageBox() if this fails? */
536 if (!FD31_ScanDir(lfs->ofnW, hWnd, path))
540 strcpyW(path, tmpstr2);
543 SetDlgItemTextW( hWnd, edt1, path );
547 /***********************************************************************
548 * FD31_Validate [internal]
549 * called on: click Ok button, Enter in edit, DoubleClick in file list
551 static LRESULT FD31_Validate( const FD31_DATA *lfs, LPCWSTR path, UINT control, INT itemIndex,
555 HWND hWnd = lfs->hwnd;
556 OPENFILENAMEW ofnsav;
557 LPOPENFILENAMEW ofnW = lfs->ofnW;
558 WCHAR filename[BUFFILE];
560 ofnsav = *ofnW; /* for later restoring */
562 /* get current file name */
564 lstrcpynW(filename, path, sizeof(filename)/sizeof(WCHAR));
566 GetDlgItemTextW( hWnd, edt1, filename, sizeof(filename)/sizeof(WCHAR));
568 TRACE("got filename = %s\n", debugstr_w(filename));
569 /* if we did not click in file list to get there */
572 if (!FD31_TestPath( lfs, filename) )
575 FD31_UpdateResult(lfs, filename);
578 { /* called internally after a change in a combo */
581 FD31_CallWindowProc(lfs, lfs->lbselchstring, control,
582 MAKELONG(itemIndex,CD_LBSELCHANGE));
587 FD31_UpdateFileTitle(lfs);
590 lRet = FD31_CallWindowProc(lfs, lfs->fileokstring,
594 *ofnW = ofnsav; /* restore old state */
598 if ((ofnW->Flags & OFN_ALLOWMULTISELECT) && (ofnW->Flags & OFN_EXPLORER))
602 LPWSTR str = ofnW->lpstrFile;
603 LPWSTR ptr = strrchrW(str, '\\');
604 str[lstrlenW(str) + 1] = '\0';
611 /***********************************************************************
612 * FD31_DiskChange [internal]
613 * called when a new item is picked in the disk selection combo
615 static LRESULT FD31_DiskChange( const FD31_DATA *lfs )
618 HWND hWnd = lfs->hwnd;
620 WCHAR diskname[BUFFILE];
622 FD31_StripEditControl(hWnd);
623 lRet = SendDlgItemMessageW(hWnd, cmb2, CB_GETCURSEL, 0, 0L);
626 pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
627 SendDlgItemMessageW(hWnd, cmb2, CB_GETLBTEXT, lRet,
629 wsprintfW(diskname, FILE_specc, pstr[2]);
630 HeapFree(GetProcessHeap(), 0, pstr);
632 return FD31_Validate( lfs, diskname, cmb2, lRet, TRUE );
635 /***********************************************************************
636 * FD31_FileTypeChange [internal]
637 * called when a new item is picked in the file type combo
639 static LRESULT FD31_FileTypeChange( const FD31_DATA *lfs )
644 lRet = SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETCURSEL, 0, 0);
647 lfs->ofnW->nFilterIndex = lRet + 1;
649 lfs->ofnA->nFilterIndex = lRet + 1;
650 pstr = (LPWSTR)SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETITEMDATA, lRet, 0);
651 TRACE("Selected filter : %s\n", debugstr_w(pstr));
653 return FD31_Validate( lfs, pstr, cmb1, lRet, TRUE );
656 /***********************************************************************
657 * FD31_WMCommand [internal]
659 static LRESULT FD31_WMCommand( HWND hWnd, LPARAM lParam, UINT notification,
660 UINT control, const FD31_DATA *lfs )
664 case lst1: /* file list */
665 FD31_StripEditControl(hWnd);
666 if (notification == LBN_DBLCLK)
668 return SendMessageW(hWnd, WM_COMMAND, IDOK, 0);
670 else if (notification == LBN_SELCHANGE)
671 return FD31_FileListSelect( lfs );
674 case lst2: /* directory list */
675 FD31_StripEditControl(hWnd);
676 if (notification == LBN_DBLCLK)
677 return FD31_DirListDblClick( lfs );
680 case cmb1: /* file type drop list */
681 if (notification == CBN_SELCHANGE)
682 return FD31_FileTypeChange( lfs );
691 case cmb2: /* disk dropdown combo */
692 if (notification == CBN_SELCHANGE)
693 return FD31_DiskChange( lfs );
697 TRACE("OK pressed\n");
698 if (FD31_Validate( lfs, NULL, control, 0, FALSE ))
699 EndDialog(hWnd, TRUE);
703 EndDialog(hWnd, FALSE);
706 case IDABORT: /* can be sent by the hook procedure */
707 EndDialog(hWnd, TRUE);
713 /************************************************************************
714 * FD31_MapStringPairsToW [internal]
715 * map string pairs to Unicode
717 static LPWSTR FD31_MapStringPairsToW(LPCSTR strA, UINT size)
727 n = s + 1 - strA; /* Don't forget the other \0 */
728 if (n < size) n = size;
730 len = MultiByteToWideChar( CP_ACP, 0, strA, n, NULL, 0 );
731 x = HeapAlloc(GetProcessHeap(),0, len * sizeof(WCHAR));
732 MultiByteToWideChar( CP_ACP, 0, strA, n, x, len );
737 /************************************************************************
738 * FD31_DupToW [internal]
739 * duplicates an Ansi string to unicode, with a buffer size
741 static LPWSTR FD31_DupToW(LPCSTR str, DWORD size)
744 if (str && (size > 0))
746 strW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
747 if (strW) MultiByteToWideChar( CP_ACP, 0, str, -1, strW, size );
752 /************************************************************************
753 * FD31_MapOfnStructA [internal]
754 * map a 32 bits Ansi structure to a Unicode one
756 static void FD31_MapOfnStructA(const OPENFILENAMEA *ofnA, LPOPENFILENAMEW ofnW, BOOL open)
758 UNICODE_STRING usBuffer;
760 ofnW->lStructSize = sizeof(OPENFILENAMEW);
761 ofnW->hwndOwner = ofnA->hwndOwner;
762 ofnW->hInstance = ofnA->hInstance;
763 if (ofnA->lpstrFilter)
764 ofnW->lpstrFilter = FD31_MapStringPairsToW(ofnA->lpstrFilter, 0);
766 if ((ofnA->lpstrCustomFilter) && (*(ofnA->lpstrCustomFilter)))
767 ofnW->lpstrCustomFilter = FD31_MapStringPairsToW(ofnA->lpstrCustomFilter, ofnA->nMaxCustFilter);
768 ofnW->nMaxCustFilter = ofnA->nMaxCustFilter;
769 ofnW->nFilterIndex = ofnA->nFilterIndex;
770 ofnW->nMaxFile = ofnA->nMaxFile;
771 ofnW->lpstrFile = FD31_DupToW(ofnA->lpstrFile, ofnW->nMaxFile);
772 ofnW->nMaxFileTitle = ofnA->nMaxFileTitle;
773 ofnW->lpstrFileTitle = FD31_DupToW(ofnA->lpstrFileTitle, ofnW->nMaxFileTitle);
774 if (ofnA->lpstrInitialDir)
776 RtlCreateUnicodeStringFromAsciiz (&usBuffer,ofnA->lpstrInitialDir);
777 ofnW->lpstrInitialDir = usBuffer.Buffer;
779 if (ofnA->lpstrTitle) {
780 RtlCreateUnicodeStringFromAsciiz (&usBuffer, ofnA->lpstrTitle);
781 ofnW->lpstrTitle = usBuffer.Buffer;
786 LoadStringW(COMDLG32_hInstance, open ? IDS_OPEN_FILE : IDS_SAVE_AS,
787 buf, sizeof(buf)/sizeof(WCHAR));
788 len = lstrlenW(buf)+1;
789 title_tmp = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
790 memcpy(title_tmp, buf, len * sizeof(WCHAR));
791 ofnW->lpstrTitle = title_tmp;
793 ofnW->Flags = ofnA->Flags;
794 ofnW->nFileOffset = ofnA->nFileOffset;
795 ofnW->nFileExtension = ofnA->nFileExtension;
796 ofnW->lpstrDefExt = FD31_DupToW(ofnA->lpstrDefExt, 3);
797 if ((ofnA->Flags & OFN_ENABLETEMPLATE) && (ofnA->lpTemplateName))
799 if (!IS_INTRESOURCE(ofnA->lpTemplateName))
801 RtlCreateUnicodeStringFromAsciiz (&usBuffer,ofnA->lpTemplateName);
802 ofnW->lpTemplateName = usBuffer.Buffer;
804 else /* numbered resource */
805 ofnW->lpTemplateName = (LPCWSTR) ofnA->lpTemplateName;
810 /************************************************************************
811 * FD31_FreeOfnW [internal]
812 * Undo all allocations done by FD31_MapOfnStructA
814 static void FD31_FreeOfnW(OPENFILENAMEW *ofnW)
816 HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrFilter);
817 HeapFree(GetProcessHeap(), 0, ofnW->lpstrCustomFilter);
818 HeapFree(GetProcessHeap(), 0, ofnW->lpstrFile);
819 HeapFree(GetProcessHeap(), 0, ofnW->lpstrFileTitle);
820 HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrInitialDir);
821 HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrTitle);
822 if (!IS_INTRESOURCE(ofnW->lpTemplateName))
823 HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpTemplateName);
826 /************************************************************************
827 * FD31_DestroyPrivate [internal]
828 * destroys the private object
830 static void FD31_DestroyPrivate(PFD31_DATA lfs)
835 TRACE("destroying private allocation %p\n", lfs);
837 /* if ofnW has been allocated, have to free everything in it */
840 FD31_FreeOfnW(lfs->ofnW);
841 HeapFree(GetProcessHeap(), 0, lfs->ofnW);
843 HeapFree(GetProcessHeap(), 0, lfs);
844 RemovePropA(hwnd, FD31_OFN_PROP);
847 /***********************************************************************
848 * FD31_GetTemplate [internal]
850 * Get a template (or FALSE if failure) when 16 bits dialogs are used
851 * by a 32 bits application
854 static BOOL FD31_GetTemplate(PFD31_DATA lfs)
856 LPOPENFILENAMEW ofnW = lfs->ofnW;
857 LPOPENFILENAMEA ofnA = lfs->ofnA;
860 if (ofnW->Flags & OFN_ENABLETEMPLATEHANDLE)
862 if (!(lfs->template = LockResource( ofnW->hInstance )))
864 COMDLG32_SetCommDlgExtendedError( CDERR_LOADRESFAILURE );
868 else if (ofnW->Flags & OFN_ENABLETEMPLATE)
872 hResInfo = FindResourceA( ofnA->hInstance, ofnA->lpTemplateName, (LPSTR)RT_DIALOG );
874 hResInfo = FindResourceW( ofnW->hInstance, ofnW->lpTemplateName, (LPWSTR)RT_DIALOG );
877 COMDLG32_SetCommDlgExtendedError( CDERR_FINDRESFAILURE );
880 if (!(hDlgTmpl = LoadResource( ofnW->hInstance, hResInfo )) ||
881 !(lfs->template = LockResource( hDlgTmpl )))
883 COMDLG32_SetCommDlgExtendedError( CDERR_LOADRESFAILURE );
887 else /* get it from internal Wine resource */
890 if (!(hResInfo = FindResourceA( COMDLG32_hInstance, lfs->open ? "OPEN_FILE" : "SAVE_FILE", (LPSTR)RT_DIALOG )))
892 COMDLG32_SetCommDlgExtendedError( CDERR_FINDRESFAILURE );
895 if (!(hDlgTmpl = LoadResource( COMDLG32_hInstance, hResInfo )) ||
896 !(lfs->template = LockResource( hDlgTmpl )))
898 COMDLG32_SetCommDlgExtendedError( CDERR_LOADRESFAILURE );
905 /************************************************************************
906 * FD31_AllocPrivate [internal]
907 * allocate a private object to hold 32 bits Unicode
908 * structure that will be used throughout the calls, while
909 * keeping available the original structures and a few variables
910 * On entry : type = dialog procedure type (16,32A,32W)
911 * dlgType = dialog type (open or save)
913 static PFD31_DATA FD31_AllocPrivate(LPARAM lParam, UINT dlgType, BOOL IsUnicode)
915 PFD31_DATA lfs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FD31_DATA));
917 TRACE("alloc private buf %p\n", lfs);
918 if (!lfs) return NULL;
920 lfs->lParam = lParam;
921 lfs->open = (dlgType == OPEN_DIALOG);
926 lfs->ofnW = (LPOPENFILENAMEW) lParam;
927 if (lfs->ofnW->Flags & OFN_ENABLEHOOK)
928 if (lfs->ofnW->lpfnHook)
933 lfs->ofnA = (LPOPENFILENAMEA) lParam;
934 if (lfs->ofnA->Flags & OFN_ENABLEHOOK)
935 if (lfs->ofnA->lpfnHook)
937 lfs->ofnW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lfs->ofnW));
938 FD31_MapOfnStructA(lfs->ofnA, lfs->ofnW, lfs->open);
941 if (! FD31_GetTemplate(lfs))
943 FD31_DestroyPrivate(lfs);
946 lfs->lbselchstring = RegisterWindowMessageA(LBSELCHSTRINGA);
947 lfs->fileokstring = RegisterWindowMessageA(FILEOKSTRINGA);
952 /***********************************************************************
953 * FD31_WMInitDialog [internal]
955 static LONG FD31_WMInitDialog(HWND hWnd, WPARAM wParam, LPARAM lParam)
958 WCHAR tmpstr[BUFFILE];
959 LPWSTR pstr, old_pstr;
961 PFD31_DATA lfs = (PFD31_DATA) lParam;
963 if (!lfs) return FALSE;
964 SetPropA(hWnd, FD31_OFN_PROP, lfs);
968 TRACE("flags=%x initialdir=%s\n", ofn->Flags, debugstr_w(ofn->lpstrInitialDir));
970 SetWindowTextW( hWnd, ofn->lpstrTitle );
971 /* read custom filter information */
972 if (ofn->lpstrCustomFilter)
974 pstr = ofn->lpstrCustomFilter;
976 TRACE("lpstrCustomFilter = %p\n", pstr);
980 i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
981 (LPARAM)(ofn->lpstrCustomFilter) + n );
982 n += lstrlenW(pstr) + 1;
983 pstr += lstrlenW(pstr) + 1;
984 TRACE("add str=%s associated to %s\n",
985 debugstr_w(old_pstr), debugstr_w(pstr));
986 SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
987 n += lstrlenW(pstr) + 1;
988 pstr += lstrlenW(pstr) + 1;
991 /* read filter information */
992 if (ofn->lpstrFilter) {
993 pstr = (LPWSTR) ofn->lpstrFilter;
997 i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
998 (LPARAM)(ofn->lpstrFilter + n) );
999 n += lstrlenW(pstr) + 1;
1000 pstr += lstrlenW(pstr) + 1;
1001 TRACE("add str=%s associated to %s\n",
1002 debugstr_w(old_pstr), debugstr_w(pstr));
1003 SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
1004 n += lstrlenW(pstr) + 1;
1005 pstr += lstrlenW(pstr) + 1;
1008 /* set default filter */
1009 if (ofn->nFilterIndex == 0 && ofn->lpstrCustomFilter == NULL)
1010 ofn->nFilterIndex = 1;
1011 SendDlgItemMessageW(hWnd, cmb1, CB_SETCURSEL, ofn->nFilterIndex - 1, 0);
1012 if (ofn->lpstrFile && ofn->lpstrFile[0])
1014 TRACE( "SetText of edt1 to %s\n", debugstr_w(ofn->lpstrFile) );
1015 SetDlgItemTextW( hWnd, edt1, ofn->lpstrFile );
1019 lstrcpynW(tmpstr, FD31_GetFileType(ofn->lpstrCustomFilter,
1020 ofn->lpstrFilter, ofn->nFilterIndex - 1),BUFFILE);
1021 TRACE("nFilterIndex = %d, SetText of edt1 to %s\n",
1022 ofn->nFilterIndex, debugstr_w(tmpstr));
1023 SetDlgItemTextW( hWnd, edt1, tmpstr );
1025 /* get drive list */
1027 DlgDirListComboBoxW(hWnd, tmpstr, cmb2, 0, DDL_DRIVES | DDL_EXCLUSIVE);
1028 /* read initial directory */
1029 /* FIXME: Note that this is now very version-specific (See MSDN description of
1030 * the OPENFILENAME structure). For example under 2000/XP any path in the
1031 * lpstrFile overrides the lpstrInitialDir, but not under 95/98/ME
1033 if (ofn->lpstrInitialDir != NULL)
1036 lstrcpynW(tmpstr, ofn->lpstrInitialDir, 511);
1037 len = lstrlenW(tmpstr);
1038 if (len > 0 && tmpstr[len-1] != '\\' && tmpstr[len-1] != ':') {
1045 if (!FD31_ScanDir(ofn, hWnd, tmpstr)) {
1047 if (!FD31_ScanDir(ofn, hWnd, tmpstr))
1048 WARN("Couldn't read initial directory %s!\n", debugstr_w(tmpstr));
1050 /* select current drive in combo 2, omit missing drives */
1053 char str[4] = "a:\\";
1054 GetCurrentDirectoryA( sizeof(dir), dir );
1055 for(i = 0, n = -1; i < 26; i++)
1058 if (GetDriveTypeA(str) > DRIVE_NO_ROOT_DIR) n++;
1059 if (toupper(str[0]) == toupper(dir[0])) break;
1062 SendDlgItemMessageW(hWnd, cmb2, CB_SETCURSEL, n, 0);
1063 if (!(ofn->Flags & OFN_SHOWHELP))
1064 ShowWindow(GetDlgItem(hWnd, pshHelp), SW_HIDE);
1065 if (ofn->Flags & OFN_HIDEREADONLY)
1066 ShowWindow(GetDlgItem(hWnd, chx1), SW_HIDE);
1068 return FD31_CallWindowProc(lfs, WM_INITDIALOG, wParam, lfs->lParam);
1072 int FD31_GetFldrHeight(void)
1077 /***********************************************************************
1078 * FD31_WMMeasureItem [internal]
1080 static LONG FD31_WMMeasureItem(LPARAM lParam)
1082 LPMEASUREITEMSTRUCT lpmeasure;
1084 lpmeasure = (LPMEASUREITEMSTRUCT)lParam;
1085 lpmeasure->itemHeight = FD31_GetFldrHeight();
1090 /***********************************************************************
1091 * FileOpenDlgProc [internal]
1092 * Used for open and save, in fact.
1094 static INT_PTR CALLBACK FD31_FileOpenDlgProc(HWND hWnd, UINT wMsg,
1095 WPARAM wParam, LPARAM lParam)
1097 PFD31_DATA lfs = (PFD31_DATA)GetPropA( hWnd, FD31_OFN_PROP );
1099 TRACE("msg=%x wparam=%lx lParam=%lx\n", wMsg, wParam, lParam);
1100 if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
1103 lRet = (INT_PTR)FD31_CallWindowProc( lfs, wMsg, wParam, lParam );
1104 if (lRet) return lRet; /* else continue message processing */
1109 return FD31_WMInitDialog( hWnd, wParam, lParam );
1111 case WM_MEASUREITEM:
1112 return FD31_WMMeasureItem( lParam );
1115 return FD31_WMDrawItem( hWnd, wParam, lParam, !lfs->open, (DRAWITEMSTRUCT *)lParam );
1118 return FD31_WMCommand( hWnd, lParam, HIWORD(wParam), LOWORD(wParam), lfs );
1121 SetBkColor( (HDC16)wParam, 0x00C0C0C0 );
1122 switch (HIWORD(lParam))
1125 SetTextColor( (HDC16)wParam, 0x00000000 );
1127 case CTLCOLOR_STATIC:
1128 SetTextColor( (HDC16)wParam, 0x00000000 );
1137 /***********************************************************************
1138 * GetFileName31A [internal]
1140 * Creates a win31 style dialog box for the user to select a file to open/save.
1142 BOOL GetFileName31A( OPENFILENAMEA *lpofn, UINT dlgType )
1147 if (!lpofn || !FD31_Init()) return FALSE;
1149 TRACE("ofn flags %08x\n", lpofn->Flags);
1150 lfs = FD31_AllocPrivate((LPARAM) lpofn, dlgType, FALSE);
1153 bRet = DialogBoxIndirectParamA( COMDLG32_hInstance, lfs->template, lpofn->hwndOwner,
1154 FD31_FileOpenDlgProc, (LPARAM)lfs);
1155 FD31_DestroyPrivate(lfs);
1158 TRACE("return lpstrFile='%s' !\n", lpofn->lpstrFile);
1162 /***********************************************************************
1163 * GetFileName31W [internal]
1165 * Creates a win31 style dialog box for the user to select a file to open/save
1167 BOOL GetFileName31W( OPENFILENAMEW *lpofn, UINT dlgType )
1172 if (!lpofn || !FD31_Init()) return FALSE;
1174 lfs = FD31_AllocPrivate((LPARAM) lpofn, dlgType, TRUE);
1177 bRet = DialogBoxIndirectParamW( COMDLG32_hInstance, lfs->template, lpofn->hwndOwner,
1178 FD31_FileOpenDlgProc, (LPARAM)lfs);
1179 FD31_DestroyPrivate(lfs);
1182 TRACE("file %s, file offset %d, ext offset %d\n",
1183 debugstr_w(lpofn->lpstrFile), lpofn->nFileOffset, lpofn->nFileExtension);