xref: /reactos/dll/win32/comdlg32/itemdlg.c (revision 845faec4)
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, 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, sizeof(buf)/sizeof(WCHAR));
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( !(This->options & FOS_PICKFOLDERS) && (fos & FOS_PICKFOLDERS) )
2511     {
2512         WCHAR buf[30];
2513         LoadStringW(COMDLG32_hInstance, IDS_SELECT_FOLDER, buf, sizeof(buf)/sizeof(WCHAR));
2514         IFileDialog2_SetTitle(iface, buf);
2515     }
2516 
2517     This->options = fos;
2518 
2519     return S_OK;
2520 }
2521 
2522 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
2523 {
2524     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2525     TRACE("%p (%p)\n", This, pfos);
2526 
2527     if(!pfos)
2528         return E_INVALIDARG;
2529 
2530     *pfos = This->options;
2531 
2532     return S_OK;
2533 }
2534 
2535 static HRESULT WINAPI IFileDialog2_fnSetDefaultFolder(IFileDialog2 *iface, IShellItem *psi)
2536 {
2537     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2538     TRACE("%p (%p)\n", This, psi);
2539     if(This->psi_defaultfolder)
2540         IShellItem_Release(This->psi_defaultfolder);
2541 
2542     This->psi_defaultfolder = psi;
2543 
2544     if(This->psi_defaultfolder)
2545         IShellItem_AddRef(This->psi_defaultfolder);
2546 
2547     return S_OK;
2548 }
2549 
2550 static HRESULT WINAPI IFileDialog2_fnSetFolder(IFileDialog2 *iface, IShellItem *psi)
2551 {
2552     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2553     TRACE("%p (%p)\n", This, psi);
2554     if(This->psi_setfolder)
2555         IShellItem_Release(This->psi_setfolder);
2556 
2557     This->psi_setfolder = psi;
2558 
2559     if(This->psi_setfolder)
2560         IShellItem_AddRef(This->psi_setfolder);
2561 
2562     return S_OK;
2563 }
2564 
2565 static HRESULT WINAPI IFileDialog2_fnGetFolder(IFileDialog2 *iface, IShellItem **ppsi)
2566 {
2567     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2568     TRACE("%p (%p)\n", This, ppsi);
2569     if(!ppsi)
2570         return E_INVALIDARG;
2571 
2572     /* FIXME:
2573        If the dialog is shown, return the current(ly selected) folder. */
2574 
2575     *ppsi = NULL;
2576     if(This->psi_folder)
2577         *ppsi = This->psi_folder;
2578     else if(This->psi_setfolder)
2579         *ppsi = This->psi_setfolder;
2580     else if(This->psi_defaultfolder)
2581         *ppsi = This->psi_defaultfolder;
2582 
2583     if(*ppsi)
2584     {
2585         IShellItem_AddRef(*ppsi);
2586         return S_OK;
2587     }
2588 
2589     return E_FAIL;
2590 }
2591 
2592 static HRESULT WINAPI IFileDialog2_fnGetCurrentSelection(IFileDialog2 *iface, IShellItem **ppsi)
2593 {
2594     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2595     HRESULT hr;
2596     TRACE("%p (%p)\n", This, ppsi);
2597 
2598     if(!ppsi)
2599         return E_INVALIDARG;
2600 
2601     if(This->psia_selection)
2602     {
2603         /* FIXME: Check filename edit box */
2604         hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
2605         return hr;
2606     }
2607 
2608     return E_FAIL;
2609 }
2610 
2611 static HRESULT WINAPI IFileDialog2_fnSetFileName(IFileDialog2 *iface, LPCWSTR pszName)
2612 {
2613     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2614     TRACE("%p (%s)\n", iface, debugstr_w(pszName));
2615 
2616     set_file_name(This, pszName);
2617 
2618     return S_OK;
2619 }
2620 
2621 static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *pszName)
2622 {
2623     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2624     TRACE("%p (%p)\n", iface, pszName);
2625 
2626     if(!pszName)
2627         return E_INVALIDARG;
2628 
2629     *pszName = NULL;
2630     get_file_name(This, pszName);
2631     return *pszName ? S_OK : E_FAIL;
2632 }
2633 
2634 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
2635 {
2636     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2637     TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
2638 
2639     LocalFree(This->custom_title);
2640     This->custom_title = StrDupW(pszTitle);
2641     update_control_text(This);
2642 
2643     return S_OK;
2644 }
2645 
2646 static HRESULT WINAPI IFileDialog2_fnSetOkButtonLabel(IFileDialog2 *iface, LPCWSTR pszText)
2647 {
2648     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2649     TRACE("%p (%s)\n", This, debugstr_w(pszText));
2650 
2651     LocalFree(This->custom_okbutton);
2652     This->custom_okbutton = StrDupW(pszText);
2653     update_control_text(This);
2654     update_layout(This);
2655 
2656     return S_OK;
2657 }
2658 
2659 static HRESULT WINAPI IFileDialog2_fnSetFileNameLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2660 {
2661     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2662     TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2663 
2664     LocalFree(This->custom_filenamelabel);
2665     This->custom_filenamelabel = StrDupW(pszLabel);
2666     update_control_text(This);
2667     update_layout(This);
2668 
2669     return S_OK;
2670 }
2671 
2672 static HRESULT WINAPI IFileDialog2_fnGetResult(IFileDialog2 *iface, IShellItem **ppsi)
2673 {
2674     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2675     HRESULT hr;
2676     TRACE("%p (%p)\n", This, ppsi);
2677 
2678     if(!ppsi)
2679         return E_INVALIDARG;
2680 
2681     if(This->psia_results)
2682     {
2683         UINT item_count;
2684         hr = IShellItemArray_GetCount(This->psia_results, &item_count);
2685         if(SUCCEEDED(hr))
2686         {
2687             if(item_count != 1)
2688                 return E_FAIL;
2689 
2690             /* Adds a reference. */
2691             hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
2692         }
2693 
2694         return hr;
2695     }
2696 
2697     return E_UNEXPECTED;
2698 }
2699 
2700 static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *psi, FDAP fdap)
2701 {
2702     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2703     FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
2704     return S_OK;
2705 }
2706 
2707 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
2708 {
2709     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2710     TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
2711 
2712     LocalFree(This->default_ext);
2713     This->default_ext = StrDupW(pszDefaultExtension);
2714 
2715     return S_OK;
2716 }
2717 
2718 static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
2719 {
2720     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2721     TRACE("%p (0x%08x)\n", This, hr);
2722 
2723     if(This->dlg_hwnd)
2724         EndDialog(This->dlg_hwnd, hr);
2725 
2726     return S_OK;
2727 }
2728 
2729 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
2730 {
2731     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2732     TRACE("%p (%s)\n", This, debugstr_guid(guid));
2733     This->client_guid = *guid;
2734     return S_OK;
2735 }
2736 
2737 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
2738 {
2739     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2740     FIXME("stub - %p\n", This);
2741     return E_NOTIMPL;
2742 }
2743 
2744 static HRESULT WINAPI IFileDialog2_fnSetFilter(IFileDialog2 *iface, IShellItemFilter *pFilter)
2745 {
2746     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2747     FIXME("stub - %p (%p)\n", This, pFilter);
2748     return E_NOTIMPL;
2749 }
2750 
2751 static HRESULT WINAPI IFileDialog2_fnSetCancelButtonLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2752 {
2753     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2754     TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2755 
2756     LocalFree(This->custom_cancelbutton);
2757     This->custom_cancelbutton = StrDupW(pszLabel);
2758     update_control_text(This);
2759     update_layout(This);
2760 
2761     return S_OK;
2762 }
2763 
2764 static HRESULT WINAPI IFileDialog2_fnSetNavigationRoot(IFileDialog2 *iface, IShellItem *psi)
2765 {
2766     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2767     FIXME("stub - %p (%p)\n", This, psi);
2768     return E_NOTIMPL;
2769 }
2770 
2771 static const IFileDialog2Vtbl vt_IFileDialog2 = {
2772     IFileDialog2_fnQueryInterface,
2773     IFileDialog2_fnAddRef,
2774     IFileDialog2_fnRelease,
2775     IFileDialog2_fnShow,
2776     IFileDialog2_fnSetFileTypes,
2777     IFileDialog2_fnSetFileTypeIndex,
2778     IFileDialog2_fnGetFileTypeIndex,
2779     IFileDialog2_fnAdvise,
2780     IFileDialog2_fnUnadvise,
2781     IFileDialog2_fnSetOptions,
2782     IFileDialog2_fnGetOptions,
2783     IFileDialog2_fnSetDefaultFolder,
2784     IFileDialog2_fnSetFolder,
2785     IFileDialog2_fnGetFolder,
2786     IFileDialog2_fnGetCurrentSelection,
2787     IFileDialog2_fnSetFileName,
2788     IFileDialog2_fnGetFileName,
2789     IFileDialog2_fnSetTitle,
2790     IFileDialog2_fnSetOkButtonLabel,
2791     IFileDialog2_fnSetFileNameLabel,
2792     IFileDialog2_fnGetResult,
2793     IFileDialog2_fnAddPlace,
2794     IFileDialog2_fnSetDefaultExtension,
2795     IFileDialog2_fnClose,
2796     IFileDialog2_fnSetClientGuid,
2797     IFileDialog2_fnClearClientData,
2798     IFileDialog2_fnSetFilter,
2799     IFileDialog2_fnSetCancelButtonLabel,
2800     IFileDialog2_fnSetNavigationRoot
2801 };
2802 
2803 /**************************************************************************
2804  * IFileOpenDialog
2805  */
2806 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
2807 {
2808     return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
2809 }
2810 
2811 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
2812                                                        REFIID riid, void **ppvObject)
2813 {
2814     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2815     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2816 }
2817 
2818 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
2819 {
2820     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2821     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2822 }
2823 
2824 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
2825 {
2826     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2827     return IFileDialog2_Release(&This->IFileDialog2_iface);
2828 }
2829 
2830 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
2831 {
2832     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2833     return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2834 }
2835 
2836 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
2837                                                      const COMDLG_FILTERSPEC *rgFilterSpec)
2838 {
2839     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2840     return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2841 }
2842 
2843 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
2844 {
2845     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2846     return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2847 }
2848 
2849 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
2850 {
2851     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2852     return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2853 }
2854 
2855 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
2856                                                DWORD *pdwCookie)
2857 {
2858     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2859     return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2860 }
2861 
2862 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
2863 {
2864     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2865     return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2866 }
2867 
2868 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
2869 {
2870     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2871     return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2872 }
2873 
2874 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2875 {
2876     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2877     return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2878 }
2879 
2880 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
2881 {
2882     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2883     return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2884 }
2885 
2886 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
2887 {
2888     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2889     return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2890 }
2891 
2892 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
2893 {
2894     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2895     return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2896 }
2897 
2898 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
2899 {
2900     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2901     return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2902 }
2903 
2904 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
2905 {
2906     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2907     return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2908 }
2909 
2910 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
2911 {
2912     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2913     return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2914 }
2915 
2916 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
2917 {
2918     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2919     return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2920 }
2921 
2922 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
2923 {
2924     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2925     return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2926 }
2927 
2928 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
2929 {
2930     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2931     return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2932 }
2933 
2934 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
2935 {
2936     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2937     return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2938 }
2939 
2940 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
2941 {
2942     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2943     return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2944 }
2945 
2946 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultExtension(IFileOpenDialog *iface,
2947                                                             LPCWSTR pszDefaultExtension)
2948 {
2949     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2950     return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2951 }
2952 
2953 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
2954 {
2955     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2956     return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2957 }
2958 
2959 static HRESULT WINAPI IFileOpenDialog_fnSetClientGuid(IFileOpenDialog *iface, REFGUID guid)
2960 {
2961     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2962     return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2963 }
2964 
2965 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
2966 {
2967     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2968     return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2969 }
2970 
2971 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
2972 {
2973     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2974     return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2975 }
2976 
2977 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
2978 {
2979     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2980     TRACE("%p (%p)\n", This, ppenum);
2981 
2982     *ppenum = This->psia_results;
2983 
2984     if(*ppenum)
2985     {
2986         IShellItemArray_AddRef(*ppenum);
2987         return S_OK;
2988     }
2989 
2990     return E_FAIL;
2991 }
2992 
2993 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
2994 {
2995     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2996     TRACE("%p (%p)\n", This, ppsai);
2997 
2998     if(This->psia_selection)
2999     {
3000         *ppsai = This->psia_selection;
3001         IShellItemArray_AddRef(*ppsai);
3002         return S_OK;
3003     }
3004 
3005     return E_FAIL;
3006 }
3007 
3008 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
3009     IFileOpenDialog_fnQueryInterface,
3010     IFileOpenDialog_fnAddRef,
3011     IFileOpenDialog_fnRelease,
3012     IFileOpenDialog_fnShow,
3013     IFileOpenDialog_fnSetFileTypes,
3014     IFileOpenDialog_fnSetFileTypeIndex,
3015     IFileOpenDialog_fnGetFileTypeIndex,
3016     IFileOpenDialog_fnAdvise,
3017     IFileOpenDialog_fnUnadvise,
3018     IFileOpenDialog_fnSetOptions,
3019     IFileOpenDialog_fnGetOptions,
3020     IFileOpenDialog_fnSetDefaultFolder,
3021     IFileOpenDialog_fnSetFolder,
3022     IFileOpenDialog_fnGetFolder,
3023     IFileOpenDialog_fnGetCurrentSelection,
3024     IFileOpenDialog_fnSetFileName,
3025     IFileOpenDialog_fnGetFileName,
3026     IFileOpenDialog_fnSetTitle,
3027     IFileOpenDialog_fnSetOkButtonLabel,
3028     IFileOpenDialog_fnSetFileNameLabel,
3029     IFileOpenDialog_fnGetResult,
3030     IFileOpenDialog_fnAddPlace,
3031     IFileOpenDialog_fnSetDefaultExtension,
3032     IFileOpenDialog_fnClose,
3033     IFileOpenDialog_fnSetClientGuid,
3034     IFileOpenDialog_fnClearClientData,
3035     IFileOpenDialog_fnSetFilter,
3036     IFileOpenDialog_fnGetResults,
3037     IFileOpenDialog_fnGetSelectedItems
3038 };
3039 
3040 /**************************************************************************
3041  * IFileSaveDialog
3042  */
3043 static inline FileDialogImpl *impl_from_IFileSaveDialog(IFileSaveDialog *iface)
3044 {
3045     return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
3046 }
3047 
3048 static HRESULT WINAPI IFileSaveDialog_fnQueryInterface(IFileSaveDialog *iface,
3049                                                        REFIID riid,
3050                                                        void **ppvObject)
3051 {
3052     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3053     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3054 }
3055 
3056 static ULONG WINAPI IFileSaveDialog_fnAddRef(IFileSaveDialog *iface)
3057 {
3058     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3059     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3060 }
3061 
3062 static ULONG WINAPI IFileSaveDialog_fnRelease(IFileSaveDialog *iface)
3063 {
3064     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3065     return IFileDialog2_Release(&This->IFileDialog2_iface);
3066 }
3067 
3068 static HRESULT WINAPI IFileSaveDialog_fnShow(IFileSaveDialog *iface, HWND hwndOwner)
3069 {
3070     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3071     return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
3072 }
3073 
3074 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypes(IFileSaveDialog *iface, UINT cFileTypes,
3075                                                      const COMDLG_FILTERSPEC *rgFilterSpec)
3076 {
3077     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3078     return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
3079 }
3080 
3081 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypeIndex(IFileSaveDialog *iface, UINT iFileType)
3082 {
3083     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3084     return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
3085 }
3086 
3087 static HRESULT WINAPI IFileSaveDialog_fnGetFileTypeIndex(IFileSaveDialog *iface, UINT *piFileType)
3088 {
3089     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3090     return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
3091 }
3092 
3093 static HRESULT WINAPI IFileSaveDialog_fnAdvise(IFileSaveDialog *iface, IFileDialogEvents *pfde,
3094                                                DWORD *pdwCookie)
3095 {
3096     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3097     return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
3098 }
3099 
3100 static HRESULT WINAPI IFileSaveDialog_fnUnadvise(IFileSaveDialog *iface, DWORD dwCookie)
3101 {
3102     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3103     return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
3104 }
3105 
3106 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
3107 {
3108     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3109     return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
3110 }
3111 
3112 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
3113 {
3114     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3115     return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
3116 }
3117 
3118 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultFolder(IFileSaveDialog *iface, IShellItem *psi)
3119 {
3120     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3121     return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
3122 }
3123 
3124 static HRESULT WINAPI IFileSaveDialog_fnSetFolder(IFileSaveDialog *iface, IShellItem *psi)
3125 {
3126     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3127     return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
3128 }
3129 
3130 static HRESULT WINAPI IFileSaveDialog_fnGetFolder(IFileSaveDialog *iface, IShellItem **ppsi)
3131 {
3132     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3133     return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
3134 }
3135 
3136 static HRESULT WINAPI IFileSaveDialog_fnGetCurrentSelection(IFileSaveDialog *iface, IShellItem **ppsi)
3137 {
3138     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3139     return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
3140 }
3141 
3142 static HRESULT WINAPI IFileSaveDialog_fnSetFileName(IFileSaveDialog *iface, LPCWSTR pszName)
3143 {
3144     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3145     return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
3146 }
3147 
3148 static HRESULT WINAPI IFileSaveDialog_fnGetFileName(IFileSaveDialog *iface, LPWSTR *pszName)
3149 {
3150     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3151     return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
3152 }
3153 
3154 static HRESULT WINAPI IFileSaveDialog_fnSetTitle(IFileSaveDialog *iface, LPCWSTR pszTitle)
3155 {
3156     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3157     return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
3158 }
3159 
3160 static HRESULT WINAPI IFileSaveDialog_fnSetOkButtonLabel(IFileSaveDialog *iface, LPCWSTR pszText)
3161 {
3162     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3163     return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
3164 }
3165 
3166 static HRESULT WINAPI IFileSaveDialog_fnSetFileNameLabel(IFileSaveDialog *iface, LPCWSTR pszLabel)
3167 {
3168     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3169     return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
3170 }
3171 
3172 static HRESULT WINAPI IFileSaveDialog_fnGetResult(IFileSaveDialog *iface, IShellItem **ppsi)
3173 {
3174     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3175     return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
3176 }
3177 
3178 static HRESULT WINAPI IFileSaveDialog_fnAddPlace(IFileSaveDialog *iface, IShellItem *psi, FDAP fdap)
3179 {
3180     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3181     return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
3182 }
3183 
3184 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultExtension(IFileSaveDialog *iface,
3185                                                             LPCWSTR pszDefaultExtension)
3186 {
3187     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3188     return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
3189 }
3190 
3191 static HRESULT WINAPI IFileSaveDialog_fnClose(IFileSaveDialog *iface, HRESULT hr)
3192 {
3193     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3194     return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
3195 }
3196 
3197 static HRESULT WINAPI IFileSaveDialog_fnSetClientGuid(IFileSaveDialog *iface, REFGUID guid)
3198 {
3199     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3200     return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
3201 }
3202 
3203 static HRESULT WINAPI IFileSaveDialog_fnClearClientData(IFileSaveDialog *iface)
3204 {
3205     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3206     return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
3207 }
3208 
3209 static HRESULT WINAPI IFileSaveDialog_fnSetFilter(IFileSaveDialog *iface, IShellItemFilter *pFilter)
3210 {
3211     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3212     return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
3213 }
3214 
3215 static HRESULT WINAPI IFileSaveDialog_fnSetSaveAsItem(IFileSaveDialog* iface, IShellItem *psi)
3216 {
3217     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3218     FIXME("stub - %p (%p)\n", This, psi);
3219     return E_NOTIMPL;
3220 }
3221 
3222 static HRESULT WINAPI IFileSaveDialog_fnSetProperties(IFileSaveDialog* iface, IPropertyStore *pStore)
3223 {
3224     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3225     FIXME("stub - %p (%p)\n", This, pStore);
3226     return E_NOTIMPL;
3227 }
3228 
3229 static HRESULT WINAPI IFileSaveDialog_fnSetCollectedProperties(IFileSaveDialog* iface,
3230                                                                IPropertyDescriptionList *pList,
3231                                                                BOOL fAppendDefault)
3232 {
3233     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3234     FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
3235     return E_NOTIMPL;
3236 }
3237 
3238 static HRESULT WINAPI IFileSaveDialog_fnGetProperties(IFileSaveDialog* iface, IPropertyStore **ppStore)
3239 {
3240     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3241     FIXME("stub - %p (%p)\n", This, ppStore);
3242     return E_NOTIMPL;
3243 }
3244 
3245 static HRESULT WINAPI IFileSaveDialog_fnApplyProperties(IFileSaveDialog* iface,
3246                                                         IShellItem *psi,
3247                                                         IPropertyStore *pStore,
3248                                                         HWND hwnd,
3249                                                         IFileOperationProgressSink *pSink)
3250 {
3251     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3252     FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
3253     return E_NOTIMPL;
3254 }
3255 
3256 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
3257     IFileSaveDialog_fnQueryInterface,
3258     IFileSaveDialog_fnAddRef,
3259     IFileSaveDialog_fnRelease,
3260     IFileSaveDialog_fnShow,
3261     IFileSaveDialog_fnSetFileTypes,
3262     IFileSaveDialog_fnSetFileTypeIndex,
3263     IFileSaveDialog_fnGetFileTypeIndex,
3264     IFileSaveDialog_fnAdvise,
3265     IFileSaveDialog_fnUnadvise,
3266     IFileSaveDialog_fnSetOptions,
3267     IFileSaveDialog_fnGetOptions,
3268     IFileSaveDialog_fnSetDefaultFolder,
3269     IFileSaveDialog_fnSetFolder,
3270     IFileSaveDialog_fnGetFolder,
3271     IFileSaveDialog_fnGetCurrentSelection,
3272     IFileSaveDialog_fnSetFileName,
3273     IFileSaveDialog_fnGetFileName,
3274     IFileSaveDialog_fnSetTitle,
3275     IFileSaveDialog_fnSetOkButtonLabel,
3276     IFileSaveDialog_fnSetFileNameLabel,
3277     IFileSaveDialog_fnGetResult,
3278     IFileSaveDialog_fnAddPlace,
3279     IFileSaveDialog_fnSetDefaultExtension,
3280     IFileSaveDialog_fnClose,
3281     IFileSaveDialog_fnSetClientGuid,
3282     IFileSaveDialog_fnClearClientData,
3283     IFileSaveDialog_fnSetFilter,
3284     IFileSaveDialog_fnSetSaveAsItem,
3285     IFileSaveDialog_fnSetProperties,
3286     IFileSaveDialog_fnSetCollectedProperties,
3287     IFileSaveDialog_fnGetProperties,
3288     IFileSaveDialog_fnApplyProperties
3289 };
3290 
3291 /**************************************************************************
3292  * IExplorerBrowserEvents implementation
3293  */
3294 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
3295 {
3296     return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
3297 }
3298 
3299 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
3300                                                               REFIID riid, void **ppvObject)
3301 {
3302     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3303     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
3304 
3305     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3306 }
3307 
3308 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
3309 {
3310     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3311     TRACE("%p\n", This);
3312     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3313 }
3314 
3315 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
3316 {
3317     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3318     TRACE("%p\n", This);
3319     return IFileDialog2_Release(&This->IFileDialog2_iface);
3320 }
3321 
3322 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
3323                                                                    PCIDLIST_ABSOLUTE pidlFolder)
3324 {
3325     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3326     IShellItem *psi;
3327     HRESULT hr;
3328     TRACE("%p (%p)\n", This, pidlFolder);
3329 
3330     hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
3331     if(SUCCEEDED(hr))
3332     {
3333         hr = events_OnFolderChanging(This, psi);
3334         IShellItem_Release(psi);
3335 
3336         /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
3337         if(hr == S_FALSE)
3338             hr = E_FAIL;
3339 
3340         return hr;
3341     }
3342     else
3343         ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
3344 
3345     return S_OK;
3346 }
3347 
3348 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
3349                                                              IShellView *psv)
3350 {
3351     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3352     TRACE("%p (%p)\n", This, psv);
3353     return S_OK;
3354 }
3355 
3356 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
3357                                                                     PCIDLIST_ABSOLUTE pidlFolder)
3358 {
3359     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3360     HRESULT hr;
3361     TRACE("%p (%p)\n", This, pidlFolder);
3362 
3363     if(This->psi_folder)
3364         IShellItem_Release(This->psi_folder);
3365 
3366     hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
3367     if(FAILED(hr))
3368     {
3369         ERR("Failed to get the current folder.\n");
3370         This->psi_folder = NULL;
3371     }
3372 
3373     events_OnFolderChange(This);
3374 
3375     return S_OK;
3376 }
3377 
3378 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
3379                                                                   PCIDLIST_ABSOLUTE pidlFolder)
3380 {
3381     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3382     TRACE("%p (%p)\n", This, pidlFolder);
3383     return S_OK;
3384 }
3385 
3386 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
3387     IExplorerBrowserEvents_fnQueryInterface,
3388     IExplorerBrowserEvents_fnAddRef,
3389     IExplorerBrowserEvents_fnRelease,
3390     IExplorerBrowserEvents_fnOnNavigationPending,
3391     IExplorerBrowserEvents_fnOnViewCreated,
3392     IExplorerBrowserEvents_fnOnNavigationComplete,
3393     IExplorerBrowserEvents_fnOnNavigationFailed
3394 };
3395 
3396 /**************************************************************************
3397  * IServiceProvider implementation
3398  */
3399 static inline FileDialogImpl *impl_from_IServiceProvider(IServiceProvider *iface)
3400 {
3401     return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
3402 }
3403 
3404 static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface,
3405                                                         REFIID riid, void **ppvObject)
3406 {
3407     FileDialogImpl *This = impl_from_IServiceProvider(iface);
3408     TRACE("%p\n", This);
3409     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3410 }
3411 
3412 static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface)
3413 {
3414     FileDialogImpl *This = impl_from_IServiceProvider(iface);
3415     TRACE("%p\n", This);
3416     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3417 }
3418 
3419 static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface)
3420 {
3421     FileDialogImpl *This = impl_from_IServiceProvider(iface);
3422     TRACE("%p\n", This);
3423     return IFileDialog2_Release(&This->IFileDialog2_iface);
3424 }
3425 
3426 static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
3427                                                       REFGUID guidService,
3428                                                       REFIID riid, void **ppv)
3429 {
3430     FileDialogImpl *This = impl_from_IServiceProvider(iface);
3431     HRESULT hr = E_NOTIMPL;
3432     TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
3433 
3434     *ppv = NULL;
3435     if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
3436         hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
3437     else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
3438         hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
3439     else
3440         FIXME("Interface %s requested from unknown service %s\n",
3441               debugstr_guid(riid), debugstr_guid(guidService));
3442 
3443     return hr;
3444 }
3445 
3446 static const IServiceProviderVtbl vt_IServiceProvider = {
3447     IServiceProvider_fnQueryInterface,
3448     IServiceProvider_fnAddRef,
3449     IServiceProvider_fnRelease,
3450     IServiceProvider_fnQueryService
3451 };
3452 
3453 /**************************************************************************
3454  * ICommDlgBrowser3 implementation
3455  */
3456 static inline FileDialogImpl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface)
3457 {
3458     return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
3459 }
3460 
3461 static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface,
3462                                                         REFIID riid, void **ppvObject)
3463 {
3464     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3465     TRACE("%p\n", This);
3466     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3467 }
3468 
3469 static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface)
3470 {
3471     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3472     TRACE("%p\n", This);
3473     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3474 }
3475 
3476 static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface)
3477 {
3478     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3479     TRACE("%p\n", This);
3480     return IFileDialog2_Release(&This->IFileDialog2_iface);
3481 }
3482 
3483 static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3 *iface,
3484                                                           IShellView *shv)
3485 {
3486     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3487     HRESULT hr;
3488     TRACE("%p (%p)\n", This, shv);
3489 
3490     hr = on_default_action(This);
3491 
3492     if(SUCCEEDED(hr))
3493         EndDialog(This->dlg_hwnd, S_OK);
3494 
3495     return S_OK;
3496 }
3497 
3498 static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange(ICommDlgBrowser3 *iface,
3499                                                        IShellView *shv, ULONG uChange )
3500 {
3501     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3502     IDataObject *new_selection;
3503     HRESULT hr;
3504     TRACE("%p (%p, %x)\n", This, shv, uChange);
3505 
3506     switch(uChange)
3507     {
3508     case CDBOSC_SELCHANGE:
3509         if(This->psia_selection)
3510         {
3511             IShellItemArray_Release(This->psia_selection);
3512             This->psia_selection = NULL;
3513         }
3514 
3515         hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
3516         if(SUCCEEDED(hr))
3517         {
3518             hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
3519                                                       (void**)&This->psia_selection);
3520             if(SUCCEEDED(hr))
3521             {
3522                 fill_filename_from_selection(This);
3523                 events_OnSelectionChange(This);
3524             }
3525 
3526             IDataObject_Release(new_selection);
3527         }
3528         break;
3529     default:
3530         TRACE("Unhandled state change\n");
3531     }
3532     return S_OK;
3533 }
3534 
3535 static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
3536                                                        IShellView *shv, LPCITEMIDLIST pidl)
3537 {
3538     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3539     IShellItem *psi;
3540     LPWSTR filename;
3541     LPITEMIDLIST parent_pidl;
3542     HRESULT hr;
3543     ULONG attr;
3544     TRACE("%p (%p, %p)\n", This, shv, pidl);
3545 
3546     if(!This->filterspec_count && !(This->options & FOS_PICKFOLDERS))
3547         return S_OK;
3548 
3549     hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
3550     if(SUCCEEDED(hr))
3551     {
3552         LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
3553         hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
3554         ILFree(parent_pidl);
3555         ILFree(full_pidl);
3556     }
3557     if(FAILED(hr))
3558     {
3559         ERR("Failed to get shellitem (%08x).\n", hr);
3560         return S_OK;
3561     }
3562 
3563     hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
3564     if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
3565     {
3566         IShellItem_Release(psi);
3567         return S_OK;
3568     }
3569 
3570     if((This->options & FOS_PICKFOLDERS) && !(attr & (SFGAO_FOLDER | SFGAO_LINK)))
3571     {
3572         IShellItem_Release(psi);
3573         return S_FALSE;
3574     }
3575 
3576     hr = S_OK;
3577     if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
3578     {
3579         if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
3580             hr = S_FALSE;
3581         CoTaskMemFree(filename);
3582     }
3583 
3584     IShellItem_Release(psi);
3585     return hr;
3586 }
3587 
3588 static HRESULT WINAPI ICommDlgBrowser3_fnNotify(ICommDlgBrowser3 *iface,
3589                                                 IShellView *ppshv, DWORD dwNotifyType)
3590 {
3591     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3592     FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
3593     return E_NOTIMPL;
3594 }
3595 
3596 static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText(ICommDlgBrowser3 *iface,
3597                                                             IShellView *pshv,
3598                                                             LPWSTR pszText, int cchMax)
3599 {
3600     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3601     FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
3602     return E_NOTIMPL;
3603 }
3604 
3605 static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags(ICommDlgBrowser3 *iface, DWORD *pdwFlags)
3606 {
3607     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3608     FIXME("Stub: %p (%p)\n", This, pdwFlags);
3609     return E_NOTIMPL;
3610 }
3611 
3612 static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked(ICommDlgBrowser3 *iface,
3613                                                          IShellView *pshv, int iColumn)
3614 {
3615     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3616     FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
3617     return E_NOTIMPL;
3618 }
3619 
3620 static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter(ICommDlgBrowser3 *iface,
3621                                                           LPWSTR pszFileSpec, int cchFileSpec)
3622 {
3623     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3624     FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
3625     return E_NOTIMPL;
3626 }
3627 
3628 static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated(ICommDlgBrowser3 *iface,
3629                                                           IShellView *pshv)
3630 {
3631     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3632     FIXME("Stub: %p (%p)\n", This, pshv);
3633     return E_NOTIMPL;
3634 }
3635 
3636 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
3637     ICommDlgBrowser3_fnQueryInterface,
3638     ICommDlgBrowser3_fnAddRef,
3639     ICommDlgBrowser3_fnRelease,
3640     ICommDlgBrowser3_fnOnDefaultCommand,
3641     ICommDlgBrowser3_fnOnStateChange,
3642     ICommDlgBrowser3_fnIncludeObject,
3643     ICommDlgBrowser3_fnNotify,
3644     ICommDlgBrowser3_fnGetDefaultMenuText,
3645     ICommDlgBrowser3_fnGetViewFlags,
3646     ICommDlgBrowser3_fnOnColumnClicked,
3647     ICommDlgBrowser3_fnGetCurrentFilter,
3648     ICommDlgBrowser3_fnOnPreviewCreated
3649 };
3650 
3651 /**************************************************************************
3652  * IOleWindow implementation
3653  */
3654 static inline FileDialogImpl *impl_from_IOleWindow(IOleWindow *iface)
3655 {
3656     return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
3657 }
3658 
3659 static HRESULT WINAPI IOleWindow_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
3660 {
3661     FileDialogImpl *This = impl_from_IOleWindow(iface);
3662     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3663 }
3664 
3665 static ULONG WINAPI IOleWindow_fnAddRef(IOleWindow *iface)
3666 {
3667     FileDialogImpl *This = impl_from_IOleWindow(iface);
3668     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3669 }
3670 
3671 static ULONG WINAPI IOleWindow_fnRelease(IOleWindow *iface)
3672 {
3673     FileDialogImpl *This = impl_from_IOleWindow(iface);
3674     return IFileDialog2_Release(&This->IFileDialog2_iface);
3675 }
3676 
3677 static HRESULT WINAPI IOleWindow_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMOde)
3678 {
3679     FileDialogImpl *This = impl_from_IOleWindow(iface);
3680     FIXME("Stub: %p (%d)\n", This, fEnterMOde);
3681     return E_NOTIMPL;
3682 }
3683 
3684 static HRESULT WINAPI IOleWindow_fnGetWindow(IOleWindow *iface, HWND *phwnd)
3685 {
3686     FileDialogImpl *This = impl_from_IOleWindow(iface);
3687     TRACE("%p (%p)\n", This, phwnd);
3688     *phwnd = This->dlg_hwnd;
3689     return S_OK;
3690 }
3691 
3692 static const IOleWindowVtbl vt_IOleWindow = {
3693     IOleWindow_fnQueryInterface,
3694     IOleWindow_fnAddRef,
3695     IOleWindow_fnRelease,
3696     IOleWindow_fnGetWindow,
3697     IOleWindow_fnContextSensitiveHelp
3698 };
3699 
3700 /**************************************************************************
3701  * IFileDialogCustomize implementation
3702  */
3703 static inline FileDialogImpl *impl_from_IFileDialogCustomize(IFileDialogCustomize *iface)
3704 {
3705     return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialogCustomize_iface);
3706 }
3707 
3708 static HRESULT WINAPI IFileDialogCustomize_fnQueryInterface(IFileDialogCustomize *iface,
3709                                                             REFIID riid, void **ppvObject)
3710 {
3711     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3712     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3713 }
3714 
3715 static ULONG WINAPI IFileDialogCustomize_fnAddRef(IFileDialogCustomize *iface)
3716 {
3717     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3718     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3719 }
3720 
3721 static ULONG WINAPI IFileDialogCustomize_fnRelease(IFileDialogCustomize *iface)
3722 {
3723     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3724     return IFileDialog2_Release(&This->IFileDialog2_iface);
3725 }
3726 
3727 static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCustomize *iface,
3728                                                                 DWORD dwIDCtl)
3729 {
3730     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3731     MENUINFO mi;
3732     TRACE("%p (%d)\n", This, dwIDCtl);
3733 
3734     if (This->hmenu_opendropdown || get_cctrl(This, dwIDCtl))
3735         return E_UNEXPECTED;
3736 
3737     This->hmenu_opendropdown = CreatePopupMenu();
3738 
3739     if (!This->hmenu_opendropdown)
3740         return E_OUTOFMEMORY;
3741 
3742     mi.cbSize = sizeof(mi);
3743     mi.fMask = MIM_STYLE;
3744     mi.dwStyle = MNS_NOTIFYBYPOS;
3745     SetMenuInfo(This->hmenu_opendropdown, &mi);
3746 
3747     This->cctrl_opendropdown.hwnd = NULL;
3748     This->cctrl_opendropdown.wrapper_hwnd = NULL;
3749     This->cctrl_opendropdown.id = dwIDCtl;
3750     This->cctrl_opendropdown.dlgid = 0;
3751     This->cctrl_opendropdown.type = IDLG_CCTRL_OPENDROPDOWN;
3752     This->cctrl_opendropdown.cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
3753     list_init(&This->cctrl_opendropdown.sub_cctrls);
3754     list_init(&This->cctrl_opendropdown.sub_items);
3755 
3756     return S_OK;
3757 }
3758 
3759 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
3760                                                      DWORD dwIDCtl,
3761                                                      LPCWSTR pszLabel)
3762 {
3763     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3764     customctrl *ctrl;
3765     TBBUTTON tbb;
3766     HRESULT hr;
3767     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3768 
3769     hr = cctrl_create_new(This, dwIDCtl, NULL, TOOLBARCLASSNAMEW,
3770                           TBSTYLE_FLAT | CCS_NODIVIDER, 0,
3771                           This->cctrl_def_height, &ctrl);
3772     if(SUCCEEDED(hr))
3773     {
3774         SendMessageW(ctrl->hwnd, TB_BUTTONSTRUCTSIZE, sizeof(tbb), 0);
3775         ctrl->type = IDLG_CCTRL_MENU;
3776 
3777         /* Add the actual button with a popup menu. */
3778         tbb.iBitmap = I_IMAGENONE;
3779         tbb.dwData = (DWORD_PTR)CreatePopupMenu();
3780         tbb.iString = (DWORD_PTR)pszLabel;
3781         tbb.fsState = TBSTATE_ENABLED;
3782         tbb.fsStyle = BTNS_WHOLEDROPDOWN;
3783         tbb.idCommand = 1;
3784 
3785         SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
3786     }
3787 
3788     return hr;
3789 }
3790 
3791 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
3792                                                            DWORD dwIDCtl,
3793                                                            LPCWSTR pszLabel)
3794 {
3795     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3796     customctrl *ctrl;
3797     HRESULT hr;
3798     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3799 
3800     hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
3801                           This->cctrl_def_height, &ctrl);
3802     if(SUCCEEDED(hr))
3803         ctrl->type = IDLG_CCTRL_PUSHBUTTON;
3804 
3805     return hr;
3806 }
3807 
3808 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
3809                                                          DWORD dwIDCtl)
3810 {
3811     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3812     customctrl *ctrl;
3813     HRESULT hr;
3814     TRACE("%p (%d)\n", This, dwIDCtl);
3815 
3816     hr =  cctrl_create_new(This, dwIDCtl, NULL, WC_COMBOBOXW, CBS_DROPDOWNLIST, 0,
3817                            This->cctrl_def_height, &ctrl);
3818     if(SUCCEEDED(hr))
3819         ctrl->type = IDLG_CCTRL_COMBOBOX;
3820 
3821     return hr;
3822 }
3823 
3824 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
3825                                                                 DWORD dwIDCtl)
3826 {
3827     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3828     customctrl *ctrl;
3829     HRESULT hr;
3830     TRACE("%p (%d)\n", This, dwIDCtl);
3831 
3832     hr =  cctrl_create_new(This, dwIDCtl, NULL, radiobuttonlistW, 0, 0, 0, &ctrl);
3833     if(SUCCEEDED(hr))
3834     {
3835         ctrl->type = IDLG_CCTRL_RADIOBUTTONLIST;
3836         SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)This);
3837     }
3838 
3839     return hr;
3840 }
3841 
3842 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
3843                                                             DWORD dwIDCtl,
3844                                                             LPCWSTR pszLabel,
3845                                                             BOOL bChecked)
3846 {
3847     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3848     customctrl *ctrl;
3849     HRESULT hr;
3850     TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
3851 
3852     hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX|BS_MULTILINE, 0,
3853                           This->cctrl_def_height, &ctrl);
3854     if(SUCCEEDED(hr))
3855     {
3856         ctrl->type = IDLG_CCTRL_CHECKBUTTON;
3857         SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
3858     }
3859 
3860     return hr;
3861 }
3862 
3863 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
3864                                                         DWORD dwIDCtl,
3865                                                         LPCWSTR pszText)
3866 {
3867     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3868     customctrl *ctrl;
3869     HRESULT hr;
3870     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3871 
3872     hr = cctrl_create_new(This, dwIDCtl, pszText, WC_EDITW, ES_AUTOHSCROLL, WS_EX_CLIENTEDGE,
3873                           This->cctrl_def_height, &ctrl);
3874     if(SUCCEEDED(hr))
3875         ctrl->type = IDLG_CCTRL_EDITBOX;
3876 
3877     return hr;
3878 }
3879 
3880 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
3881                                                           DWORD dwIDCtl)
3882 {
3883     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3884     customctrl *ctrl;
3885     HRESULT hr;
3886     TRACE("%p (%d)\n", This, dwIDCtl);
3887 
3888     hr = cctrl_create_new(This, dwIDCtl, NULL, WC_STATICW, SS_ETCHEDHORZ, 0,
3889                           GetSystemMetrics(SM_CYEDGE), &ctrl);
3890     if(SUCCEEDED(hr))
3891         ctrl->type = IDLG_CCTRL_SEPARATOR;
3892 
3893     return hr;
3894 }
3895 
3896 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
3897                                                      DWORD dwIDCtl,
3898                                                      LPCWSTR pszText)
3899 {
3900     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3901     customctrl *ctrl;
3902     HRESULT hr;
3903     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3904 
3905     hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
3906                           This->cctrl_def_height, &ctrl);
3907     if(SUCCEEDED(hr))
3908         ctrl->type = IDLG_CCTRL_TEXT;
3909 
3910     return hr;
3911 }
3912 
3913 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
3914                                                              DWORD dwIDCtl,
3915                                                              LPCWSTR pszLabel)
3916 {
3917     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3918     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3919     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3920 
3921     if(!ctrl) return E_INVALIDARG;
3922 
3923     switch(ctrl->type)
3924     {
3925     case IDLG_CCTRL_MENU:
3926     case IDLG_CCTRL_PUSHBUTTON:
3927     case IDLG_CCTRL_CHECKBUTTON:
3928     case IDLG_CCTRL_TEXT:
3929     case IDLG_CCTRL_VISUALGROUP:
3930         SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
3931         break;
3932     case IDLG_CCTRL_OPENDROPDOWN:
3933         return E_NOTIMPL;
3934     default:
3935         break;
3936     }
3937 
3938     return S_OK;
3939 }
3940 
3941 static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomize *iface,
3942                                                              DWORD dwIDCtl,
3943                                                              CDCONTROLSTATEF *pdwState)
3944 {
3945     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3946     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3947     TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
3948 
3949     if(!ctrl || ctrl->type == IDLG_CCTRL_OPENDROPDOWN) return E_NOTIMPL;
3950 
3951     *pdwState = ctrl->cdcstate;
3952     return S_OK;
3953 }
3954 
3955 static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomize *iface,
3956                                                              DWORD dwIDCtl,
3957                                                              CDCONTROLSTATEF dwState)
3958 {
3959     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3960     customctrl *ctrl = get_cctrl(This,dwIDCtl);
3961     TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
3962 
3963     if(ctrl && ctrl->hwnd)
3964     {
3965         LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
3966 
3967         if(dwState & CDCS_ENABLED)
3968             wndstyle &= ~(WS_DISABLED);
3969         else
3970             wndstyle |= WS_DISABLED;
3971 
3972         if(dwState & CDCS_VISIBLE)
3973             wndstyle |= WS_VISIBLE;
3974         else
3975             wndstyle &= ~(WS_VISIBLE);
3976 
3977         SetWindowLongW(ctrl->hwnd, GWL_STYLE, wndstyle);
3978 
3979         /* We save the state separately since at least one application
3980          * relies on being able to hide a control. */
3981         ctrl->cdcstate = dwState;
3982     }
3983 
3984     return S_OK;
3985 }
3986 
3987 static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize *iface,
3988                                                             DWORD dwIDCtl,
3989                                                             WCHAR **ppszText)
3990 {
3991     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3992     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3993     WCHAR len, *text;
3994     TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
3995 
3996     if(!ctrl || !ctrl->hwnd || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
3997         return E_FAIL;
3998 
3999     text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
4000     if(!text) return E_FAIL;
4001 
4002     SendMessageW(ctrl->hwnd, WM_GETTEXT, len+1, (LPARAM)text);
4003     *ppszText = text;
4004     return S_OK;
4005 }
4006 
4007 static HRESULT WINAPI IFileDialogCustomize_fnSetEditBoxText(IFileDialogCustomize *iface,
4008                                                             DWORD dwIDCtl,
4009                                                             LPCWSTR pszText)
4010 {
4011     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4012     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4013     TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
4014 
4015     if(!ctrl || ctrl->type != IDLG_CCTRL_EDITBOX)
4016         return E_FAIL;
4017 
4018     SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
4019     return S_OK;
4020 }
4021 
4022 static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCustomize *iface,
4023                                                                  DWORD dwIDCtl,
4024                                                                  BOOL *pbChecked)
4025 {
4026     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4027     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4028     TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
4029 
4030     if(ctrl && ctrl->hwnd)
4031         *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
4032 
4033     return S_OK;
4034 }
4035 
4036 static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCustomize *iface,
4037                                                                  DWORD dwIDCtl,
4038                                                                  BOOL bChecked)
4039 {
4040     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4041     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4042     TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
4043 
4044     if(ctrl && ctrl->hwnd)
4045         SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
4046 
4047     return S_OK;
4048 }
4049 
4050 static UINT get_combobox_index_from_id(HWND cb_hwnd, DWORD dwIDItem)
4051 {
4052     UINT count = SendMessageW(cb_hwnd, CB_GETCOUNT, 0, 0);
4053     UINT i;
4054     if(!count || (count == CB_ERR))
4055         return -1;
4056 
4057     for(i = 0; i < count; i++)
4058         if(SendMessageW(cb_hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
4059             return i;
4060 
4061     TRACE("Item with id %d not found in combobox %p (item count: %d)\n", dwIDItem, cb_hwnd, count);
4062     return -1;
4063 }
4064 
4065 static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize *iface,
4066                                                             DWORD dwIDCtl,
4067                                                             DWORD dwIDItem,
4068                                                             LPCWSTR pszLabel)
4069 {
4070     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4071     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4072     HRESULT hr;
4073     TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4074 
4075     if(!ctrl) return E_FAIL;
4076 
4077     switch(ctrl->type)
4078     {
4079     case IDLG_CCTRL_COMBOBOX:
4080     {
4081         UINT index;
4082         cctrl_item* item;
4083 
4084         hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4085 
4086         if (FAILED(hr)) return hr;
4087 
4088         index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
4089         SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
4090 
4091         return S_OK;
4092     }
4093     case IDLG_CCTRL_MENU:
4094     case IDLG_CCTRL_OPENDROPDOWN:
4095     {
4096         cctrl_item* item;
4097         HMENU hmenu;
4098 
4099         hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4100 
4101         if (FAILED(hr)) return hr;
4102 
4103         if (ctrl->type == IDLG_CCTRL_MENU)
4104         {
4105             TBBUTTON tbb;
4106             SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4107             hmenu = (HMENU)tbb.dwData;
4108         }
4109         else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4110             hmenu = This->hmenu_opendropdown;
4111 
4112         AppendMenuW(hmenu, MF_STRING, dwIDItem, pszLabel);
4113         return S_OK;
4114     }
4115     case IDLG_CCTRL_RADIOBUTTONLIST:
4116     {
4117         cctrl_item* item;
4118 
4119         hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4120 
4121         if (SUCCEEDED(hr))
4122         {
4123             item->hwnd = CreateWindowExW(0, WC_BUTTONW, pszLabel,
4124                 WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|BS_RADIOBUTTON|BS_MULTILINE,
4125                 0, 0, 0, 0, ctrl->hwnd, ULongToHandle(dwIDItem), COMDLG32_hInstance, 0);
4126 
4127             if (!item->hwnd)
4128             {
4129                 ERR("Failed to create radio button\n");
4130                 list_remove(&item->entry);
4131                 item_free(item);
4132                 return E_FAIL;
4133             }
4134         }
4135 
4136         return hr;
4137     }
4138     default:
4139         break;
4140     }
4141 
4142     return E_NOINTERFACE; /* win7 */
4143 }
4144 
4145 static HRESULT WINAPI IFileDialogCustomize_fnRemoveControlItem(IFileDialogCustomize *iface,
4146                                                                DWORD dwIDCtl,
4147                                                                DWORD dwIDItem)
4148 {
4149     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4150     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4151     TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4152 
4153     if(!ctrl) return E_FAIL;
4154 
4155     switch(ctrl->type)
4156     {
4157     case IDLG_CCTRL_COMBOBOX:
4158     {
4159         cctrl_item* item;
4160         DWORD position;
4161 
4162         item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4163 
4164         if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
4165         {
4166             if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0) == CB_ERR)
4167                 return E_FAIL;
4168         }
4169 
4170         list_remove(&item->entry);
4171         item_free(item);
4172 
4173         return S_OK;
4174     }
4175     case IDLG_CCTRL_MENU:
4176     case IDLG_CCTRL_OPENDROPDOWN:
4177     {
4178         HMENU hmenu;
4179         cctrl_item* item;
4180 
4181         item = get_item(ctrl, dwIDItem, 0, NULL);
4182 
4183         if (!item)
4184             return E_UNEXPECTED;
4185 
4186         if (item->cdcstate & CDCS_VISIBLE)
4187         {
4188             if (ctrl->type == IDLG_CCTRL_MENU)
4189             {
4190                 TBBUTTON tbb;
4191                 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4192                 hmenu = (HMENU)tbb.dwData;
4193             }
4194             else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4195                 hmenu = This->hmenu_opendropdown;
4196 
4197             if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
4198                 return E_UNEXPECTED;
4199         }
4200 
4201         list_remove(&item->entry);
4202         item_free(item);
4203 
4204         return S_OK;
4205     }
4206     case IDLG_CCTRL_RADIOBUTTONLIST:
4207     {
4208         cctrl_item* item;
4209 
4210         item = get_item(ctrl, dwIDItem, 0, NULL);
4211 
4212         if (!item)
4213             return E_UNEXPECTED;
4214 
4215         list_remove(&item->entry);
4216         item_free(item);
4217 
4218         return S_OK;
4219     }
4220     default:
4221         break;
4222     }
4223 
4224     return E_FAIL;
4225 }
4226 
4227 static HRESULT WINAPI IFileDialogCustomize_fnRemoveAllControlItems(IFileDialogCustomize *iface,
4228                                                                    DWORD dwIDCtl)
4229 {
4230     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4231     TRACE("%p (%d)\n", This, dwIDCtl);
4232 
4233     /* Not implemented by native */
4234     return E_NOTIMPL;
4235 }
4236 
4237 static HRESULT WINAPI IFileDialogCustomize_fnGetControlItemState(IFileDialogCustomize *iface,
4238                                                                  DWORD dwIDCtl,
4239                                                                  DWORD dwIDItem,
4240                                                                  CDCONTROLSTATEF *pdwState)
4241 {
4242     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4243     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4244     TRACE("%p (%d, %d, %p)\n", This, dwIDCtl, dwIDItem, pdwState);
4245 
4246     if(!ctrl) return E_FAIL;
4247 
4248     switch(ctrl->type)
4249     {
4250     case IDLG_CCTRL_COMBOBOX:
4251     case IDLG_CCTRL_MENU:
4252     case IDLG_CCTRL_OPENDROPDOWN:
4253     case IDLG_CCTRL_RADIOBUTTONLIST:
4254     {
4255         cctrl_item* item;
4256 
4257         item = get_item(ctrl, dwIDItem, 0, NULL);
4258 
4259         if (!item)
4260             return E_UNEXPECTED;
4261 
4262         *pdwState = item->cdcstate;
4263 
4264         return S_OK;
4265     }
4266     default:
4267         break;
4268     }
4269 
4270     return E_FAIL;
4271 }
4272 
4273 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCustomize *iface,
4274                                                                  DWORD dwIDCtl,
4275                                                                  DWORD dwIDItem,
4276                                                                  CDCONTROLSTATEF dwState)
4277 {
4278     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4279     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4280     TRACE("%p (%d, %d, %x)\n", This, dwIDCtl, dwIDItem, dwState);
4281 
4282     if(!ctrl) return E_FAIL;
4283 
4284     switch(ctrl->type)
4285     {
4286     case IDLG_CCTRL_COMBOBOX:
4287     {
4288         cctrl_item* item;
4289         BOOL visible, was_visible;
4290         DWORD position;
4291 
4292         item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4293 
4294         if (!item)
4295             return E_UNEXPECTED;
4296 
4297         visible = ((dwState & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4298         was_visible = ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4299 
4300         if (visible && !was_visible)
4301         {
4302             SendMessageW(ctrl->hwnd, CB_INSERTSTRING, position, (LPARAM)item->label);
4303             SendMessageW(ctrl->hwnd, CB_SETITEMDATA, position, dwIDItem);
4304         }
4305         else if (!visible && was_visible)
4306         {
4307             SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0);
4308         }
4309 
4310         item->cdcstate = dwState;
4311 
4312         return S_OK;
4313     }
4314     case IDLG_CCTRL_MENU:
4315     case IDLG_CCTRL_OPENDROPDOWN:
4316     {
4317         HMENU hmenu;
4318         cctrl_item* item;
4319         CDCONTROLSTATEF prev_state;
4320         DWORD position;
4321 
4322         item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, &position);
4323 
4324         if (!item)
4325             return E_UNEXPECTED;
4326 
4327         prev_state = item->cdcstate;
4328 
4329         if (ctrl->type == IDLG_CCTRL_MENU)
4330         {
4331             TBBUTTON tbb;
4332             SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4333             hmenu = (HMENU)tbb.dwData;
4334         }
4335         else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4336             hmenu = This->hmenu_opendropdown;
4337 
4338         if (dwState & CDCS_VISIBLE)
4339         {
4340             if (prev_state & CDCS_VISIBLE)
4341             {
4342                 /* change state */
4343                 EnableMenuItem(hmenu, dwIDItem,
4344                     MF_BYCOMMAND|((dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED));
4345             }
4346             else
4347             {
4348                 /* show item */
4349                 MENUITEMINFOW mii;
4350 
4351                 mii.cbSize = sizeof(mii);
4352                 mii.fMask = MIIM_ID|MIIM_STATE|MIIM_STRING;
4353                 mii.fState = (dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED;
4354                 mii.wID = dwIDItem;
4355                 mii.dwTypeData = item->label;
4356 
4357                 InsertMenuItemW(hmenu, position, TRUE, &mii);
4358             }
4359         }
4360         else if (prev_state & CDCS_VISIBLE)
4361         {
4362             /* hide item */
4363             DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND);
4364         }
4365 
4366         item->cdcstate = dwState;
4367 
4368         if (ctrl->type == IDLG_CCTRL_OPENDROPDOWN)
4369         {
4370             update_control_text(This);
4371             update_layout(This);
4372         }
4373 
4374         return S_OK;
4375     }
4376     case IDLG_CCTRL_RADIOBUTTONLIST:
4377     {
4378         cctrl_item* item;
4379 
4380         item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, NULL);
4381 
4382         if (!item)
4383             return E_UNEXPECTED;
4384 
4385         /* Oddly, native allows setting this but doesn't seem to do anything with it. */
4386         item->cdcstate = dwState;
4387 
4388         return S_OK;
4389     }
4390     default:
4391         break;
4392     }
4393 
4394     return E_FAIL;
4395 }
4396 
4397 static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogCustomize *iface,
4398                                                                     DWORD dwIDCtl,
4399                                                                     DWORD *pdwIDItem)
4400 {
4401     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4402     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4403     TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwIDItem);
4404 
4405     if(!ctrl) return E_FAIL;
4406 
4407     switch(ctrl->type)
4408     {
4409     case IDLG_CCTRL_COMBOBOX:
4410     {
4411         UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
4412         if(index == CB_ERR)
4413             return E_FAIL;
4414 
4415         *pdwIDItem = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
4416         return S_OK;
4417     }
4418     case IDLG_CCTRL_OPENDROPDOWN:
4419         if (This->opendropdown_has_selection)
4420         {
4421             *pdwIDItem = This->opendropdown_selection;
4422             return S_OK;
4423         }
4424         else
4425         {
4426             /* Return first enabled item. */
4427             cctrl_item* item = get_first_item(ctrl);
4428 
4429             if (item)
4430             {
4431                 *pdwIDItem = item->id;
4432                 return S_OK;
4433             }
4434 
4435             WARN("no enabled items in open dropdown\n");
4436             return E_FAIL;
4437         }
4438     case IDLG_CCTRL_RADIOBUTTONLIST:
4439     {
4440         cctrl_item* item;
4441 
4442         LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
4443         {
4444             if (SendMessageW(item->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED)
4445             {
4446                 *pdwIDItem = item->id;
4447                 return S_OK;
4448             }
4449         }
4450 
4451         WARN("no checked items in radio button list\n");
4452         return E_FAIL;
4453     }
4454     default:
4455         FIXME("Unsupported control type %d\n", ctrl->type);
4456     }
4457 
4458     return E_NOTIMPL;
4459 }
4460 
4461 static HRESULT WINAPI IFileDialogCustomize_fnSetSelectedControlItem(IFileDialogCustomize *iface,
4462                                                                     DWORD dwIDCtl,
4463                                                                     DWORD dwIDItem)
4464 {
4465     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4466     customctrl *ctrl = get_cctrl(This, dwIDCtl);
4467     TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4468 
4469     if(!ctrl) return E_INVALIDARG;
4470 
4471     switch(ctrl->type)
4472     {
4473     case IDLG_CCTRL_COMBOBOX:
4474     {
4475         UINT index = get_combobox_index_from_id(ctrl->hwnd, dwIDItem);
4476 
4477         if(index == -1)
4478             return E_INVALIDARG;
4479 
4480         if(SendMessageW(ctrl->hwnd, CB_SETCURSEL, index, 0) == CB_ERR)
4481             return E_FAIL;
4482 
4483         return S_OK;
4484     }
4485     case IDLG_CCTRL_RADIOBUTTONLIST:
4486     {
4487         cctrl_item* item;
4488 
4489         item = get_item(ctrl, dwIDItem, 0, NULL);
4490 
4491         if (item)
4492         {
4493             radiobuttonlist_set_selected_item(This, ctrl, item);
4494             return S_OK;
4495         }
4496 
4497         return E_INVALIDARG;
4498     }
4499     default:
4500         FIXME("Unsupported control type %d\n", ctrl->type);
4501     }
4502 
4503     return E_INVALIDARG;
4504 }
4505 
4506 static HRESULT WINAPI IFileDialogCustomize_fnStartVisualGroup(IFileDialogCustomize *iface,
4507                                                               DWORD dwIDCtl,
4508                                                               LPCWSTR pszLabel)
4509 {
4510     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4511     customctrl *vg;
4512     HRESULT hr;
4513     TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
4514 
4515     if(This->cctrl_active_vg)
4516         return E_UNEXPECTED;
4517 
4518     hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_STATICW, 0, 0,
4519                           This->cctrl_def_height, &vg);
4520     if(SUCCEEDED(hr))
4521     {
4522         vg->type = IDLG_CCTRL_VISUALGROUP;
4523         This->cctrl_active_vg = vg;
4524     }
4525 
4526     return hr;
4527 }
4528 
4529 static HRESULT WINAPI IFileDialogCustomize_fnEndVisualGroup(IFileDialogCustomize *iface)
4530 {
4531     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4532     TRACE("%p\n", This);
4533 
4534     This->cctrl_active_vg = NULL;
4535 
4536     return S_OK;
4537 }
4538 
4539 static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize *iface,
4540                                                            DWORD dwIDCtl)
4541 {
4542     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4543     FIXME("stub - %p (%d)\n", This, dwIDCtl);
4544     return S_OK;
4545 }
4546 
4547 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCustomize *iface,
4548                                                                 DWORD dwIDCtl,
4549                                                                 DWORD dwIDItem,
4550                                                                 LPCWSTR pszLabel)
4551 {
4552     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4553     FIXME("stub - %p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4554     return E_NOTIMPL;
4555 }
4556 
4557 static const IFileDialogCustomizeVtbl vt_IFileDialogCustomize = {
4558     IFileDialogCustomize_fnQueryInterface,
4559     IFileDialogCustomize_fnAddRef,
4560     IFileDialogCustomize_fnRelease,
4561     IFileDialogCustomize_fnEnableOpenDropDown,
4562     IFileDialogCustomize_fnAddMenu,
4563     IFileDialogCustomize_fnAddPushButton,
4564     IFileDialogCustomize_fnAddComboBox,
4565     IFileDialogCustomize_fnAddRadioButtonList,
4566     IFileDialogCustomize_fnAddCheckButton,
4567     IFileDialogCustomize_fnAddEditBox,
4568     IFileDialogCustomize_fnAddSeparator,
4569     IFileDialogCustomize_fnAddText,
4570     IFileDialogCustomize_fnSetControlLabel,
4571     IFileDialogCustomize_fnGetControlState,
4572     IFileDialogCustomize_fnSetControlState,
4573     IFileDialogCustomize_fnGetEditBoxText,
4574     IFileDialogCustomize_fnSetEditBoxText,
4575     IFileDialogCustomize_fnGetCheckButtonState,
4576     IFileDialogCustomize_fnSetCheckButtonState,
4577     IFileDialogCustomize_fnAddControlItem,
4578     IFileDialogCustomize_fnRemoveControlItem,
4579     IFileDialogCustomize_fnRemoveAllControlItems,
4580     IFileDialogCustomize_fnGetControlItemState,
4581     IFileDialogCustomize_fnSetControlItemState,
4582     IFileDialogCustomize_fnGetSelectedControlItem,
4583     IFileDialogCustomize_fnSetSelectedControlItem,
4584     IFileDialogCustomize_fnStartVisualGroup,
4585     IFileDialogCustomize_fnEndVisualGroup,
4586     IFileDialogCustomize_fnMakeProminent,
4587     IFileDialogCustomize_fnSetControlItemText
4588 };
4589 
4590 static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv, enum ITEMDLG_TYPE type)
4591 {
4592     FileDialogImpl *fdimpl;
4593     HRESULT hr;
4594     IShellFolder *psf;
4595     TRACE("%p, %s, %p\n", pUnkOuter, debugstr_guid(riid), ppv);
4596 
4597     if(!ppv)
4598         return E_POINTER;
4599     if(pUnkOuter)
4600         return CLASS_E_NOAGGREGATION;
4601 
4602     fdimpl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FileDialogImpl));
4603     if(!fdimpl)
4604         return E_OUTOFMEMORY;
4605 
4606     fdimpl->ref = 1;
4607     fdimpl->IFileDialog2_iface.lpVtbl = &vt_IFileDialog2;
4608     fdimpl->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
4609     fdimpl->IServiceProvider_iface.lpVtbl = &vt_IServiceProvider;
4610     fdimpl->ICommDlgBrowser3_iface.lpVtbl = &vt_ICommDlgBrowser3;
4611     fdimpl->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
4612     fdimpl->IFileDialogCustomize_iface.lpVtbl = &vt_IFileDialogCustomize;
4613 
4614     if(type == ITEMDLG_TYPE_OPEN)
4615     {
4616         fdimpl->dlg_type = ITEMDLG_TYPE_OPEN;
4617         fdimpl->u.IFileOpenDialog_iface.lpVtbl = &vt_IFileOpenDialog;
4618         fdimpl->options = FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
4619         fdimpl->custom_title = fdimpl->custom_okbutton = NULL;
4620     }
4621     else
4622     {
4623         WCHAR buf[16];
4624         fdimpl->dlg_type = ITEMDLG_TYPE_SAVE;
4625         fdimpl->u.IFileSaveDialog_iface.lpVtbl = &vt_IFileSaveDialog;
4626         fdimpl->options = FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
4627 
4628         LoadStringW(COMDLG32_hInstance, IDS_SAVE, buf, sizeof(buf)/sizeof(WCHAR));
4629         fdimpl->custom_title = StrDupW(buf);
4630         fdimpl->custom_okbutton = StrDupW(buf);
4631     }
4632 
4633     list_init(&fdimpl->events_clients);
4634 
4635     /* FIXME: The default folder setting should be restored for the
4636      * application if it was previously set. */
4637     SHGetDesktopFolder(&psf);
4638     SHGetItemFromObject((IUnknown*)psf, &IID_IShellItem, (void**)&fdimpl->psi_defaultfolder);
4639     IShellFolder_Release(psf);
4640 
4641     hr = init_custom_controls(fdimpl);
4642     if(FAILED(hr))
4643     {
4644         ERR("Failed to initialize custom controls (0x%08x).\n", hr);
4645         IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4646         return E_FAIL;
4647     }
4648 
4649     hr = IFileDialog2_QueryInterface(&fdimpl->IFileDialog2_iface, riid, ppv);
4650     IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4651     return hr;
4652 }
4653 
4654 HRESULT FileOpenDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4655 {
4656     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_OPEN);
4657 }
4658 
4659 HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4660 {
4661     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);
4662 }
4663 
4664 #endif /* Win 7 */
4665