xref: /reactos/dll/win32/comdlg32/itemdlg.c (revision 53221834)
1 /*
2  * Common Item Dialog
3  *
4  * Copyright 2010,2011 David Hedberg
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #ifndef __REACTOS__ /* Win 7 */
22 
23 #include <stdarg.h>
24 
25 #define COBJMACROS
26 #define NONAMELESSUNION
27 
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "wingdi.h"
32 #include "winreg.h"
33 #include "shlwapi.h"
34 
35 #include "commdlg.h"
36 #include "cdlg.h"
37 #include "filedlgbrowser.h"
38 
39 #include "wine/debug.h"
40 #include "wine/list.h"
41 
42 #define IDC_NAV_TOOLBAR      200
43 #define IDC_NAVBACK          201
44 #define IDC_NAVFORWARD       202
45 
46 #include <initguid.h>
47 /* This seems to be another version of IID_IFileDialogCustomize. If
48  * there is any difference I have yet to find it. */
49 DEFINE_GUID(IID_IFileDialogCustomizeAlt, 0x8016B7B3, 0x3D49, 0x4504, 0xA0,0xAA, 0x2A,0x37,0x49,0x4E,0x60,0x6F);
50 
51 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
52 
53 static const WCHAR notifysink_childW[] = {'n','f','s','_','c','h','i','l','d',0};
54 static const WCHAR floatnotifysinkW[] = {'F','l','o','a','t','N','o','t','i','f','y','S','i','n','k',0};
55 static const WCHAR radiobuttonlistW[] = {'R','a','d','i','o','B','u','t','t','o','n','L','i','s','t',0};
56 
57 enum ITEMDLG_TYPE {
58     ITEMDLG_TYPE_OPEN,
59     ITEMDLG_TYPE_SAVE
60 };
61 
62 enum ITEMDLG_CCTRL_TYPE {
63     IDLG_CCTRL_MENU,
64     IDLG_CCTRL_PUSHBUTTON,
65     IDLG_CCTRL_COMBOBOX,
66     IDLG_CCTRL_RADIOBUTTONLIST,
67     IDLG_CCTRL_CHECKBUTTON,
68     IDLG_CCTRL_EDITBOX,
69     IDLG_CCTRL_SEPARATOR,
70     IDLG_CCTRL_TEXT,
71     IDLG_CCTRL_OPENDROPDOWN,
72     IDLG_CCTRL_VISUALGROUP
73 };
74 
75 typedef struct cctrl_item {
76     DWORD id, parent_id;
77     LPWSTR label;
78     CDCONTROLSTATEF cdcstate;
79     HWND hwnd;
80     struct list entry;
81 } cctrl_item;
82 
83 typedef struct {
84     HWND hwnd, wrapper_hwnd;
85     UINT id, dlgid;
86     enum ITEMDLG_CCTRL_TYPE type;
87     CDCONTROLSTATEF cdcstate;
88     struct list entry;
89 
90     struct list sub_cctrls;
91     struct list sub_cctrls_entry;
92     struct list sub_items;
93 } customctrl;
94 
95 typedef struct {
96     struct list entry;
97     IFileDialogEvents *pfde;
98     DWORD cookie;
99 } events_client;
100 
101 typedef struct FileDialogImpl {
102     IFileDialog2 IFileDialog2_iface;
103     union {
104         IFileOpenDialog IFileOpenDialog_iface;
105         IFileSaveDialog IFileSaveDialog_iface;
106     } u;
107     enum ITEMDLG_TYPE dlg_type;
108     IExplorerBrowserEvents IExplorerBrowserEvents_iface;
109     IServiceProvider       IServiceProvider_iface;
110     ICommDlgBrowser3       ICommDlgBrowser3_iface;
111     IOleWindow             IOleWindow_iface;
112     IFileDialogCustomize   IFileDialogCustomize_iface;
113     LONG ref;
114 
115     FILEOPENDIALOGOPTIONS options;
116     COMDLG_FILTERSPEC *filterspecs;
117     UINT filterspec_count;
118     UINT filetypeindex;
119 
120     struct list events_clients;
121     DWORD events_next_cookie;
122 
123     IShellItemArray *psia_selection;
124     IShellItemArray *psia_results;
125     IShellItem *psi_defaultfolder;
126     IShellItem *psi_setfolder;
127     IShellItem *psi_folder;
128 
129     HWND dlg_hwnd;
130     IExplorerBrowser *peb;
131     DWORD ebevents_cookie;
132 
133     LPWSTR set_filename;
134     LPWSTR default_ext;
135     LPWSTR custom_title;
136     LPWSTR custom_okbutton;
137     LPWSTR custom_cancelbutton;
138     LPWSTR custom_filenamelabel;
139 
140     UINT cctrl_width, cctrl_def_height, cctrls_cols;
141     UINT cctrl_indent, dpi_x, dpi_y;
142     HWND cctrls_hwnd;
143     struct list cctrls;
144     UINT_PTR cctrl_next_dlgid;
145     customctrl *cctrl_active_vg;
146 
147     HMENU hmenu_opendropdown;
148     customctrl cctrl_opendropdown;
149     HFONT hfont_opendropdown;
150     BOOL opendropdown_has_selection;
151     DWORD opendropdown_selection;
152 
153     GUID client_guid;
154 } FileDialogImpl;
155 
156 /**************************************************************************
157  * Event wrappers.
158  */
159 static HRESULT events_OnFileOk(FileDialogImpl *This)
160 {
161     events_client *cursor;
162     HRESULT hr = S_OK;
163     TRACE("%p\n", This);
164 
165     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
166     {
167         TRACE("Notifying %p\n", cursor);
168         hr = IFileDialogEvents_OnFileOk(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
169         if(FAILED(hr) && hr != E_NOTIMPL)
170             break;
171     }
172 
173     if(hr == E_NOTIMPL)
174         hr = S_OK;
175 
176     return hr;
177 }
178 
179 static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder)
180 {
181     events_client *cursor;
182     HRESULT hr = S_OK;
183     TRACE("%p (%p)\n", This, folder);
184 
185     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
186     {
187         TRACE("Notifying %p\n", cursor);
188         hr = IFileDialogEvents_OnFolderChanging(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, folder);
189         if(FAILED(hr) && hr != E_NOTIMPL)
190             break;
191     }
192 
193     if(hr == E_NOTIMPL)
194         hr = S_OK;
195 
196     return hr;
197 }
198 
199 static void events_OnFolderChange(FileDialogImpl *This)
200 {
201     events_client *cursor;
202     TRACE("%p\n", This);
203 
204     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
205     {
206         TRACE("Notifying %p\n", cursor);
207         IFileDialogEvents_OnFolderChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
208     }
209 }
210 
211 static void events_OnSelectionChange(FileDialogImpl *This)
212 {
213     events_client *cursor;
214     TRACE("%p\n", This);
215 
216     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
217     {
218         TRACE("Notifying %p\n", cursor);
219         IFileDialogEvents_OnSelectionChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
220     }
221 }
222 
223 static void events_OnTypeChange(FileDialogImpl *This)
224 {
225     events_client *cursor;
226     TRACE("%p\n", This);
227 
228     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
229     {
230         TRACE("Notifying %p\n", cursor);
231         IFileDialogEvents_OnTypeChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
232     }
233 }
234 
235 static HRESULT events_OnOverwrite(FileDialogImpl *This, IShellItem *shellitem)
236 {
237     events_client *cursor;
238     HRESULT hr = S_OK;
239     FDE_OVERWRITE_RESPONSE response = FDEOR_DEFAULT;
240     TRACE("%p %p\n", This, shellitem);
241 
242     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
243     {
244         TRACE("Notifying %p\n", cursor);
245         hr = IFileDialogEvents_OnOverwrite(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, shellitem, &response);
246         TRACE("<-- hr=%x response=%u\n", hr, response);
247         if(FAILED(hr) && hr != E_NOTIMPL)
248             break;
249     }
250 
251     if(hr == E_NOTIMPL)
252         hr = S_OK;
253 
254     if(SUCCEEDED(hr))
255     {
256         if (response == FDEOR_DEFAULT)
257         {
258             WCHAR buf[100];
259             int answer;
260 
261             LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, buf, 100);
262             answer = MessageBoxW(This->dlg_hwnd, buf, This->custom_title,
263                        MB_YESNO | MB_ICONEXCLAMATION);
264             if (answer == IDNO || answer == IDCANCEL)
265             {
266                 hr = E_FAIL;
267             }
268         }
269         else if (response == FDEOR_REFUSE)
270             hr = E_FAIL;
271     }
272 
273     return hr;
274 }
275 
276 static inline HRESULT get_cctrl_event(IFileDialogEvents *pfde, IFileDialogControlEvents **pfdce)
277 {
278     return IFileDialogEvents_QueryInterface(pfde, &IID_IFileDialogControlEvents, (void**)pfdce);
279 }
280 
281 static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id)
282 {
283     events_client *cursor;
284     TRACE("%p\n", This);
285 
286     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
287     {
288         IFileDialogControlEvents *pfdce;
289         if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
290         {
291             TRACE("Notifying %p\n", cursor);
292             IFileDialogControlEvents_OnButtonClicked(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
293             IFileDialogControlEvents_Release(pfdce);
294         }
295     }
296 
297     return S_OK;
298 }
299 
300 static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DWORD item_id)
301 {
302     events_client *cursor;
303     TRACE("%p %i %i\n", This, ctl_id, item_id);
304 
305     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
306     {
307         IFileDialogControlEvents *pfdce;
308         if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
309         {
310             TRACE("Notifying %p\n", cursor);
311             IFileDialogControlEvents_OnItemSelected(pfdce, &This->IFileDialogCustomize_iface, ctl_id, item_id);
312             IFileDialogControlEvents_Release(pfdce);
313         }
314     }
315 
316     return S_OK;
317 }
318 
319 static HRESULT cctrl_event_OnCheckButtonToggled(FileDialogImpl *This, DWORD ctl_id, BOOL checked)
320 {
321     events_client *cursor;
322     TRACE("%p\n", This);
323 
324     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
325     {
326         IFileDialogControlEvents *pfdce;
327         if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
328         {
329             TRACE("Notifying %p\n", cursor);
330             IFileDialogControlEvents_OnCheckButtonToggled(pfdce, &This->IFileDialogCustomize_iface, ctl_id, checked);
331             IFileDialogControlEvents_Release(pfdce);
332         }
333     }
334 
335     return S_OK;
336 }
337 
338 static HRESULT cctrl_event_OnControlActivating(FileDialogImpl *This,
339                                                   DWORD ctl_id)
340 {
341     events_client *cursor;
342     TRACE("%p\n", This);
343 
344     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
345     {
346         IFileDialogControlEvents *pfdce;
347         if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
348         {
349             TRACE("Notifying %p\n", cursor);
350             IFileDialogControlEvents_OnControlActivating(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
351             IFileDialogControlEvents_Release(pfdce);
352         }
353     }
354 
355     return S_OK;
356 }
357 
358 /**************************************************************************
359  * Helper functions.
360  */
361 static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
362 {
363     HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
364     UINT len;
365 
366     if(!hwnd_edit)
367     {
368         if(This->set_filename)
369         {
370             len = lstrlenW(This->set_filename);
371             *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
372             lstrcpyW(*str, This->set_filename);
373             return len;
374         }
375         return 0;
376     }
377 
378     len = SendMessageW(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);
379     *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
380     if(!*str)
381         return 0;
382 
383     SendMessageW(hwnd_edit, WM_GETTEXT, len+1, (LPARAM)*str);
384     return len;
385 }
386 
387 static BOOL set_file_name(FileDialogImpl *This, LPCWSTR str)
388 {
389     if(This->set_filename)
390         LocalFree(This->set_filename);
391 
392     This->set_filename = str ? StrDupW(str) : NULL;
393 
394     return SetDlgItemTextW(This->dlg_hwnd, IDC_FILENAME, This->set_filename);
395 }
396 
397 static void fill_filename_from_selection(FileDialogImpl *This)
398 {
399     IShellItem *psi;
400     LPWSTR *names;
401     HRESULT hr;
402     UINT item_count, valid_count;
403     UINT len_total, i;
404 
405     if(!This->psia_selection)
406         return;
407 
408     hr = IShellItemArray_GetCount(This->psia_selection, &item_count);
409     if(FAILED(hr) || !item_count)
410         return;
411 
412     names = HeapAlloc(GetProcessHeap(), 0, item_count*sizeof(LPWSTR));
413 
414     /* Get names of the selected items */
415     valid_count = 0; len_total = 0;
416     for(i = 0; i < item_count; i++)
417     {
418         hr = IShellItemArray_GetItemAt(This->psia_selection, i, &psi);
419         if(SUCCEEDED(hr))
420         {
421             UINT attr;
422 
423             hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &attr);
424             if(SUCCEEDED(hr) &&
425                (( (This->options & FOS_PICKFOLDERS) && !(attr & SFGAO_FOLDER)) ||
426                 (!(This->options & FOS_PICKFOLDERS) &&  (attr & SFGAO_FOLDER))))
427                 continue;
428 
429             hr = IShellItem_GetDisplayName(psi, (This->options & FOS_PICKFOLDERS) ? SIGDN_FILESYSPATH : SIGDN_PARENTRELATIVEPARSING, &names[valid_count]);
430             if(SUCCEEDED(hr))
431             {
432                 len_total += lstrlenW(names[valid_count]) + 3;
433                 valid_count++;
434             }
435             IShellItem_Release(psi);
436         }
437     }
438 
439     if(valid_count == 1)
440     {
441         set_file_name(This, names[0]);
442         CoTaskMemFree(names[0]);
443     }
444     else if(valid_count > 1)
445     {
446         LPWSTR string = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len_total);
447         LPWSTR cur_point = string;
448 
449         for(i = 0; i < valid_count; i++)
450         {
451             LPWSTR file = names[i];
452             *cur_point++ = '\"';
453             lstrcpyW(cur_point, file);
454             cur_point += lstrlenW(file);
455             *cur_point++ = '\"';
456             *cur_point++ = ' ';
457             CoTaskMemFree(file);
458         }
459         *(cur_point-1) = '\0';
460 
461         set_file_name(This, string);
462         HeapFree(GetProcessHeap(), 0, string);
463     }
464 
465     HeapFree(GetProcessHeap(), 0, names);
466     return;
467 }
468 
469 static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec)
470 {
471     WCHAR *endpos, *ext;
472 
473     lstrcpyW(buf, spec);
474     if( (endpos = StrChrW(buf, ';')) )
475         *endpos = '\0';
476 
477     ext = PathFindExtensionW(buf);
478     if(StrChrW(ext, '*'))
479         return NULL;
480 
481     return ext;
482 }
483 
484 static BOOL shell_item_exists(IShellItem* shellitem)
485 {
486     LPWSTR filename;
487     HRESULT hr;
488     BOOL result;
489 
490     hr = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &filename);
491     if (SUCCEEDED(hr))
492     {
493         /* FIXME: Implement SFGAO_VALIDATE in Wine and use it instead. */
494         result = (GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES);
495         CoTaskMemFree(filename);
496     }
497     else
498     {
499         SFGAOF attributes;
500         result = SUCCEEDED(IShellItem_GetAttributes(shellitem, SFGAO_VALIDATE, &attributes));
501     }
502 
503     return result;
504 }
505 
506 static HRESULT on_default_action(FileDialogImpl *This)
507 {
508     IShellFolder *psf_parent, *psf_desktop;
509     LPITEMIDLIST *pidla;
510     LPITEMIDLIST current_folder;
511     LPWSTR fn_iter, files = NULL, tmp_files;
512     UINT file_count = 0, len, i;
513     int open_action;
514     HRESULT hr, ret = E_FAIL;
515 
516     len = get_file_name(This, &tmp_files);
517     if(len)
518     {
519         UINT size_used;
520         file_count = COMDLG32_SplitFileNames(tmp_files, len, &files, &size_used);
521         CoTaskMemFree(tmp_files);
522     }
523     if(!file_count) return E_FAIL;
524 
525     hr = SHGetIDListFromObject((IUnknown*)This->psi_folder, &current_folder);
526     if(FAILED(hr))
527     {
528         ERR("Failed to get pidl for current directory.\n");
529         HeapFree(GetProcessHeap(), 0, files);
530         return hr;
531     }
532 
533     TRACE("Acting on %d file(s).\n", file_count);
534 
535     pidla = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST) * file_count);
536     open_action = ONOPEN_OPEN;
537     fn_iter = files;
538 
539     for(i = 0; i < file_count && open_action == ONOPEN_OPEN; i++)
540     {
541         WCHAR canon_filename[MAX_PATH];
542         psf_parent = NULL;
543 
544         COMDLG32_GetCanonicalPath(current_folder, fn_iter, canon_filename);
545 
546         if( (This->options & FOS_NOVALIDATE) &&
547             !(This->options & FOS_FILEMUSTEXIST) )
548             open_action = ONOPEN_OPEN;
549         else
550             open_action = ONOPEN_BROWSE;
551 
552         open_action = FILEDLG95_ValidatePathAction(canon_filename, &psf_parent, This->dlg_hwnd,
553                                                    This->options & ~FOS_FILEMUSTEXIST,
554                                                    (This->dlg_type == ITEMDLG_TYPE_SAVE),
555                                                    open_action);
556 
557         /* Add the proper extension */
558         if(open_action == ONOPEN_OPEN)
559         {
560             static const WCHAR dotW[] = {'.',0};
561 
562             if(This->dlg_type == ITEMDLG_TYPE_SAVE)
563             {
564                 WCHAR extbuf[MAX_PATH], *newext = NULL;
565 
566                 if(This->filterspec_count)
567                 {
568                     newext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
569                 }
570                 else if(This->default_ext)
571                 {
572                     lstrcpyW(extbuf, dotW);
573                     lstrcatW(extbuf, This->default_ext);
574                     newext = extbuf;
575                 }
576 
577                 if(newext)
578                 {
579                     WCHAR *ext = PathFindExtensionW(canon_filename);
580                     if(lstrcmpW(ext, newext))
581                         lstrcatW(canon_filename, newext);
582                 }
583             }
584             else
585             {
586                 if( !(This->options & FOS_NOVALIDATE) && (This->options & FOS_FILEMUSTEXIST) &&
587                     !PathFileExistsW(canon_filename))
588                 {
589                     if(This->default_ext)
590                     {
591                         lstrcatW(canon_filename, dotW);
592                         lstrcatW(canon_filename, This->default_ext);
593 
594                         if(!PathFileExistsW(canon_filename))
595                         {
596                             FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
597                             open_action = ONOPEN_BROWSE;
598                         }
599                     }
600                     else
601                     {
602                         FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
603                         open_action = ONOPEN_BROWSE;
604                     }
605                 }
606             }
607         }
608 
609         pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename);
610 
611         if(psf_parent && !(open_action == ONOPEN_BROWSE))
612             IShellFolder_Release(psf_parent);
613 
614         fn_iter += (WCHAR)lstrlenW(fn_iter) + 1;
615     }
616 
617     HeapFree(GetProcessHeap(), 0, files);
618     ILFree(current_folder);
619 
620     if((This->options & FOS_PICKFOLDERS) && open_action == ONOPEN_BROWSE)
621         open_action = ONOPEN_OPEN; /* FIXME: Multiple folders? */
622 
623     switch(open_action)
624     {
625     case ONOPEN_SEARCH:
626         FIXME("Filtering not implemented.\n");
627         break;
628 
629     case ONOPEN_BROWSE:
630         hr = IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psf_parent, SBSP_DEFBROWSER);
631         if(FAILED(hr))
632             ERR("Failed to browse to directory: %08x\n", hr);
633 
634         IShellFolder_Release(psf_parent);
635         break;
636 
637     case ONOPEN_OPEN:
638         hr = SHGetDesktopFolder(&psf_desktop);
639         if(SUCCEEDED(hr))
640         {
641             if(This->psia_results)
642             {
643                 IShellItemArray_Release(This->psia_results);
644                 This->psia_results = NULL;
645             }
646 
647             hr = SHCreateShellItemArray(NULL, psf_desktop, file_count, (PCUITEMID_CHILD_ARRAY)pidla,
648                                         &This->psia_results);
649 
650             IShellFolder_Release(psf_desktop);
651 
652             if(FAILED(hr))
653                 break;
654 
655             if(This->options & FOS_PICKFOLDERS)
656             {
657                 SFGAOF attributes;
658                 hr = IShellItemArray_GetAttributes(This->psia_results, SIATTRIBFLAGS_AND, SFGAO_FOLDER, &attributes);
659                 if(hr != S_OK)
660                 {
661                     WCHAR buf[64];
662                     LoadStringW(COMDLG32_hInstance, IDS_INVALID_FOLDERNAME, buf, ARRAY_SIZE(buf));
663 
664                     MessageBoxW(This->dlg_hwnd, buf, This->custom_title, MB_OK | MB_ICONEXCLAMATION);
665 
666                     IShellItemArray_Release(This->psia_results);
667                     This->psia_results = NULL;
668                     break;
669                 }
670             }
671 
672             if((This->options & FOS_OVERWRITEPROMPT) && This->dlg_type == ITEMDLG_TYPE_SAVE)
673             {
674                 IShellItem *shellitem;
675 
676                 for (i=0; SUCCEEDED(hr) && i<file_count; i++)
677                 {
678                     hr = IShellItemArray_GetItemAt(This->psia_results, i, &shellitem);
679                     if (SUCCEEDED(hr))
680                     {
681                         if (shell_item_exists(shellitem))
682                             hr = events_OnOverwrite(This, shellitem);
683 
684                         IShellItem_Release(shellitem);
685                     }
686                 }
687 
688                 if (FAILED(hr))
689                     break;
690             }
691 
692             if(events_OnFileOk(This) == S_OK)
693                 ret = S_OK;
694         }
695         break;
696 
697     default:
698         ERR("Failed.\n");
699         break;
700     }
701 
702     /* Clean up */
703     for(i = 0; i < file_count; i++)
704         ILFree(pidla[i]);
705     HeapFree(GetProcessHeap(), 0, pidla);
706 
707     /* Success closes the dialog */
708     return ret;
709 }
710 
711 static void show_opendropdown(FileDialogImpl *This)
712 {
713     HWND open_hwnd;
714     RECT open_rc;
715     MSG msg;
716 
717     open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
718 
719     GetWindowRect(open_hwnd, &open_rc);
720 
721     if (TrackPopupMenu(This->hmenu_opendropdown, 0, open_rc.left, open_rc.bottom, 0, This->dlg_hwnd, NULL) &&
722         PeekMessageW(&msg, This->dlg_hwnd, WM_MENUCOMMAND, WM_MENUCOMMAND, PM_REMOVE))
723     {
724         MENUITEMINFOW mii;
725 
726         This->opendropdown_has_selection = TRUE;
727 
728         mii.cbSize = sizeof(mii);
729         mii.fMask = MIIM_ID;
730         GetMenuItemInfoW((HMENU)msg.lParam, msg.wParam, TRUE, &mii);
731         This->opendropdown_selection = mii.wID;
732 
733         if(SUCCEEDED(on_default_action(This)))
734             EndDialog(This->dlg_hwnd, S_OK);
735         else
736             This->opendropdown_has_selection = FALSE;
737     }
738 }
739 
740 /**************************************************************************
741  * Control item functions.
742  */
743 
744 static void item_free(cctrl_item *item)
745 {
746     DestroyWindow(item->hwnd);
747     HeapFree(GetProcessHeap(), 0, item->label);
748     HeapFree(GetProcessHeap(), 0, item);
749 }
750 
751 static cctrl_item* get_item(customctrl* parent, DWORD itemid, CDCONTROLSTATEF visible_flags, DWORD* position)
752 {
753     DWORD dummy;
754     cctrl_item* item;
755 
756     if (!position) position = &dummy;
757 
758     *position = 0;
759 
760     LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
761     {
762         if (item->id == itemid)
763             return item;
764 
765         if ((item->cdcstate & visible_flags) == visible_flags)
766             (*position)++;
767     }
768 
769     return NULL;
770 }
771 
772 static cctrl_item* get_first_item(customctrl* parent)
773 {
774     cctrl_item* item;
775 
776     LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
777     {
778         if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
779             return item;
780     }
781 
782     return NULL;
783 }
784 
785 static HRESULT add_item(customctrl* parent, DWORD itemid, LPCWSTR label, cctrl_item** result)
786 {
787     cctrl_item* item;
788     LPWSTR label_copy;
789 
790     if (get_item(parent, itemid, 0, NULL))
791         return E_INVALIDARG;
792 
793     item = HeapAlloc(GetProcessHeap(), 0, sizeof(*item));
794     label_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(label)+1)*sizeof(WCHAR));
795 
796     if (!item || !label_copy)
797     {
798         HeapFree(GetProcessHeap(), 0, item);
799         HeapFree(GetProcessHeap(), 0, label_copy);
800         return E_OUTOFMEMORY;
801     }
802 
803     item->id = itemid;
804     item->parent_id = parent->id;
805     lstrcpyW(label_copy, label);
806     item->label = label_copy;
807     item->cdcstate = CDCS_VISIBLE|CDCS_ENABLED;
808     item->hwnd = NULL;
809     list_add_tail(&parent->sub_items, &item->entry);
810 
811     *result = item;
812 
813     return S_OK;
814 }
815 
816 /**************************************************************************
817  * Control functions.
818  */
819 static inline customctrl *get_cctrl_from_dlgid(FileDialogImpl *This, DWORD dlgid)
820 {
821     customctrl *ctrl, *sub_ctrl;
822 
823     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
824     {
825         if(ctrl->dlgid == dlgid)
826             return ctrl;
827 
828         LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
829             if(sub_ctrl->dlgid == dlgid)
830                 return sub_ctrl;
831     }
832 
833     ERR("Failed to find control with dialog id %d\n", dlgid);
834     return NULL;
835 }
836 
837 static inline customctrl *get_cctrl(FileDialogImpl *This, DWORD ctlid)
838 {
839     customctrl *ctrl, *sub_ctrl;
840 
841     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
842     {
843         if(ctrl->id == ctlid)
844             return ctrl;
845 
846         LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
847             if(sub_ctrl->id == ctlid)
848                 return sub_ctrl;
849     }
850 
851     if (This->hmenu_opendropdown && This->cctrl_opendropdown.id == ctlid)
852         return &This->cctrl_opendropdown;
853 
854     TRACE("No existing control with control id %d\n", ctlid);
855     return NULL;
856 }
857 
858 static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multiline)
859 {
860     LPWSTR text;
861     UINT len, final_width;
862     UINT lines, final_height;
863     SIZE size;
864     RECT rc;
865     HDC hdc;
866     WCHAR *c;
867     HFONT font;
868 
869     TRACE("\n");
870 
871     len = SendMessageW(hctrl, WM_GETTEXTLENGTH, 0, 0);
872     text = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
873     if(!text) return;
874     SendMessageW(hctrl, WM_GETTEXT, len+1, (LPARAM)text);
875 
876     hdc = GetDC(hctrl);
877     font = (HFONT)SendMessageW(hctrl, WM_GETFONT, 0, 0);
878     font = SelectObject(hdc, font);
879     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &size);
880     SelectObject(hdc, font);
881     ReleaseDC(hctrl, hdc);
882 
883     if(len && multiline)
884     {
885         /* FIXME: line-wrap */
886         for(lines = 1, c = text; *c != '\0'; c++)
887             if(*c == '\n') lines++;
888 
889         final_height = size.cy*lines + 2*4;
890     }
891     else
892     {
893         GetWindowRect(hctrl, &rc);
894         final_height = rc.bottom - rc.top;
895     }
896 
897     final_width = min(max(size.cx, min_width) + 4, max_width);
898     SetWindowPos(hctrl, NULL, 0, 0, final_width, final_height,
899                  SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
900 
901     HeapFree(GetProcessHeap(), 0, text);
902 }
903 
904 static UINT ctrl_get_height(customctrl *ctrl) {
905     RECT rc;
906     GetWindowRect(ctrl->wrapper_hwnd, &rc);
907     return rc.bottom - rc.top;
908 }
909 
910 static void ctrl_free(customctrl *ctrl)
911 {
912     customctrl *sub_cur1, *sub_cur2;
913     cctrl_item *item_cur1, *item_cur2;
914 
915     TRACE("Freeing control %p\n", ctrl);
916     if(ctrl->type == IDLG_CCTRL_MENU)
917     {
918         TBBUTTON tbb;
919         SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
920         DestroyMenu((HMENU)tbb.dwData);
921     }
922 
923     LIST_FOR_EACH_ENTRY_SAFE(sub_cur1, sub_cur2, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
924     {
925         list_remove(&sub_cur1->sub_cctrls_entry);
926         ctrl_free(sub_cur1);
927     }
928 
929     LIST_FOR_EACH_ENTRY_SAFE(item_cur1, item_cur2, &ctrl->sub_items, cctrl_item, entry)
930     {
931         list_remove(&item_cur1->entry);
932         item_free(item_cur1);
933     }
934 
935     DestroyWindow(ctrl->hwnd);
936     HeapFree(GetProcessHeap(), 0, ctrl);
937 }
938 
939 static void customctrl_resize(FileDialogImpl *This, customctrl *ctrl)
940 {
941     RECT rc;
942     UINT total_height;
943     UINT max_width, size;
944     customctrl *sub_ctrl;
945 
946     switch(ctrl->type)
947     {
948     case IDLG_CCTRL_PUSHBUTTON:
949     case IDLG_CCTRL_COMBOBOX:
950     case IDLG_CCTRL_CHECKBUTTON:
951     case IDLG_CCTRL_TEXT:
952         size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
953         ctrl_resize(ctrl->hwnd, size, size, TRUE);
954         GetWindowRect(ctrl->hwnd, &rc);
955         SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
956                      SWP_NOZORDER|SWP_NOMOVE);
957         break;
958     case IDLG_CCTRL_VISUALGROUP:
959         total_height = 0;
960         ctrl_resize(ctrl->hwnd, 0, This->cctrl_indent, TRUE);
961 
962         LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
963         {
964             customctrl_resize(This, sub_ctrl);
965             SetWindowPos(sub_ctrl->wrapper_hwnd, NULL, This->cctrl_indent, total_height, 0, 0,
966                          SWP_NOZORDER|SWP_NOSIZE);
967 
968             total_height += ctrl_get_height(sub_ctrl);
969         }
970 
971         /* The label should be right adjusted */
972         {
973             UINT width, height;
974 
975             GetWindowRect(ctrl->hwnd, &rc);
976             width = rc.right - rc.left;
977             height = rc.bottom - rc.top;
978 
979             SetWindowPos(ctrl->hwnd, NULL, This->cctrl_indent - width, 0, width, height, SWP_NOZORDER);
980         }
981 
982         /* Resize the wrapper window to fit all the sub controls */
983         SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, This->cctrl_width + This->cctrl_indent, total_height,
984                      SWP_NOZORDER|SWP_NOMOVE);
985         break;
986     case IDLG_CCTRL_RADIOBUTTONLIST:
987     {
988         cctrl_item* item;
989 
990         total_height = 0;
991         max_width = 0;
992 
993         LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
994         {
995             size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
996             ctrl_resize(item->hwnd, size, size, TRUE);
997             SetWindowPos(item->hwnd, NULL, 0, total_height, 0, 0,
998                          SWP_NOZORDER|SWP_NOSIZE);
999 
1000             GetWindowRect(item->hwnd, &rc);
1001 
1002             total_height += rc.bottom - rc.top;
1003             max_width = max(rc.right - rc.left, max_width);
1004         }
1005 
1006         SetWindowPos(ctrl->hwnd, NULL, 0, 0, max_width, total_height,
1007                      SWP_NOZORDER|SWP_NOMOVE);
1008 
1009         SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, max_width, total_height,
1010                      SWP_NOZORDER|SWP_NOMOVE);
1011 
1012         break;
1013     }
1014     case IDLG_CCTRL_EDITBOX:
1015     case IDLG_CCTRL_SEPARATOR:
1016     case IDLG_CCTRL_MENU:
1017     case IDLG_CCTRL_OPENDROPDOWN:
1018         /* Nothing */
1019         break;
1020     }
1021 }
1022 
1023 static LRESULT notifysink_on_create(HWND hwnd, CREATESTRUCTW *crs)
1024 {
1025     FileDialogImpl *This = crs->lpCreateParams;
1026     TRACE("%p\n", This);
1027 
1028     SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1029     return TRUE;
1030 }
1031 
1032 static LRESULT notifysink_on_bn_clicked(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
1033 {
1034     customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
1035 
1036     TRACE("%p, %lx\n", This, wparam);
1037 
1038     if(ctrl)
1039     {
1040         if(ctrl->type == IDLG_CCTRL_CHECKBUTTON)
1041         {
1042             BOOL checked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
1043             cctrl_event_OnCheckButtonToggled(This, ctrl->id, checked);
1044         }
1045         else
1046             cctrl_event_OnButtonClicked(This, ctrl->id);
1047     }
1048 
1049     return TRUE;
1050 }
1051 
1052 static LRESULT notifysink_on_cbn_selchange(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
1053 {
1054     customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
1055     TRACE("%p, %p (%lx)\n", This, ctrl, wparam);
1056 
1057     if(ctrl)
1058     {
1059         UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
1060         UINT selid = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
1061 
1062         cctrl_event_OnItemSelected(This, ctrl->id, selid);
1063     }
1064     return TRUE;
1065 }
1066 
1067 static LRESULT notifysink_on_tvn_dropdown(FileDialogImpl *This, LPARAM lparam)
1068 {
1069     NMTOOLBARW *nmtb = (NMTOOLBARW*)lparam;
1070     customctrl *ctrl = get_cctrl_from_dlgid(This, GetDlgCtrlID(nmtb->hdr.hwndFrom));
1071     POINT pt = { 0, nmtb->rcButton.bottom };
1072     TBBUTTON tbb;
1073     UINT idcmd;
1074 
1075     TRACE("%p, %p (%lx)\n", This, ctrl, lparam);
1076 
1077     if(ctrl)
1078     {
1079         cctrl_event_OnControlActivating(This,ctrl->id);
1080 
1081         SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
1082         ClientToScreen(ctrl->hwnd, &pt);
1083         idcmd = TrackPopupMenu((HMENU)tbb.dwData, TPM_RETURNCMD, pt.x, pt.y, 0, This->dlg_hwnd, NULL);
1084         if(idcmd)
1085             cctrl_event_OnItemSelected(This, ctrl->id, idcmd);
1086     }
1087 
1088     return TBDDRET_DEFAULT;
1089 }
1090 
1091 static LRESULT notifysink_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1092 {
1093     switch(HIWORD(wparam))
1094     {
1095     case BN_CLICKED:          return notifysink_on_bn_clicked(This, hwnd, wparam);
1096     case CBN_SELCHANGE:       return notifysink_on_cbn_selchange(This, hwnd, wparam);
1097     }
1098 
1099     return FALSE;
1100 }
1101 
1102 static LRESULT notifysink_on_wm_notify(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1103 {
1104     NMHDR *nmhdr = (NMHDR*)lparam;
1105 
1106     switch(nmhdr->code)
1107     {
1108     case TBN_DROPDOWN:        return notifysink_on_tvn_dropdown(This, lparam);
1109     }
1110 
1111     return FALSE;
1112 }
1113 
1114 static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
1115 {
1116     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1117     customctrl *ctrl;
1118     HWND hwnd_child;
1119     RECT rc;
1120 
1121     switch(message)
1122     {
1123     case WM_NCCREATE:         return notifysink_on_create(hwnd, (CREATESTRUCTW*)lparam);
1124     case WM_COMMAND:          return notifysink_on_wm_command(This, hwnd, wparam, lparam);
1125     case WM_NOTIFY:           return notifysink_on_wm_notify(This, hwnd, wparam, lparam);
1126     case WM_SIZE:
1127         hwnd_child = GetPropW(hwnd, notifysink_childW);
1128         ctrl = (customctrl*)GetWindowLongPtrW(hwnd_child, GWLP_USERDATA);
1129         if(ctrl && ctrl->type != IDLG_CCTRL_VISUALGROUP)
1130         {
1131             GetClientRect(hwnd, &rc);
1132             SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
1133         }
1134         return TRUE;
1135     }
1136 
1137     return DefWindowProcW(hwnd, message, wparam, lparam);
1138 }
1139 
1140 static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
1141                                 LPCWSTR text, LPCWSTR wndclass, DWORD ctrl_wsflags,
1142                                 DWORD ctrl_exflags, UINT height, customctrl **ppctrl)
1143 {
1144     HWND ns_hwnd, control_hwnd, parent_hwnd;
1145     DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
1146     customctrl *ctrl;
1147 
1148     if(get_cctrl(This, id))
1149         return E_UNEXPECTED; /* Duplicate id */
1150 
1151     if(This->cctrl_active_vg)
1152         parent_hwnd = This->cctrl_active_vg->wrapper_hwnd;
1153     else
1154         parent_hwnd = This->cctrls_hwnd;
1155 
1156     ns_hwnd = CreateWindowExW(0, floatnotifysinkW, NULL, wsflags,
1157                               0, 0, This->cctrl_width, height, parent_hwnd,
1158                               (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
1159     control_hwnd = CreateWindowExW(ctrl_exflags, wndclass, text, wsflags | ctrl_wsflags,
1160                                    0, 0, This->cctrl_width, height, ns_hwnd,
1161                                    (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, 0);
1162 
1163     if(!ns_hwnd || !control_hwnd)
1164     {
1165         ERR("Failed to create wrapper (%p) or control (%p)\n", ns_hwnd, control_hwnd);
1166         DestroyWindow(ns_hwnd);
1167         DestroyWindow(control_hwnd);
1168 
1169         return E_FAIL;
1170     }
1171 
1172     SetPropW(ns_hwnd, notifysink_childW, control_hwnd);
1173 
1174     ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(customctrl));
1175     if(!ctrl)
1176         return E_OUTOFMEMORY;
1177 
1178     ctrl->hwnd = control_hwnd;
1179     ctrl->wrapper_hwnd = ns_hwnd;
1180     ctrl->id = id;
1181     ctrl->dlgid = This->cctrl_next_dlgid;
1182     ctrl->cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
1183     list_init(&ctrl->sub_cctrls);
1184     list_init(&ctrl->sub_items);
1185 
1186     if(This->cctrl_active_vg)
1187         list_add_tail(&This->cctrl_active_vg->sub_cctrls, &ctrl->sub_cctrls_entry);
1188     else
1189         list_add_tail(&This->cctrls, &ctrl->entry);
1190 
1191     SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)ctrl);
1192 
1193     if(ppctrl) *ppctrl = ctrl;
1194 
1195     This->cctrl_next_dlgid++;
1196     return S_OK;
1197 }
1198 
1199 /**************************************************************************
1200  * Container functions.
1201  */
1202 static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
1203 {
1204     UINT container_height;
1205     UINT column_width;
1206     UINT nr_of_cols;
1207     UINT max_control_height, total_height = 0;
1208     UINT cur_col_pos, cur_row_pos;
1209     customctrl *ctrl;
1210     BOOL fits_height;
1211     UINT cspacing = MulDiv(90, This->dpi_x, USER_DEFAULT_SCREEN_DPI);    /* Columns are spaced with 90px */
1212     UINT rspacing = MulDiv(4, This->dpi_y, USER_DEFAULT_SCREEN_DPI);     /* Rows are spaced with 4 px. */
1213 
1214     /* Given the new width of the container, this function determines the
1215      * needed height of the container and places the controls according to
1216      * the new layout. Returns the new height.
1217      */
1218 
1219     TRACE("%p\n", This);
1220 
1221     column_width = This->cctrl_width + cspacing;
1222     nr_of_cols = (container_width - This->cctrl_indent + cspacing) / column_width;
1223 
1224     /* We don't need to do anything unless the number of visible columns has changed. */
1225     if(nr_of_cols == This->cctrls_cols)
1226     {
1227         RECT rc;
1228         GetWindowRect(This->cctrls_hwnd, &rc);
1229         return rc.bottom - rc.top;
1230     }
1231 
1232     This->cctrls_cols = nr_of_cols;
1233 
1234     /* Get the size of the tallest control, and the total size of
1235      * all the controls to figure out the number of slots we need.
1236      */
1237     max_control_height = 0;
1238     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1239     {
1240         if(ctrl->cdcstate & CDCS_VISIBLE)
1241         {
1242             UINT control_height = ctrl_get_height(ctrl);
1243             max_control_height = max(max_control_height, control_height);
1244 
1245             total_height +=  control_height + rspacing;
1246         }
1247     }
1248 
1249     if(!total_height)
1250         return 0;
1251 
1252     container_height = max(total_height / nr_of_cols, max_control_height + rspacing);
1253     TRACE("Guess: container_height: %d\n",container_height);
1254 
1255     /* Incrementally increase container_height until all the controls
1256      * fit.
1257      */
1258     do {
1259         UINT columns_needed = 1;
1260         cur_row_pos = 0;
1261 
1262         fits_height = TRUE;
1263         LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1264         {
1265             if(ctrl->cdcstate & CDCS_VISIBLE)
1266             {
1267                 UINT control_height = ctrl_get_height(ctrl);
1268 
1269                 if(cur_row_pos + control_height > container_height)
1270                 {
1271                     if(++columns_needed > nr_of_cols)
1272                     {
1273                         container_height += 1;
1274                         fits_height = FALSE;
1275                         break;
1276                     }
1277                     cur_row_pos = 0;
1278                 }
1279 
1280                 cur_row_pos += control_height + rspacing;
1281             }
1282         }
1283     } while(!fits_height);
1284 
1285     TRACE("Final container height: %d\n", container_height);
1286 
1287     /* Move the controls to their final destination
1288      */
1289     cur_col_pos = 0; cur_row_pos = 0;
1290     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1291     {
1292         if(ctrl->cdcstate & CDCS_VISIBLE)
1293         {
1294             RECT rc;
1295             UINT control_height, control_indent;
1296             GetWindowRect(ctrl->wrapper_hwnd, &rc);
1297             control_height = rc.bottom - rc.top;
1298 
1299             if(cur_row_pos + control_height > container_height)
1300             {
1301                 cur_row_pos = 0;
1302                 cur_col_pos += This->cctrl_width + cspacing;
1303             }
1304 
1305 
1306             if(ctrl->type == IDLG_CCTRL_VISUALGROUP)
1307                 control_indent = 0;
1308             else
1309                 control_indent = This->cctrl_indent;
1310 
1311             SetWindowPos(ctrl->wrapper_hwnd, NULL, cur_col_pos + control_indent, cur_row_pos, 0, 0,
1312                          SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
1313 
1314             cur_row_pos += control_height + rspacing;
1315         }
1316     }
1317 
1318     /* Sanity check */
1319     if(cur_row_pos + This->cctrl_width > container_width)
1320         ERR("-- Failed to place controls properly.\n");
1321 
1322     return container_height;
1323 }
1324 
1325 static void ctrl_set_font(customctrl *ctrl, HFONT font)
1326 {
1327     customctrl *sub_ctrl;
1328     cctrl_item* item;
1329 
1330     SendMessageW(ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1331 
1332     LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
1333     {
1334         ctrl_set_font(sub_ctrl, font);
1335     }
1336 
1337     if (ctrl->type == IDLG_CCTRL_RADIOBUTTONLIST)
1338     {
1339         LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
1340         {
1341             SendMessageW(item->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1342         }
1343     }
1344 }
1345 
1346 static void ctrl_container_reparent(FileDialogImpl *This, HWND parent)
1347 {
1348     LONG wndstyle;
1349 
1350     if(parent)
1351     {
1352         customctrl *ctrl;
1353         HFONT font;
1354 
1355         wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1356         wndstyle &= ~(WS_POPUP);
1357         wndstyle |= WS_CHILD;
1358         SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1359 
1360         SetParent(This->cctrls_hwnd, parent);
1361         ShowWindow(This->cctrls_hwnd, TRUE);
1362 
1363         /* Set the fonts to match the dialog font. */
1364         font = (HFONT)SendMessageW(parent, WM_GETFONT, 0, 0);
1365         if(!font)
1366             ERR("Failed to get font handle from dialog.\n");
1367 
1368         LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1369         {
1370             if(font) ctrl_set_font(ctrl, font);
1371             customctrl_resize(This, ctrl);
1372         }
1373     }
1374     else
1375     {
1376         ShowWindow(This->cctrls_hwnd, FALSE);
1377 
1378         wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1379         wndstyle &= ~(WS_CHILD);
1380         wndstyle |= WS_POPUP;
1381         SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1382 
1383         SetParent(This->cctrls_hwnd, NULL);
1384     }
1385 }
1386 
1387 static LRESULT ctrl_container_on_create(HWND hwnd, CREATESTRUCTW *crs)
1388 {
1389     FileDialogImpl *This = crs->lpCreateParams;
1390     TRACE("%p\n", This);
1391 
1392     SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1393     return TRUE;
1394 }
1395 
1396 static LRESULT ctrl_container_on_wm_destroy(FileDialogImpl *This)
1397 {
1398     customctrl *cur1, *cur2;
1399     TRACE("%p\n", This);
1400 
1401     LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->cctrls, customctrl, entry)
1402     {
1403         list_remove(&cur1->entry);
1404         ctrl_free(cur1);
1405     }
1406 
1407     return TRUE;
1408 }
1409 
1410 static LRESULT CALLBACK ctrl_container_wndproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1411 {
1412     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1413 
1414     switch(umessage)
1415     {
1416     case WM_NCCREATE:         return ctrl_container_on_create(hwnd, (CREATESTRUCTW*)lparam);
1417     case WM_DESTROY:          return ctrl_container_on_wm_destroy(This);
1418     default:                  return DefWindowProcW(hwnd, umessage, wparam, lparam);
1419     }
1420 
1421     return FALSE;
1422 }
1423 
1424 static void radiobuttonlist_set_selected_item(FileDialogImpl *This, customctrl *ctrl, cctrl_item *item)
1425 {
1426     cctrl_item *cursor;
1427 
1428     LIST_FOR_EACH_ENTRY(cursor, &ctrl->sub_items, cctrl_item, entry)
1429     {
1430         SendMessageW(cursor->hwnd, BM_SETCHECK, (cursor == item) ? BST_CHECKED : BST_UNCHECKED, 0);
1431     }
1432 }
1433 
1434 static LRESULT radiobuttonlist_on_bn_clicked(FileDialogImpl *This, HWND hwnd, HWND child)
1435 {
1436     DWORD ctrl_id = (DWORD)GetWindowLongPtrW(hwnd, GWLP_ID);
1437     customctrl *ctrl;
1438     cctrl_item *item;
1439     BOOL found_item=FALSE;
1440 
1441     ctrl = get_cctrl_from_dlgid(This, ctrl_id);
1442 
1443     if (!ctrl)
1444     {
1445         ERR("Can't find this control\n");
1446         return 0;
1447     }
1448 
1449     LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
1450     {
1451         if (item->hwnd == child)
1452         {
1453             found_item = TRUE;
1454             break;
1455         }
1456     }
1457 
1458     if (!found_item)
1459     {
1460         ERR("Can't find control item\n");
1461         return 0;
1462     }
1463 
1464     radiobuttonlist_set_selected_item(This, ctrl, item);
1465 
1466     cctrl_event_OnItemSelected(This, ctrl->id, item->id);
1467 
1468     return 0;
1469 }
1470 
1471 static LRESULT radiobuttonlist_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1472 {
1473     switch(HIWORD(wparam))
1474     {
1475     case BN_CLICKED:          return radiobuttonlist_on_bn_clicked(This, hwnd, (HWND)lparam);
1476     }
1477 
1478     return FALSE;
1479 }
1480 
1481 static LRESULT CALLBACK radiobuttonlist_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
1482 {
1483     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1484 
1485     switch(message)
1486     {
1487     case WM_COMMAND:        return radiobuttonlist_on_wm_command(This, hwnd, wparam, lparam);
1488     }
1489 
1490     return DefWindowProcW(hwnd, message, wparam, lparam);
1491 }
1492 
1493 static HRESULT init_custom_controls(FileDialogImpl *This)
1494 {
1495     WNDCLASSW wc;
1496     HDC hdc;
1497     static const WCHAR ctrl_container_classname[] =
1498         {'i','d','l','g','_','c','o','n','t','a','i','n','e','r','_','p','a','n','e',0};
1499 
1500     InitCommonControlsEx(NULL);
1501 
1502     if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
1503     {
1504         wc.style            = CS_HREDRAW | CS_VREDRAW;
1505         wc.lpfnWndProc      = ctrl_container_wndproc;
1506         wc.cbClsExtra       = 0;
1507         wc.cbWndExtra       = 0;
1508         wc.hInstance        = COMDLG32_hInstance;
1509         wc.hIcon            = 0;
1510         wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1511         wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
1512         wc.lpszMenuName     = NULL;
1513         wc.lpszClassName    = ctrl_container_classname;
1514 
1515         if(!RegisterClassW(&wc)) return E_FAIL;
1516     }
1517 
1518     This->cctrls_hwnd = CreateWindowExW(0, ctrl_container_classname, NULL,
1519                                         WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1520                                         0, 0, 0, 0, NULL, 0,
1521                                         COMDLG32_hInstance, This);
1522     if(!This->cctrls_hwnd)
1523         return E_FAIL;
1524 
1525     hdc = GetDC(This->cctrls_hwnd);
1526     This->dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
1527     This->dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
1528     ReleaseDC(This->cctrls_hwnd, hdc);
1529 
1530     This->cctrl_width = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);      /* Controls have a fixed width */
1531     This->cctrl_indent = MulDiv(100, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1532     This->cctrl_def_height = MulDiv(23, This->dpi_y, USER_DEFAULT_SCREEN_DPI);
1533     This->cctrls_cols = 0;
1534 
1535     This->cctrl_next_dlgid = 0x2000;
1536     list_init(&This->cctrls);
1537     This->cctrl_active_vg = NULL;
1538 
1539     SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, WS_TABSTOP);
1540 
1541     /* Register class for  */
1542     if( !GetClassInfoW(COMDLG32_hInstance, floatnotifysinkW, &wc) ||
1543         wc.hInstance != COMDLG32_hInstance)
1544     {
1545         wc.style            = CS_HREDRAW | CS_VREDRAW;
1546         wc.lpfnWndProc      = notifysink_proc;
1547         wc.cbClsExtra       = 0;
1548         wc.cbWndExtra       = 0;
1549         wc.hInstance        = COMDLG32_hInstance;
1550         wc.hIcon            = 0;
1551         wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1552         wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
1553         wc.lpszMenuName     = NULL;
1554         wc.lpszClassName    = floatnotifysinkW;
1555 
1556         if (!RegisterClassW(&wc))
1557             ERR("Failed to register FloatNotifySink window class.\n");
1558     }
1559 
1560     if( !GetClassInfoW(COMDLG32_hInstance, radiobuttonlistW, &wc) ||
1561         wc.hInstance != COMDLG32_hInstance)
1562     {
1563         wc.style            = CS_HREDRAW | CS_VREDRAW;
1564         wc.lpfnWndProc      = radiobuttonlist_proc;
1565         wc.cbClsExtra       = 0;
1566         wc.cbWndExtra       = 0;
1567         wc.hInstance        = COMDLG32_hInstance;
1568         wc.hIcon            = 0;
1569         wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1570         wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
1571         wc.lpszMenuName     = NULL;
1572         wc.lpszClassName    = radiobuttonlistW;
1573 
1574         if (!RegisterClassW(&wc))
1575             ERR("Failed to register RadioButtonList window class.\n");
1576     }
1577 
1578     return S_OK;
1579 }
1580 
1581 /**************************************************************************
1582  * Window related functions.
1583  */
1584 static BOOL update_open_dropdown(FileDialogImpl *This)
1585 {
1586     /* Show or hide the open dropdown button as appropriate */
1587     BOOL show=FALSE, showing;
1588     HWND open_hwnd, dropdown_hwnd;
1589 
1590     if (This->hmenu_opendropdown)
1591     {
1592         INT num_visible_items=0;
1593         cctrl_item* item;
1594 
1595         LIST_FOR_EACH_ENTRY(item, &This->cctrl_opendropdown.sub_items, cctrl_item, entry)
1596         {
1597             if (item->cdcstate & CDCS_VISIBLE)
1598             {
1599                 num_visible_items++;
1600                 if (num_visible_items >= 2)
1601                 {
1602                     show = TRUE;
1603                     break;
1604                 }
1605             }
1606         }
1607     }
1608 
1609     open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1610     dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
1611 
1612     showing = (GetWindowLongPtrW(dropdown_hwnd, GWL_STYLE) & WS_VISIBLE) != 0;
1613 
1614     if (showing != show)
1615     {
1616         RECT open_rc, dropdown_rc;
1617 
1618         GetWindowRect(open_hwnd, &open_rc);
1619         GetWindowRect(dropdown_hwnd, &dropdown_rc);
1620 
1621         if (show)
1622         {
1623             ShowWindow(dropdown_hwnd, SW_SHOW);
1624 
1625             SetWindowPos(open_hwnd, NULL, 0, 0,
1626                 (open_rc.right - open_rc.left) - (dropdown_rc.right - dropdown_rc.left),
1627                 open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1628         }
1629         else
1630         {
1631             ShowWindow(dropdown_hwnd, SW_HIDE);
1632 
1633             SetWindowPos(open_hwnd, NULL, 0, 0,
1634                 (open_rc.right - open_rc.left) + (dropdown_rc.right - dropdown_rc.left),
1635                 open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1636         }
1637     }
1638 
1639     return show;
1640 }
1641 
1642 static void update_layout(FileDialogImpl *This)
1643 {
1644     HDWP hdwp;
1645     HWND hwnd;
1646     RECT dialog_rc;
1647     RECT cancel_rc, dropdown_rc, open_rc;
1648     RECT filetype_rc, filename_rc, filenamelabel_rc;
1649     RECT toolbar_rc, ebrowser_rc, customctrls_rc;
1650     static const UINT vspacing = 4, hspacing = 4;
1651     static const UINT min_width = 320, min_height = 200;
1652     BOOL show_dropdown;
1653 
1654     if (!GetClientRect(This->dlg_hwnd, &dialog_rc))
1655     {
1656         TRACE("Invalid dialog window, not updating layout\n");
1657         return;
1658     }
1659 
1660     if(dialog_rc.right < min_width || dialog_rc.bottom < min_height)
1661     {
1662         TRACE("Dialog size (%d, %d) too small, not updating layout\n", dialog_rc.right, dialog_rc.bottom);
1663         return;
1664     }
1665 
1666     /****
1667      * Calculate the size of the dialog and all the parts.
1668      */
1669 
1670     /* Cancel button */
1671     hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL);
1672     if(hwnd)
1673     {
1674         int cancel_width, cancel_height;
1675         GetWindowRect(hwnd, &cancel_rc);
1676         cancel_width = cancel_rc.right - cancel_rc.left;
1677         cancel_height = cancel_rc.bottom - cancel_rc.top;
1678 
1679         cancel_rc.left = dialog_rc.right - cancel_width - hspacing;
1680         cancel_rc.top = dialog_rc.bottom - cancel_height - vspacing;
1681         cancel_rc.right = cancel_rc.left + cancel_width;
1682         cancel_rc.bottom = cancel_rc.top + cancel_height;
1683     }
1684 
1685     /* Open/Save dropdown */
1686     show_dropdown = update_open_dropdown(This);
1687 
1688     if(show_dropdown)
1689     {
1690         int dropdown_width, dropdown_height;
1691         hwnd = GetDlgItem(This->dlg_hwnd, psh1);
1692 
1693         GetWindowRect(hwnd, &dropdown_rc);
1694         dropdown_width = dropdown_rc.right - dropdown_rc.left;
1695         dropdown_height = dropdown_rc.bottom - dropdown_rc.top;
1696 
1697         dropdown_rc.left = cancel_rc.left - dropdown_width - hspacing;
1698         dropdown_rc.top = cancel_rc.top;
1699         dropdown_rc.right = dropdown_rc.left + dropdown_width;
1700         dropdown_rc.bottom = dropdown_rc.top + dropdown_height;
1701     }
1702     else
1703     {
1704         dropdown_rc.left = dropdown_rc.right = cancel_rc.left - hspacing;
1705         dropdown_rc.top = cancel_rc.top;
1706         dropdown_rc.bottom = cancel_rc.bottom;
1707     }
1708 
1709     /* Open/Save button */
1710     hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1711     if(hwnd)
1712     {
1713         int open_width, open_height;
1714         GetWindowRect(hwnd, &open_rc);
1715         open_width = open_rc.right - open_rc.left;
1716         open_height = open_rc.bottom - open_rc.top;
1717 
1718         open_rc.left = dropdown_rc.left - open_width;
1719         open_rc.top = dropdown_rc.top;
1720         open_rc.right = open_rc.left + open_width;
1721         open_rc.bottom = open_rc.top + open_height;
1722     }
1723 
1724     /* The filetype combobox. */
1725     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1726     if(hwnd)
1727     {
1728         int filetype_width, filetype_height;
1729         GetWindowRect(hwnd, &filetype_rc);
1730 
1731         filetype_width = filetype_rc.right - filetype_rc.left;
1732         filetype_height = filetype_rc.bottom - filetype_rc.top;
1733 
1734         filetype_rc.right = cancel_rc.right;
1735 
1736         filetype_rc.left = filetype_rc.right - filetype_width;
1737         filetype_rc.top = cancel_rc.top - filetype_height - vspacing;
1738         filetype_rc.bottom = filetype_rc.top + filetype_height;
1739 
1740         if(!This->filterspec_count)
1741             filetype_rc.left = filetype_rc.right;
1742     }
1743 
1744     /* Filename label. */
1745     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC);
1746     if(hwnd)
1747     {
1748         int filetypelabel_width, filetypelabel_height;
1749         GetWindowRect(hwnd, &filenamelabel_rc);
1750 
1751         filetypelabel_width = filenamelabel_rc.right - filenamelabel_rc.left;
1752         filetypelabel_height = filenamelabel_rc.bottom - filenamelabel_rc.top;
1753 
1754         filenamelabel_rc.left = 160; /* FIXME */
1755         filenamelabel_rc.top = filetype_rc.top;
1756         filenamelabel_rc.right = filenamelabel_rc.left + filetypelabel_width;
1757         filenamelabel_rc.bottom = filenamelabel_rc.top + filetypelabel_height;
1758     }
1759 
1760     /* Filename edit box. */
1761     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
1762     if(hwnd)
1763     {
1764         int filename_width, filename_height;
1765         GetWindowRect(hwnd, &filename_rc);
1766 
1767         filename_width = filetype_rc.left - filenamelabel_rc.right - hspacing*2;
1768         filename_height = filename_rc.bottom - filename_rc.top;
1769 
1770         filename_rc.left = filenamelabel_rc.right + hspacing;
1771         filename_rc.top = filetype_rc.top;
1772         filename_rc.right = filename_rc.left + filename_width;
1773         filename_rc.bottom = filename_rc.top + filename_height;
1774     }
1775 
1776     hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR);
1777     if(hwnd)
1778     {
1779         GetWindowRect(hwnd, &toolbar_rc);
1780         MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2);
1781     }
1782 
1783     /* The custom controls */
1784     customctrls_rc.left = dialog_rc.left + hspacing;
1785     customctrls_rc.right = dialog_rc.right - hspacing;
1786     customctrls_rc.bottom = filename_rc.top - vspacing;
1787     customctrls_rc.top = customctrls_rc.bottom -
1788         ctrl_container_resize(This, customctrls_rc.right - customctrls_rc.left);
1789 
1790     /* The ExplorerBrowser control. */
1791     ebrowser_rc.left = dialog_rc.left + hspacing;
1792     ebrowser_rc.top = toolbar_rc.bottom + vspacing;
1793     ebrowser_rc.right = dialog_rc.right - hspacing;
1794     ebrowser_rc.bottom = customctrls_rc.top - vspacing;
1795 
1796     /****
1797      * Move everything to the right place.
1798      */
1799 
1800     /* FIXME: The Save Dialog uses a slightly different layout. */
1801     hdwp = BeginDeferWindowPos(7);
1802 
1803     if(hdwp && This->peb)
1804         IExplorerBrowser_SetRect(This->peb, &hdwp, ebrowser_rc);
1805 
1806     if(hdwp && This->cctrls_hwnd)
1807         DeferWindowPos(hdwp, This->cctrls_hwnd, NULL,
1808                        customctrls_rc.left, customctrls_rc.top,
1809                        customctrls_rc.right - customctrls_rc.left, customctrls_rc.bottom - customctrls_rc.top,
1810                        SWP_NOZORDER | SWP_NOACTIVATE);
1811 
1812     /* The default controls */
1813     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE)) )
1814         DeferWindowPos(hdwp, hwnd, NULL, filetype_rc.left, filetype_rc.top, 0, 0,
1815                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1816 
1817     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
1818         DeferWindowPos(hdwp, hwnd, NULL, filename_rc.left, filename_rc.top,
1819                        filename_rc.right - filename_rc.left, filename_rc.bottom - filename_rc.top,
1820                        SWP_NOZORDER | SWP_NOACTIVATE);
1821 
1822     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)) )
1823         DeferWindowPos(hdwp, hwnd, NULL, filenamelabel_rc.left, filenamelabel_rc.top, 0, 0,
1824                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1825 
1826     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDOK)) )
1827         DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
1828                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1829 
1830     if(hdwp && This->hmenu_opendropdown && (hwnd = GetDlgItem(This->dlg_hwnd, psh1)))
1831         DeferWindowPos(hdwp, hwnd, NULL, dropdown_rc.left, dropdown_rc.top, 0, 0,
1832                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1833 
1834     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
1835         DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
1836                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1837 
1838     if(hdwp)
1839         EndDeferWindowPos(hdwp);
1840     else
1841         ERR("Failed to position dialog controls.\n");
1842 
1843     return;
1844 }
1845 
1846 static HRESULT init_explorerbrowser(FileDialogImpl *This)
1847 {
1848     IShellItem *psi_folder;
1849     IObjectWithSite *client;
1850     FOLDERSETTINGS fos;
1851     RECT rc = {0};
1852     HRESULT hr;
1853 
1854     /* Create ExplorerBrowser instance */
1855     OleInitialize(NULL);
1856 
1857     hr = CoCreateInstance(&CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER,
1858                           &IID_IExplorerBrowser, (void**)&This->peb);
1859     if(FAILED(hr))
1860     {
1861         ERR("Failed to instantiate ExplorerBrowser control.\n");
1862         return hr;
1863     }
1864 
1865     IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES | EBO_NOBORDER);
1866 
1867     hr = IExplorerBrowser_Initialize(This->peb, This->dlg_hwnd, &rc, NULL);
1868     if(FAILED(hr))
1869     {
1870         ERR("Failed to initialize the ExplorerBrowser control.\n");
1871         IExplorerBrowser_Release(This->peb);
1872         This->peb = NULL;
1873         return hr;
1874     }
1875     hr = IExplorerBrowser_Advise(This->peb, &This->IExplorerBrowserEvents_iface, &This->ebevents_cookie);
1876     if(FAILED(hr))
1877         ERR("Advise (ExplorerBrowser) failed.\n");
1878 
1879     /* Get previous options? */
1880     fos.ViewMode = fos.fFlags = 0;
1881     if(!(This->options & FOS_ALLOWMULTISELECT))
1882         fos.fFlags |= FWF_SINGLESEL;
1883 
1884     IExplorerBrowser_SetFolderSettings(This->peb, &fos);
1885 
1886     hr = IExplorerBrowser_QueryInterface(This->peb, &IID_IObjectWithSite, (void**)&client);
1887     if (hr == S_OK)
1888     {
1889         hr = IObjectWithSite_SetSite(client, (IUnknown*)&This->IFileDialog2_iface);
1890         IObjectWithSite_Release(client);
1891         if(FAILED(hr))
1892             ERR("SetSite failed, 0x%08x\n", hr);
1893     }
1894 
1895     /* Browse somewhere */
1896     psi_folder = This->psi_setfolder ? This->psi_setfolder : This->psi_defaultfolder;
1897     IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psi_folder, SBSP_DEFBROWSER);
1898 
1899     return S_OK;
1900 }
1901 
1902 static void init_toolbar(FileDialogImpl *This, HWND hwnd)
1903 {
1904     HWND htoolbar;
1905     TBADDBITMAP tbab;
1906     TBBUTTON button[2];
1907 
1908     htoolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE,
1909                                0, 0, 0, 0,
1910                                hwnd, (HMENU)IDC_NAV_TOOLBAR, NULL, NULL);
1911 
1912     tbab.hInst = HINST_COMMCTRL;
1913     tbab.nID = IDB_HIST_LARGE_COLOR;
1914     SendMessageW(htoolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
1915 
1916     button[0].iBitmap = HIST_BACK;
1917     button[0].idCommand = IDC_NAVBACK;
1918     button[0].fsState = TBSTATE_ENABLED;
1919     button[0].fsStyle = BTNS_BUTTON;
1920     button[0].dwData = 0;
1921     button[0].iString = 0;
1922 
1923     button[1].iBitmap = HIST_FORWARD;
1924     button[1].idCommand = IDC_NAVFORWARD;
1925     button[1].fsState = TBSTATE_ENABLED;
1926     button[1].fsStyle = BTNS_BUTTON;
1927     button[1].dwData = 0;
1928     button[1].iString = 0;
1929 
1930     SendMessageW(htoolbar, TB_ADDBUTTONSW, 2, (LPARAM)button);
1931     SendMessageW(htoolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(24,24));
1932     SendMessageW(htoolbar, TB_AUTOSIZE, 0, 0);
1933 }
1934 
1935 static void update_control_text(FileDialogImpl *This)
1936 {
1937     HWND hitem;
1938     LPCWSTR custom_okbutton;
1939     cctrl_item* item;
1940     UINT min_width = MulDiv(50, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1941     UINT max_width = MulDiv(250, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1942 
1943     if(This->custom_title)
1944         SetWindowTextW(This->dlg_hwnd, This->custom_title);
1945 
1946     if(This->hmenu_opendropdown && (item = get_first_item(&This->cctrl_opendropdown)))
1947         custom_okbutton = item->label;
1948     else
1949         custom_okbutton = This->custom_okbutton;
1950 
1951     if(custom_okbutton &&
1952        (hitem = GetDlgItem(This->dlg_hwnd, IDOK)))
1953     {
1954         SetWindowTextW(hitem, custom_okbutton);
1955         ctrl_resize(hitem, min_width, max_width, FALSE);
1956     }
1957 
1958     if(This->custom_cancelbutton &&
1959        (hitem = GetDlgItem(This->dlg_hwnd, IDCANCEL)))
1960     {
1961         SetWindowTextW(hitem, This->custom_cancelbutton);
1962         ctrl_resize(hitem, min_width, max_width, FALSE);
1963     }
1964 
1965     if(This->custom_filenamelabel &&
1966        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)))
1967     {
1968         SetWindowTextW(hitem, This->custom_filenamelabel);
1969         ctrl_resize(hitem, min_width, max_width, FALSE);
1970     }
1971 }
1972 
1973 static LRESULT CALLBACK dropdown_subclass_proc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1974 {
1975     static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
1976     static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
1977 
1978     if (umessage == WM_LBUTTONDOWN)
1979     {
1980         FileDialogImpl *This = GetPropW(hwnd, prop_this);
1981 
1982         SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
1983         show_opendropdown(This);
1984         SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
1985 
1986         return 0;
1987     }
1988 
1989     return CallWindowProcW((WNDPROC)GetPropW(hwnd, prop_oldwndproc), hwnd, umessage, wparam, lparam);
1990 }
1991 
1992 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
1993 {
1994     FileDialogImpl *This = (FileDialogImpl*)lParam;
1995     HWND hitem;
1996 
1997     TRACE("(%p, %p)\n", This, hwnd);
1998 
1999     SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
2000     This->dlg_hwnd = hwnd;
2001 
2002     hitem = GetDlgItem(This->dlg_hwnd, pshHelp);
2003     if(hitem) ShowWindow(hitem, SW_HIDE);
2004 
2005     hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPESTATIC);
2006     if(hitem) ShowWindow(hitem, SW_HIDE);
2007 
2008     /* Fill filetypes combobox, or hide it. */
2009     hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
2010     if(This->filterspec_count)
2011     {
2012         HDC hdc;
2013         HFONT font;
2014         SIZE size;
2015         UINT i, maxwidth = 0;
2016 
2017         hdc = GetDC(hitem);
2018         font = (HFONT)SendMessageW(hitem, WM_GETFONT, 0, 0);
2019         SelectObject(hdc, font);
2020 
2021         for(i = 0; i < This->filterspec_count; i++)
2022         {
2023             SendMessageW(hitem, CB_ADDSTRING, 0, (LPARAM)This->filterspecs[i].pszName);
2024 
2025             if(GetTextExtentPoint32W(hdc, This->filterspecs[i].pszName, lstrlenW(This->filterspecs[i].pszName), &size))
2026                 maxwidth = max(maxwidth, size.cx);
2027         }
2028         ReleaseDC(hitem, hdc);
2029 
2030         if(maxwidth > 0)
2031         {
2032             maxwidth += GetSystemMetrics(SM_CXVSCROLL) + 4;
2033             SendMessageW(hitem, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0);
2034         }
2035         else
2036             ERR("Failed to calculate width of filetype dropdown\n");
2037 
2038         SendMessageW(hitem, CB_SETCURSEL, This->filetypeindex, 0);
2039     }
2040     else
2041         ShowWindow(hitem, SW_HIDE);
2042 
2043     if(This->set_filename &&
2044        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
2045         SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
2046 
2047     if(This->hmenu_opendropdown)
2048     {
2049         HWND dropdown_hwnd;
2050         LOGFONTW lfw, lfw_marlett;
2051         HFONT dialog_font;
2052         static const WCHAR marlett[] = {'M','a','r','l','e','t','t',0};
2053         static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
2054         static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
2055 
2056         dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
2057 
2058         /* Change dropdown button font to Marlett */
2059         dialog_font = (HFONT)SendMessageW(dropdown_hwnd, WM_GETFONT, 0, 0);
2060 
2061         GetObjectW(dialog_font, sizeof(lfw), &lfw);
2062 
2063         memset(&lfw_marlett, 0, sizeof(lfw_marlett));
2064         lstrcpyW(lfw_marlett.lfFaceName, marlett);
2065         lfw_marlett.lfHeight = lfw.lfHeight;
2066         lfw_marlett.lfCharSet = SYMBOL_CHARSET;
2067 
2068         This->hfont_opendropdown = CreateFontIndirectW(&lfw_marlett);
2069 
2070         SendMessageW(dropdown_hwnd, WM_SETFONT, (LPARAM)This->hfont_opendropdown, 0);
2071 
2072         /* Subclass button so we can handle LBUTTONDOWN */
2073         SetPropW(dropdown_hwnd, prop_this, This);
2074         SetPropW(dropdown_hwnd, prop_oldwndproc,
2075             (HANDLE)SetWindowLongPtrW(dropdown_hwnd, GWLP_WNDPROC, (LONG_PTR)dropdown_subclass_proc));
2076     }
2077 
2078     ctrl_container_reparent(This, This->dlg_hwnd);
2079     init_explorerbrowser(This);
2080     init_toolbar(This, hwnd);
2081     update_control_text(This);
2082     update_layout(This);
2083 
2084     if(This->filterspec_count)
2085         events_OnTypeChange(This);
2086 
2087     if ((hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)))
2088         SetFocus(hitem);
2089 
2090     return FALSE;
2091 }
2092 
2093 static LRESULT on_wm_size(FileDialogImpl *This)
2094 {
2095     update_layout(This);
2096     return FALSE;
2097 }
2098 
2099 static LRESULT on_wm_getminmaxinfo(FileDialogImpl *This, LPARAM lparam)
2100 {
2101     MINMAXINFO *mmi = (MINMAXINFO*)lparam;
2102     TRACE("%p (%p)\n", This, mmi);
2103 
2104     /* FIXME */
2105     mmi->ptMinTrackSize.x = 640;
2106     mmi->ptMinTrackSize.y = 480;
2107 
2108     return FALSE;
2109 }
2110 
2111 static LRESULT on_wm_destroy(FileDialogImpl *This)
2112 {
2113     TRACE("%p\n", This);
2114 
2115     if(This->peb)
2116     {
2117         IExplorerBrowser_Destroy(This->peb);
2118         IExplorerBrowser_Release(This->peb);
2119         This->peb = NULL;
2120     }
2121 
2122     ctrl_container_reparent(This, NULL);
2123     This->dlg_hwnd = NULL;
2124 
2125     DeleteObject(This->hfont_opendropdown);
2126     This->hfont_opendropdown = NULL;
2127 
2128     return TRUE;
2129 }
2130 
2131 static LRESULT on_idok(FileDialogImpl *This)
2132 {
2133     TRACE("%p\n", This);
2134 
2135     if(SUCCEEDED(on_default_action(This)))
2136         EndDialog(This->dlg_hwnd, S_OK);
2137 
2138     return FALSE;
2139 }
2140 
2141 static LRESULT on_idcancel(FileDialogImpl *This)
2142 {
2143     TRACE("%p\n", This);
2144 
2145     EndDialog(This->dlg_hwnd, HRESULT_FROM_WIN32(ERROR_CANCELLED));
2146 
2147     return FALSE;
2148 }
2149 
2150 static LRESULT on_command_opendropdown(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2151 {
2152     if(HIWORD(wparam) == BN_CLICKED)
2153     {
2154         HWND hwnd = (HWND)lparam;
2155         SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
2156         show_opendropdown(This);
2157         SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
2158     }
2159 
2160     return FALSE;
2161 }
2162 
2163 static LRESULT on_browse_back(FileDialogImpl *This)
2164 {
2165     TRACE("%p\n", This);
2166     IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEBACK);
2167     return FALSE;
2168 }
2169 
2170 static LRESULT on_browse_forward(FileDialogImpl *This)
2171 {
2172     TRACE("%p\n", This);
2173     IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEFORWARD);
2174     return FALSE;
2175 }
2176 
2177 static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2178 {
2179     if(HIWORD(wparam) == CBN_SELCHANGE)
2180     {
2181         IShellView *psv;
2182         HRESULT hr;
2183         LPWSTR filename;
2184         UINT prev_index = This->filetypeindex;
2185 
2186         This->filetypeindex = SendMessageW((HWND)lparam, CB_GETCURSEL, 0, 0);
2187         TRACE("File type selection changed to %d.\n", This->filetypeindex);
2188 
2189         if(prev_index == This->filetypeindex)
2190             return FALSE;
2191 
2192         hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&psv);
2193         if(SUCCEEDED(hr))
2194         {
2195             IShellView_Refresh(psv);
2196             IShellView_Release(psv);
2197         }
2198 
2199         if(This->dlg_type == ITEMDLG_TYPE_SAVE && get_file_name(This, &filename))
2200         {
2201             WCHAR buf[MAX_PATH], extbuf[MAX_PATH], *ext;
2202 
2203             ext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
2204             if(ext)
2205             {
2206                 lstrcpyW(buf, filename);
2207 
2208                 if(PathMatchSpecW(buf, This->filterspecs[prev_index].pszSpec))
2209                     PathRemoveExtensionW(buf);
2210 
2211                 lstrcatW(buf, ext);
2212                 set_file_name(This, buf);
2213             }
2214             CoTaskMemFree(filename);
2215         }
2216 
2217         /* The documentation claims that OnTypeChange is called only
2218          * when the dialog is opened, but this is obviously not the
2219          * case. */
2220         events_OnTypeChange(This);
2221     }
2222 
2223     return FALSE;
2224 }
2225 
2226 static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2227 {
2228     switch(LOWORD(wparam))
2229     {
2230     case IDOK:                return on_idok(This);
2231     case IDCANCEL:            return on_idcancel(This);
2232     case psh1:                return on_command_opendropdown(This, wparam, lparam);
2233     case IDC_NAVBACK:         return on_browse_back(This);
2234     case IDC_NAVFORWARD:      return on_browse_forward(This);
2235     case IDC_FILETYPE:        return on_command_filetype(This, wparam, lparam);
2236     default:                  TRACE("Unknown command.\n");
2237     }
2238     return FALSE;
2239 }
2240 
2241 static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
2242 {
2243     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
2244 
2245     switch(umessage)
2246     {
2247     case WM_INITDIALOG:       return on_wm_initdialog(hwnd, lparam);
2248     case WM_COMMAND:          return on_wm_command(This, wparam, lparam);
2249     case WM_SIZE:             return on_wm_size(This);
2250     case WM_GETMINMAXINFO:    return on_wm_getminmaxinfo(This, lparam);
2251     case WM_DESTROY:          return on_wm_destroy(This);
2252     }
2253 
2254     return FALSE;
2255 }
2256 
2257 static HRESULT create_dialog(FileDialogImpl *This, HWND parent)
2258 {
2259     INT_PTR res;
2260 
2261     SetLastError(0);
2262     res = DialogBoxParamW(COMDLG32_hInstance,
2263                           MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
2264                           parent, itemdlg_dlgproc, (LPARAM)This);
2265     This->dlg_hwnd = NULL;
2266     if(res == -1)
2267     {
2268         ERR("Failed to show dialog (LastError: %d)\n", GetLastError());
2269         return E_FAIL;
2270     }
2271 
2272     TRACE("Returning 0x%08x\n", (HRESULT)res);
2273     return (HRESULT)res;
2274 }
2275 
2276 /**************************************************************************
2277  * IFileDialog implementation
2278  */
2279 static inline FileDialogImpl *impl_from_IFileDialog2(IFileDialog2 *iface)
2280 {
2281     return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialog2_iface);
2282 }
2283 
2284 static HRESULT WINAPI IFileDialog2_fnQueryInterface(IFileDialog2 *iface,
2285                                                     REFIID riid,
2286                                                     void **ppvObject)
2287 {
2288     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2289     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
2290 
2291     *ppvObject = NULL;
2292     if(IsEqualGUID(riid, &IID_IUnknown) ||
2293        IsEqualGUID(riid, &IID_IFileDialog) ||
2294        IsEqualGUID(riid, &IID_IFileDialog2))
2295     {
2296         *ppvObject = iface;
2297     }
2298     else if(IsEqualGUID(riid, &IID_IFileOpenDialog) && This->dlg_type == ITEMDLG_TYPE_OPEN)
2299     {
2300         *ppvObject = &This->u.IFileOpenDialog_iface;
2301     }
2302     else if(IsEqualGUID(riid, &IID_IFileSaveDialog) && This->dlg_type == ITEMDLG_TYPE_SAVE)
2303     {
2304         *ppvObject = &This->u.IFileSaveDialog_iface;
2305     }
2306     else if(IsEqualGUID(riid, &IID_IExplorerBrowserEvents))
2307     {
2308         *ppvObject = &This->IExplorerBrowserEvents_iface;
2309     }
2310     else if(IsEqualGUID(riid, &IID_IServiceProvider))
2311     {
2312         *ppvObject = &This->IServiceProvider_iface;
2313     }
2314     else if(IsEqualGUID(&IID_ICommDlgBrowser3, riid) ||
2315             IsEqualGUID(&IID_ICommDlgBrowser2, riid) ||
2316             IsEqualGUID(&IID_ICommDlgBrowser, riid))
2317     {
2318         *ppvObject = &This->ICommDlgBrowser3_iface;
2319     }
2320     else if(IsEqualGUID(&IID_IOleWindow, riid))
2321     {
2322         *ppvObject = &This->IOleWindow_iface;
2323     }
2324     else if(IsEqualGUID(riid, &IID_IFileDialogCustomize) ||
2325             IsEqualGUID(riid, &IID_IFileDialogCustomizeAlt))
2326     {
2327         *ppvObject = &This->IFileDialogCustomize_iface;
2328     }
2329     else
2330         FIXME("Unknown interface requested: %s.\n", debugstr_guid(riid));
2331 
2332     if(*ppvObject)
2333     {
2334         IUnknown_AddRef((IUnknown*)*ppvObject);
2335         return S_OK;
2336     }
2337 
2338     return E_NOINTERFACE;
2339 }
2340 
2341 static ULONG WINAPI IFileDialog2_fnAddRef(IFileDialog2 *iface)
2342 {
2343     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2344     LONG ref = InterlockedIncrement(&This->ref);
2345     TRACE("%p - ref %d\n", This, ref);
2346 
2347     return ref;
2348 }
2349 
2350 static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface)
2351 {
2352     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2353     LONG ref = InterlockedDecrement(&This->ref);
2354     TRACE("%p - ref %d\n", This, ref);
2355 
2356     if(!ref)
2357     {
2358         UINT i;
2359         for(i = 0; i < This->filterspec_count; i++)
2360         {
2361             LocalFree((void*)This->filterspecs[i].pszName);
2362             LocalFree((void*)This->filterspecs[i].pszSpec);
2363         }
2364         HeapFree(GetProcessHeap(), 0, This->filterspecs);
2365 
2366         DestroyWindow(This->cctrls_hwnd);
2367 
2368         if(This->psi_defaultfolder) IShellItem_Release(This->psi_defaultfolder);
2369         if(This->psi_setfolder)     IShellItem_Release(This->psi_setfolder);
2370         if(This->psi_folder)        IShellItem_Release(This->psi_folder);
2371         if(This->psia_selection)    IShellItemArray_Release(This->psia_selection);
2372         if(This->psia_results)      IShellItemArray_Release(This->psia_results);
2373 
2374         LocalFree(This->set_filename);
2375         LocalFree(This->default_ext);
2376         LocalFree(This->custom_title);
2377         LocalFree(This->custom_okbutton);
2378         LocalFree(This->custom_cancelbutton);
2379         LocalFree(This->custom_filenamelabel);
2380 
2381         DestroyMenu(This->hmenu_opendropdown);
2382         DeleteObject(This->hfont_opendropdown);
2383 
2384         HeapFree(GetProcessHeap(), 0, This);
2385     }
2386 
2387     return ref;
2388 }
2389 
2390 static HRESULT WINAPI IFileDialog2_fnShow(IFileDialog2 *iface, HWND hwndOwner)
2391 {
2392     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2393     TRACE("%p (%p)\n", iface, hwndOwner);
2394 
2395     This->opendropdown_has_selection = FALSE;
2396 
2397     return create_dialog(This, hwndOwner);
2398 }
2399 
2400 static HRESULT WINAPI IFileDialog2_fnSetFileTypes(IFileDialog2 *iface, UINT cFileTypes,
2401                                                   const COMDLG_FILTERSPEC *rgFilterSpec)
2402 {
2403     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2404     UINT i;
2405     TRACE("%p (%d, %p)\n", This, cFileTypes, rgFilterSpec);
2406 
2407     if(This->filterspecs)
2408         return E_UNEXPECTED;
2409 
2410     if(!rgFilterSpec)
2411         return E_INVALIDARG;
2412 
2413     if(!cFileTypes)
2414         return S_OK;
2415 
2416     This->filterspecs = HeapAlloc(GetProcessHeap(), 0, sizeof(COMDLG_FILTERSPEC)*cFileTypes);
2417     for(i = 0; i < cFileTypes; i++)
2418     {
2419         This->filterspecs[i].pszName = StrDupW(rgFilterSpec[i].pszName);
2420         This->filterspecs[i].pszSpec = StrDupW(rgFilterSpec[i].pszSpec);
2421     }
2422     This->filterspec_count = cFileTypes;
2423 
2424     return S_OK;
2425 }
2426 
2427 static HRESULT WINAPI IFileDialog2_fnSetFileTypeIndex(IFileDialog2 *iface, UINT iFileType)
2428 {
2429     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2430     TRACE("%p (%d)\n", This, iFileType);
2431 
2432     if(!This->filterspecs)
2433         return E_FAIL;
2434 
2435     iFileType = max(iFileType, 1);
2436     iFileType = min(iFileType, This->filterspec_count);
2437     This->filetypeindex = iFileType-1;
2438 
2439     return S_OK;
2440 }
2441 
2442 static HRESULT WINAPI IFileDialog2_fnGetFileTypeIndex(IFileDialog2 *iface, UINT *piFileType)
2443 {
2444     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2445     TRACE("%p (%p)\n", This, piFileType);
2446 
2447     if(!piFileType)
2448         return E_INVALIDARG;
2449 
2450     if(This->filterspec_count == 0)
2451         *piFileType = 0;
2452     else
2453         *piFileType = This->filetypeindex + 1;
2454 
2455     return S_OK;
2456 }
2457 
2458 static HRESULT WINAPI IFileDialog2_fnAdvise(IFileDialog2 *iface, IFileDialogEvents *pfde, DWORD *pdwCookie)
2459 {
2460     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2461     events_client *client;
2462     TRACE("%p (%p, %p)\n", This, pfde, pdwCookie);
2463 
2464     if(!pfde || !pdwCookie)
2465         return E_INVALIDARG;
2466 
2467     client = HeapAlloc(GetProcessHeap(), 0, sizeof(events_client));
2468     client->pfde = pfde;
2469     client->cookie = ++This->events_next_cookie;
2470 
2471     IFileDialogEvents_AddRef(pfde);
2472     *pdwCookie = client->cookie;
2473 
2474     list_add_tail(&This->events_clients, &client->entry);
2475 
2476     return S_OK;
2477 }
2478 
2479 static HRESULT WINAPI IFileDialog2_fnUnadvise(IFileDialog2 *iface, DWORD dwCookie)
2480 {
2481     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2482     events_client *client, *found = NULL;
2483     TRACE("%p (%d)\n", This, dwCookie);
2484 
2485     LIST_FOR_EACH_ENTRY(client, &This->events_clients, events_client, entry)
2486     {
2487         if(client->cookie == dwCookie)
2488         {
2489             found = client;
2490             break;
2491         }
2492     }
2493 
2494     if(found)
2495     {
2496         list_remove(&found->entry);
2497         IFileDialogEvents_Release(found->pfde);
2498         HeapFree(GetProcessHeap(), 0, found);
2499         return S_OK;
2500     }
2501 
2502     return E_INVALIDARG;
2503 }
2504 
2505 static HRESULT WINAPI IFileDialog2_fnSetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS fos)
2506 {
2507     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2508     TRACE("%p (0x%x)\n", This, fos);
2509 
2510     if (fos & ~(FOS_OVERWRITEPROMPT | FOS_STRICTFILETYPES | FOS_NOCHANGEDIR | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM
2511             | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_ALLOWMULTISELECT | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST
2512             | FOS_CREATEPROMPT | FOS_SHAREAWARE | FOS_NOREADONLYRETURN | FOS_NOTESTFILECREATE | FOS_HIDEMRUPLACES
2513             | FOS_HIDEPINNEDPLACES | FOS_NODEREFERENCELINKS | FOS_DONTADDTORECENT | FOS_FORCESHOWHIDDEN
2514             | FOS_DEFAULTNOMINIMODE | FOS_FORCEPREVIEWPANEON | FOS_SUPPORTSTREAMABLEITEMS))
2515     {
2516         WARN("Invalid option %#x\n", fos);
2517         return E_INVALIDARG;
2518     }
2519 
2520     if( !(This->options & FOS_PICKFOLDERS) && (fos & FOS_PICKFOLDERS) )
2521     {
2522         WCHAR buf[30];
2523         LoadStringW(COMDLG32_hInstance, IDS_SELECT_FOLDER, buf, ARRAY_SIZE(buf));
2524         IFileDialog2_SetTitle(iface, buf);
2525     }
2526 
2527     This->options = fos;
2528 
2529     return S_OK;
2530 }
2531 
2532 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
2533 {
2534     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2535     TRACE("%p (%p)\n", This, pfos);
2536 
2537     if(!pfos)
2538         return E_INVALIDARG;
2539 
2540     *pfos = This->options;
2541 
2542     return S_OK;
2543 }
2544 
2545 static HRESULT WINAPI IFileDialog2_fnSetDefaultFolder(IFileDialog2 *iface, IShellItem *psi)
2546 {
2547     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2548     TRACE("%p (%p)\n", This, psi);
2549     if(This->psi_defaultfolder)
2550         IShellItem_Release(This->psi_defaultfolder);
2551 
2552     This->psi_defaultfolder = psi;
2553 
2554     if(This->psi_defaultfolder)
2555         IShellItem_AddRef(This->psi_defaultfolder);
2556 
2557     return S_OK;
2558 }
2559 
2560 static HRESULT WINAPI IFileDialog2_fnSetFolder(IFileDialog2 *iface, IShellItem *psi)
2561 {
2562     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2563     TRACE("%p (%p)\n", This, psi);
2564     if(This->psi_setfolder)
2565         IShellItem_Release(This->psi_setfolder);
2566 
2567     This->psi_setfolder = psi;
2568 
2569     if(This->psi_setfolder)
2570         IShellItem_AddRef(This->psi_setfolder);
2571 
2572     return S_OK;
2573 }
2574 
2575 static HRESULT WINAPI IFileDialog2_fnGetFolder(IFileDialog2 *iface, IShellItem **ppsi)
2576 {
2577     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2578     TRACE("%p (%p)\n", This, ppsi);
2579     if(!ppsi)
2580         return E_INVALIDARG;
2581 
2582     /* FIXME:
2583        If the dialog is shown, return the current(ly selected) folder. */
2584 
2585     *ppsi = NULL;
2586     if(This->psi_folder)
2587         *ppsi = This->psi_folder;
2588     else if(This->psi_setfolder)
2589         *ppsi = This->psi_setfolder;
2590     else if(This->psi_defaultfolder)
2591         *ppsi = This->psi_defaultfolder;
2592 
2593     if(*ppsi)
2594     {
2595         IShellItem_AddRef(*ppsi);
2596         return S_OK;
2597     }
2598 
2599     return E_FAIL;
2600 }
2601 
2602 static HRESULT WINAPI IFileDialog2_fnGetCurrentSelection(IFileDialog2 *iface, IShellItem **ppsi)
2603 {
2604     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2605     HRESULT hr;
2606     TRACE("%p (%p)\n", This, ppsi);
2607 
2608     if(!ppsi)
2609         return E_INVALIDARG;
2610 
2611     if(This->psia_selection)
2612     {
2613         /* FIXME: Check filename edit box */
2614         hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
2615         return hr;
2616     }
2617 
2618     return E_FAIL;
2619 }
2620 
2621 static HRESULT WINAPI IFileDialog2_fnSetFileName(IFileDialog2 *iface, LPCWSTR pszName)
2622 {
2623     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2624     TRACE("%p (%s)\n", iface, debugstr_w(pszName));
2625 
2626     set_file_name(This, pszName);
2627 
2628     return S_OK;
2629 }
2630 
2631 static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *pszName)
2632 {
2633     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2634     TRACE("%p (%p)\n", iface, pszName);
2635 
2636     if(!pszName)
2637         return E_INVALIDARG;
2638 
2639     *pszName = NULL;
2640     get_file_name(This, pszName);
2641     return *pszName ? S_OK : E_FAIL;
2642 }
2643 
2644 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
2645 {
2646     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2647     TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
2648 
2649     LocalFree(This->custom_title);
2650     This->custom_title = StrDupW(pszTitle);
2651     update_control_text(This);
2652 
2653     return S_OK;
2654 }
2655 
2656 static HRESULT WINAPI IFileDialog2_fnSetOkButtonLabel(IFileDialog2 *iface, LPCWSTR pszText)
2657 {
2658     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2659     TRACE("%p (%s)\n", This, debugstr_w(pszText));
2660 
2661     LocalFree(This->custom_okbutton);
2662     This->custom_okbutton = StrDupW(pszText);
2663     update_control_text(This);
2664     update_layout(This);
2665 
2666     return S_OK;
2667 }
2668 
2669 static HRESULT WINAPI IFileDialog2_fnSetFileNameLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2670 {
2671     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2672     TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2673 
2674     LocalFree(This->custom_filenamelabel);
2675     This->custom_filenamelabel = StrDupW(pszLabel);
2676     update_control_text(This);
2677     update_layout(This);
2678 
2679     return S_OK;
2680 }
2681 
2682 static HRESULT WINAPI IFileDialog2_fnGetResult(IFileDialog2 *iface, IShellItem **ppsi)
2683 {
2684     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2685     HRESULT hr;
2686     TRACE("%p (%p)\n", This, ppsi);
2687 
2688     if(!ppsi)
2689         return E_INVALIDARG;
2690 
2691     if(This->psia_results)
2692     {
2693         UINT item_count;
2694         hr = IShellItemArray_GetCount(This->psia_results, &item_count);
2695         if(SUCCEEDED(hr))
2696         {
2697             if(item_count != 1)
2698                 return E_FAIL;
2699 
2700             /* Adds a reference. */
2701             hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
2702         }
2703 
2704         return hr;
2705     }
2706 
2707     return E_UNEXPECTED;
2708 }
2709 
2710 static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *psi, FDAP fdap)
2711 {
2712     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2713     FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
2714     return S_OK;
2715 }
2716 
2717 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
2718 {
2719     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2720     TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
2721 
2722     LocalFree(This->default_ext);
2723     This->default_ext = StrDupW(pszDefaultExtension);
2724 
2725     return S_OK;
2726 }
2727 
2728 static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
2729 {
2730     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2731     TRACE("%p (0x%08x)\n", This, hr);
2732 
2733     if(This->dlg_hwnd)
2734         EndDialog(This->dlg_hwnd, hr);
2735 
2736     return S_OK;
2737 }
2738 
2739 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
2740 {
2741     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2742     TRACE("%p (%s)\n", This, debugstr_guid(guid));
2743     This->client_guid = *guid;
2744     return S_OK;
2745 }
2746 
2747 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
2748 {
2749     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2750     FIXME("stub - %p\n", This);
2751     return E_NOTIMPL;
2752 }
2753 
2754 static HRESULT WINAPI IFileDialog2_fnSetFilter(IFileDialog2 *iface, IShellItemFilter *pFilter)
2755 {
2756     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2757     FIXME("stub - %p (%p)\n", This, pFilter);
2758     return E_NOTIMPL;
2759 }
2760 
2761 static HRESULT WINAPI IFileDialog2_fnSetCancelButtonLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2762 {
2763     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2764     TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2765 
2766     LocalFree(This->custom_cancelbutton);
2767     This->custom_cancelbutton = StrDupW(pszLabel);
2768     update_control_text(This);
2769     update_layout(This);
2770 
2771     return S_OK;
2772 }
2773 
2774 static HRESULT WINAPI IFileDialog2_fnSetNavigationRoot(IFileDialog2 *iface, IShellItem *psi)
2775 {
2776     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2777     FIXME("stub - %p (%p)\n", This, psi);
2778     return E_NOTIMPL;
2779 }
2780 
2781 static const IFileDialog2Vtbl vt_IFileDialog2 = {
2782     IFileDialog2_fnQueryInterface,
2783     IFileDialog2_fnAddRef,
2784     IFileDialog2_fnRelease,
2785     IFileDialog2_fnShow,
2786     IFileDialog2_fnSetFileTypes,
2787     IFileDialog2_fnSetFileTypeIndex,
2788     IFileDialog2_fnGetFileTypeIndex,
2789     IFileDialog2_fnAdvise,
2790     IFileDialog2_fnUnadvise,
2791     IFileDialog2_fnSetOptions,
2792     IFileDialog2_fnGetOptions,
2793     IFileDialog2_fnSetDefaultFolder,
2794     IFileDialog2_fnSetFolder,
2795     IFileDialog2_fnGetFolder,
2796     IFileDialog2_fnGetCurrentSelection,
2797     IFileDialog2_fnSetFileName,
2798     IFileDialog2_fnGetFileName,
2799     IFileDialog2_fnSetTitle,
2800     IFileDialog2_fnSetOkButtonLabel,
2801     IFileDialog2_fnSetFileNameLabel,
2802     IFileDialog2_fnGetResult,
2803     IFileDialog2_fnAddPlace,
2804     IFileDialog2_fnSetDefaultExtension,
2805     IFileDialog2_fnClose,
2806     IFileDialog2_fnSetClientGuid,
2807     IFileDialog2_fnClearClientData,
2808     IFileDialog2_fnSetFilter,
2809     IFileDialog2_fnSetCancelButtonLabel,
2810     IFileDialog2_fnSetNavigationRoot
2811 };
2812 
2813 /**************************************************************************
2814  * IFileOpenDialog
2815  */
2816 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
2817 {
2818     return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
2819 }
2820 
2821 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
2822                                                        REFIID riid, void **ppvObject)
2823 {
2824     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2825     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2826 }
2827 
2828 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
2829 {
2830     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2831     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2832 }
2833 
2834 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
2835 {
2836     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2837     return IFileDialog2_Release(&This->IFileDialog2_iface);
2838 }
2839 
2840 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
2841 {
2842     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2843     return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2844 }
2845 
2846 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
2847                                                      const COMDLG_FILTERSPEC *rgFilterSpec)
2848 {
2849     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2850     return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2851 }
2852 
2853 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
2854 {
2855     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2856     return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2857 }
2858 
2859 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
2860 {
2861     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2862     return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2863 }
2864 
2865 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
2866                                                DWORD *pdwCookie)
2867 {
2868     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2869     return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2870 }
2871 
2872 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
2873 {
2874     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2875     return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2876 }
2877 
2878 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
2879 {
2880     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2881     return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2882 }
2883 
2884 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2885 {
2886     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2887     return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2888 }
2889 
2890 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
2891 {
2892     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2893     return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2894 }
2895 
2896 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
2897 {
2898     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2899     return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2900 }
2901 
2902 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
2903 {
2904     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2905     return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2906 }
2907 
2908 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
2909 {
2910     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2911     return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2912 }
2913 
2914 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
2915 {
2916     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2917     return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2918 }
2919 
2920 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
2921 {
2922     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2923     return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2924 }
2925 
2926 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
2927 {
2928     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2929     return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2930 }
2931 
2932 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
2933 {
2934     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2935     return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2936 }
2937 
2938 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
2939 {
2940     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2941     return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2942 }
2943 
2944 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
2945 {
2946     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2947     return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2948 }
2949 
2950 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
2951 {
2952     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2953     return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2954 }
2955 
2956 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultExtension(IFileOpenDialog *iface,
2957                                                             LPCWSTR pszDefaultExtension)
2958 {
2959     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2960     return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2961 }
2962 
2963 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
2964 {
2965     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2966     return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2967 }
2968 
2969 static HRESULT WINAPI IFileOpenDialog_fnSetClientGuid(IFileOpenDialog *iface, REFGUID guid)
2970 {
2971     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2972     return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2973 }
2974 
2975 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
2976 {
2977     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2978     return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2979 }
2980 
2981 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
2982 {
2983     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2984     return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2985 }
2986 
2987 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
2988 {
2989     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2990     TRACE("%p (%p)\n", This, ppenum);
2991 
2992     *ppenum = This->psia_results;
2993 
2994     if(*ppenum)
2995     {
2996         IShellItemArray_AddRef(*ppenum);
2997         return S_OK;
2998     }
2999 
3000     return E_FAIL;
3001 }
3002 
3003 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
3004 {
3005     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
3006     TRACE("%p (%p)\n", This, ppsai);
3007 
3008     if(This->psia_selection)
3009     {
3010         *ppsai = This->psia_selection;
3011         IShellItemArray_AddRef(*ppsai);
3012         return S_OK;
3013     }
3014 
3015     return E_FAIL;
3016 }
3017 
3018 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
3019     IFileOpenDialog_fnQueryInterface,
3020     IFileOpenDialog_fnAddRef,
3021     IFileOpenDialog_fnRelease,
3022     IFileOpenDialog_fnShow,
3023     IFileOpenDialog_fnSetFileTypes,
3024     IFileOpenDialog_fnSetFileTypeIndex,
3025     IFileOpenDialog_fnGetFileTypeIndex,
3026     IFileOpenDialog_fnAdvise,
3027     IFileOpenDialog_fnUnadvise,
3028     IFileOpenDialog_fnSetOptions,
3029     IFileOpenDialog_fnGetOptions,
3030     IFileOpenDialog_fnSetDefaultFolder,
3031     IFileOpenDialog_fnSetFolder,
3032     IFileOpenDialog_fnGetFolder,
3033     IFileOpenDialog_fnGetCurrentSelection,
3034     IFileOpenDialog_fnSetFileName,
3035     IFileOpenDialog_fnGetFileName,
3036     IFileOpenDialog_fnSetTitle,
3037     IFileOpenDialog_fnSetOkButtonLabel,
3038     IFileOpenDialog_fnSetFileNameLabel,
3039     IFileOpenDialog_fnGetResult,
3040     IFileOpenDialog_fnAddPlace,
3041     IFileOpenDialog_fnSetDefaultExtension,
3042     IFileOpenDialog_fnClose,
3043     IFileOpenDialog_fnSetClientGuid,
3044     IFileOpenDialog_fnClearClientData,
3045     IFileOpenDialog_fnSetFilter,
3046     IFileOpenDialog_fnGetResults,
3047     IFileOpenDialog_fnGetSelectedItems
3048 };
3049 
3050 /**************************************************************************
3051  * IFileSaveDialog
3052  */
3053 static inline FileDialogImpl *impl_from_IFileSaveDialog(IFileSaveDialog *iface)
3054 {
3055     return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
3056 }
3057 
3058 static HRESULT WINAPI IFileSaveDialog_fnQueryInterface(IFileSaveDialog *iface,
3059                                                        REFIID riid,
3060                                                        void **ppvObject)
3061 {
3062     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3063     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3064 }
3065 
3066 static ULONG WINAPI IFileSaveDialog_fnAddRef(IFileSaveDialog *iface)
3067 {
3068     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3069     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3070 }
3071 
3072 static ULONG WINAPI IFileSaveDialog_fnRelease(IFileSaveDialog *iface)
3073 {
3074     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3075     return IFileDialog2_Release(&This->IFileDialog2_iface);
3076 }
3077 
3078 static HRESULT WINAPI IFileSaveDialog_fnShow(IFileSaveDialog *iface, HWND hwndOwner)
3079 {
3080     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3081     return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
3082 }
3083 
3084 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypes(IFileSaveDialog *iface, UINT cFileTypes,
3085                                                      const COMDLG_FILTERSPEC *rgFilterSpec)
3086 {
3087     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3088     return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
3089 }
3090 
3091 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypeIndex(IFileSaveDialog *iface, UINT iFileType)
3092 {
3093     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3094     return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
3095 }
3096 
3097 static HRESULT WINAPI IFileSaveDialog_fnGetFileTypeIndex(IFileSaveDialog *iface, UINT *piFileType)
3098 {
3099     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3100     return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
3101 }
3102 
3103 static HRESULT WINAPI IFileSaveDialog_fnAdvise(IFileSaveDialog *iface, IFileDialogEvents *pfde,
3104                                                DWORD *pdwCookie)
3105 {
3106     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3107     return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
3108 }
3109 
3110 static HRESULT WINAPI IFileSaveDialog_fnUnadvise(IFileSaveDialog *iface, DWORD dwCookie)
3111 {
3112     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3113     return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
3114 }
3115 
3116 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
3117 {
3118     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3119     return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
3120 }
3121 
3122 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
3123 {
3124     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3125     return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
3126 }
3127 
3128 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultFolder(IFileSaveDialog *iface, IShellItem *psi)
3129 {
3130     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3131     return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
3132 }
3133 
3134 static HRESULT WINAPI IFileSaveDialog_fnSetFolder(IFileSaveDialog *iface, IShellItem *psi)
3135 {
3136     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3137     return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
3138 }
3139 
3140 static HRESULT WINAPI IFileSaveDialog_fnGetFolder(IFileSaveDialog *iface, IShellItem **ppsi)
3141 {
3142     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3143     return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
3144 }
3145 
3146 static HRESULT WINAPI IFileSaveDialog_fnGetCurrentSelection(IFileSaveDialog *iface, IShellItem **ppsi)
3147 {
3148     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3149     return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
3150 }
3151 
3152 static HRESULT WINAPI IFileSaveDialog_fnSetFileName(IFileSaveDialog *iface, LPCWSTR pszName)
3153 {
3154     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3155     return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
3156 }
3157 
3158 static HRESULT WINAPI IFileSaveDialog_fnGetFileName(IFileSaveDialog *iface, LPWSTR *pszName)
3159 {
3160     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3161     return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
3162 }
3163 
3164 static HRESULT WINAPI IFileSaveDialog_fnSetTitle(IFileSaveDialog *iface, LPCWSTR pszTitle)
3165 {
3166     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3167     return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
3168 }
3169 
3170 static HRESULT WINAPI IFileSaveDialog_fnSetOkButtonLabel(IFileSaveDialog *iface, LPCWSTR pszText)
3171 {
3172     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3173     return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
3174 }
3175 
3176 static HRESULT WINAPI IFileSaveDialog_fnSetFileNameLabel(IFileSaveDialog *iface, LPCWSTR pszLabel)
3177 {
3178     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3179     return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
3180 }
3181 
3182 static HRESULT WINAPI IFileSaveDialog_fnGetResult(IFileSaveDialog *iface, IShellItem **ppsi)
3183 {
3184     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3185     return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
3186 }
3187 
3188 static HRESULT WINAPI IFileSaveDialog_fnAddPlace(IFileSaveDialog *iface, IShellItem *psi, FDAP fdap)
3189 {
3190     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3191     return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
3192 }
3193 
3194 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultExtension(IFileSaveDialog *iface,
3195                                                             LPCWSTR pszDefaultExtension)
3196 {
3197     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3198     return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
3199 }
3200 
3201 static HRESULT WINAPI IFileSaveDialog_fnClose(IFileSaveDialog *iface, HRESULT hr)
3202 {
3203     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3204     return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
3205 }
3206 
3207 static HRESULT WINAPI IFileSaveDialog_fnSetClientGuid(IFileSaveDialog *iface, REFGUID guid)
3208 {
3209     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3210     return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
3211 }
3212 
3213 static HRESULT WINAPI IFileSaveDialog_fnClearClientData(IFileSaveDialog *iface)
3214 {
3215     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3216     return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
3217 }
3218 
3219 static HRESULT WINAPI IFileSaveDialog_fnSetFilter(IFileSaveDialog *iface, IShellItemFilter *pFilter)
3220 {
3221     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3222     return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
3223 }
3224 
3225 static HRESULT WINAPI IFileSaveDialog_fnSetSaveAsItem(IFileSaveDialog* iface, IShellItem *psi)
3226 {
3227     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3228     FIXME("stub - %p (%p)\n", This, psi);
3229     return E_NOTIMPL;
3230 }
3231 
3232 static HRESULT WINAPI IFileSaveDialog_fnSetProperties(IFileSaveDialog* iface, IPropertyStore *pStore)
3233 {
3234     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3235     FIXME("stub - %p (%p)\n", This, pStore);
3236     return E_NOTIMPL;
3237 }
3238 
3239 static HRESULT WINAPI IFileSaveDialog_fnSetCollectedProperties(IFileSaveDialog* iface,
3240                                                                IPropertyDescriptionList *pList,
3241                                                                BOOL fAppendDefault)
3242 {
3243     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3244     FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
3245     return E_NOTIMPL;
3246 }
3247 
3248 static HRESULT WINAPI IFileSaveDialog_fnGetProperties(IFileSaveDialog* iface, IPropertyStore **ppStore)
3249 {
3250     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3251     FIXME("stub - %p (%p)\n", This, ppStore);
3252     return E_NOTIMPL;
3253 }
3254 
3255 static HRESULT WINAPI IFileSaveDialog_fnApplyProperties(IFileSaveDialog* iface,
3256                                                         IShellItem *psi,
3257                                                         IPropertyStore *pStore,
3258                                                         HWND hwnd,
3259                                                         IFileOperationProgressSink *pSink)
3260 {
3261     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3262     FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
3263     return E_NOTIMPL;
3264 }
3265 
3266 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
3267     IFileSaveDialog_fnQueryInterface,
3268     IFileSaveDialog_fnAddRef,
3269     IFileSaveDialog_fnRelease,
3270     IFileSaveDialog_fnShow,
3271     IFileSaveDialog_fnSetFileTypes,
3272     IFileSaveDialog_fnSetFileTypeIndex,
3273     IFileSaveDialog_fnGetFileTypeIndex,
3274     IFileSaveDialog_fnAdvise,
3275     IFileSaveDialog_fnUnadvise,
3276     IFileSaveDialog_fnSetOptions,
3277     IFileSaveDialog_fnGetOptions,
3278     IFileSaveDialog_fnSetDefaultFolder,
3279     IFileSaveDialog_fnSetFolder,
3280     IFileSaveDialog_fnGetFolder,
3281     IFileSaveDialog_fnGetCurrentSelection,
3282     IFileSaveDialog_fnSetFileName,
3283     IFileSaveDialog_fnGetFileName,
3284     IFileSaveDialog_fnSetTitle,
3285     IFileSaveDialog_fnSetOkButtonLabel,
3286     IFileSaveDialog_fnSetFileNameLabel,
3287     IFileSaveDialog_fnGetResult,
3288     IFileSaveDialog_fnAddPlace,
3289     IFileSaveDialog_fnSetDefaultExtension,
3290     IFileSaveDialog_fnClose,
3291     IFileSaveDialog_fnSetClientGuid,
3292     IFileSaveDialog_fnClearClientData,
3293     IFileSaveDialog_fnSetFilter,
3294     IFileSaveDialog_fnSetSaveAsItem,
3295     IFileSaveDialog_fnSetProperties,
3296     IFileSaveDialog_fnSetCollectedProperties,
3297     IFileSaveDialog_fnGetProperties,
3298     IFileSaveDialog_fnApplyProperties
3299 };
3300 
3301 /**************************************************************************
3302  * IExplorerBrowserEvents implementation
3303  */
3304 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
3305 {
3306     return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
3307 }
3308 
3309 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
3310                                                               REFIID riid, void **ppvObject)
3311 {
3312     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3313     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
3314 
3315     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3316 }
3317 
3318 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
3319 {
3320     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3321     TRACE("%p\n", This);
3322     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3323 }
3324 
3325 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
3326 {
3327     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3328     TRACE("%p\n", This);
3329     return IFileDialog2_Release(&This->IFileDialog2_iface);
3330 }
3331 
3332 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
3333                                                                    PCIDLIST_ABSOLUTE pidlFolder)
3334 {
3335     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3336     IShellItem *psi;
3337     HRESULT hr;
3338     TRACE("%p (%p)\n", This, pidlFolder);
3339 
3340     hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
3341     if(SUCCEEDED(hr))
3342     {
3343         hr = events_OnFolderChanging(This, psi);
3344         IShellItem_Release(psi);
3345 
3346         /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
3347         if(hr == S_FALSE)
3348             hr = E_FAIL;
3349 
3350         return hr;
3351     }
3352     else
3353         ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
3354 
3355     return S_OK;
3356 }
3357 
3358 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
3359                                                              IShellView *psv)
3360 {
3361     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3362     TRACE("%p (%p)\n", This, psv);
3363     return S_OK;
3364 }
3365 
3366 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
3367                                                                     PCIDLIST_ABSOLUTE pidlFolder)
3368 {
3369     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3370     HRESULT hr;
3371     TRACE("%p (%p)\n", This, pidlFolder);
3372 
3373     if(This->psi_folder)
3374         IShellItem_Release(This->psi_folder);
3375 
3376     hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
3377     if(FAILED(hr))
3378     {
3379         ERR("Failed to get the current folder.\n");
3380         This->psi_folder = NULL;
3381     }
3382 
3383     events_OnFolderChange(This);
3384 
3385     return S_OK;
3386 }
3387 
3388 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
3389                                                                   PCIDLIST_ABSOLUTE pidlFolder)
3390 {
3391     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3392     TRACE("%p (%p)\n", This, pidlFolder);
3393     return S_OK;
3394 }
3395 
3396 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
3397     IExplorerBrowserEvents_fnQueryInterface,
3398     IExplorerBrowserEvents_fnAddRef,
3399     IExplorerBrowserEvents_fnRelease,
3400     IExplorerBrowserEvents_fnOnNavigationPending,
3401     IExplorerBrowserEvents_fnOnViewCreated,
3402     IExplorerBrowserEvents_fnOnNavigationComplete,
3403     IExplorerBrowserEvents_fnOnNavigationFailed
3404 };
3405 
3406 /**************************************************************************
3407  * IServiceProvider implementation
3408  */
3409 static inline FileDialogImpl *impl_from_IServiceProvider(IServiceProvider *iface)
3410 {
3411     return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
3412 }
3413 
3414 static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface,
3415                                                         REFIID riid, void **ppvObject)
3416 {
3417     FileDialogImpl *This = impl_from_IServiceProvider(iface);
3418     TRACE("%p\n", This);
3419     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3420 }
3421 
3422 static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface)
3423 {
3424     FileDialogImpl *This = impl_from_IServiceProvider(iface);
3425     TRACE("%p\n", This);
3426     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3427 }
3428 
3429 static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface)
3430 {
3431     FileDialogImpl *This = impl_from_IServiceProvider(iface);
3432     TRACE("%p\n", This);
3433     return IFileDialog2_Release(&This->IFileDialog2_iface);
3434 }
3435 
3436 static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
3437                                                       REFGUID guidService,
3438                                                       REFIID riid, void **ppv)
3439 {
3440     FileDialogImpl *This = impl_from_IServiceProvider(iface);
3441     HRESULT hr = E_NOTIMPL;
3442     TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
3443 
3444     *ppv = NULL;
3445     if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
3446         hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
3447     else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
3448         hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
3449     else
3450         FIXME("Interface %s requested from unknown service %s\n",
3451               debugstr_guid(riid), debugstr_guid(guidService));
3452 
3453     return hr;
3454 }
3455 
3456 static const IServiceProviderVtbl vt_IServiceProvider = {
3457     IServiceProvider_fnQueryInterface,
3458     IServiceProvider_fnAddRef,
3459     IServiceProvider_fnRelease,
3460     IServiceProvider_fnQueryService
3461 };
3462 
3463 /**************************************************************************
3464  * ICommDlgBrowser3 implementation
3465  */
3466 static inline FileDialogImpl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface)
3467 {
3468     return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
3469 }
3470 
3471 static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface,
3472                                                         REFIID riid, void **ppvObject)
3473 {
3474     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3475     TRACE("%p\n", This);
3476     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3477 }
3478 
3479 static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface)
3480 {
3481     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3482     TRACE("%p\n", This);
3483     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3484 }
3485 
3486 static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface)
3487 {
3488     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3489     TRACE("%p\n", This);
3490     return IFileDialog2_Release(&This->IFileDialog2_iface);
3491 }
3492 
3493 static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3 *iface,
3494                                                           IShellView *shv)
3495 {
3496     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3497     HRESULT hr;
3498     TRACE("%p (%p)\n", This, shv);
3499 
3500     hr = on_default_action(This);
3501 
3502     if(SUCCEEDED(hr))
3503         EndDialog(This->dlg_hwnd, S_OK);
3504 
3505     return S_OK;
3506 }
3507 
3508 static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange(ICommDlgBrowser3 *iface,
3509                                                        IShellView *shv, ULONG uChange )
3510 {
3511     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3512     IDataObject *new_selection;
3513     HRESULT hr;
3514     TRACE("%p (%p, %x)\n", This, shv, uChange);
3515 
3516     switch(uChange)
3517     {
3518     case CDBOSC_SELCHANGE:
3519         if(This->psia_selection)
3520         {
3521             IShellItemArray_Release(This->psia_selection);
3522             This->psia_selection = NULL;
3523         }
3524 
3525         hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
3526         if(SUCCEEDED(hr))
3527         {
3528             hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
3529                                                       (void**)&This->psia_selection);
3530             if(SUCCEEDED(hr))
3531             {
3532                 fill_filename_from_selection(This);
3533                 events_OnSelectionChange(This);
3534             }
3535 
3536             IDataObject_Release(new_selection);
3537         }
3538         break;
3539     default:
3540         TRACE("Unhandled state change\n");
3541     }
3542     return S_OK;
3543 }
3544 
3545 static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
3546                                                        IShellView *shv, LPCITEMIDLIST pidl)
3547 {
3548     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3549     IShellItem *psi;
3550     LPWSTR filename;
3551     LPITEMIDLIST parent_pidl;
3552     HRESULT hr;
3553     ULONG attr;
3554     TRACE("%p (%p, %p)\n", This, shv, pidl);
3555 
3556     if(!This->filterspec_count && !(This->options & FOS_PICKFOLDERS))
3557         return S_OK;
3558 
3559     hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
3560     if(SUCCEEDED(hr))
3561     {
3562         LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
3563         hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
3564         ILFree(parent_pidl);
3565         ILFree(full_pidl);
3566     }
3567     if(FAILED(hr))
3568     {
3569         ERR("Failed to get shellitem (%08x).\n", hr);
3570         return S_OK;
3571     }
3572 
3573     hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
3574     if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
3575     {
3576         IShellItem_Release(psi);
3577         return S_OK;
3578     }
3579 
3580     if((This->options & FOS_PICKFOLDERS) && !(attr & (SFGAO_FOLDER | SFGAO_LINK)))
3581     {
3582         IShellItem_Release(psi);
3583         return S_FALSE;
3584     }
3585 
3586     hr = S_OK;
3587     if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
3588     {
3589         if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
3590             hr = S_FALSE;
3591         CoTaskMemFree(filename);
3592     }
3593 
3594     IShellItem_Release(psi);
3595     return hr;
3596 }
3597 
3598 static HRESULT WINAPI ICommDlgBrowser3_fnNotify(ICommDlgBrowser3 *iface,
3599                                                 IShellView *ppshv, DWORD dwNotifyType)
3600 {
3601     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3602     FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
3603     return E_NOTIMPL;
3604 }
3605 
3606 static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText(ICommDlgBrowser3 *iface,
3607                                                             IShellView *pshv,
3608                                                             LPWSTR pszText, int cchMax)
3609 {
3610     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3611     FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
3612     return E_NOTIMPL;
3613 }
3614 
3615 static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags(ICommDlgBrowser3 *iface, DWORD *pdwFlags)
3616 {
3617     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3618     FIXME("Stub: %p (%p)\n", This, pdwFlags);
3619     return E_NOTIMPL;
3620 }
3621 
3622 static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked(ICommDlgBrowser3 *iface,
3623                                                          IShellView *pshv, int iColumn)
3624 {
3625     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3626     FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
3627     return E_NOTIMPL;
3628 }
3629 
3630 static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter(ICommDlgBrowser3 *iface,
3631                                                           LPWSTR pszFileSpec, int cchFileSpec)
3632 {
3633     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3634     FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
3635     return E_NOTIMPL;
3636 }
3637 
3638 static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated(ICommDlgBrowser3 *iface,
3639                                                           IShellView *pshv)
3640 {
3641     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3642     FIXME("Stub: %p (%p)\n", This, pshv);
3643     return E_NOTIMPL;
3644 }
3645 
3646 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
3647     ICommDlgBrowser3_fnQueryInterface,
3648     ICommDlgBrowser3_fnAddRef,
3649     ICommDlgBrowser3_fnRelease,
3650     ICommDlgBrowser3_fnOnDefaultCommand,
3651     ICommDlgBrowser3_fnOnStateChange,
3652     ICommDlgBrowser3_fnIncludeObject,
3653     ICommDlgBrowser3_fnNotify,
3654     ICommDlgBrowser3_fnGetDefaultMenuText,
3655     ICommDlgBrowser3_fnGetViewFlags,
3656     ICommDlgBrowser3_fnOnColumnClicked,
3657     ICommDlgBrowser3_fnGetCurrentFilter,
3658     ICommDlgBrowser3_fnOnPreviewCreated
3659 };
3660 
3661 /**************************************************************************
3662  * IOleWindow implementation
3663  */
3664 static inline FileDialogImpl *impl_from_IOleWindow(IOleWindow *iface)
3665 {
3666     return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
3667 }
3668 
3669 static HRESULT WINAPI IOleWindow_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
3670 {
3671     FileDialogImpl *This = impl_from_IOleWindow(iface);
3672     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3673 }
3674 
3675 static ULONG WINAPI IOleWindow_fnAddRef(IOleWindow *iface)
3676 {
3677     FileDialogImpl *This = impl_from_IOleWindow(iface);
3678     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3679 }
3680 
3681 static ULONG WINAPI IOleWindow_fnRelease(IOleWindow *iface)
3682 {
3683     FileDialogImpl *This = impl_from_IOleWindow(iface);
3684     return IFileDialog2_Release(&This->IFileDialog2_iface);
3685 }
3686 
3687 static HRESULT WINAPI IOleWindow_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMOde)
3688 {
3689     FileDialogImpl *This = impl_from_IOleWindow(iface);
3690     FIXME("Stub: %p (%d)\n", This, fEnterMOde);
3691     return E_NOTIMPL;
3692 }
3693 
3694 static HRESULT WINAPI IOleWindow_fnGetWindow(IOleWindow *iface, HWND *phwnd)
3695 {
3696     FileDialogImpl *This = impl_from_IOleWindow(iface);
3697     TRACE("%p (%p)\n", This, phwnd);
3698     *phwnd = This->dlg_hwnd;
3699     return S_OK;
3700 }
3701 
3702 static const IOleWindowVtbl vt_IOleWindow = {
3703     IOleWindow_fnQueryInterface,
3704     IOleWindow_fnAddRef,
3705     IOleWindow_fnRelease,
3706     IOleWindow_fnGetWindow,
3707     IOleWindow_fnContextSensitiveHelp
3708 };
3709 
3710 /**************************************************************************
3711  * IFileDialogCustomize implementation
3712  */
3713 static inline FileDialogImpl *impl_from_IFileDialogCustomize(IFileDialogCustomize *iface)
3714 {
3715     return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialogCustomize_iface);
3716 }
3717 
3718 static HRESULT WINAPI IFileDialogCustomize_fnQueryInterface(IFileDialogCustomize *iface,
3719                                                             REFIID riid, void **ppvObject)
3720 {
3721     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3722     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3723 }
3724 
3725 static ULONG WINAPI IFileDialogCustomize_fnAddRef(IFileDialogCustomize *iface)
3726 {
3727     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3728     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3729 }
3730 
3731 static ULONG WINAPI IFileDialogCustomize_fnRelease(IFileDialogCustomize *iface)
3732 {
3733     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3734     return IFileDialog2_Release(&This->IFileDialog2_iface);
3735 }
3736 
3737 static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCustomize *iface,
3738                                                                 DWORD dwIDCtl)
3739 {
3740     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3741     MENUINFO mi;
3742     TRACE("%p (%d)\n", This, dwIDCtl);
3743 
3744     if (This->hmenu_opendropdown || get_cctrl(This, dwIDCtl))
3745         return E_UNEXPECTED;
3746 
3747     This->hmenu_opendropdown = CreatePopupMenu();
3748 
3749     if (!This->hmenu_opendropdown)
3750         return E_OUTOFMEMORY;
3751 
3752     mi.cbSize = sizeof(mi);
3753     mi.fMask = MIM_STYLE;
3754     mi.dwStyle = MNS_NOTIFYBYPOS;
3755     SetMenuInfo(This->hmenu_opendropdown, &mi);
3756 
3757     This->cctrl_opendropdown.hwnd = NULL;
3758     This->cctrl_opendropdown.wrapper_hwnd = NULL;
3759     This->cctrl_opendropdown.id = dwIDCtl;
3760     This->cctrl_opendropdown.dlgid = 0;
3761     This->cctrl_opendropdown.type = IDLG_CCTRL_OPENDROPDOWN;
3762     This->cctrl_opendropdown.cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
3763     list_init(&This->cctrl_opendropdown.sub_cctrls);
3764     list_init(&This->cctrl_opendropdown.sub_items);
3765 
3766     return S_OK;
3767 }
3768 
3769 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
3770                                                      DWORD dwIDCtl,
3771                                                      LPCWSTR pszLabel)
3772 {
3773     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3774     customctrl *ctrl;
3775     TBBUTTON tbb;
3776     HRESULT hr;
3777     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3778 
3779     hr = cctrl_create_new(This, dwIDCtl, NULL, TOOLBARCLASSNAMEW,
3780                           TBSTYLE_FLAT | CCS_NODIVIDER, 0,
3781                           This->cctrl_def_height, &ctrl);
3782     if(SUCCEEDED(hr))
3783     {
3784         SendMessageW(ctrl->hwnd, TB_BUTTONSTRUCTSIZE, sizeof(tbb), 0);
3785         ctrl->type = IDLG_CCTRL_MENU;
3786 
3787         /* Add the actual button with a popup menu. */
3788         tbb.iBitmap = I_IMAGENONE;
3789         tbb.dwData = (DWORD_PTR)CreatePopupMenu();
3790         tbb.iString = (DWORD_PTR)pszLabel;
3791         tbb.fsState = TBSTATE_ENABLED;
3792         tbb.fsStyle = BTNS_WHOLEDROPDOWN;
3793         tbb.idCommand = 1;
3794 
3795         SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
3796     }
3797 
3798     return hr;
3799 }
3800 
3801 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
3802                                                            DWORD dwIDCtl,
3803                                                            LPCWSTR pszLabel)
3804 {
3805     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3806     customctrl *ctrl;
3807     HRESULT hr;
3808     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3809 
3810     hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
3811                           This->cctrl_def_height, &ctrl);
3812     if(SUCCEEDED(hr))
3813         ctrl->type = IDLG_CCTRL_PUSHBUTTON;
3814 
3815     return hr;
3816 }
3817 
3818 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
3819                                                          DWORD dwIDCtl)
3820 {
3821     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3822     customctrl *ctrl;
3823     HRESULT hr;
3824     TRACE("%p (%d)\n", This, dwIDCtl);
3825 
3826     hr =  cctrl_create_new(This, dwIDCtl, NULL, WC_COMBOBOXW, CBS_DROPDOWNLIST, 0,
3827                            This->cctrl_def_height, &ctrl);
3828     if(SUCCEEDED(hr))
3829         ctrl->type = IDLG_CCTRL_COMBOBOX;
3830 
3831     return hr;
3832 }
3833 
3834 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
3835                                                                 DWORD dwIDCtl)
3836 {
3837     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3838     customctrl *ctrl;
3839     HRESULT hr;
3840     TRACE("%p (%d)\n", This, dwIDCtl);
3841 
3842     hr =  cctrl_create_new(This, dwIDCtl, NULL, radiobuttonlistW, 0, 0, 0, &ctrl);
3843     if(SUCCEEDED(hr))
3844     {
3845         ctrl->type = IDLG_CCTRL_RADIOBUTTONLIST;
3846         SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)This);
3847     }
3848 
3849     return hr;
3850 }
3851 
3852 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
3853                                                             DWORD dwIDCtl,
3854                                                             LPCWSTR pszLabel,
3855                                                             BOOL bChecked)
3856 {
3857     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3858     customctrl *ctrl;
3859     HRESULT hr;
3860     TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
3861 
3862     hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX|BS_MULTILINE, 0,
3863                           This->cctrl_def_height, &ctrl);
3864     if(SUCCEEDED(hr))
3865     {
3866         ctrl->type = IDLG_CCTRL_CHECKBUTTON;
3867         SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
3868     }
3869 
3870     return hr;
3871 }
3872 
3873 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
3874                                                         DWORD dwIDCtl,
3875                                                         LPCWSTR pszText)
3876 {
3877     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3878     customctrl *ctrl;
3879     HRESULT hr;
3880     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3881 
3882     hr = cctrl_create_new(This, dwIDCtl, pszText, WC_EDITW, ES_AUTOHSCROLL, WS_EX_CLIENTEDGE,
3883                           This->cctrl_def_height, &ctrl);
3884     if(SUCCEEDED(hr))
3885         ctrl->type = IDLG_CCTRL_EDITBOX;
3886 
3887     return hr;
3888 }
3889 
3890 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
3891                                                           DWORD dwIDCtl)
3892 {
3893     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3894     customctrl *ctrl;
3895     HRESULT hr;
3896     TRACE("%p (%d)\n", This, dwIDCtl);
3897 
3898     hr = cctrl_create_new(This, dwIDCtl, NULL, WC_STATICW, SS_ETCHEDHORZ, 0,
3899                           GetSystemMetrics(SM_CYEDGE), &ctrl);
3900     if(SUCCEEDED(hr))
3901         ctrl->type = IDLG_CCTRL_SEPARATOR;
3902 
3903     return hr;
3904 }
3905 
3906 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
3907                                                      DWORD dwIDCtl,
3908                                                      LPCWSTR pszText)
3909 {
3910     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3911     customctrl *ctrl;
3912     HRESULT hr;
3913     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3914 
3915     hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
3916                           This->cctrl_def_height, &ctrl);
3917     if(SUCCEEDED(hr))
3918         ctrl->type = IDLG_CCTRL_TEXT;
3919 
3920     return hr;
3921 }
3922 
3923 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
3924                                                              DWORD dwIDCtl,
3925                                                              LPCWSTR pszLabel)
3926 {
3927     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3928     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3929     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3930 
3931     if(!ctrl) return E_INVALIDARG;
3932 
3933     switch(ctrl->type)
3934     {
3935     case IDLG_CCTRL_MENU:
3936     case IDLG_CCTRL_PUSHBUTTON:
3937     case IDLG_CCTRL_CHECKBUTTON:
3938     case IDLG_CCTRL_TEXT:
3939     case IDLG_CCTRL_VISUALGROUP:
3940         SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
3941         break;
3942     case IDLG_CCTRL_OPENDROPDOWN:
3943         return E_NOTIMPL;
3944     default:
3945         break;
3946     }
3947 
3948     return S_OK;
3949 }
3950 
3951 static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomize *iface,
3952                                                              DWORD dwIDCtl,
3953                                                              CDCONTROLSTATEF *pdwState)
3954 {
3955     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3956     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3957     TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
3958 
3959     if(!ctrl || ctrl->type == IDLG_CCTRL_OPENDROPDOWN) return E_NOTIMPL;
3960 
3961     *pdwState = ctrl->cdcstate;
3962     return S_OK;
3963 }
3964 
3965 static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomize *iface,
3966                                                              DWORD dwIDCtl,
3967                                                              CDCONTROLSTATEF dwState)
3968 {
3969     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3970     customctrl *ctrl = get_cctrl(This,dwIDCtl);
3971     TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
3972 
3973     if(ctrl && ctrl->hwnd)
3974     {
3975         LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
3976 
3977         if(dwState & CDCS_ENABLED)
3978             wndstyle &= ~(WS_DISABLED);
3979         else
3980             wndstyle |= WS_DISABLED;
3981 
3982         if(dwState & CDCS_VISIBLE)
3983             wndstyle |= WS_VISIBLE;
3984         else
3985             wndstyle &= ~(WS_VISIBLE);
3986 
3987         SetWindowLongW(ctrl->hwnd, GWL_STYLE, wndstyle);
3988 
3989         /* We save the state separately since at least one application
3990          * relies on being able to hide a control. */
3991         ctrl->cdcstate = dwState;
3992     }
3993 
3994     return S_OK;
3995 }
3996 
3997 static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize *iface,
3998                                                             DWORD dwIDCtl,
3999                                                             WCHAR **ppszText)
4000 {
4001     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4002     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4003     WCHAR len, *text;
4004     TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
4005 
4006     if(!ctrl || !ctrl->hwnd || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
4007         return E_FAIL;
4008 
4009     text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
4010     if(!text) return E_FAIL;
4011 
4012     SendMessageW(ctrl->hwnd, WM_GETTEXT, len+1, (LPARAM)text);
4013     *ppszText = text;
4014     return S_OK;
4015 }
4016 
4017 static HRESULT WINAPI IFileDialogCustomize_fnSetEditBoxText(IFileDialogCustomize *iface,
4018                                                             DWORD dwIDCtl,
4019                                                             LPCWSTR pszText)
4020 {
4021     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4022     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4023     TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
4024 
4025     if(!ctrl || ctrl->type != IDLG_CCTRL_EDITBOX)
4026         return E_FAIL;
4027 
4028     SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
4029     return S_OK;
4030 }
4031 
4032 static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCustomize *iface,
4033                                                                  DWORD dwIDCtl,
4034                                                                  BOOL *pbChecked)
4035 {
4036     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4037     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4038     TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
4039 
4040     if(ctrl && ctrl->hwnd)
4041         *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
4042 
4043     return S_OK;
4044 }
4045 
4046 static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCustomize *iface,
4047                                                                  DWORD dwIDCtl,
4048                                                                  BOOL bChecked)
4049 {
4050     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4051     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4052     TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
4053 
4054     if(ctrl && ctrl->hwnd)
4055         SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
4056 
4057     return S_OK;
4058 }
4059 
4060 static UINT get_combobox_index_from_id(HWND cb_hwnd, DWORD dwIDItem)
4061 {
4062     UINT count = SendMessageW(cb_hwnd, CB_GETCOUNT, 0, 0);
4063     UINT i;
4064     if(!count || (count == CB_ERR))
4065         return -1;
4066 
4067     for(i = 0; i < count; i++)
4068         if(SendMessageW(cb_hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
4069             return i;
4070 
4071     TRACE("Item with id %d not found in combobox %p (item count: %d)\n", dwIDItem, cb_hwnd, count);
4072     return -1;
4073 }
4074 
4075 static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize *iface,
4076                                                             DWORD dwIDCtl,
4077                                                             DWORD dwIDItem,
4078                                                             LPCWSTR pszLabel)
4079 {
4080     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4081     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4082     HRESULT hr;
4083     TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4084 
4085     if(!ctrl) return E_FAIL;
4086 
4087     switch(ctrl->type)
4088     {
4089     case IDLG_CCTRL_COMBOBOX:
4090     {
4091         UINT index;
4092         cctrl_item* item;
4093 
4094         hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4095 
4096         if (FAILED(hr)) return hr;
4097 
4098         index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
4099         SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
4100 
4101         return S_OK;
4102     }
4103     case IDLG_CCTRL_MENU:
4104     case IDLG_CCTRL_OPENDROPDOWN:
4105     {
4106         cctrl_item* item;
4107         HMENU hmenu;
4108 
4109         hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4110 
4111         if (FAILED(hr)) return hr;
4112 
4113         if (ctrl->type == IDLG_CCTRL_MENU)
4114         {
4115             TBBUTTON tbb;
4116             SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4117             hmenu = (HMENU)tbb.dwData;
4118         }
4119         else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4120             hmenu = This->hmenu_opendropdown;
4121 
4122         AppendMenuW(hmenu, MF_STRING, dwIDItem, pszLabel);
4123         return S_OK;
4124     }
4125     case IDLG_CCTRL_RADIOBUTTONLIST:
4126     {
4127         cctrl_item* item;
4128 
4129         hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4130 
4131         if (SUCCEEDED(hr))
4132         {
4133             item->hwnd = CreateWindowExW(0, WC_BUTTONW, pszLabel,
4134                 WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|BS_RADIOBUTTON|BS_MULTILINE,
4135                 0, 0, 0, 0, ctrl->hwnd, ULongToHandle(dwIDItem), COMDLG32_hInstance, 0);
4136 
4137             if (!item->hwnd)
4138             {
4139                 ERR("Failed to create radio button\n");
4140                 list_remove(&item->entry);
4141                 item_free(item);
4142                 return E_FAIL;
4143             }
4144         }
4145 
4146         return hr;
4147     }
4148     default:
4149         break;
4150     }
4151 
4152     return E_NOINTERFACE; /* win7 */
4153 }
4154 
4155 static HRESULT WINAPI IFileDialogCustomize_fnRemoveControlItem(IFileDialogCustomize *iface,
4156                                                                DWORD dwIDCtl,
4157                                                                DWORD dwIDItem)
4158 {
4159     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4160     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4161     TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4162 
4163     if(!ctrl) return E_FAIL;
4164 
4165     switch(ctrl->type)
4166     {
4167     case IDLG_CCTRL_COMBOBOX:
4168     {
4169         cctrl_item* item;
4170         DWORD position;
4171 
4172         item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4173 
4174         if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
4175         {
4176             if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0) == CB_ERR)
4177                 return E_FAIL;
4178         }
4179 
4180         list_remove(&item->entry);
4181         item_free(item);
4182 
4183         return S_OK;
4184     }
4185     case IDLG_CCTRL_MENU:
4186     case IDLG_CCTRL_OPENDROPDOWN:
4187     {
4188         HMENU hmenu;
4189         cctrl_item* item;
4190 
4191         item = get_item(ctrl, dwIDItem, 0, NULL);
4192 
4193         if (!item)
4194             return E_UNEXPECTED;
4195 
4196         if (item->cdcstate & CDCS_VISIBLE)
4197         {
4198             if (ctrl->type == IDLG_CCTRL_MENU)
4199             {
4200                 TBBUTTON tbb;
4201                 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4202                 hmenu = (HMENU)tbb.dwData;
4203             }
4204             else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4205                 hmenu = This->hmenu_opendropdown;
4206 
4207             if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
4208                 return E_UNEXPECTED;
4209         }
4210 
4211         list_remove(&item->entry);
4212         item_free(item);
4213 
4214         return S_OK;
4215     }
4216     case IDLG_CCTRL_RADIOBUTTONLIST:
4217     {
4218         cctrl_item* item;
4219 
4220         item = get_item(ctrl, dwIDItem, 0, NULL);
4221 
4222         if (!item)
4223             return E_UNEXPECTED;
4224 
4225         list_remove(&item->entry);
4226         item_free(item);
4227 
4228         return S_OK;
4229     }
4230     default:
4231         break;
4232     }
4233 
4234     return E_FAIL;
4235 }
4236 
4237 static HRESULT WINAPI IFileDialogCustomize_fnRemoveAllControlItems(IFileDialogCustomize *iface,
4238                                                                    DWORD dwIDCtl)
4239 {
4240     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4241     TRACE("%p (%d)\n", This, dwIDCtl);
4242 
4243     /* Not implemented by native */
4244     return E_NOTIMPL;
4245 }
4246 
4247 static HRESULT WINAPI IFileDialogCustomize_fnGetControlItemState(IFileDialogCustomize *iface,
4248                                                                  DWORD dwIDCtl,
4249                                                                  DWORD dwIDItem,
4250                                                                  CDCONTROLSTATEF *pdwState)
4251 {
4252     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4253     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4254     TRACE("%p (%d, %d, %p)\n", This, dwIDCtl, dwIDItem, pdwState);
4255 
4256     if(!ctrl) return E_FAIL;
4257 
4258     switch(ctrl->type)
4259     {
4260     case IDLG_CCTRL_COMBOBOX:
4261     case IDLG_CCTRL_MENU:
4262     case IDLG_CCTRL_OPENDROPDOWN:
4263     case IDLG_CCTRL_RADIOBUTTONLIST:
4264     {
4265         cctrl_item* item;
4266 
4267         item = get_item(ctrl, dwIDItem, 0, NULL);
4268 
4269         if (!item)
4270             return E_UNEXPECTED;
4271 
4272         *pdwState = item->cdcstate;
4273 
4274         return S_OK;
4275     }
4276     default:
4277         break;
4278     }
4279 
4280     return E_FAIL;
4281 }
4282 
4283 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCustomize *iface,
4284                                                                  DWORD dwIDCtl,
4285                                                                  DWORD dwIDItem,
4286                                                                  CDCONTROLSTATEF dwState)
4287 {
4288     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4289     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4290     TRACE("%p (%d, %d, %x)\n", This, dwIDCtl, dwIDItem, dwState);
4291 
4292     if(!ctrl) return E_FAIL;
4293 
4294     switch(ctrl->type)
4295     {
4296     case IDLG_CCTRL_COMBOBOX:
4297     {
4298         cctrl_item* item;
4299         BOOL visible, was_visible;
4300         DWORD position;
4301 
4302         item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4303 
4304         if (!item)
4305             return E_UNEXPECTED;
4306 
4307         visible = ((dwState & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4308         was_visible = ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4309 
4310         if (visible && !was_visible)
4311         {
4312             SendMessageW(ctrl->hwnd, CB_INSERTSTRING, position, (LPARAM)item->label);
4313             SendMessageW(ctrl->hwnd, CB_SETITEMDATA, position, dwIDItem);
4314         }
4315         else if (!visible && was_visible)
4316         {
4317             SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0);
4318         }
4319 
4320         item->cdcstate = dwState;
4321 
4322         return S_OK;
4323     }
4324     case IDLG_CCTRL_MENU:
4325     case IDLG_CCTRL_OPENDROPDOWN:
4326     {
4327         HMENU hmenu;
4328         cctrl_item* item;
4329         CDCONTROLSTATEF prev_state;
4330         DWORD position;
4331 
4332         item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, &position);
4333 
4334         if (!item)
4335             return E_UNEXPECTED;
4336 
4337         prev_state = item->cdcstate;
4338 
4339         if (ctrl->type == IDLG_CCTRL_MENU)
4340         {
4341             TBBUTTON tbb;
4342             SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4343             hmenu = (HMENU)tbb.dwData;
4344         }
4345         else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4346             hmenu = This->hmenu_opendropdown;
4347 
4348         if (dwState & CDCS_VISIBLE)
4349         {
4350             if (prev_state & CDCS_VISIBLE)
4351             {
4352                 /* change state */
4353                 EnableMenuItem(hmenu, dwIDItem,
4354                     MF_BYCOMMAND|((dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED));
4355             }
4356             else
4357             {
4358                 /* show item */
4359                 MENUITEMINFOW mii;
4360 
4361                 mii.cbSize = sizeof(mii);
4362                 mii.fMask = MIIM_ID|MIIM_STATE|MIIM_STRING;
4363                 mii.fState = (dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED;
4364                 mii.wID = dwIDItem;
4365                 mii.dwTypeData = item->label;
4366 
4367                 InsertMenuItemW(hmenu, position, TRUE, &mii);
4368             }
4369         }
4370         else if (prev_state & CDCS_VISIBLE)
4371         {
4372             /* hide item */
4373             DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND);
4374         }
4375 
4376         item->cdcstate = dwState;
4377 
4378         if (ctrl->type == IDLG_CCTRL_OPENDROPDOWN)
4379         {
4380             update_control_text(This);
4381             update_layout(This);
4382         }
4383 
4384         return S_OK;
4385     }
4386     case IDLG_CCTRL_RADIOBUTTONLIST:
4387     {
4388         cctrl_item* item;
4389 
4390         item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, NULL);
4391 
4392         if (!item)
4393             return E_UNEXPECTED;
4394 
4395         /* Oddly, native allows setting this but doesn't seem to do anything with it. */
4396         item->cdcstate = dwState;
4397 
4398         return S_OK;
4399     }
4400     default:
4401         break;
4402     }
4403 
4404     return E_FAIL;
4405 }
4406 
4407 static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogCustomize *iface,
4408                                                                     DWORD dwIDCtl,
4409                                                                     DWORD *pdwIDItem)
4410 {
4411     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4412     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4413     TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwIDItem);
4414 
4415     if(!ctrl) return E_FAIL;
4416 
4417     switch(ctrl->type)
4418     {
4419     case IDLG_CCTRL_COMBOBOX:
4420     {
4421         UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
4422         if(index == CB_ERR)
4423             return E_FAIL;
4424 
4425         *pdwIDItem = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
4426         return S_OK;
4427     }
4428     case IDLG_CCTRL_OPENDROPDOWN:
4429         if (This->opendropdown_has_selection)
4430         {
4431             *pdwIDItem = This->opendropdown_selection;
4432             return S_OK;
4433         }
4434         else
4435         {
4436             /* Return first enabled item. */
4437             cctrl_item* item = get_first_item(ctrl);
4438 
4439             if (item)
4440             {
4441                 *pdwIDItem = item->id;
4442                 return S_OK;
4443             }
4444 
4445             WARN("no enabled items in open dropdown\n");
4446             return E_FAIL;
4447         }
4448     case IDLG_CCTRL_RADIOBUTTONLIST:
4449     {
4450         cctrl_item* item;
4451 
4452         LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
4453         {
4454             if (SendMessageW(item->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED)
4455             {
4456                 *pdwIDItem = item->id;
4457                 return S_OK;
4458             }
4459         }
4460 
4461         WARN("no checked items in radio button list\n");
4462         return E_FAIL;
4463     }
4464     default:
4465         FIXME("Unsupported control type %d\n", ctrl->type);
4466     }
4467 
4468     return E_NOTIMPL;
4469 }
4470 
4471 static HRESULT WINAPI IFileDialogCustomize_fnSetSelectedControlItem(IFileDialogCustomize *iface,
4472                                                                     DWORD dwIDCtl,
4473                                                                     DWORD dwIDItem)
4474 {
4475     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4476     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4477     TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4478 
4479     if(!ctrl) return E_INVALIDARG;
4480 
4481     switch(ctrl->type)
4482     {
4483     case IDLG_CCTRL_COMBOBOX:
4484     {
4485         UINT index = get_combobox_index_from_id(ctrl->hwnd, dwIDItem);
4486 
4487         if(index == -1)
4488             return E_INVALIDARG;
4489 
4490         if(SendMessageW(ctrl->hwnd, CB_SETCURSEL, index, 0) == CB_ERR)
4491             return E_FAIL;
4492 
4493         return S_OK;
4494     }
4495     case IDLG_CCTRL_RADIOBUTTONLIST:
4496     {
4497         cctrl_item* item;
4498 
4499         item = get_item(ctrl, dwIDItem, 0, NULL);
4500 
4501         if (item)
4502         {
4503             radiobuttonlist_set_selected_item(This, ctrl, item);
4504             return S_OK;
4505         }
4506 
4507         return E_INVALIDARG;
4508     }
4509     default:
4510         FIXME("Unsupported control type %d\n", ctrl->type);
4511     }
4512 
4513     return E_INVALIDARG;
4514 }
4515 
4516 static HRESULT WINAPI IFileDialogCustomize_fnStartVisualGroup(IFileDialogCustomize *iface,
4517                                                               DWORD dwIDCtl,
4518                                                               LPCWSTR pszLabel)
4519 {
4520     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4521     customctrl *vg;
4522     HRESULT hr;
4523     TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
4524 
4525     if(This->cctrl_active_vg)
4526         return E_UNEXPECTED;
4527 
4528     hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_STATICW, 0, 0,
4529                           This->cctrl_def_height, &vg);
4530     if(SUCCEEDED(hr))
4531     {
4532         vg->type = IDLG_CCTRL_VISUALGROUP;
4533         This->cctrl_active_vg = vg;
4534     }
4535 
4536     return hr;
4537 }
4538 
4539 static HRESULT WINAPI IFileDialogCustomize_fnEndVisualGroup(IFileDialogCustomize *iface)
4540 {
4541     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4542     TRACE("%p\n", This);
4543 
4544     This->cctrl_active_vg = NULL;
4545 
4546     return S_OK;
4547 }
4548 
4549 static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize *iface,
4550                                                            DWORD dwIDCtl)
4551 {
4552     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4553     FIXME("stub - %p (%d)\n", This, dwIDCtl);
4554     return S_OK;
4555 }
4556 
4557 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCustomize *iface,
4558                                                                 DWORD dwIDCtl,
4559                                                                 DWORD dwIDItem,
4560                                                                 LPCWSTR pszLabel)
4561 {
4562     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4563     FIXME("stub - %p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4564     return E_NOTIMPL;
4565 }
4566 
4567 static const IFileDialogCustomizeVtbl vt_IFileDialogCustomize = {
4568     IFileDialogCustomize_fnQueryInterface,
4569     IFileDialogCustomize_fnAddRef,
4570     IFileDialogCustomize_fnRelease,
4571     IFileDialogCustomize_fnEnableOpenDropDown,
4572     IFileDialogCustomize_fnAddMenu,
4573     IFileDialogCustomize_fnAddPushButton,
4574     IFileDialogCustomize_fnAddComboBox,
4575     IFileDialogCustomize_fnAddRadioButtonList,
4576     IFileDialogCustomize_fnAddCheckButton,
4577     IFileDialogCustomize_fnAddEditBox,
4578     IFileDialogCustomize_fnAddSeparator,
4579     IFileDialogCustomize_fnAddText,
4580     IFileDialogCustomize_fnSetControlLabel,
4581     IFileDialogCustomize_fnGetControlState,
4582     IFileDialogCustomize_fnSetControlState,
4583     IFileDialogCustomize_fnGetEditBoxText,
4584     IFileDialogCustomize_fnSetEditBoxText,
4585     IFileDialogCustomize_fnGetCheckButtonState,
4586     IFileDialogCustomize_fnSetCheckButtonState,
4587     IFileDialogCustomize_fnAddControlItem,
4588     IFileDialogCustomize_fnRemoveControlItem,
4589     IFileDialogCustomize_fnRemoveAllControlItems,
4590     IFileDialogCustomize_fnGetControlItemState,
4591     IFileDialogCustomize_fnSetControlItemState,
4592     IFileDialogCustomize_fnGetSelectedControlItem,
4593     IFileDialogCustomize_fnSetSelectedControlItem,
4594     IFileDialogCustomize_fnStartVisualGroup,
4595     IFileDialogCustomize_fnEndVisualGroup,
4596     IFileDialogCustomize_fnMakeProminent,
4597     IFileDialogCustomize_fnSetControlItemText
4598 };
4599 
4600 static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv, enum ITEMDLG_TYPE type)
4601 {
4602     FileDialogImpl *fdimpl;
4603     HRESULT hr;
4604     IShellFolder *psf;
4605     TRACE("%p, %s, %p\n", pUnkOuter, debugstr_guid(riid), ppv);
4606 
4607     if(!ppv)
4608         return E_POINTER;
4609     if(pUnkOuter)
4610         return CLASS_E_NOAGGREGATION;
4611 
4612     fdimpl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FileDialogImpl));
4613     if(!fdimpl)
4614         return E_OUTOFMEMORY;
4615 
4616     fdimpl->ref = 1;
4617     fdimpl->IFileDialog2_iface.lpVtbl = &vt_IFileDialog2;
4618     fdimpl->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
4619     fdimpl->IServiceProvider_iface.lpVtbl = &vt_IServiceProvider;
4620     fdimpl->ICommDlgBrowser3_iface.lpVtbl = &vt_ICommDlgBrowser3;
4621     fdimpl->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
4622     fdimpl->IFileDialogCustomize_iface.lpVtbl = &vt_IFileDialogCustomize;
4623 
4624     if(type == ITEMDLG_TYPE_OPEN)
4625     {
4626         fdimpl->dlg_type = ITEMDLG_TYPE_OPEN;
4627         fdimpl->u.IFileOpenDialog_iface.lpVtbl = &vt_IFileOpenDialog;
4628         fdimpl->options = FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
4629         fdimpl->custom_title = fdimpl->custom_okbutton = NULL;
4630     }
4631     else
4632     {
4633         WCHAR buf[16];
4634         fdimpl->dlg_type = ITEMDLG_TYPE_SAVE;
4635         fdimpl->u.IFileSaveDialog_iface.lpVtbl = &vt_IFileSaveDialog;
4636         fdimpl->options = FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
4637 
4638         LoadStringW(COMDLG32_hInstance, IDS_SAVE, buf, ARRAY_SIZE(buf));
4639         fdimpl->custom_title = StrDupW(buf);
4640         fdimpl->custom_okbutton = StrDupW(buf);
4641     }
4642 
4643     list_init(&fdimpl->events_clients);
4644 
4645     /* FIXME: The default folder setting should be restored for the
4646      * application if it was previously set. */
4647     SHGetDesktopFolder(&psf);
4648     SHGetItemFromObject((IUnknown*)psf, &IID_IShellItem, (void**)&fdimpl->psi_defaultfolder);
4649     IShellFolder_Release(psf);
4650 
4651     hr = init_custom_controls(fdimpl);
4652     if(FAILED(hr))
4653     {
4654         ERR("Failed to initialize custom controls (0x%08x).\n", hr);
4655         IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4656         return E_FAIL;
4657     }
4658 
4659     hr = IFileDialog2_QueryInterface(&fdimpl->IFileDialog2_iface, riid, ppv);
4660     IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4661     return hr;
4662 }
4663 
4664 HRESULT FileOpenDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4665 {
4666     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_OPEN);
4667 }
4668 
4669 HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4670 {
4671     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);
4672 }
4673 
4674 #endif /* Win 7 */
4675