comctl32/tooltips: Reset window subclass data when removing tools.
[wine.git] / dlls / comdlg32 / filedlg31.c
blob040991573746c24720d1c2b82fa538c459d48f85
1 /*
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
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 "winreg.h"
34 #include "winternl.h"
35 #include "commdlg.h"
36 #include "shlwapi.h"
37 #include "cderr.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
41 #include "cdlg.h"
43 #define BUFFILE 512
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 = FALSE;
82 if (!initialized) {
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");
93 return FALSE;
95 initialized = TRUE;
97 return TRUE;
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, '\\');
110 if (cp != NULL) {
111 strcpyW(temp, cp+1);
113 cp = strrchrW(temp, ':');
114 if (cp != NULL) {
115 strcpyW(temp, cp+1);
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)
127 BOOL ret;
129 if (lfs->ofnA)
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);
136 return ret;
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);
144 return ret;
147 /***********************************************************************
148 * FD31_GetFileType [internal]
150 static LPCWSTR FD31_GetFileType(LPCWSTR cfptr, LPCWSTR fptr, const WORD index)
152 int n, i;
153 i = 0;
154 if (cfptr)
155 for ( ;(n = lstrlenW(cfptr)) != 0; i++)
157 cfptr += n + 1;
158 if (i == index)
159 return cfptr;
160 cfptr += lstrlenW(cfptr) + 1;
162 if (fptr)
163 for ( ;(n = lstrlenW(fptr)) != 0; i++)
165 fptr += n + 1;
166 if (i == index)
167 return fptr;
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];
179 HWND hdlg;
180 LRESULT lRet = TRUE;
181 HCURSOR hCursorWait, oldCursor;
183 TRACE("Trying to change to %s\n", debugstr_w(newPath));
184 if ( newPath[0] && !SetCurrentDirectoryW( newPath ))
185 return FALSE;
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);
194 /* list of files */
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);
201 while (filter) {
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);
219 return lRet;
222 /***********************************************************************
223 * FD31_WMDrawItem [internal]
225 static LONG FD31_WMDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam,
226 int savedlg, const DRAWITEMSTRUCT *lpdis)
228 WCHAR *str;
229 HICON hIcon;
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,
236 (LPARAM)str);
238 if ((lpdis->itemState & ODS_SELECTED) && !savedlg)
240 oldBk = SetBkColor( lpdis->hDC, GetSysColor( COLOR_HIGHLIGHT ) );
241 oldText = SetTextColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
243 if (savedlg)
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);
259 return TRUE;
262 if (lpdis->CtlType == ODT_LISTBOX && lpdis->CtlID == lst2)
264 if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
265 return FALSE;
266 SendMessageW(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID,
267 (LPARAM)str);
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);
288 return TRUE;
290 if (lpdis->CtlType == ODT_COMBOBOX && lpdis->CtlID == cmb2)
292 char root[] = "a:";
293 if (!(str = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
294 return FALSE;
295 SendMessageW(lpdis->hwndItem, CB_GETLBTEXT, lpdis->itemID,
296 (LPARAM)str);
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;
303 case DRIVE_FIXED:
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);
322 return TRUE;
324 return FALSE;
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)
333 int lenstr2;
334 LPOPENFILENAMEW ofnW = lfs->ofnW;
335 LPOPENFILENAMEA ofnA = lfs->ofnA;
336 WCHAR tmpstr2[BUFFILE];
337 WCHAR *p;
339 TRACE("%s\n", debugstr_w(tmpstr));
340 if(ofnW->Flags & OFN_NOVALIDATE)
341 tmpstr2[0] = '\0';
342 else
343 GetCurrentDirectoryW(BUFFILE, tmpstr2);
344 lenstr2 = strlenW(tmpstr2);
345 if (lenstr2 > 3)
346 tmpstr2[lenstr2++]='\\';
347 lstrcpynW(tmpstr2+lenstr2, tmpstr, BUFFILE-lenstr2);
348 if (!ofnW->lpstrFile)
349 return;
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 */
365 if (ofnA)
367 LPSTR lpszTemp;
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)
390 LONG lRet;
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 );
399 if (ofnA)
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 )
413 LONG lRet;
414 HWND hWnd = lfs->hwnd;
415 LPWSTR pstr;
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,
423 (LPARAM)pstr);
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);
435 /* notify the app */
436 if (lfs->hook)
438 if (FD31_CallWindowProc(lfs, lfs->lbselchstring, lst2,
439 MAKELONG(lRet,CD_LBSELCHANGE)))
440 return TRUE;
442 return TRUE;
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 )
451 LONG lRet;
452 HWND hWnd = lfs->hwnd;
453 LPWSTR pstr;
455 lRet = SendDlgItemMessageW(lfs->hwnd, lst1, LB_GETCURSEL, 0, 0);
456 if (lRet == LB_ERR)
457 return TRUE;
459 /* set the edit control to the chosen file */
460 if ((pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC)))
462 SendDlgItemMessageW(hWnd, lst1, LB_GETTEXT, lRet,
463 (LPARAM)pstr);
464 SetDlgItemTextW( hWnd, edt1, pstr );
465 HeapFree(GetProcessHeap(), 0, pstr);
467 if (lfs->hook)
469 FD31_CallWindowProc(lfs, lfs->lbselchstring, lst1,
470 MAKELONG(lRet,CD_LBSELCHANGE));
472 /* FIXME: for OFN_ALLOWMULTISELECT we need CD_LBSELSUB, CD_SELADD,
473 CD_LBSELNOITEMS */
474 return TRUE;
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;
500 else
502 strcpyW(tmpstr2, path);
503 if(!(lfs->ofnW->Flags & OFN_NOVALIDATE))
504 *path = 0;
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 */
531 *pBeginFileName = 0;
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))
538 return FALSE;
540 strcpyW(path, tmpstr2);
542 else
543 SetDlgItemTextW( hWnd, edt1, path );
544 return TRUE;
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,
552 BOOL internalUse )
554 LONG lRet;
555 HWND hWnd = lfs->hwnd;
556 OPENFILENAMEW ofnsav;
557 LPOPENFILENAMEW ofnW = lfs->ofnW;
558 WCHAR filename[BUFFILE];
559 int copied_size = min( ofnW->lStructSize, sizeof(ofnsav) );
561 memcpy( &ofnsav, ofnW, copied_size ); /* for later restoring */
563 /* get current file name */
564 if (path)
565 lstrcpynW(filename, path, sizeof(filename)/sizeof(WCHAR));
566 else
567 GetDlgItemTextW( hWnd, edt1, filename, sizeof(filename)/sizeof(WCHAR));
569 TRACE("got filename = %s\n", debugstr_w(filename));
570 /* if we did not click in file list to get there */
571 if (control != lst1)
573 if (!FD31_TestPath( lfs, filename) )
574 return FALSE;
576 FD31_UpdateResult(lfs, filename);
578 if (internalUse)
579 { /* called internally after a change in a combo */
580 if (lfs->hook)
582 FD31_CallWindowProc(lfs, lfs->lbselchstring, control,
583 MAKELONG(itemIndex,CD_LBSELCHANGE));
585 return TRUE;
588 FD31_UpdateFileTitle(lfs);
589 if (lfs->hook)
591 lRet = FD31_CallWindowProc(lfs, lfs->fileokstring,
592 0, lfs->lParam );
593 if (lRet)
595 memcpy( ofnW, &ofnsav, copied_size ); /* restore old state */
596 return FALSE;
599 if ((ofnW->Flags & OFN_ALLOWMULTISELECT) && (ofnW->Flags & OFN_EXPLORER))
601 if (ofnW->lpstrFile)
603 LPWSTR str = ofnW->lpstrFile;
604 LPWSTR ptr = strrchrW(str, '\\');
605 str[lstrlenW(str) + 1] = '\0';
606 *ptr = 0;
609 return TRUE;
612 /***********************************************************************
613 * FD31_DiskChange [internal]
614 * called when a new item is picked in the disk selection combo
616 static LRESULT FD31_DiskChange( const FD31_DATA *lfs )
618 LONG lRet;
619 HWND hWnd = lfs->hwnd;
620 LPWSTR pstr;
621 WCHAR diskname[BUFFILE];
623 FD31_StripEditControl(hWnd);
624 lRet = SendDlgItemMessageW(hWnd, cmb2, CB_GETCURSEL, 0, 0L);
625 if (lRet == LB_ERR)
626 return 0;
627 pstr = HeapAlloc(GetProcessHeap(), 0, BUFFILEALLOC);
628 SendDlgItemMessageW(hWnd, cmb2, CB_GETLBTEXT, lRet,
629 (LPARAM)pstr);
630 wsprintfW(diskname, FILE_specc, pstr[2]);
631 HeapFree(GetProcessHeap(), 0, pstr);
633 return FD31_Validate( lfs, diskname, cmb2, lRet, TRUE );
636 /***********************************************************************
637 * FD31_FileTypeChange [internal]
638 * called when a new item is picked in the file type combo
640 static LRESULT FD31_FileTypeChange( const FD31_DATA *lfs )
642 LONG lRet;
643 LPWSTR pstr;
645 lRet = SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETCURSEL, 0, 0);
646 if (lRet == LB_ERR)
647 return TRUE;
648 lfs->ofnW->nFilterIndex = lRet + 1;
649 if (lfs->ofnA)
650 lfs->ofnA->nFilterIndex = lRet + 1;
651 pstr = (LPWSTR)SendDlgItemMessageW(lfs->hwnd, cmb1, CB_GETITEMDATA, lRet, 0);
652 TRACE("Selected filter : %s\n", debugstr_w(pstr));
654 return FD31_Validate( lfs, pstr, cmb1, lRet, TRUE );
657 /***********************************************************************
658 * FD31_WMCommand [internal]
660 static LRESULT FD31_WMCommand( HWND hWnd, LPARAM lParam, UINT notification,
661 UINT control, const FD31_DATA *lfs )
663 switch (control)
665 case lst1: /* file list */
666 FD31_StripEditControl(hWnd);
667 if (notification == LBN_DBLCLK)
669 return SendMessageW(hWnd, WM_COMMAND, IDOK, 0);
671 else if (notification == LBN_SELCHANGE)
672 return FD31_FileListSelect( lfs );
673 break;
675 case lst2: /* directory list */
676 FD31_StripEditControl(hWnd);
677 if (notification == LBN_DBLCLK)
678 return FD31_DirListDblClick( lfs );
679 break;
681 case cmb1: /* file type drop list */
682 if (notification == CBN_SELCHANGE)
683 return FD31_FileTypeChange( lfs );
684 break;
686 case chx1:
687 break;
689 case pshHelp:
690 break;
692 case cmb2: /* disk dropdown combo */
693 if (notification == CBN_SELCHANGE)
694 return FD31_DiskChange( lfs );
695 break;
697 case IDOK:
698 TRACE("OK pressed\n");
699 if (FD31_Validate( lfs, NULL, control, 0, FALSE ))
700 EndDialog(hWnd, TRUE);
701 return TRUE;
703 case IDCANCEL:
704 EndDialog(hWnd, FALSE);
705 return TRUE;
707 case IDABORT: /* can be sent by the hook procedure */
708 EndDialog(hWnd, TRUE);
709 return TRUE;
711 return FALSE;
714 /************************************************************************
715 * FD31_MapStringPairsToW [internal]
716 * map string pairs to Unicode
718 static LPWSTR FD31_MapStringPairsToW(LPCSTR strA, UINT size)
720 LPCSTR s;
721 LPWSTR x;
722 unsigned int n, len;
724 s = strA;
725 while (*s)
726 s = s+strlen(s)+1;
727 s++;
728 n = s + 1 - strA; /* Don't forget the other \0 */
729 if (n < size) n = size;
731 len = MultiByteToWideChar( CP_ACP, 0, strA, n, NULL, 0 );
732 x = HeapAlloc(GetProcessHeap(),0, len * sizeof(WCHAR));
733 MultiByteToWideChar( CP_ACP, 0, strA, n, x, len );
734 return x;
738 /************************************************************************
739 * FD31_DupToW [internal]
740 * duplicates an Ansi string to unicode, with a buffer size
742 static LPWSTR FD31_DupToW(LPCSTR str, DWORD size)
744 LPWSTR strW = NULL;
745 if (str && (size > 0))
747 strW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
748 if (strW) MultiByteToWideChar( CP_ACP, 0, str, -1, strW, size );
750 return strW;
753 /************************************************************************
754 * FD31_MapOfnStructA [internal]
755 * map a 32 bits Ansi structure to a Unicode one
757 static void FD31_MapOfnStructA(const OPENFILENAMEA *ofnA, LPOPENFILENAMEW ofnW, BOOL open)
759 UNICODE_STRING usBuffer;
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;
782 } else {
783 WCHAR buf[16];
784 LPWSTR title_tmp;
785 int len;
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;
807 if (ofnW->lStructSize > OPENFILENAME_SIZE_VERSION_400W)
809 ofnW->pvReserved = ofnA->pvReserved;
810 ofnW->dwReserved = ofnA->dwReserved;
811 ofnW->FlagsEx = ofnA->FlagsEx;
816 /************************************************************************
817 * FD31_FreeOfnW [internal]
818 * Undo all allocations done by FD31_MapOfnStructA
820 static void FD31_FreeOfnW(OPENFILENAMEW *ofnW)
822 HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrFilter);
823 HeapFree(GetProcessHeap(), 0, ofnW->lpstrCustomFilter);
824 HeapFree(GetProcessHeap(), 0, ofnW->lpstrFile);
825 HeapFree(GetProcessHeap(), 0, ofnW->lpstrFileTitle);
826 HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrInitialDir);
827 HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpstrTitle);
828 if (!IS_INTRESOURCE(ofnW->lpTemplateName))
829 HeapFree(GetProcessHeap(), 0, (LPWSTR) ofnW->lpTemplateName);
832 /************************************************************************
833 * FD31_DestroyPrivate [internal]
834 * destroys the private object
836 static void FD31_DestroyPrivate(PFD31_DATA lfs)
838 HWND hwnd;
839 if (!lfs) return;
840 hwnd = lfs->hwnd;
841 TRACE("destroying private allocation %p\n", lfs);
843 /* if ofnW has been allocated, have to free everything in it */
844 if (lfs->ofnA)
846 FD31_FreeOfnW(lfs->ofnW);
847 HeapFree(GetProcessHeap(), 0, lfs->ofnW);
849 HeapFree(GetProcessHeap(), 0, lfs);
850 RemovePropA(hwnd, FD31_OFN_PROP);
853 /***********************************************************************
854 * FD31_GetTemplate [internal]
856 * Get a template (or FALSE if failure) when 16 bits dialogs are used
857 * by a 32 bits application
860 static BOOL FD31_GetTemplate(PFD31_DATA lfs)
862 LPOPENFILENAMEW ofnW = lfs->ofnW;
863 LPOPENFILENAMEA ofnA = lfs->ofnA;
864 HANDLE hDlgTmpl;
866 if (ofnW->Flags & OFN_ENABLETEMPLATEHANDLE)
868 if (!(lfs->template = LockResource( ofnW->hInstance )))
870 COMDLG32_SetCommDlgExtendedError( CDERR_LOADRESFAILURE );
871 return FALSE;
874 else if (ofnW->Flags & OFN_ENABLETEMPLATE)
876 HRSRC hResInfo;
877 if (ofnA)
878 hResInfo = FindResourceA( ofnA->hInstance, ofnA->lpTemplateName, (LPSTR)RT_DIALOG );
879 else
880 hResInfo = FindResourceW( ofnW->hInstance, ofnW->lpTemplateName, (LPWSTR)RT_DIALOG );
881 if (!hResInfo)
883 COMDLG32_SetCommDlgExtendedError( CDERR_FINDRESFAILURE );
884 return FALSE;
886 if (!(hDlgTmpl = LoadResource( ofnW->hInstance, hResInfo )) ||
887 !(lfs->template = LockResource( hDlgTmpl )))
889 COMDLG32_SetCommDlgExtendedError( CDERR_LOADRESFAILURE );
890 return FALSE;
893 else /* get it from internal Wine resource */
895 HRSRC hResInfo;
896 if (!(hResInfo = FindResourceA( COMDLG32_hInstance, lfs->open ? "OPEN_FILE" : "SAVE_FILE", (LPSTR)RT_DIALOG )))
898 COMDLG32_SetCommDlgExtendedError( CDERR_FINDRESFAILURE );
899 return FALSE;
901 if (!(hDlgTmpl = LoadResource( COMDLG32_hInstance, hResInfo )) ||
902 !(lfs->template = LockResource( hDlgTmpl )))
904 COMDLG32_SetCommDlgExtendedError( CDERR_LOADRESFAILURE );
905 return FALSE;
908 return TRUE;
911 /************************************************************************
912 * FD31_AllocPrivate [internal]
913 * allocate a private object to hold 32 bits Unicode
914 * structure that will be used throughout the calls, while
915 * keeping available the original structures and a few variables
916 * On entry : type = dialog procedure type (16,32A,32W)
917 * dlgType = dialog type (open or save)
919 static PFD31_DATA FD31_AllocPrivate(LPARAM lParam, UINT dlgType, BOOL IsUnicode)
921 PFD31_DATA lfs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FD31_DATA));
923 TRACE("alloc private buf %p\n", lfs);
924 if (!lfs) return NULL;
925 lfs->hook = FALSE;
926 lfs->lParam = lParam;
927 lfs->open = (dlgType == OPEN_DIALOG);
929 if (IsUnicode)
931 lfs->ofnA = NULL;
932 lfs->ofnW = (LPOPENFILENAMEW) lParam;
933 if (lfs->ofnW->Flags & OFN_ENABLEHOOK)
934 if (lfs->ofnW->lpfnHook)
935 lfs->hook = TRUE;
937 else
939 lfs->ofnA = (LPOPENFILENAMEA) lParam;
940 if (lfs->ofnA->Flags & OFN_ENABLEHOOK)
941 if (lfs->ofnA->lpfnHook)
942 lfs->hook = TRUE;
943 lfs->ofnW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lfs->ofnA->lStructSize);
944 lfs->ofnW->lStructSize = lfs->ofnA->lStructSize;
945 FD31_MapOfnStructA(lfs->ofnA, lfs->ofnW, lfs->open);
948 if (! FD31_GetTemplate(lfs))
950 FD31_DestroyPrivate(lfs);
951 return NULL;
953 lfs->lbselchstring = RegisterWindowMessageA(LBSELCHSTRINGA);
954 lfs->fileokstring = RegisterWindowMessageA(FILEOKSTRINGA);
956 return lfs;
959 /***********************************************************************
960 * FD31_WMInitDialog [internal]
962 static LONG FD31_WMInitDialog(HWND hWnd, WPARAM wParam, LPARAM lParam)
964 int i, n;
965 WCHAR tmpstr[BUFFILE];
966 LPWSTR pstr, old_pstr;
967 LPOPENFILENAMEW ofn;
968 PFD31_DATA lfs = (PFD31_DATA) lParam;
970 if (!lfs) return FALSE;
971 SetPropA(hWnd, FD31_OFN_PROP, lfs);
972 lfs->hwnd = hWnd;
973 ofn = lfs->ofnW;
975 TRACE("flags=%x initialdir=%s\n", ofn->Flags, debugstr_w(ofn->lpstrInitialDir));
977 SetWindowTextW( hWnd, ofn->lpstrTitle );
978 /* read custom filter information */
979 if (ofn->lpstrCustomFilter)
981 pstr = ofn->lpstrCustomFilter;
982 n = 0;
983 TRACE("lpstrCustomFilter = %p\n", pstr);
984 while(*pstr)
986 old_pstr = pstr;
987 i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
988 (LPARAM)(ofn->lpstrCustomFilter) + n );
989 n += lstrlenW(pstr) + 1;
990 pstr += lstrlenW(pstr) + 1;
991 TRACE("add str=%s associated to %s\n",
992 debugstr_w(old_pstr), debugstr_w(pstr));
993 SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
994 n += lstrlenW(pstr) + 1;
995 pstr += lstrlenW(pstr) + 1;
998 /* read filter information */
999 if (ofn->lpstrFilter) {
1000 pstr = (LPWSTR) ofn->lpstrFilter;
1001 n = 0;
1002 while(*pstr) {
1003 old_pstr = pstr;
1004 i = SendDlgItemMessageW(hWnd, cmb1, CB_ADDSTRING, 0,
1005 (LPARAM)(ofn->lpstrFilter + n) );
1006 n += lstrlenW(pstr) + 1;
1007 pstr += lstrlenW(pstr) + 1;
1008 TRACE("add str=%s associated to %s\n",
1009 debugstr_w(old_pstr), debugstr_w(pstr));
1010 SendDlgItemMessageW(hWnd, cmb1, CB_SETITEMDATA, i, (LPARAM)pstr);
1011 n += lstrlenW(pstr) + 1;
1012 pstr += lstrlenW(pstr) + 1;
1015 /* set default filter */
1016 if (ofn->nFilterIndex == 0 && ofn->lpstrCustomFilter == NULL)
1017 ofn->nFilterIndex = 1;
1018 SendDlgItemMessageW(hWnd, cmb1, CB_SETCURSEL, ofn->nFilterIndex - 1, 0);
1019 if (ofn->lpstrFile && ofn->lpstrFile[0])
1021 TRACE( "SetText of edt1 to %s\n", debugstr_w(ofn->lpstrFile) );
1022 SetDlgItemTextW( hWnd, edt1, ofn->lpstrFile );
1024 else
1026 lstrcpynW(tmpstr, FD31_GetFileType(ofn->lpstrCustomFilter,
1027 ofn->lpstrFilter, ofn->nFilterIndex - 1),BUFFILE);
1028 TRACE("nFilterIndex = %d, SetText of edt1 to %s\n",
1029 ofn->nFilterIndex, debugstr_w(tmpstr));
1030 SetDlgItemTextW( hWnd, edt1, tmpstr );
1032 /* get drive list */
1033 *tmpstr = 0;
1034 DlgDirListComboBoxW(hWnd, tmpstr, cmb2, 0, DDL_DRIVES | DDL_EXCLUSIVE);
1035 /* read initial directory */
1036 /* FIXME: Note that this is now very version-specific (See MSDN description of
1037 * the OPENFILENAME structure). For example under 2000/XP any path in the
1038 * lpstrFile overrides the lpstrInitialDir, but not under 95/98/ME
1040 if (ofn->lpstrInitialDir != NULL)
1042 int len;
1043 lstrcpynW(tmpstr, ofn->lpstrInitialDir, 511);
1044 len = lstrlenW(tmpstr);
1045 if (len > 0 && tmpstr[len-1] != '\\' && tmpstr[len-1] != ':') {
1046 tmpstr[len]='\\';
1047 tmpstr[len+1]='\0';
1050 else
1051 *tmpstr = 0;
1052 if (!FD31_ScanDir(ofn, hWnd, tmpstr)) {
1053 *tmpstr = 0;
1054 if (!FD31_ScanDir(ofn, hWnd, tmpstr))
1055 WARN("Couldn't read initial directory %s!\n", debugstr_w(tmpstr));
1057 /* select current drive in combo 2, omit missing drives */
1059 char dir[MAX_PATH];
1060 char str[4] = "a:\\";
1061 GetCurrentDirectoryA( sizeof(dir), dir );
1062 for(i = 0, n = -1; i < 26; i++)
1064 str[0] = 'a' + i;
1065 if (GetDriveTypeA(str) > DRIVE_NO_ROOT_DIR) n++;
1066 if (toupper(str[0]) == toupper(dir[0])) break;
1069 SendDlgItemMessageW(hWnd, cmb2, CB_SETCURSEL, n, 0);
1070 if (!(ofn->Flags & OFN_SHOWHELP))
1071 ShowWindow(GetDlgItem(hWnd, pshHelp), SW_HIDE);
1072 if (ofn->Flags & OFN_HIDEREADONLY)
1073 ShowWindow(GetDlgItem(hWnd, chx1), SW_HIDE);
1074 if (lfs->hook)
1075 return FD31_CallWindowProc(lfs, WM_INITDIALOG, wParam, lfs->lParam);
1076 return TRUE;
1079 static int FD31_GetFldrHeight(void)
1081 return fldrHeight;
1084 /***********************************************************************
1085 * FD31_WMMeasureItem [internal]
1087 static LONG FD31_WMMeasureItem(LPARAM lParam)
1089 LPMEASUREITEMSTRUCT lpmeasure;
1091 lpmeasure = (LPMEASUREITEMSTRUCT)lParam;
1092 lpmeasure->itemHeight = FD31_GetFldrHeight();
1093 return TRUE;
1097 /***********************************************************************
1098 * FileOpenDlgProc [internal]
1099 * Used for open and save, in fact.
1101 static INT_PTR CALLBACK FD31_FileOpenDlgProc(HWND hWnd, UINT wMsg,
1102 WPARAM wParam, LPARAM lParam)
1104 PFD31_DATA lfs = (PFD31_DATA)GetPropA( hWnd, FD31_OFN_PROP );
1106 TRACE("msg=%x wparam=%lx lParam=%lx\n", wMsg, wParam, lParam);
1107 if ((wMsg != WM_INITDIALOG) && lfs && lfs->hook)
1109 INT_PTR lRet;
1110 lRet = (INT_PTR)FD31_CallWindowProc( lfs, wMsg, wParam, lParam );
1111 if (lRet) return lRet; /* else continue message processing */
1113 switch (wMsg)
1115 case WM_INITDIALOG:
1116 return FD31_WMInitDialog( hWnd, wParam, lParam );
1118 case WM_MEASUREITEM:
1119 return FD31_WMMeasureItem( lParam );
1121 case WM_DRAWITEM:
1122 return FD31_WMDrawItem( hWnd, wParam, lParam, !lfs->open, (DRAWITEMSTRUCT *)lParam );
1124 case WM_COMMAND:
1125 return FD31_WMCommand( hWnd, lParam, HIWORD(wParam), LOWORD(wParam), lfs );
1126 #if 0
1127 case WM_CTLCOLOR:
1128 SetBkColor( (HDC16)wParam, 0x00C0C0C0 );
1129 switch (HIWORD(lParam))
1131 case CTLCOLOR_BTN:
1132 SetTextColor( (HDC16)wParam, 0x00000000 );
1133 return hGRAYBrush;
1134 case CTLCOLOR_STATIC:
1135 SetTextColor( (HDC16)wParam, 0x00000000 );
1136 return hGRAYBrush;
1138 break;
1139 #endif
1141 return FALSE;
1144 /***********************************************************************
1145 * GetFileName31A [internal]
1147 * Creates a win31 style dialog box for the user to select a file to open/save.
1149 BOOL GetFileName31A( OPENFILENAMEA *lpofn, UINT dlgType )
1151 BOOL bRet = FALSE;
1152 PFD31_DATA lfs;
1154 if (!lpofn || !FD31_Init()) return FALSE;
1156 TRACE("ofn flags %08x\n", lpofn->Flags);
1157 lfs = FD31_AllocPrivate((LPARAM) lpofn, dlgType, FALSE);
1158 if (lfs)
1160 bRet = DialogBoxIndirectParamA( COMDLG32_hInstance, lfs->template, lpofn->hwndOwner,
1161 FD31_FileOpenDlgProc, (LPARAM)lfs);
1162 FD31_DestroyPrivate(lfs);
1165 TRACE("return lpstrFile='%s' !\n", lpofn->lpstrFile);
1166 return bRet;
1169 /***********************************************************************
1170 * GetFileName31W [internal]
1172 * Creates a win31 style dialog box for the user to select a file to open/save
1174 BOOL GetFileName31W( OPENFILENAMEW *lpofn, UINT dlgType )
1176 BOOL bRet = FALSE;
1177 PFD31_DATA lfs;
1179 if (!lpofn || !FD31_Init()) return FALSE;
1181 lfs = FD31_AllocPrivate((LPARAM) lpofn, dlgType, TRUE);
1182 if (lfs)
1184 bRet = DialogBoxIndirectParamW( COMDLG32_hInstance, lfs->template, lpofn->hwndOwner,
1185 FD31_FileOpenDlgProc, (LPARAM)lfs);
1186 FD31_DestroyPrivate(lfs);
1189 TRACE("file %s, file offset %d, ext offset %d\n",
1190 debugstr_w(lpofn->lpstrFile), lpofn->nFileOffset, lpofn->nFileExtension);
1191 return bRet;