comdlg32: Mark one vtable as static.
[wine] / dlls / comdlg32 / itemdlg.c
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 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "wingdi.h"
31 #include "winreg.h"
32 #include "shlwapi.h"
33
34 #include "commdlg.h"
35 #include "cdlg.h"
36 #include "filedlgbrowser.h"
37
38 #include "wine/debug.h"
39 #include "wine/list.h"
40
41 #define IDC_NAV_TOOLBAR      200
42 #define IDC_NAVBACK          201
43 #define IDC_NAVFORWARD       202
44
45 #include <initguid.h>
46 /* This seems to be another version of IID_IFileDialogCustomize. If
47  * there is any difference I have yet to find it. */
48 DEFINE_GUID(IID_IFileDialogCustomizeAlt, 0x8016B7B3, 0x3D49, 0x4504, 0xA0,0xAA, 0x2A,0x37,0x49,0x4E,0x60,0x6F);
49
50 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
51
52 static const WCHAR notifysink_childW[] = {'n','f','s','_','c','h','i','l','d',0};
53 static const WCHAR floatnotifysinkW[] = {'F','l','o','a','t','N','o','t','i','f','y','S','i','n','k',0};
54
55 enum ITEMDLG_TYPE {
56     ITEMDLG_TYPE_OPEN,
57     ITEMDLG_TYPE_SAVE
58 };
59
60 enum ITEMDLG_CCTRL_TYPE {
61     IDLG_CCTRL_MENU,
62     IDLG_CCTRL_PUSHBUTTON,
63     IDLG_CCTRL_COMBOBOX,
64     IDLG_CCTRL_RADIOBUTTONLIST,
65     IDLG_CCTRL_CHECKBUTTON,
66     IDLG_CCTRL_EDITBOX,
67     IDLG_CCTRL_SEPARATOR,
68     IDLG_CCTRL_TEXT
69 };
70
71 typedef struct {
72     HWND hwnd, wrapper_hwnd;
73     UINT id, dlgid;
74     enum ITEMDLG_CCTRL_TYPE type;
75     CDCONTROLSTATEF cdcstate;
76     struct list entry;
77 } customctrl;
78
79 typedef struct {
80     struct list entry;
81     IFileDialogEvents *pfde;
82     DWORD cookie;
83 } events_client;
84
85 typedef struct FileDialogImpl {
86     IFileDialog2 IFileDialog2_iface;
87     union {
88         IFileOpenDialog IFileOpenDialog_iface;
89         IFileSaveDialog IFileSaveDialog_iface;
90     } u;
91     enum ITEMDLG_TYPE dlg_type;
92     IExplorerBrowserEvents IExplorerBrowserEvents_iface;
93     IServiceProvider       IServiceProvider_iface;
94     ICommDlgBrowser3       ICommDlgBrowser3_iface;
95     IOleWindow             IOleWindow_iface;
96     IFileDialogCustomize   IFileDialogCustomize_iface;
97     LONG ref;
98
99     FILEOPENDIALOGOPTIONS options;
100     COMDLG_FILTERSPEC *filterspecs;
101     UINT filterspec_count;
102     UINT filetypeindex;
103
104     struct list events_clients;
105     DWORD events_next_cookie;
106
107     IShellItemArray *psia_selection;
108     IShellItemArray *psia_results;
109     IShellItem *psi_defaultfolder;
110     IShellItem *psi_setfolder;
111     IShellItem *psi_folder;
112
113     HWND dlg_hwnd;
114     IExplorerBrowser *peb;
115     DWORD ebevents_cookie;
116
117     LPWSTR set_filename;
118     LPWSTR default_ext;
119     LPWSTR custom_title;
120     LPWSTR custom_okbutton;
121     LPWSTR custom_cancelbutton;
122     LPWSTR custom_filenamelabel;
123
124     UINT cctrl_width, cctrl_def_height, cctrls_cols;
125     HWND cctrls_hwnd;
126     struct list cctrls;
127     UINT_PTR cctrl_next_dlgid;
128 } FileDialogImpl;
129
130 /**************************************************************************
131  * Event wrappers.
132  */
133 static HRESULT events_OnFileOk(FileDialogImpl *This)
134 {
135     events_client *cursor;
136     HRESULT hr = S_OK;
137     TRACE("%p\n", This);
138
139     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
140     {
141         TRACE("Notifying %p\n", cursor);
142         hr = IFileDialogEvents_OnFileOk(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
143         if(FAILED(hr) && hr != E_NOTIMPL)
144             break;
145     }
146
147     if(hr == E_NOTIMPL)
148         hr = S_OK;
149
150     return hr;
151 }
152
153 static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder)
154 {
155     events_client *cursor;
156     HRESULT hr = S_OK;
157     TRACE("%p (%p)\n", This, folder);
158
159     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
160     {
161         TRACE("Notifying %p\n", cursor);
162         hr = IFileDialogEvents_OnFolderChanging(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, folder);
163         if(FAILED(hr) && hr != E_NOTIMPL)
164             break;
165     }
166
167     if(hr == E_NOTIMPL)
168         hr = S_OK;
169
170     return hr;
171 }
172
173 static void events_OnFolderChange(FileDialogImpl *This)
174 {
175     events_client *cursor;
176     TRACE("%p\n", This);
177
178     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
179     {
180         TRACE("Notifying %p\n", cursor);
181         IFileDialogEvents_OnFolderChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
182     }
183 }
184
185 static void events_OnSelectionChange(FileDialogImpl *This)
186 {
187     events_client *cursor;
188     TRACE("%p\n", This);
189
190     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
191     {
192         TRACE("Notifying %p\n", cursor);
193         IFileDialogEvents_OnSelectionChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
194     }
195 }
196
197 static inline HRESULT get_cctrl_event(IFileDialogEvents *pfde, IFileDialogControlEvents **pfdce)
198 {
199     return IFileDialogEvents_QueryInterface(pfde, &IID_IFileDialogControlEvents, (void**)pfdce);
200 }
201
202 static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id)
203 {
204     events_client *cursor;
205     TRACE("%p\n", This);
206
207     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
208     {
209         IFileDialogControlEvents *pfdce;
210         if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
211         {
212             TRACE("Notifying %p\n", cursor);
213             IFileDialogControlEvents_OnButtonClicked(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
214             IFileDialogControlEvents_Release(pfdce);
215         }
216     }
217
218     return S_OK;
219 }
220
221 static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DWORD item_id)
222 {
223     events_client *cursor;
224     TRACE("%p\n", This);
225
226     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
227     {
228         IFileDialogControlEvents *pfdce;
229         if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
230         {
231             TRACE("Notifying %p\n", cursor);
232             IFileDialogControlEvents_OnItemSelected(pfdce, &This->IFileDialogCustomize_iface, ctl_id, item_id);
233             IFileDialogControlEvents_Release(pfdce);
234         }
235     }
236
237     return S_OK;
238 }
239
240 static HRESULT cctrl_event_OnCheckButtonToggled(FileDialogImpl *This, DWORD ctl_id, BOOL checked)
241 {
242     events_client *cursor;
243     TRACE("%p\n", This);
244
245     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
246     {
247         IFileDialogControlEvents *pfdce;
248         if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
249         {
250             TRACE("Notifying %p\n", cursor);
251             IFileDialogControlEvents_OnCheckButtonToggled(pfdce, &This->IFileDialogCustomize_iface, ctl_id, checked);
252             IFileDialogControlEvents_Release(pfdce);
253         }
254     }
255
256     return S_OK;
257 }
258
259 static HRESULT cctrl_event_OnControlActivating(FileDialogImpl *This,
260                                                   DWORD ctl_id)
261 {
262     events_client *cursor;
263     TRACE("%p\n", This);
264
265     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
266     {
267         IFileDialogControlEvents *pfdce;
268         if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
269         {
270             TRACE("Notifying %p\n", cursor);
271             IFileDialogControlEvents_OnControlActivating(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
272             IFileDialogControlEvents_Release(pfdce);
273         }
274     }
275
276     return S_OK;
277 }
278
279 /**************************************************************************
280  * Helper functions.
281  */
282 static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
283 {
284     HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
285     UINT len;
286
287     if(!hwnd_edit)
288     {
289         if(This->set_filename)
290         {
291             len = lstrlenW(This->set_filename);
292             *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
293             lstrcpyW(*str, This->set_filename);
294             return len;
295         }
296         return FALSE;
297     }
298
299     len = SendMessageW(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);
300     *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
301     if(!*str)
302         return FALSE;
303
304     SendMessageW(hwnd_edit, WM_GETTEXT, len+1, (LPARAM)*str);
305     return len;
306 }
307
308 static BOOL set_file_name(FileDialogImpl *This, LPCWSTR str)
309 {
310     HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
311
312     if(This->set_filename)
313         LocalFree(This->set_filename);
314
315     This->set_filename = StrDupW(str);
316
317     return SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str);
318 }
319
320 static void fill_filename_from_selection(FileDialogImpl *This)
321 {
322     IShellItem *psi;
323     LPWSTR *names;
324     HRESULT hr;
325     UINT item_count, valid_count;
326     UINT len_total, i;
327
328     if(!This->psia_selection)
329         return;
330
331     hr = IShellItemArray_GetCount(This->psia_selection, &item_count);
332     if(FAILED(hr) || !item_count)
333         return;
334
335     names = HeapAlloc(GetProcessHeap(), 0, item_count*sizeof(LPWSTR));
336
337     /* Get names of the selected items */
338     valid_count = 0; len_total = 0;
339     for(i = 0; i < item_count; i++)
340     {
341         hr = IShellItemArray_GetItemAt(This->psia_selection, i, &psi);
342         if(SUCCEEDED(hr))
343         {
344             UINT attr;
345
346             hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &attr);
347             if(SUCCEEDED(hr) && (attr & SFGAO_FOLDER))
348                 continue; /* FIXME: FOS_PICKFOLDERS */
349
350             hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &names[valid_count]);
351             if(SUCCEEDED(hr))
352             {
353                 len_total += lstrlenW(names[valid_count]) + 3;
354                 valid_count++;
355             }
356             IShellItem_Release(psi);
357         }
358     }
359
360     if(valid_count == 1)
361     {
362         set_file_name(This, names[0]);
363         CoTaskMemFree(names[0]);
364     }
365     else if(valid_count > 1)
366     {
367         LPWSTR string = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len_total);
368         LPWSTR cur_point = string;
369
370         for(i = 0; i < valid_count; i++)
371         {
372             LPWSTR file = names[i];
373             *cur_point++ = '\"';
374             lstrcpyW(cur_point, file);
375             cur_point += lstrlenW(file);
376             *cur_point++ = '\"';
377             *cur_point++ = ' ';
378             CoTaskMemFree(file);
379         }
380         *(cur_point-1) = '\0';
381
382         set_file_name(This, string);
383         HeapFree(GetProcessHeap(), 0, string);
384     }
385
386     HeapFree(GetProcessHeap(), 0, names);
387     return;
388 }
389
390 static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec)
391 {
392     WCHAR *endpos, *ext;
393
394     lstrcpyW(buf, spec);
395     if( (endpos = StrChrW(buf, ';')) )
396         *endpos = '\0';
397
398     ext = PathFindExtensionW(buf);
399     if(StrChrW(ext, '*'))
400         return NULL;
401
402     return ext;
403 }
404
405 static HRESULT on_default_action(FileDialogImpl *This)
406 {
407     IShellFolder *psf_parent, *psf_desktop;
408     LPITEMIDLIST *pidla;
409     LPITEMIDLIST current_folder;
410     LPWSTR fn_iter, files, tmp_files;
411     UINT file_count = 0, len, i;
412     int open_action;
413     HRESULT hr, ret = E_FAIL;
414
415     len = get_file_name(This, &tmp_files);
416     if(len)
417     {
418         UINT size_used;
419         file_count = COMDLG32_SplitFileNames(tmp_files, len, &files, &size_used);
420     }
421     if(!file_count) return E_FAIL;
422
423     hr = SHGetIDListFromObject((IUnknown*)This->psi_folder, &current_folder);
424     if(FAILED(hr))
425     {
426         ERR("Failed to get pidl for current directory.\n");
427         return hr;
428     }
429
430     TRACE("Acting on %d file(s).\n", file_count);
431
432     pidla = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST) * file_count);
433     open_action = ONOPEN_OPEN;
434     fn_iter = files;
435
436     for(i = 0; i < file_count && open_action == ONOPEN_OPEN; i++)
437     {
438         WCHAR canon_filename[MAX_PATH];
439         psf_parent = NULL;
440
441         COMDLG32_GetCanonicalPath(current_folder, fn_iter, canon_filename);
442
443         if( (This->options & FOS_NOVALIDATE) &&
444             !(This->options & FOS_FILEMUSTEXIST) )
445             open_action = ONOPEN_OPEN;
446         else
447             open_action = ONOPEN_BROWSE;
448
449         open_action = FILEDLG95_ValidatePathAction(canon_filename, &psf_parent, This->dlg_hwnd,
450                                                    This->options & ~FOS_FILEMUSTEXIST,
451                                                    (This->dlg_type == ITEMDLG_TYPE_SAVE),
452                                                    open_action);
453
454         /* Add the proper extension */
455         if(open_action == ONOPEN_OPEN)
456         {
457             static const WCHAR dotW[] = {'.',0};
458
459             if(This->dlg_type == ITEMDLG_TYPE_SAVE)
460             {
461                 WCHAR extbuf[MAX_PATH], *newext = NULL;
462
463                 if(This->filterspec_count)
464                 {
465                     newext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
466                 }
467                 else if(This->default_ext)
468                 {
469                     lstrcpyW(extbuf, dotW);
470                     lstrcatW(extbuf, This->default_ext);
471                     newext = extbuf;
472                 }
473
474                 if(newext)
475                 {
476                     WCHAR *ext = PathFindExtensionW(canon_filename);
477                     if(lstrcmpW(ext, newext))
478                         lstrcatW(canon_filename, newext);
479                 }
480             }
481             else
482             {
483                 if( !(This->options & FOS_NOVALIDATE) && (This->options & FOS_FILEMUSTEXIST) &&
484                     !PathFileExistsW(canon_filename))
485                 {
486                     if(This->default_ext)
487                     {
488                         lstrcatW(canon_filename, dotW);
489                         lstrcatW(canon_filename, This->default_ext);
490
491                         if(!PathFileExistsW(canon_filename))
492                         {
493                             FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
494                             open_action = ONOPEN_BROWSE;
495                         }
496                     }
497                     else
498                     {
499                         FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
500                         open_action = ONOPEN_BROWSE;
501                     }
502                 }
503             }
504         }
505
506         pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename);
507
508         if(psf_parent && !(open_action == ONOPEN_BROWSE))
509             IShellItem_Release(psf_parent);
510
511         fn_iter += (WCHAR)lstrlenW(fn_iter) + 1;
512     }
513
514     HeapFree(GetProcessHeap(), 0, files);
515     ILFree(current_folder);
516
517     if((This->options & FOS_PICKFOLDERS) && open_action == ONOPEN_BROWSE)
518         open_action = ONOPEN_OPEN; /* FIXME: Multiple folders? */
519
520     switch(open_action)
521     {
522     case ONOPEN_SEARCH:
523         FIXME("Filtering not implemented.\n");
524         break;
525
526     case ONOPEN_BROWSE:
527         hr = IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psf_parent, SBSP_DEFBROWSER);
528         if(FAILED(hr))
529             ERR("Failed to browse to directory: %08x\n", hr);
530
531         IShellItem_Release(psf_parent);
532         break;
533
534     case ONOPEN_OPEN:
535         if(events_OnFileOk(This) != S_OK)
536             break;
537
538         hr = SHGetDesktopFolder(&psf_desktop);
539         if(SUCCEEDED(hr))
540         {
541             if(This->psia_results)
542                 IShellItemArray_Release(This->psia_results);
543
544             hr = SHCreateShellItemArray(NULL, psf_desktop, file_count, (PCUITEMID_CHILD_ARRAY)pidla,
545                                         &This->psia_results);
546             if(SUCCEEDED(hr))
547                 ret = S_OK;
548
549             IShellFolder_Release(psf_desktop);
550         }
551         break;
552
553     default:
554         ERR("Failed.\n");
555         break;
556     }
557
558     /* Clean up */
559     for(i = 0; i < file_count; i++)
560         ILFree(pidla[i]);
561     HeapFree(GetProcessHeap(), 0, pidla);
562
563     /* Success closes the dialog */
564     return ret;
565 }
566
567 /**************************************************************************
568  * Control functions.
569  */
570 static inline customctrl *get_cctrl_from_dlgid(FileDialogImpl *This, DWORD dlgid)
571 {
572     customctrl *ctrl;
573
574     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
575         if(ctrl->dlgid == dlgid)
576             return ctrl;
577
578     ERR("Failed to find control with dialog id %d\n", dlgid);
579     return NULL;
580 }
581
582 static inline customctrl *get_cctrl(FileDialogImpl *This, DWORD ctlid)
583 {
584     customctrl *ctrl;
585
586     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
587         if(ctrl->id == ctlid)
588             return ctrl;
589
590     ERR("Failed to find control with control id %d\n", ctlid);
591     return NULL;
592 }
593
594 static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multiline)
595 {
596     LPWSTR text;
597     UINT len, final_width;
598     UINT lines, final_height;
599     SIZE size;
600     RECT rc;
601     HDC hdc;
602     WCHAR *c;
603
604     TRACE("\n");
605
606     len = SendMessageW(hctrl, WM_GETTEXTLENGTH, 0, 0);
607     text = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
608     if(!text) return;
609     SendMessageW(hctrl, WM_GETTEXT, len+1, (LPARAM)text);
610
611     hdc = GetDC(hctrl);
612     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &size);
613     ReleaseDC(hctrl, hdc);
614
615     if(len && multiline)
616     {
617         /* FIXME: line-wrap */
618         for(lines = 1, c = text; *c != '\0'; c++)
619             if(*c == '\n') lines++;
620
621         final_height = size.cy*lines + 2*4;
622     }
623     else
624     {
625         GetWindowRect(hctrl, &rc);
626         final_height = rc.bottom - rc.top;
627     }
628
629     final_width = min(max(size.cx, min_width) + 4, max_width);
630     SetWindowPos(hctrl, NULL, 0, 0, final_width, final_height,
631                  SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
632
633     HeapFree(GetProcessHeap(), 0, text);
634 }
635
636 static void customctrl_resize(FileDialogImpl *This, customctrl *ctrl)
637 {
638     RECT rc;
639
640     switch(ctrl->type)
641     {
642     case IDLG_CCTRL_PUSHBUTTON:
643     case IDLG_CCTRL_COMBOBOX:
644     case IDLG_CCTRL_CHECKBUTTON:
645     case IDLG_CCTRL_TEXT:
646         ctrl_resize(ctrl->hwnd, 160, 160, TRUE);
647         GetWindowRect(ctrl->hwnd, &rc);
648         SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
649                      SWP_NOZORDER|SWP_NOMOVE|SWP_NOZORDER);
650     case IDLG_CCTRL_RADIOBUTTONLIST:
651     case IDLG_CCTRL_EDITBOX:
652     case IDLG_CCTRL_SEPARATOR:
653     case IDLG_CCTRL_MENU:
654         /* Nothing */
655         break;
656     }
657 }
658
659 static LRESULT notifysink_on_create(HWND hwnd, CREATESTRUCTW *crs)
660 {
661     FileDialogImpl *This = crs->lpCreateParams;
662     TRACE("%p\n", This);
663
664     SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
665     return TRUE;
666 }
667
668 static LRESULT notifysink_on_bn_clicked(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
669 {
670     customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
671
672     TRACE("%p, %lx\n", This, wparam);
673
674     if(ctrl)
675     {
676         if(ctrl->type == IDLG_CCTRL_CHECKBUTTON)
677         {
678             BOOL checked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
679             cctrl_event_OnCheckButtonToggled(This, ctrl->id, checked);
680         }
681         else
682             cctrl_event_OnButtonClicked(This, ctrl->id);
683     }
684
685     return TRUE;
686 }
687
688 static LRESULT notifysink_on_cbn_selchange(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
689 {
690     customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
691     TRACE("%p, %p (%lx)\n", This, ctrl, wparam);
692
693     if(ctrl)
694     {
695         UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
696         UINT selid = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
697
698         cctrl_event_OnItemSelected(This, ctrl->id, selid);
699     }
700     return TRUE;
701 }
702
703 static LRESULT notifysink_on_tvn_dropdown(FileDialogImpl *This, LPARAM lparam)
704 {
705     NMTOOLBARW *nmtb = (NMTOOLBARW*)lparam;
706     customctrl *ctrl = get_cctrl_from_dlgid(This, GetDlgCtrlID(nmtb->hdr.hwndFrom));
707     POINT pt = { 0, nmtb->rcButton.bottom };
708     TBBUTTON tbb;
709     UINT idcmd;
710
711     TRACE("%p, %p (%lx)\n", This, ctrl, lparam);
712
713     if(ctrl)
714     {
715         cctrl_event_OnControlActivating(This,ctrl->id);
716
717         SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
718         ClientToScreen(ctrl->hwnd, &pt);
719         idcmd = TrackPopupMenu((HMENU)tbb.dwData, TPM_RETURNCMD, pt.x, pt.y, 0, This->dlg_hwnd, NULL);
720         if(idcmd)
721             cctrl_event_OnItemSelected(This, ctrl->id, idcmd);
722     }
723
724     return TBDDRET_DEFAULT;
725 }
726
727 static LRESULT notifysink_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
728 {
729     switch(HIWORD(wparam))
730     {
731     case BN_CLICKED:          return notifysink_on_bn_clicked(This, hwnd, wparam);
732     case CBN_SELCHANGE:       return notifysink_on_cbn_selchange(This, hwnd, wparam);
733     }
734
735     return FALSE;
736 }
737
738 static LRESULT notifysink_on_wm_notify(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
739 {
740     NMHDR *nmhdr = (NMHDR*)lparam;
741
742     switch(nmhdr->code)
743     {
744     case TBN_DROPDOWN:        return notifysink_on_tvn_dropdown(This, lparam);
745     }
746
747     return FALSE;
748 }
749
750 static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
751 {
752     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
753     HWND hwnd_child;
754     RECT rc;
755
756     switch(message)
757     {
758     case WM_NCCREATE:         return notifysink_on_create(hwnd, (CREATESTRUCTW*)lparam);
759     case WM_COMMAND:          return notifysink_on_wm_command(This, hwnd, wparam, lparam);
760     case WM_NOTIFY:           return notifysink_on_wm_notify(This, hwnd, wparam, lparam);
761     case WM_SIZE:
762         hwnd_child = GetPropW(hwnd, notifysink_childW);
763         GetClientRect(hwnd, &rc);
764         SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
765         return TRUE;
766     }
767
768     return DefWindowProcW(hwnd, message, wparam, lparam);
769 }
770
771 static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
772                                 LPCWSTR text, LPCWSTR wndclass, DWORD ctrl_wsflags,
773                                 DWORD ctrl_exflags, UINT height, customctrl **ppctrl)
774 {
775     HWND ns_hwnd, control_hwnd;
776     DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
777     customctrl *ctrl;
778
779     if(get_cctrl(This, id))
780         return E_UNEXPECTED; /* Duplicate id */
781
782     ns_hwnd = CreateWindowExW(0, floatnotifysinkW, NULL, wsflags,
783                               0, 0, This->cctrl_width, height, This->cctrls_hwnd,
784                               (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
785     control_hwnd = CreateWindowExW(ctrl_exflags, wndclass, text, wsflags | ctrl_wsflags,
786                                    0, 0, This->cctrl_width, height, ns_hwnd,
787                                    (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, 0);
788
789     if(!ns_hwnd || !control_hwnd)
790     {
791         ERR("Failed to create wrapper (%p) or control (%p)\n", ns_hwnd, control_hwnd);
792         DestroyWindow(ns_hwnd);
793         DestroyWindow(control_hwnd);
794
795         return E_FAIL;
796     }
797
798     SetPropW(ns_hwnd, notifysink_childW, control_hwnd);
799
800     ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(customctrl));
801     if(!ctrl)
802         return E_OUTOFMEMORY;
803
804     ctrl->hwnd = control_hwnd;
805     ctrl->wrapper_hwnd = ns_hwnd;
806     ctrl->id = id;
807     ctrl->dlgid = This->cctrl_next_dlgid;
808     ctrl->cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
809     list_add_tail(&This->cctrls, &ctrl->entry);
810     if(ppctrl) *ppctrl = ctrl;
811
812     This->cctrl_next_dlgid++;
813     return S_OK;
814 }
815
816 /**************************************************************************
817  * Container functions.
818  */
819 static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
820 {
821     UINT container_height;
822     UINT column_width;
823     UINT nr_of_cols;
824     UINT max_control_height, total_height = 0;
825     UINT cur_col_pos, cur_row_pos;
826     customctrl *ctrl;
827     BOOL fits_height;
828     static const UINT col_indent = 100; /* The first column is indented 100px */
829     static const UINT cspacing = 90;    /* Columns are spaced with 90px */
830     static const UINT rspacing = 4;     /* Rows are spaced with 4 px. */
831
832     /* Given the new width of the container, this function determines the
833      * needed height of the container and places the controls according to
834      * the new layout. Returns the new height.
835      */
836
837     TRACE("%p\n", This);
838
839     column_width = This->cctrl_width + cspacing;
840     nr_of_cols = (container_width - col_indent + cspacing) / column_width;
841
842     /* We don't need to do anything unless the number of visible columns has changed. */
843     if(nr_of_cols == This->cctrls_cols)
844     {
845         RECT rc;
846         GetWindowRect(This->cctrls_hwnd, &rc);
847         return rc.bottom - rc.top;
848     }
849
850    This->cctrls_cols = nr_of_cols;
851
852     /* Get the size of the tallest control, and the total size of
853      * all the controls to figure out the number of slots we need.
854      */
855     max_control_height = 0;
856     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
857     {
858         if(ctrl->cdcstate & CDCS_VISIBLE)
859         {
860             RECT rc;
861             UINT control_height;
862             GetWindowRect(ctrl->wrapper_hwnd, &rc);
863             control_height = rc.bottom - rc.top;
864             max_control_height = max(max_control_height, control_height);
865
866             total_height +=  control_height + rspacing;
867         }
868     }
869
870     if(!total_height)
871         return 0;
872
873     container_height = max(total_height / nr_of_cols, max_control_height + rspacing);
874     TRACE("Guess: container_height: %d\n",container_height);
875
876     /* Incrementally increase container_height until all the controls
877      * fit.
878      */
879     do {
880         UINT columns_needed = 1;
881         cur_row_pos = 0;
882
883         fits_height = TRUE;
884         LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
885         {
886             if(ctrl->cdcstate & CDCS_VISIBLE)
887             {
888                 RECT rc;
889                 UINT control_height;
890                 GetWindowRect(ctrl->wrapper_hwnd, &rc);
891                 control_height = rc.bottom - rc.top;
892
893                 if(cur_row_pos + control_height > container_height)
894                 {
895                     if(++columns_needed > nr_of_cols)
896                     {
897                         container_height += 1;
898                         fits_height = FALSE;
899                         break;
900                     }
901                     cur_row_pos = 0;
902                 }
903
904                 cur_row_pos += control_height + rspacing;
905             }
906         }
907     } while(!fits_height);
908
909     TRACE("Final container height: %d\n", container_height);
910
911     /* Move the controls to their final destination
912      */
913     cur_col_pos = col_indent, cur_row_pos = 0;
914     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
915     {
916         if(ctrl->cdcstate & CDCS_VISIBLE)
917         {
918             RECT rc;
919             UINT control_height;
920             GetWindowRect(ctrl->wrapper_hwnd, &rc);
921             control_height = rc.bottom - rc.top;
922
923             if(cur_row_pos + control_height > container_height)
924             {
925                 cur_row_pos = 0;
926                 cur_col_pos += This->cctrl_width + cspacing;
927             }
928
929             SetWindowPos(ctrl->wrapper_hwnd, NULL, cur_col_pos, cur_row_pos, 0, 0,
930                          SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
931
932             cur_row_pos += control_height + rspacing;
933         }
934     }
935
936     /* Sanity check */
937     if(cur_row_pos + This->cctrl_width > container_width)
938         ERR("-- Failed to place controls properly.\n");
939
940     return container_height;
941 }
942
943 static void ctrl_container_reparent(FileDialogImpl *This, HWND parent)
944 {
945     LONG wndstyle;
946
947     if(parent)
948     {
949         customctrl *ctrl;
950         HFONT font;
951
952         wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
953         wndstyle &= ~(WS_POPUP);
954         wndstyle |= WS_CHILD;
955         SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
956
957         SetParent(This->cctrls_hwnd, parent);
958         ShowWindow(This->cctrls_hwnd, TRUE);
959
960         /* Set the fonts to match the dialog font. */
961         font = (HFONT)SendMessageW(parent, WM_GETFONT, 0, 0);
962         if(!font)
963             ERR("Failed to get font handle from dialog.\n");
964
965         LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
966         {
967             if(font) SendMessageW(ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
968             customctrl_resize(This, ctrl);
969         }
970     }
971     else
972     {
973         ShowWindow(This->cctrls_hwnd, FALSE);
974
975         wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
976         wndstyle &= ~(WS_CHILD);
977         wndstyle |= WS_POPUP;
978         SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
979
980         SetParent(This->cctrls_hwnd, NULL);
981     }
982 }
983
984 static LRESULT ctrl_container_on_create(HWND hwnd, CREATESTRUCTW *crs)
985 {
986     FileDialogImpl *This = crs->lpCreateParams;
987     TRACE("%p\n", This);
988
989     SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
990     return TRUE;
991 }
992
993 static LRESULT ctrl_container_on_wm_destroy(FileDialogImpl *This)
994 {
995     customctrl *cur1, *cur2;
996     TRACE("%p", This);
997
998     LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->cctrls, customctrl, entry)
999     {
1000         TRACE("Freeing control %p\n", cur1);
1001         list_remove(&cur1->entry);
1002
1003         if(cur1->type == IDLG_CCTRL_MENU)
1004         {
1005             TBBUTTON tbb;
1006             SendMessageW(cur1->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
1007             DestroyMenu((HMENU)tbb.dwData);
1008         }
1009
1010         DestroyWindow(cur1->hwnd);
1011         HeapFree(GetProcessHeap(), 0, cur1);
1012     }
1013
1014     return TRUE;
1015 }
1016
1017 static LRESULT CALLBACK ctrl_container_wndproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1018 {
1019     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1020
1021     switch(umessage)
1022     {
1023     case WM_NCCREATE:         return ctrl_container_on_create(hwnd, (CREATESTRUCTW*)lparam);
1024     case WM_DESTROY:          return ctrl_container_on_wm_destroy(This);
1025     default:                  return DefWindowProcW(hwnd, umessage, wparam, lparam);
1026     }
1027
1028     return FALSE;
1029 }
1030
1031 static HRESULT init_custom_controls(FileDialogImpl *This)
1032 {
1033     WNDCLASSW wc;
1034     static const WCHAR ctrl_container_classname[] =
1035         {'i','d','l','g','_','c','o','n','t','a','i','n','e','r','_','p','a','n','e',0};
1036
1037     InitCommonControlsEx(NULL);
1038
1039     This->cctrl_width = 160;      /* Controls have a fixed width */
1040     This->cctrl_def_height = 23;
1041     This->cctrls_cols = 0;
1042
1043     This->cctrl_next_dlgid = 0x2000;
1044     list_init(&This->cctrls);
1045
1046     if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
1047     {
1048         wc.style            = CS_HREDRAW | CS_VREDRAW;
1049         wc.lpfnWndProc      = ctrl_container_wndproc;
1050         wc.cbClsExtra       = 0;
1051         wc.cbWndExtra       = 0;
1052         wc.hInstance        = COMDLG32_hInstance;
1053         wc.hIcon            = 0;
1054         wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1055         wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
1056         wc.lpszMenuName     = NULL;
1057         wc.lpszClassName    = ctrl_container_classname;
1058
1059         if(!RegisterClassW(&wc)) return E_FAIL;
1060     }
1061
1062     This->cctrls_hwnd = CreateWindowExW(0, ctrl_container_classname, NULL,
1063                                         WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1064                                         0, 0, 0, 0, NULL, 0,
1065                                         COMDLG32_hInstance, (void*)This);
1066     if(!This->cctrls_hwnd)
1067         return E_FAIL;
1068
1069     SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, WS_TABSTOP);
1070
1071     /* Register class for  */
1072     if( !GetClassInfoW(COMDLG32_hInstance, floatnotifysinkW, &wc) ||
1073         wc.hInstance != COMDLG32_hInstance)
1074     {
1075         wc.style            = CS_HREDRAW | CS_VREDRAW;
1076         wc.lpfnWndProc      = notifysink_proc;
1077         wc.cbClsExtra       = 0;
1078         wc.cbWndExtra       = 0;
1079         wc.hInstance        = COMDLG32_hInstance;
1080         wc.hIcon            = 0;
1081         wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1082         wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
1083         wc.lpszMenuName     = NULL;
1084         wc.lpszClassName    = floatnotifysinkW;
1085
1086         if (!RegisterClassW(&wc))
1087             ERR("Failed to register FloatNotifySink window class.\n");
1088     }
1089
1090     return S_OK;
1091 }
1092
1093 /**************************************************************************
1094  * Window related functions.
1095  */
1096 static SIZE update_layout(FileDialogImpl *This)
1097 {
1098     HDWP hdwp;
1099     HWND hwnd;
1100     RECT dialog_rc;
1101     RECT cancel_rc, open_rc;
1102     RECT filetype_rc, filename_rc, filenamelabel_rc;
1103     RECT toolbar_rc, ebrowser_rc, customctrls_rc;
1104     int missing_width, missing_height;
1105     static const UINT vspacing = 4, hspacing = 4;
1106     SIZE ret;
1107
1108     GetClientRect(This->dlg_hwnd, &dialog_rc);
1109
1110     missing_width = max(0, 320 - dialog_rc.right);
1111     missing_height = max(0, 200 - dialog_rc.bottom);
1112
1113     if(missing_width || missing_height)
1114     {
1115         TRACE("Missing (%d, %d)\n", missing_width, missing_height);
1116         ret.cx = missing_width;
1117         ret.cy = missing_height;
1118         return ret;
1119     }
1120
1121     /****
1122      * Calculate the size of the dialog and all the parts.
1123      */
1124
1125     /* Cancel button */
1126     hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL);
1127     if(hwnd)
1128     {
1129         int cancel_width, cancel_height;
1130         GetWindowRect(hwnd, &cancel_rc);
1131         cancel_width = cancel_rc.right - cancel_rc.left;
1132         cancel_height = cancel_rc.bottom - cancel_rc.top;
1133
1134         cancel_rc.left = dialog_rc.right - cancel_width - hspacing;
1135         cancel_rc.top = dialog_rc.bottom - cancel_height - vspacing;
1136         cancel_rc.right = cancel_rc.left + cancel_width;
1137         cancel_rc.bottom = cancel_rc.top + cancel_height;
1138     }
1139
1140     /* Open/Save button */
1141     hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1142     if(hwnd)
1143     {
1144         int open_width, open_height;
1145         GetWindowRect(hwnd, &open_rc);
1146         open_width = open_rc.right - open_rc.left;
1147         open_height = open_rc.bottom - open_rc.top;
1148
1149         open_rc.left = cancel_rc.left - open_width - hspacing;
1150         open_rc.top = cancel_rc.top;
1151         open_rc.right = open_rc.left + open_width;
1152         open_rc.bottom = open_rc.top + open_height;
1153     }
1154
1155     /* The filetype combobox. */
1156     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1157     if(hwnd)
1158     {
1159         int filetype_width, filetype_height;
1160         GetWindowRect(hwnd, &filetype_rc);
1161
1162         filetype_width = filetype_rc.right - filetype_rc.left;
1163         filetype_height = filetype_rc.bottom - filetype_rc.top;
1164
1165         filetype_rc.right = cancel_rc.right;
1166
1167         filetype_rc.left = filetype_rc.right - filetype_width;
1168         filetype_rc.top = cancel_rc.top - filetype_height - vspacing;
1169         filetype_rc.bottom = filetype_rc.top + filetype_height;
1170
1171         if(!This->filterspec_count)
1172             filetype_rc.left = filetype_rc.right;
1173     }
1174
1175     /* Filename label. */
1176     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC);
1177     if(hwnd)
1178     {
1179         int filetypelabel_width, filetypelabel_height;
1180         GetWindowRect(hwnd, &filenamelabel_rc);
1181
1182         filetypelabel_width = filenamelabel_rc.right - filenamelabel_rc.left;
1183         filetypelabel_height = filenamelabel_rc.bottom - filenamelabel_rc.top;
1184
1185         filenamelabel_rc.left = 160; /* FIXME */
1186         filenamelabel_rc.top = filetype_rc.top;
1187         filenamelabel_rc.right = filenamelabel_rc.left + filetypelabel_width;
1188         filenamelabel_rc.bottom = filenamelabel_rc.top + filetypelabel_height;
1189     }
1190
1191     /* Filename edit box. */
1192     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
1193     if(hwnd)
1194     {
1195         int filename_width, filename_height;
1196         GetWindowRect(hwnd, &filename_rc);
1197
1198         filename_width = filetype_rc.left - filenamelabel_rc.right - hspacing*2;
1199         filename_height = filename_rc.bottom - filename_rc.top;
1200
1201         filename_rc.left = filenamelabel_rc.right + hspacing;
1202         filename_rc.top = filetype_rc.top;
1203         filename_rc.right = filename_rc.left + filename_width;
1204         filename_rc.bottom = filename_rc.top + filename_height;
1205     }
1206
1207     hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR);
1208     if(hwnd)
1209     {
1210         GetWindowRect(hwnd, &toolbar_rc);
1211         MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2);
1212     }
1213
1214     /* The custom controls */
1215     customctrls_rc.left = dialog_rc.left + vspacing;
1216     customctrls_rc.right = dialog_rc.right - vspacing;
1217     customctrls_rc.bottom = filename_rc.top - hspacing;
1218     customctrls_rc.top = customctrls_rc.bottom -
1219         ctrl_container_resize(This, customctrls_rc.right - customctrls_rc.left);
1220
1221     /* The ExplorerBrowser control. */
1222     ebrowser_rc.left = dialog_rc.left + vspacing;
1223     ebrowser_rc.top = toolbar_rc.bottom + vspacing;
1224     ebrowser_rc.right = dialog_rc.right - hspacing;
1225     ebrowser_rc.bottom = customctrls_rc.top - hspacing;
1226
1227     /****
1228      * Move everything to the right place.
1229      */
1230
1231     /* FIXME: The Save Dialog uses a slightly different layout. */
1232     hdwp = BeginDeferWindowPos(7);
1233
1234     if(hdwp && This->peb)
1235         IExplorerBrowser_SetRect(This->peb, &hdwp, ebrowser_rc);
1236
1237     if(hdwp && This->cctrls_hwnd)
1238         DeferWindowPos(hdwp, This->cctrls_hwnd, NULL,
1239                        customctrls_rc.left, customctrls_rc.top,
1240                        customctrls_rc.right - customctrls_rc.left, customctrls_rc.bottom - customctrls_rc.top,
1241                        SWP_NOZORDER | SWP_NOACTIVATE);
1242
1243     /* The default controls */
1244     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE)) )
1245         DeferWindowPos(hdwp, hwnd, NULL, filetype_rc.left, filetype_rc.top, 0, 0,
1246                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1247
1248     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
1249         DeferWindowPos(hdwp, hwnd, NULL, filename_rc.left, filename_rc.top,
1250                        filename_rc.right - filename_rc.left, filename_rc.bottom - filename_rc.top,
1251                        SWP_NOZORDER | SWP_NOACTIVATE);
1252
1253     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)) )
1254         DeferWindowPos(hdwp, hwnd, NULL, filenamelabel_rc.left, filenamelabel_rc.top, 0, 0,
1255                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1256
1257     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDOK)) )
1258         DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
1259                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1260
1261     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
1262         DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
1263                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1264
1265     if(hdwp)
1266         EndDeferWindowPos(hdwp);
1267     else
1268         ERR("Failed to position dialog controls.\n");
1269
1270     ret.cx = 0; ret.cy = 0;
1271     return ret;
1272 }
1273
1274 static HRESULT init_explorerbrowser(FileDialogImpl *This)
1275 {
1276     IShellItem *psi_folder;
1277     FOLDERSETTINGS fos;
1278     RECT rc = {0};
1279     HRESULT hr;
1280
1281     /* Create ExplorerBrowser instance */
1282     OleInitialize(NULL);
1283
1284     hr = CoCreateInstance(&CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER,
1285                           &IID_IExplorerBrowser, (void**)&This->peb);
1286     if(FAILED(hr))
1287     {
1288         ERR("Failed to instantiate ExplorerBrowser control.\n");
1289         return hr;
1290     }
1291
1292     IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES);
1293
1294     hr = IExplorerBrowser_Initialize(This->peb, This->dlg_hwnd, &rc, NULL);
1295     if(FAILED(hr))
1296     {
1297         ERR("Failed to initialize the ExplorerBrowser control.\n");
1298         IExplorerBrowser_Release(This->peb);
1299         This->peb = NULL;
1300         return hr;
1301     }
1302     hr = IExplorerBrowser_Advise(This->peb, &This->IExplorerBrowserEvents_iface, &This->ebevents_cookie);
1303     if(FAILED(hr))
1304         ERR("Advise (ExplorerBrowser) failed.\n");
1305
1306     /* Get previous options? */
1307     fos.ViewMode = fos.fFlags = 0;
1308     if(!(This->options & FOS_ALLOWMULTISELECT))
1309         fos.fFlags |= FWF_SINGLESEL;
1310
1311     IExplorerBrowser_SetFolderSettings(This->peb, &fos);
1312
1313     hr = IUnknown_SetSite((IUnknown*)This->peb, (IUnknown*)This);
1314     if(FAILED(hr))
1315         ERR("SetSite (ExplorerBrowser) failed.\n");
1316
1317     /* Browse somewhere */
1318     psi_folder = This->psi_setfolder ? This->psi_setfolder : This->psi_defaultfolder;
1319     IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psi_folder, SBSP_DEFBROWSER);
1320
1321     return S_OK;
1322 }
1323
1324 static void init_toolbar(FileDialogImpl *This, HWND hwnd)
1325 {
1326     HWND htoolbar;
1327     TBADDBITMAP tbab;
1328     TBBUTTON button[2];
1329
1330     htoolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE,
1331                                0, 0, 0, 0,
1332                                hwnd, (HMENU)IDC_NAV_TOOLBAR, NULL, NULL);
1333
1334     tbab.hInst = HINST_COMMCTRL;
1335     tbab.nID = IDB_HIST_LARGE_COLOR;
1336     SendMessageW(htoolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
1337
1338     button[0].iBitmap = HIST_BACK;
1339     button[0].idCommand = IDC_NAVBACK;
1340     button[0].fsState = TBSTATE_ENABLED;
1341     button[0].fsStyle = BTNS_BUTTON;
1342     button[0].dwData = 0;
1343     button[0].iString = 0;
1344
1345     button[1].iBitmap = HIST_FORWARD;
1346     button[1].idCommand = IDC_NAVFORWARD;
1347     button[1].fsState = TBSTATE_ENABLED;
1348     button[1].fsStyle = BTNS_BUTTON;
1349     button[1].dwData = 0;
1350     button[1].iString = 0;
1351
1352     SendMessageW(htoolbar, TB_ADDBUTTONSW, 2, (LPARAM)&button);
1353     SendMessageW(htoolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(24,24));
1354     SendMessageW(htoolbar, TB_AUTOSIZE, 0, 0);
1355 }
1356
1357 static void update_control_text(FileDialogImpl *This)
1358 {
1359     HWND hitem;
1360     if(This->custom_title)
1361         SetWindowTextW(This->dlg_hwnd, This->custom_title);
1362
1363     if(This->custom_okbutton &&
1364        (hitem = GetDlgItem(This->dlg_hwnd, IDOK)))
1365     {
1366         SetWindowTextW(hitem, This->custom_okbutton);
1367         ctrl_resize(hitem, 50, 250, FALSE);
1368     }
1369
1370     if(This->custom_cancelbutton &&
1371        (hitem = GetDlgItem(This->dlg_hwnd, IDCANCEL)))
1372     {
1373         SetWindowTextW(hitem, This->custom_cancelbutton);
1374         ctrl_resize(hitem, 50, 250, FALSE);
1375     }
1376
1377     if(This->custom_filenamelabel &&
1378        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)))
1379     {
1380         SetWindowTextW(hitem, This->custom_filenamelabel);
1381         ctrl_resize(hitem, 50, 250, FALSE);
1382     }
1383 }
1384
1385 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
1386 {
1387     FileDialogImpl *This = (FileDialogImpl*)lParam;
1388     HWND hitem;
1389
1390     TRACE("(%p, %p)\n", This, hwnd);
1391
1392     SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1393     This->dlg_hwnd = hwnd;
1394
1395     hitem = GetDlgItem(This->dlg_hwnd, pshHelp);
1396     if(hitem) ShowWindow(hitem, SW_HIDE);
1397
1398     hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPESTATIC);
1399     if(hitem) ShowWindow(hitem, SW_HIDE);
1400
1401     /* Fill filetypes combobox, or hide it. */
1402     hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1403     if(This->filterspec_count)
1404     {
1405         UINT i;
1406         for(i = 0; i < This->filterspec_count; i++)
1407             SendMessageW(hitem, CB_ADDSTRING, 0, (LPARAM)This->filterspecs[i].pszName);
1408
1409         SendMessageW(hitem, CB_SETCURSEL, This->filetypeindex, 0);
1410     }
1411     else
1412         ShowWindow(hitem, SW_HIDE);
1413
1414     if(This->set_filename &&
1415        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
1416         SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
1417
1418     ctrl_container_reparent(This, This->dlg_hwnd);
1419     init_explorerbrowser(This);
1420     init_toolbar(This, hwnd);
1421     update_control_text(This);
1422     update_layout(This);
1423
1424     return TRUE;
1425 }
1426
1427 static LRESULT on_wm_size(FileDialogImpl *This)
1428 {
1429     update_layout(This);
1430     return FALSE;
1431 }
1432
1433 static LRESULT on_wm_getminmaxinfo(FileDialogImpl *This, LPARAM lparam)
1434 {
1435     MINMAXINFO *mmi = (MINMAXINFO*)lparam;
1436     TRACE("%p (%p)\n", This, mmi);
1437
1438     /* FIXME */
1439     mmi->ptMinTrackSize.x = 640;
1440     mmi->ptMinTrackSize.y = 480;
1441
1442     return FALSE;
1443 }
1444
1445 static LRESULT on_wm_destroy(FileDialogImpl *This)
1446 {
1447     TRACE("%p\n", This);
1448
1449     if(This->peb)
1450     {
1451         IExplorerBrowser_Destroy(This->peb);
1452         IExplorerBrowser_Release(This->peb);
1453         This->peb = NULL;
1454     }
1455
1456     ctrl_container_reparent(This, NULL);
1457     This->dlg_hwnd = NULL;
1458
1459     return TRUE;
1460 }
1461
1462 static LRESULT on_idok(FileDialogImpl *This)
1463 {
1464     TRACE("%p\n", This);
1465
1466     if(SUCCEEDED(on_default_action(This)))
1467         EndDialog(This->dlg_hwnd, S_OK);
1468
1469     return FALSE;
1470 }
1471
1472 static LRESULT on_idcancel(FileDialogImpl *This)
1473 {
1474     TRACE("%p\n", This);
1475
1476     EndDialog(This->dlg_hwnd, HRESULT_FROM_WIN32(ERROR_CANCELLED));
1477
1478     return FALSE;
1479 }
1480
1481 static LRESULT on_browse_back(FileDialogImpl *This)
1482 {
1483     TRACE("%p\n", This);
1484     IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEBACK);
1485     return FALSE;
1486 }
1487
1488 static LRESULT on_browse_forward(FileDialogImpl *This)
1489 {
1490     TRACE("%p\n", This);
1491     IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEFORWARD);
1492     return FALSE;
1493 }
1494
1495 static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
1496 {
1497     if(HIWORD(wparam) == CBN_SELCHANGE)
1498     {
1499         IShellView *psv;
1500         HRESULT hr;
1501         LPWSTR filename;
1502         UINT prev_index = This->filetypeindex;
1503
1504         This->filetypeindex = SendMessageW((HWND)lparam, CB_GETCURSEL, 0, 0);
1505         TRACE("File type selection changed to %d.\n", This->filetypeindex);
1506
1507         if(prev_index == This->filetypeindex)
1508             return FALSE;
1509
1510         hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&psv);
1511         if(SUCCEEDED(hr))
1512         {
1513             IShellView_Refresh(psv);
1514             IShellView_Release(psv);
1515         }
1516
1517         if(This->dlg_type == ITEMDLG_TYPE_SAVE && get_file_name(This, &filename))
1518         {
1519             WCHAR buf[MAX_PATH], extbuf[MAX_PATH], *ext;
1520
1521             ext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
1522             if(ext)
1523             {
1524                 lstrcpyW(buf, filename);
1525
1526                 if(PathMatchSpecW(buf, This->filterspecs[prev_index].pszSpec))
1527                     PathRemoveExtensionW(buf);
1528
1529                 lstrcatW(buf, ext);
1530                 set_file_name(This, buf);
1531             }
1532             CoTaskMemFree(filename);
1533         }
1534     }
1535
1536     return FALSE;
1537 }
1538
1539 static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
1540 {
1541     switch(LOWORD(wparam))
1542     {
1543     case IDOK:                return on_idok(This);
1544     case IDCANCEL:            return on_idcancel(This);
1545     case IDC_NAVBACK:         return on_browse_back(This);
1546     case IDC_NAVFORWARD:      return on_browse_forward(This);
1547     case IDC_FILETYPE:        return on_command_filetype(This, wparam, lparam);
1548     default:                  TRACE("Unknown command.\n");
1549     }
1550     return FALSE;
1551 }
1552
1553 static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1554 {
1555     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1556
1557     switch(umessage)
1558     {
1559     case WM_INITDIALOG:       return on_wm_initdialog(hwnd, lparam);
1560     case WM_COMMAND:          return on_wm_command(This, wparam, lparam);
1561     case WM_SIZE:             return on_wm_size(This);
1562     case WM_GETMINMAXINFO:    return on_wm_getminmaxinfo(This, lparam);
1563     case WM_DESTROY:          return on_wm_destroy(This);
1564     }
1565
1566     return FALSE;
1567 }
1568
1569 static HRESULT create_dialog(FileDialogImpl *This, HWND parent)
1570 {
1571     INT_PTR res;
1572
1573     SetLastError(0);
1574     res = DialogBoxParamW(COMDLG32_hInstance,
1575                           MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
1576                           parent, itemdlg_dlgproc, (LPARAM)This);
1577     This->dlg_hwnd = NULL;
1578     if(res == -1)
1579     {
1580         ERR("Failed to show dialog (LastError: %d)\n", GetLastError());
1581         return E_FAIL;
1582     }
1583
1584     TRACE("Returning 0x%08x\n", (HRESULT)res);
1585     return (HRESULT)res;
1586 }
1587
1588 /**************************************************************************
1589  * IFileDialog implementation
1590  */
1591 static inline FileDialogImpl *impl_from_IFileDialog2(IFileDialog2 *iface)
1592 {
1593     return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialog2_iface);
1594 }
1595
1596 static HRESULT WINAPI IFileDialog2_fnQueryInterface(IFileDialog2 *iface,
1597                                                     REFIID riid,
1598                                                     void **ppvObject)
1599 {
1600     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1601     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
1602
1603     *ppvObject = NULL;
1604     if(IsEqualGUID(riid, &IID_IUnknown) ||
1605        IsEqualGUID(riid, &IID_IFileDialog) ||
1606        IsEqualGUID(riid, &IID_IFileDialog2))
1607     {
1608         *ppvObject = iface;
1609     }
1610     else if(IsEqualGUID(riid, &IID_IFileOpenDialog) && This->dlg_type == ITEMDLG_TYPE_OPEN)
1611     {
1612         *ppvObject = &This->u.IFileOpenDialog_iface;
1613     }
1614     else if(IsEqualGUID(riid, &IID_IFileSaveDialog) && This->dlg_type == ITEMDLG_TYPE_SAVE)
1615     {
1616         *ppvObject = &This->u.IFileSaveDialog_iface;
1617     }
1618     else if(IsEqualGUID(riid, &IID_IExplorerBrowserEvents))
1619     {
1620         *ppvObject = &This->IExplorerBrowserEvents_iface;
1621     }
1622     else if(IsEqualGUID(riid, &IID_IServiceProvider))
1623     {
1624         *ppvObject = &This->IServiceProvider_iface;
1625     }
1626     else if(IsEqualGUID(&IID_ICommDlgBrowser3, riid) ||
1627             IsEqualGUID(&IID_ICommDlgBrowser2, riid) ||
1628             IsEqualGUID(&IID_ICommDlgBrowser, riid))
1629     {
1630         *ppvObject = &This->ICommDlgBrowser3_iface;
1631     }
1632     else if(IsEqualGUID(&IID_IOleWindow, riid))
1633     {
1634         *ppvObject = &This->IOleWindow_iface;
1635     }
1636     else if(IsEqualGUID(riid, &IID_IFileDialogCustomize) ||
1637             IsEqualGUID(riid, &IID_IFileDialogCustomizeAlt))
1638     {
1639         *ppvObject = &This->IFileDialogCustomize_iface;
1640     }
1641     else
1642         FIXME("Unknown interface requested: %s.\n", debugstr_guid(riid));
1643
1644     if(*ppvObject)
1645     {
1646         IUnknown_AddRef((IUnknown*)*ppvObject);
1647         return S_OK;
1648     }
1649
1650     return E_NOINTERFACE;
1651 }
1652
1653 static ULONG WINAPI IFileDialog2_fnAddRef(IFileDialog2 *iface)
1654 {
1655     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1656     LONG ref = InterlockedIncrement(&This->ref);
1657     TRACE("%p - ref %d\n", This, ref);
1658
1659     return ref;
1660 }
1661
1662 static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface)
1663 {
1664     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1665     LONG ref = InterlockedDecrement(&This->ref);
1666     TRACE("%p - ref %d\n", This, ref);
1667
1668     if(!ref)
1669     {
1670         UINT i;
1671         for(i = 0; i < This->filterspec_count; i++)
1672         {
1673             LocalFree((void*)This->filterspecs[i].pszName);
1674             LocalFree((void*)This->filterspecs[i].pszSpec);
1675         }
1676         HeapFree(GetProcessHeap(), 0, This->filterspecs);
1677
1678         DestroyWindow(This->cctrls_hwnd);
1679
1680         if(This->psi_defaultfolder) IShellItem_Release(This->psi_defaultfolder);
1681         if(This->psi_setfolder)     IShellItem_Release(This->psi_setfolder);
1682         if(This->psi_folder)        IShellItem_Release(This->psi_folder);
1683         if(This->psia_selection)    IShellItemArray_Release(This->psia_selection);
1684         if(This->psia_results)      IShellItemArray_Release(This->psia_results);
1685
1686         LocalFree(This->set_filename);
1687         LocalFree(This->default_ext);
1688         LocalFree(This->custom_title);
1689         LocalFree(This->custom_okbutton);
1690         LocalFree(This->custom_cancelbutton);
1691         LocalFree(This->custom_filenamelabel);
1692
1693         HeapFree(GetProcessHeap(), 0, This);
1694     }
1695
1696     return ref;
1697 }
1698
1699 static HRESULT WINAPI IFileDialog2_fnShow(IFileDialog2 *iface, HWND hwndOwner)
1700 {
1701     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1702     TRACE("%p (%p)\n", iface, hwndOwner);
1703
1704     return create_dialog(This, hwndOwner);
1705 }
1706
1707 static HRESULT WINAPI IFileDialog2_fnSetFileTypes(IFileDialog2 *iface, UINT cFileTypes,
1708                                                   const COMDLG_FILTERSPEC *rgFilterSpec)
1709 {
1710     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1711     UINT i;
1712     TRACE("%p (%d, %p)\n", This, cFileTypes, rgFilterSpec);
1713
1714     if(This->filterspecs)
1715         return E_UNEXPECTED;
1716
1717     if(!rgFilterSpec)
1718         return E_INVALIDARG;
1719
1720     if(!cFileTypes)
1721         return S_OK;
1722
1723     This->filterspecs = HeapAlloc(GetProcessHeap(), 0, sizeof(COMDLG_FILTERSPEC)*cFileTypes);
1724     for(i = 0; i < cFileTypes; i++)
1725     {
1726         This->filterspecs[i].pszName = StrDupW(rgFilterSpec[i].pszName);
1727         This->filterspecs[i].pszSpec = StrDupW(rgFilterSpec[i].pszSpec);
1728     }
1729     This->filterspec_count = cFileTypes;
1730
1731     return S_OK;
1732 }
1733
1734 static HRESULT WINAPI IFileDialog2_fnSetFileTypeIndex(IFileDialog2 *iface, UINT iFileType)
1735 {
1736     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1737     TRACE("%p (%d)\n", This, iFileType);
1738
1739     if(!This->filterspecs)
1740         return E_FAIL;
1741
1742     if(iFileType >= This->filterspec_count)
1743         This->filetypeindex = This->filterspec_count - 1;
1744     else
1745         This->filetypeindex = iFileType;
1746
1747     return S_OK;
1748 }
1749
1750 static HRESULT WINAPI IFileDialog2_fnGetFileTypeIndex(IFileDialog2 *iface, UINT *piFileType)
1751 {
1752     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1753     TRACE("%p (%p)\n", This, piFileType);
1754
1755     if(!piFileType)
1756         return E_INVALIDARG;
1757
1758     *piFileType = This->filetypeindex;
1759
1760     return S_OK;
1761 }
1762
1763 static HRESULT WINAPI IFileDialog2_fnAdvise(IFileDialog2 *iface, IFileDialogEvents *pfde, DWORD *pdwCookie)
1764 {
1765     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1766     events_client *client;
1767     TRACE("%p (%p, %p)\n", This, pfde, pdwCookie);
1768
1769     if(!pfde || !pdwCookie)
1770         return E_INVALIDARG;
1771
1772     client = HeapAlloc(GetProcessHeap(), 0, sizeof(events_client));
1773     client->pfde = pfde;
1774     client->cookie = ++This->events_next_cookie;
1775
1776     IFileDialogEvents_AddRef(pfde);
1777     *pdwCookie = client->cookie;
1778
1779     list_add_tail(&This->events_clients, &client->entry);
1780
1781     return S_OK;
1782 }
1783
1784 static HRESULT WINAPI IFileDialog2_fnUnadvise(IFileDialog2 *iface, DWORD dwCookie)
1785 {
1786     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1787     events_client *client, *found = NULL;
1788     TRACE("%p (%d)\n", This, dwCookie);
1789
1790     LIST_FOR_EACH_ENTRY(client, &This->events_clients, events_client, entry)
1791     {
1792         if(client->cookie == dwCookie)
1793         {
1794             found = client;
1795             break;
1796         }
1797     }
1798
1799     if(found)
1800     {
1801         list_remove(&found->entry);
1802         IFileDialogEvents_Release(found->pfde);
1803         HeapFree(GetProcessHeap(), 0, found);
1804         return S_OK;
1805     }
1806
1807     return E_INVALIDARG;
1808 }
1809
1810 static HRESULT WINAPI IFileDialog2_fnSetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS fos)
1811 {
1812     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1813     TRACE("%p (0x%x)\n", This, fos);
1814
1815     This->options = fos;
1816
1817     return S_OK;
1818 }
1819
1820 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
1821 {
1822     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1823     TRACE("%p (%p)\n", This, pfos);
1824
1825     if(!pfos)
1826         return E_INVALIDARG;
1827
1828     *pfos = This->options;
1829
1830     return S_OK;
1831 }
1832
1833 static HRESULT WINAPI IFileDialog2_fnSetDefaultFolder(IFileDialog2 *iface, IShellItem *psi)
1834 {
1835     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1836     TRACE("%p (%p)\n", This, psi);
1837     if(This->psi_defaultfolder)
1838         IShellItem_Release(This->psi_defaultfolder);
1839
1840     This->psi_defaultfolder = psi;
1841
1842     if(This->psi_defaultfolder)
1843         IShellItem_AddRef(This->psi_defaultfolder);
1844
1845     return S_OK;
1846 }
1847
1848 static HRESULT WINAPI IFileDialog2_fnSetFolder(IFileDialog2 *iface, IShellItem *psi)
1849 {
1850     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1851     TRACE("%p (%p)\n", This, psi);
1852     if(This->psi_setfolder)
1853         IShellItem_Release(This->psi_setfolder);
1854
1855     This->psi_setfolder = psi;
1856
1857     if(This->psi_setfolder)
1858         IShellItem_AddRef(This->psi_setfolder);
1859
1860     return S_OK;
1861 }
1862
1863 static HRESULT WINAPI IFileDialog2_fnGetFolder(IFileDialog2 *iface, IShellItem **ppsi)
1864 {
1865     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1866     TRACE("%p (%p)\n", This, ppsi);
1867     if(!ppsi)
1868         return E_INVALIDARG;
1869
1870     /* FIXME:
1871        If the dialog is shown, return the current(ly selected) folder. */
1872
1873     *ppsi = NULL;
1874     if(This->psi_folder)
1875         *ppsi = This->psi_folder;
1876     else if(This->psi_setfolder)
1877         *ppsi = This->psi_setfolder;
1878     else if(This->psi_defaultfolder)
1879         *ppsi = This->psi_defaultfolder;
1880
1881     if(*ppsi)
1882     {
1883         IShellItem_AddRef(*ppsi);
1884         return S_OK;
1885     }
1886
1887     return E_FAIL;
1888 }
1889
1890 static HRESULT WINAPI IFileDialog2_fnGetCurrentSelection(IFileDialog2 *iface, IShellItem **ppsi)
1891 {
1892     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1893     HRESULT hr;
1894     TRACE("%p (%p)\n", This, ppsi);
1895
1896     if(!ppsi)
1897         return E_INVALIDARG;
1898
1899     if(This->psia_selection)
1900     {
1901         /* FIXME: Check filename edit box */
1902         hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
1903         return hr;
1904     }
1905
1906     return E_FAIL;
1907 }
1908
1909 static HRESULT WINAPI IFileDialog2_fnSetFileName(IFileDialog2 *iface, LPCWSTR pszName)
1910 {
1911     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1912     TRACE("%p (%s)\n", iface, debugstr_w(pszName));
1913
1914     set_file_name(This, pszName);
1915
1916     return S_OK;
1917 }
1918
1919 static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *pszName)
1920 {
1921     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1922     TRACE("%p (%p)\n", iface, pszName);
1923
1924     if(!pszName)
1925         return E_INVALIDARG;
1926
1927     *pszName = NULL;
1928     if(get_file_name(This, pszName))
1929         return S_OK;
1930     else
1931         return E_FAIL;
1932 }
1933
1934 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
1935 {
1936     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1937     TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
1938
1939     LocalFree(This->custom_title);
1940     This->custom_title = StrDupW(pszTitle);
1941     update_control_text(This);
1942
1943     return S_OK;
1944 }
1945
1946 static HRESULT WINAPI IFileDialog2_fnSetOkButtonLabel(IFileDialog2 *iface, LPCWSTR pszText)
1947 {
1948     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1949     TRACE("%p (%s)\n", This, debugstr_w(pszText));
1950
1951     LocalFree(This->custom_okbutton);
1952     This->custom_okbutton = StrDupW(pszText);
1953     update_control_text(This);
1954     update_layout(This);
1955
1956     return S_OK;
1957 }
1958
1959 static HRESULT WINAPI IFileDialog2_fnSetFileNameLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
1960 {
1961     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1962     TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
1963
1964     LocalFree(This->custom_filenamelabel);
1965     This->custom_filenamelabel = StrDupW(pszLabel);
1966     update_control_text(This);
1967     update_layout(This);
1968
1969     return S_OK;
1970 }
1971
1972 static HRESULT WINAPI IFileDialog2_fnGetResult(IFileDialog2 *iface, IShellItem **ppsi)
1973 {
1974     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1975     HRESULT hr;
1976     TRACE("%p (%p)\n", This, ppsi);
1977
1978     if(!ppsi)
1979         return E_INVALIDARG;
1980
1981     if(This->psia_results)
1982     {
1983         UINT item_count;
1984         hr = IShellItemArray_GetCount(This->psia_results, &item_count);
1985         if(SUCCEEDED(hr))
1986         {
1987             if(item_count != 1)
1988                 return E_FAIL;
1989
1990             /* Adds a reference. */
1991             hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
1992         }
1993
1994         return hr;
1995     }
1996
1997     return E_UNEXPECTED;
1998 }
1999
2000 static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *psi, FDAP fdap)
2001 {
2002     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2003     FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
2004     return E_NOTIMPL;
2005 }
2006
2007 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
2008 {
2009     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2010     TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
2011
2012     LocalFree(This->default_ext);
2013     This->default_ext = StrDupW(pszDefaultExtension);
2014
2015     return S_OK;
2016 }
2017
2018 static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
2019 {
2020     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2021     TRACE("%p (0x%08x)\n", This, hr);
2022
2023     if(This->dlg_hwnd)
2024         EndDialog(This->dlg_hwnd, hr);
2025
2026     return S_OK;
2027 }
2028
2029 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
2030 {
2031     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2032     FIXME("stub - %p (%s)\n", This, debugstr_guid(guid));
2033     return E_NOTIMPL;
2034 }
2035
2036 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
2037 {
2038     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2039     FIXME("stub - %p\n", This);
2040     return E_NOTIMPL;
2041 }
2042
2043 static HRESULT WINAPI IFileDialog2_fnSetFilter(IFileDialog2 *iface, IShellItemFilter *pFilter)
2044 {
2045     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2046     FIXME("stub - %p (%p)\n", This, pFilter);
2047     return E_NOTIMPL;
2048 }
2049
2050 static HRESULT WINAPI IFileDialog2_fnSetCancelButtonLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2051 {
2052     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2053     TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2054
2055     LocalFree(This->custom_cancelbutton);
2056     This->custom_cancelbutton = StrDupW(pszLabel);
2057     update_control_text(This);
2058     update_layout(This);
2059
2060     return S_OK;
2061 }
2062
2063 static HRESULT WINAPI IFileDialog2_fnSetNavigationRoot(IFileDialog2 *iface, IShellItem *psi)
2064 {
2065     FileDialogImpl *This = impl_from_IFileDialog2(iface);
2066     FIXME("stub - %p (%p)\n", This, psi);
2067     return E_NOTIMPL;
2068 }
2069
2070 static const IFileDialog2Vtbl vt_IFileDialog2 = {
2071     IFileDialog2_fnQueryInterface,
2072     IFileDialog2_fnAddRef,
2073     IFileDialog2_fnRelease,
2074     IFileDialog2_fnShow,
2075     IFileDialog2_fnSetFileTypes,
2076     IFileDialog2_fnSetFileTypeIndex,
2077     IFileDialog2_fnGetFileTypeIndex,
2078     IFileDialog2_fnAdvise,
2079     IFileDialog2_fnUnadvise,
2080     IFileDialog2_fnSetOptions,
2081     IFileDialog2_fnGetOptions,
2082     IFileDialog2_fnSetDefaultFolder,
2083     IFileDialog2_fnSetFolder,
2084     IFileDialog2_fnGetFolder,
2085     IFileDialog2_fnGetCurrentSelection,
2086     IFileDialog2_fnSetFileName,
2087     IFileDialog2_fnGetFileName,
2088     IFileDialog2_fnSetTitle,
2089     IFileDialog2_fnSetOkButtonLabel,
2090     IFileDialog2_fnSetFileNameLabel,
2091     IFileDialog2_fnGetResult,
2092     IFileDialog2_fnAddPlace,
2093     IFileDialog2_fnSetDefaultExtension,
2094     IFileDialog2_fnClose,
2095     IFileDialog2_fnSetClientGuid,
2096     IFileDialog2_fnClearClientData,
2097     IFileDialog2_fnSetFilter,
2098     IFileDialog2_fnSetCancelButtonLabel,
2099     IFileDialog2_fnSetNavigationRoot
2100 };
2101
2102 /**************************************************************************
2103  * IFileOpenDialog
2104  */
2105 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
2106 {
2107     return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
2108 }
2109
2110 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
2111                                                        REFIID riid, void **ppvObject)
2112 {
2113     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2114     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2115 }
2116
2117 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
2118 {
2119     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2120     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2121 }
2122
2123 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
2124 {
2125     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2126     return IFileDialog2_Release(&This->IFileDialog2_iface);
2127 }
2128
2129 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
2130 {
2131     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2132     return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2133 }
2134
2135 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
2136                                                      const COMDLG_FILTERSPEC *rgFilterSpec)
2137 {
2138     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2139     return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2140 }
2141
2142 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
2143 {
2144     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2145     return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2146 }
2147
2148 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
2149 {
2150     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2151     return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2152 }
2153
2154 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
2155                                                DWORD *pdwCookie)
2156 {
2157     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2158     return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2159 }
2160
2161 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
2162 {
2163     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2164     return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2165 }
2166
2167 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
2168 {
2169     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2170     return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2171 }
2172
2173 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2174 {
2175     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2176     return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2177 }
2178
2179 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
2180 {
2181     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2182     return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2183 }
2184
2185 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
2186 {
2187     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2188     return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2189 }
2190
2191 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
2192 {
2193     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2194     return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2195 }
2196
2197 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
2198 {
2199     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2200     return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2201 }
2202
2203 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
2204 {
2205     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2206     return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2207 }
2208
2209 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
2210 {
2211     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2212     return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2213 }
2214
2215 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
2216 {
2217     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2218     return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2219 }
2220
2221 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
2222 {
2223     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2224     return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2225 }
2226
2227 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
2228 {
2229     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2230     return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2231 }
2232
2233 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
2234 {
2235     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2236     return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2237 }
2238
2239 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
2240 {
2241     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2242     return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2243 }
2244
2245 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultExtension(IFileOpenDialog *iface,
2246                                                             LPCWSTR pszDefaultExtension)
2247 {
2248     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2249     return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2250 }
2251
2252 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
2253 {
2254     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2255     return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2256 }
2257
2258 static HRESULT WINAPI IFileOpenDialog_fnSetClientGuid(IFileOpenDialog *iface, REFGUID guid)
2259 {
2260     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2261     return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2262 }
2263
2264 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
2265 {
2266     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2267     return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2268 }
2269
2270 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
2271 {
2272     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2273     return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2274 }
2275
2276 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
2277 {
2278     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2279     TRACE("%p (%p)\n", This, ppenum);
2280
2281     *ppenum = This->psia_results;
2282
2283     if(*ppenum)
2284     {
2285         IShellItemArray_AddRef(*ppenum);
2286         return S_OK;
2287     }
2288
2289     return E_FAIL;
2290 }
2291
2292 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
2293 {
2294     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2295     TRACE("%p (%p)\n", This, ppsai);
2296
2297     if(This->psia_selection)
2298     {
2299         *ppsai = This->psia_selection;
2300         IShellItemArray_AddRef(*ppsai);
2301         return S_OK;
2302     }
2303
2304     return E_FAIL;
2305 }
2306
2307 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
2308     IFileOpenDialog_fnQueryInterface,
2309     IFileOpenDialog_fnAddRef,
2310     IFileOpenDialog_fnRelease,
2311     IFileOpenDialog_fnShow,
2312     IFileOpenDialog_fnSetFileTypes,
2313     IFileOpenDialog_fnSetFileTypeIndex,
2314     IFileOpenDialog_fnGetFileTypeIndex,
2315     IFileOpenDialog_fnAdvise,
2316     IFileOpenDialog_fnUnadvise,
2317     IFileOpenDialog_fnSetOptions,
2318     IFileOpenDialog_fnGetOptions,
2319     IFileOpenDialog_fnSetDefaultFolder,
2320     IFileOpenDialog_fnSetFolder,
2321     IFileOpenDialog_fnGetFolder,
2322     IFileOpenDialog_fnGetCurrentSelection,
2323     IFileOpenDialog_fnSetFileName,
2324     IFileOpenDialog_fnGetFileName,
2325     IFileOpenDialog_fnSetTitle,
2326     IFileOpenDialog_fnSetOkButtonLabel,
2327     IFileOpenDialog_fnSetFileNameLabel,
2328     IFileOpenDialog_fnGetResult,
2329     IFileOpenDialog_fnAddPlace,
2330     IFileOpenDialog_fnSetDefaultExtension,
2331     IFileOpenDialog_fnClose,
2332     IFileOpenDialog_fnSetClientGuid,
2333     IFileOpenDialog_fnClearClientData,
2334     IFileOpenDialog_fnSetFilter,
2335     IFileOpenDialog_fnGetResults,
2336     IFileOpenDialog_fnGetSelectedItems
2337 };
2338
2339 /**************************************************************************
2340  * IFileSaveDialog
2341  */
2342 static inline FileDialogImpl *impl_from_IFileSaveDialog(IFileSaveDialog *iface)
2343 {
2344     return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
2345 }
2346
2347 static HRESULT WINAPI IFileSaveDialog_fnQueryInterface(IFileSaveDialog *iface,
2348                                                        REFIID riid,
2349                                                        void **ppvObject)
2350 {
2351     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2352     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2353 }
2354
2355 static ULONG WINAPI IFileSaveDialog_fnAddRef(IFileSaveDialog *iface)
2356 {
2357     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2358     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2359 }
2360
2361 static ULONG WINAPI IFileSaveDialog_fnRelease(IFileSaveDialog *iface)
2362 {
2363     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2364     return IFileDialog2_Release(&This->IFileDialog2_iface);
2365 }
2366
2367 static HRESULT WINAPI IFileSaveDialog_fnShow(IFileSaveDialog *iface, HWND hwndOwner)
2368 {
2369     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2370     return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2371 }
2372
2373 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypes(IFileSaveDialog *iface, UINT cFileTypes,
2374                                                      const COMDLG_FILTERSPEC *rgFilterSpec)
2375 {
2376     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2377     return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2378 }
2379
2380 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypeIndex(IFileSaveDialog *iface, UINT iFileType)
2381 {
2382     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2383     return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2384 }
2385
2386 static HRESULT WINAPI IFileSaveDialog_fnGetFileTypeIndex(IFileSaveDialog *iface, UINT *piFileType)
2387 {
2388     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2389     return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2390 }
2391
2392 static HRESULT WINAPI IFileSaveDialog_fnAdvise(IFileSaveDialog *iface, IFileDialogEvents *pfde,
2393                                                DWORD *pdwCookie)
2394 {
2395     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2396     return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2397 }
2398
2399 static HRESULT WINAPI IFileSaveDialog_fnUnadvise(IFileSaveDialog *iface, DWORD dwCookie)
2400 {
2401     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2402     return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2403 }
2404
2405 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
2406 {
2407     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2408     return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2409 }
2410
2411 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2412 {
2413     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2414     return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2415 }
2416
2417 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultFolder(IFileSaveDialog *iface, IShellItem *psi)
2418 {
2419     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2420     return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2421 }
2422
2423 static HRESULT WINAPI IFileSaveDialog_fnSetFolder(IFileSaveDialog *iface, IShellItem *psi)
2424 {
2425     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2426     return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2427 }
2428
2429 static HRESULT WINAPI IFileSaveDialog_fnGetFolder(IFileSaveDialog *iface, IShellItem **ppsi)
2430 {
2431     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2432     return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2433 }
2434
2435 static HRESULT WINAPI IFileSaveDialog_fnGetCurrentSelection(IFileSaveDialog *iface, IShellItem **ppsi)
2436 {
2437     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2438     return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2439 }
2440
2441 static HRESULT WINAPI IFileSaveDialog_fnSetFileName(IFileSaveDialog *iface, LPCWSTR pszName)
2442 {
2443     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2444     return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2445 }
2446
2447 static HRESULT WINAPI IFileSaveDialog_fnGetFileName(IFileSaveDialog *iface, LPWSTR *pszName)
2448 {
2449     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2450     return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2451 }
2452
2453 static HRESULT WINAPI IFileSaveDialog_fnSetTitle(IFileSaveDialog *iface, LPCWSTR pszTitle)
2454 {
2455     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2456     return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2457 }
2458
2459 static HRESULT WINAPI IFileSaveDialog_fnSetOkButtonLabel(IFileSaveDialog *iface, LPCWSTR pszText)
2460 {
2461     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2462     return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2463 }
2464
2465 static HRESULT WINAPI IFileSaveDialog_fnSetFileNameLabel(IFileSaveDialog *iface, LPCWSTR pszLabel)
2466 {
2467     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2468     return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2469 }
2470
2471 static HRESULT WINAPI IFileSaveDialog_fnGetResult(IFileSaveDialog *iface, IShellItem **ppsi)
2472 {
2473     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2474     return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2475 }
2476
2477 static HRESULT WINAPI IFileSaveDialog_fnAddPlace(IFileSaveDialog *iface, IShellItem *psi, FDAP fdap)
2478 {
2479     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2480     return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2481 }
2482
2483 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultExtension(IFileSaveDialog *iface,
2484                                                             LPCWSTR pszDefaultExtension)
2485 {
2486     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2487     return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2488 }
2489
2490 static HRESULT WINAPI IFileSaveDialog_fnClose(IFileSaveDialog *iface, HRESULT hr)
2491 {
2492     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2493     return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2494 }
2495
2496 static HRESULT WINAPI IFileSaveDialog_fnSetClientGuid(IFileSaveDialog *iface, REFGUID guid)
2497 {
2498     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2499     return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2500 }
2501
2502 static HRESULT WINAPI IFileSaveDialog_fnClearClientData(IFileSaveDialog *iface)
2503 {
2504     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2505     return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2506 }
2507
2508 static HRESULT WINAPI IFileSaveDialog_fnSetFilter(IFileSaveDialog *iface, IShellItemFilter *pFilter)
2509 {
2510     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2511     return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2512 }
2513
2514 static HRESULT WINAPI IFileSaveDialog_fnSetSaveAsItem(IFileSaveDialog* iface, IShellItem *psi)
2515 {
2516     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2517     FIXME("stub - %p (%p)\n", This, psi);
2518     return E_NOTIMPL;
2519 }
2520
2521 static HRESULT WINAPI IFileSaveDialog_fnSetProperties(IFileSaveDialog* iface, IPropertyStore *pStore)
2522 {
2523     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2524     FIXME("stub - %p (%p)\n", This, pStore);
2525     return E_NOTIMPL;
2526 }
2527
2528 static HRESULT WINAPI IFileSaveDialog_fnSetCollectedProperties(IFileSaveDialog* iface,
2529                                                                IPropertyDescriptionList *pList,
2530                                                                BOOL fAppendDefault)
2531 {
2532     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2533     FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
2534     return E_NOTIMPL;
2535 }
2536
2537 static HRESULT WINAPI IFileSaveDialog_fnGetProperties(IFileSaveDialog* iface, IPropertyStore **ppStore)
2538 {
2539     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2540     FIXME("stub - %p (%p)\n", This, ppStore);
2541     return E_NOTIMPL;
2542 }
2543
2544 static HRESULT WINAPI IFileSaveDialog_fnApplyProperties(IFileSaveDialog* iface,
2545                                                         IShellItem *psi,
2546                                                         IPropertyStore *pStore,
2547                                                         HWND hwnd,
2548                                                         IFileOperationProgressSink *pSink)
2549 {
2550     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2551     FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
2552     return E_NOTIMPL;
2553 }
2554
2555 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
2556     IFileSaveDialog_fnQueryInterface,
2557     IFileSaveDialog_fnAddRef,
2558     IFileSaveDialog_fnRelease,
2559     IFileSaveDialog_fnShow,
2560     IFileSaveDialog_fnSetFileTypes,
2561     IFileSaveDialog_fnSetFileTypeIndex,
2562     IFileSaveDialog_fnGetFileTypeIndex,
2563     IFileSaveDialog_fnAdvise,
2564     IFileSaveDialog_fnUnadvise,
2565     IFileSaveDialog_fnSetOptions,
2566     IFileSaveDialog_fnGetOptions,
2567     IFileSaveDialog_fnSetDefaultFolder,
2568     IFileSaveDialog_fnSetFolder,
2569     IFileSaveDialog_fnGetFolder,
2570     IFileSaveDialog_fnGetCurrentSelection,
2571     IFileSaveDialog_fnSetFileName,
2572     IFileSaveDialog_fnGetFileName,
2573     IFileSaveDialog_fnSetTitle,
2574     IFileSaveDialog_fnSetOkButtonLabel,
2575     IFileSaveDialog_fnSetFileNameLabel,
2576     IFileSaveDialog_fnGetResult,
2577     IFileSaveDialog_fnAddPlace,
2578     IFileSaveDialog_fnSetDefaultExtension,
2579     IFileSaveDialog_fnClose,
2580     IFileSaveDialog_fnSetClientGuid,
2581     IFileSaveDialog_fnClearClientData,
2582     IFileSaveDialog_fnSetFilter,
2583     IFileSaveDialog_fnSetSaveAsItem,
2584     IFileSaveDialog_fnSetProperties,
2585     IFileSaveDialog_fnSetCollectedProperties,
2586     IFileSaveDialog_fnGetProperties,
2587     IFileSaveDialog_fnApplyProperties
2588 };
2589
2590 /**************************************************************************
2591  * IExplorerBrowserEvents implementation
2592  */
2593 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
2594 {
2595     return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
2596 }
2597
2598 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
2599                                                               REFIID riid, void **ppvObject)
2600 {
2601     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2602     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
2603
2604     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2605 }
2606
2607 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
2608 {
2609     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2610     TRACE("%p\n", This);
2611     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2612 }
2613
2614 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
2615 {
2616     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2617     TRACE("%p\n", This);
2618     return IFileDialog2_Release(&This->IFileDialog2_iface);
2619 }
2620
2621 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
2622                                                                    PCIDLIST_ABSOLUTE pidlFolder)
2623 {
2624     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2625     IShellItem *psi;
2626     HRESULT hr;
2627     TRACE("%p (%p)\n", This, pidlFolder);
2628
2629     hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
2630     if(SUCCEEDED(hr))
2631     {
2632         hr = events_OnFolderChanging(This, psi);
2633         IShellItem_Release(psi);
2634
2635         /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
2636         if(hr == S_FALSE)
2637             hr = E_FAIL;
2638
2639         return hr;
2640     }
2641     else
2642         ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
2643
2644     return S_OK;
2645 }
2646
2647 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
2648                                                              IShellView *psv)
2649 {
2650     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2651     TRACE("%p (%p)\n", This, psv);
2652     return S_OK;
2653 }
2654
2655 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
2656                                                                     PCIDLIST_ABSOLUTE pidlFolder)
2657 {
2658     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2659     HRESULT hr;
2660     TRACE("%p (%p)\n", This, pidlFolder);
2661
2662     if(This->psi_folder)
2663         IShellItem_Release(This->psi_folder);
2664
2665     hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
2666     if(FAILED(hr))
2667     {
2668         ERR("Failed to get the current folder.\n");
2669         This->psi_folder = NULL;
2670     }
2671
2672     events_OnFolderChange(This);
2673
2674     return S_OK;
2675 }
2676
2677 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
2678                                                                   PCIDLIST_ABSOLUTE pidlFolder)
2679 {
2680     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2681     TRACE("%p (%p)\n", This, pidlFolder);
2682     return S_OK;
2683 }
2684
2685 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
2686     IExplorerBrowserEvents_fnQueryInterface,
2687     IExplorerBrowserEvents_fnAddRef,
2688     IExplorerBrowserEvents_fnRelease,
2689     IExplorerBrowserEvents_fnOnNavigationPending,
2690     IExplorerBrowserEvents_fnOnViewCreated,
2691     IExplorerBrowserEvents_fnOnNavigationComplete,
2692     IExplorerBrowserEvents_fnOnNavigationFailed
2693 };
2694
2695 /**************************************************************************
2696  * IServiceProvider implementation
2697  */
2698 static inline FileDialogImpl *impl_from_IServiceProvider(IServiceProvider *iface)
2699 {
2700     return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
2701 }
2702
2703 static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface,
2704                                                         REFIID riid, void **ppvObject)
2705 {
2706     FileDialogImpl *This = impl_from_IServiceProvider(iface);
2707     TRACE("%p\n", This);
2708     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2709 }
2710
2711 static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface)
2712 {
2713     FileDialogImpl *This = impl_from_IServiceProvider(iface);
2714     TRACE("%p\n", This);
2715     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2716 }
2717
2718 static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface)
2719 {
2720     FileDialogImpl *This = impl_from_IServiceProvider(iface);
2721     TRACE("%p\n", This);
2722     return IFileDialog2_Release(&This->IFileDialog2_iface);
2723 }
2724
2725 static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
2726                                                       REFGUID guidService,
2727                                                       REFIID riid, void **ppv)
2728 {
2729     FileDialogImpl *This = impl_from_IServiceProvider(iface);
2730     HRESULT hr = E_FAIL;
2731     TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
2732
2733     *ppv = NULL;
2734     if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
2735         hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
2736     else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
2737         hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
2738     else
2739         FIXME("Interface %s requested from unknown service %s\n",
2740               debugstr_guid(riid), debugstr_guid(guidService));
2741
2742     return hr;
2743 }
2744
2745 static const IServiceProviderVtbl vt_IServiceProvider = {
2746     IServiceProvider_fnQueryInterface,
2747     IServiceProvider_fnAddRef,
2748     IServiceProvider_fnRelease,
2749     IServiceProvider_fnQueryService
2750 };
2751
2752 /**************************************************************************
2753  * ICommDlgBrowser3 implementation
2754  */
2755 static inline FileDialogImpl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface)
2756 {
2757     return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
2758 }
2759
2760 static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface,
2761                                                         REFIID riid, void **ppvObject)
2762 {
2763     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2764     TRACE("%p\n", This);
2765     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2766 }
2767
2768 static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface)
2769 {
2770     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2771     TRACE("%p\n", This);
2772     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2773 }
2774
2775 static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface)
2776 {
2777     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2778     TRACE("%p\n", This);
2779     return IFileDialog2_Release(&This->IFileDialog2_iface);
2780 }
2781
2782 static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3 *iface,
2783                                                           IShellView *shv)
2784 {
2785     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2786     HRESULT hr;
2787     TRACE("%p (%p)\n", This, shv);
2788
2789     hr = on_default_action(This);
2790
2791     if(SUCCEEDED(hr))
2792         EndDialog(This->dlg_hwnd, S_OK);
2793
2794     return S_OK;
2795 }
2796
2797 static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange(ICommDlgBrowser3 *iface,
2798                                                        IShellView *shv, ULONG uChange )
2799 {
2800     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2801     IDataObject *new_selection;
2802     HRESULT hr;
2803     TRACE("%p (%p, %x)\n", This, shv, uChange);
2804
2805     switch(uChange)
2806     {
2807     case CDBOSC_SELCHANGE:
2808         if(This->psia_selection)
2809         {
2810             IShellItemArray_Release(This->psia_selection);
2811             This->psia_selection = NULL;
2812         }
2813
2814         hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
2815         if(SUCCEEDED(hr))
2816         {
2817             hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
2818                                                       (void**)&This->psia_selection);
2819             if(SUCCEEDED(hr))
2820             {
2821                 fill_filename_from_selection(This);
2822                 events_OnSelectionChange(This);
2823             }
2824
2825             IDataObject_Release(new_selection);
2826         }
2827         break;
2828     default:
2829         TRACE("Unhandled state change\n");
2830     }
2831     return S_OK;
2832 }
2833
2834 static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
2835                                                        IShellView *shv, LPCITEMIDLIST pidl)
2836 {
2837     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2838     IShellItem *psi;
2839     LPWSTR filename;
2840     LPITEMIDLIST parent_pidl;
2841     HRESULT hr;
2842     ULONG attr;
2843     TRACE("%p (%p, %p)\n", This, shv, pidl);
2844
2845     if(!This->filterspec_count)
2846         return S_OK;
2847
2848     hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
2849     if(SUCCEEDED(hr))
2850     {
2851         LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
2852         hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
2853         ILFree(parent_pidl);
2854         ILFree(full_pidl);
2855     }
2856     if(FAILED(hr))
2857     {
2858         ERR("Failed to get shellitem (%08x).\n", hr);
2859         return S_OK;
2860     }
2861
2862     hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
2863     if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
2864     {
2865         IShellItem_Release(psi);
2866         return S_OK;
2867     }
2868
2869     hr = S_OK;
2870     if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
2871     {
2872         if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
2873             hr = S_FALSE;
2874         CoTaskMemFree(filename);
2875     }
2876
2877     IShellItem_Release(psi);
2878     return hr;
2879 }
2880
2881 static HRESULT WINAPI ICommDlgBrowser3_fnNotify(ICommDlgBrowser3 *iface,
2882                                                 IShellView *ppshv, DWORD dwNotifyType)
2883 {
2884     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2885     FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
2886     return E_NOTIMPL;
2887 }
2888
2889 static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText(ICommDlgBrowser3 *iface,
2890                                                             IShellView *pshv,
2891                                                             LPWSTR pszText, int cchMax)
2892 {
2893     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2894     FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
2895     return E_NOTIMPL;
2896 }
2897
2898 static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags(ICommDlgBrowser3 *iface, DWORD *pdwFlags)
2899 {
2900     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2901     FIXME("Stub: %p (%p)\n", This, pdwFlags);
2902     return E_NOTIMPL;
2903 }
2904
2905 static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked(ICommDlgBrowser3 *iface,
2906                                                          IShellView *pshv, int iColumn)
2907 {
2908     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2909     FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
2910     return E_NOTIMPL;
2911 }
2912
2913 static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter(ICommDlgBrowser3 *iface,
2914                                                           LPWSTR pszFileSpec, int cchFileSpec)
2915 {
2916     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2917     FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
2918     return E_NOTIMPL;
2919 }
2920
2921 static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated(ICommDlgBrowser3 *iface,
2922                                                           IShellView *pshv)
2923 {
2924     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2925     FIXME("Stub: %p (%p)\n", This, pshv);
2926     return E_NOTIMPL;
2927 }
2928
2929 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
2930     ICommDlgBrowser3_fnQueryInterface,
2931     ICommDlgBrowser3_fnAddRef,
2932     ICommDlgBrowser3_fnRelease,
2933     ICommDlgBrowser3_fnOnDefaultCommand,
2934     ICommDlgBrowser3_fnOnStateChange,
2935     ICommDlgBrowser3_fnIncludeObject,
2936     ICommDlgBrowser3_fnNotify,
2937     ICommDlgBrowser3_fnGetDefaultMenuText,
2938     ICommDlgBrowser3_fnGetViewFlags,
2939     ICommDlgBrowser3_fnOnColumnClicked,
2940     ICommDlgBrowser3_fnGetCurrentFilter,
2941     ICommDlgBrowser3_fnOnPreviewCreated
2942 };
2943
2944 /**************************************************************************
2945  * IOleWindow implementation
2946  */
2947 static inline FileDialogImpl *impl_from_IOleWindow(IOleWindow *iface)
2948 {
2949     return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
2950 }
2951
2952 static HRESULT WINAPI IOleWindow_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
2953 {
2954     FileDialogImpl *This = impl_from_IOleWindow(iface);
2955     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2956 }
2957
2958 static ULONG WINAPI IOleWindow_fnAddRef(IOleWindow *iface)
2959 {
2960     FileDialogImpl *This = impl_from_IOleWindow(iface);
2961     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2962 }
2963
2964 static ULONG WINAPI IOleWindow_fnRelease(IOleWindow *iface)
2965 {
2966     FileDialogImpl *This = impl_from_IOleWindow(iface);
2967     return IFileDialog2_Release(&This->IFileDialog2_iface);
2968 }
2969
2970 static HRESULT WINAPI IOleWindow_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMOde)
2971 {
2972     FileDialogImpl *This = impl_from_IOleWindow(iface);
2973     FIXME("Stub: %p (%d)\n", This, fEnterMOde);
2974     return E_NOTIMPL;
2975 }
2976
2977 static HRESULT WINAPI IOleWindow_fnGetWindow(IOleWindow *iface, HWND *phwnd)
2978 {
2979     FileDialogImpl *This = impl_from_IOleWindow(iface);
2980     TRACE("%p (%p)\n", This, phwnd);
2981     *phwnd = This->dlg_hwnd;
2982     return S_OK;
2983 }
2984
2985 static const IOleWindowVtbl vt_IOleWindow = {
2986     IOleWindow_fnQueryInterface,
2987     IOleWindow_fnAddRef,
2988     IOleWindow_fnRelease,
2989     IOleWindow_fnGetWindow,
2990     IOleWindow_fnContextSensitiveHelp
2991 };
2992
2993 /**************************************************************************
2994  * IFileDialogCustomize implementation
2995  */
2996 static inline FileDialogImpl *impl_from_IFileDialogCustomize(IFileDialogCustomize *iface)
2997 {
2998     return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialogCustomize_iface);
2999 }
3000
3001 static HRESULT WINAPI IFileDialogCustomize_fnQueryInterface(IFileDialogCustomize *iface,
3002                                                             REFIID riid, void **ppvObject)
3003 {
3004     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3005     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3006 }
3007
3008 static ULONG WINAPI IFileDialogCustomize_fnAddRef(IFileDialogCustomize *iface)
3009 {
3010     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3011     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3012 }
3013
3014 static ULONG WINAPI IFileDialogCustomize_fnRelease(IFileDialogCustomize *iface)
3015 {
3016     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3017     return IFileDialog2_Release(&This->IFileDialog2_iface);
3018 }
3019
3020 static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCustomize *iface,
3021                                                                 DWORD dwIDCtl)
3022 {
3023     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3024     FIXME("stub - %p (%d)\n", This, dwIDCtl);
3025     return E_NOTIMPL;
3026 }
3027
3028 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
3029                                                      DWORD dwIDCtl,
3030                                                      LPCWSTR pszLabel)
3031 {
3032     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3033     customctrl *ctrl;
3034     TBBUTTON tbb;
3035     HRESULT hr;
3036     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3037
3038     hr = cctrl_create_new(This, dwIDCtl, NULL, TOOLBARCLASSNAMEW,
3039                           TBSTYLE_FLAT | CCS_NODIVIDER, 0,
3040                           This->cctrl_def_height, &ctrl);
3041     if(SUCCEEDED(hr))
3042     {
3043         ctrl->type = IDLG_CCTRL_MENU;
3044
3045         /* Add the actual button with a popup menu. */
3046         tbb.iBitmap = I_IMAGENONE;
3047         tbb.dwData = (DWORD_PTR)CreatePopupMenu();
3048         tbb.iString = (DWORD_PTR)pszLabel;
3049         tbb.fsState = TBSTATE_ENABLED;
3050         tbb.fsStyle = BTNS_WHOLEDROPDOWN;
3051         tbb.idCommand = 1;
3052
3053         SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
3054     }
3055
3056     return hr;
3057 }
3058
3059 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
3060                                                            DWORD dwIDCtl,
3061                                                            LPCWSTR pszLabel)
3062 {
3063     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3064     customctrl *ctrl;
3065     HRESULT hr;
3066     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3067
3068     hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
3069                           This->cctrl_def_height, &ctrl);
3070     if(SUCCEEDED(hr))
3071         ctrl->type = IDLG_CCTRL_PUSHBUTTON;
3072
3073     return hr;
3074 }
3075
3076 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
3077                                                          DWORD dwIDCtl)
3078 {
3079     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3080     customctrl *ctrl;
3081     HRESULT hr;
3082     TRACE("%p (%d)\n", This, dwIDCtl);
3083
3084     hr =  cctrl_create_new(This, dwIDCtl, NULL, WC_COMBOBOXW, CBS_DROPDOWNLIST, 0,
3085                            This->cctrl_def_height, &ctrl);
3086     if(SUCCEEDED(hr))
3087         ctrl->type = IDLG_CCTRL_COMBOBOX;
3088
3089     return hr;
3090 }
3091
3092 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
3093                                                                 DWORD dwIDCtl)
3094 {
3095     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3096     FIXME("stub - %p (%d)\n", This, dwIDCtl);
3097     return E_NOTIMPL;
3098 }
3099
3100 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
3101                                                             DWORD dwIDCtl,
3102                                                             LPCWSTR pszLabel,
3103                                                             BOOL bChecked)
3104 {
3105     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3106     customctrl *ctrl;
3107     HRESULT hr;
3108     TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
3109
3110     hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX, 0,
3111                           This->cctrl_def_height, &ctrl);
3112     if(SUCCEEDED(hr))
3113     {
3114         ctrl->type = IDLG_CCTRL_CHECKBUTTON;
3115         SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
3116     }
3117
3118     return hr;
3119 }
3120
3121 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
3122                                                         DWORD dwIDCtl,
3123                                                         LPCWSTR pszText)
3124 {
3125     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3126     customctrl *ctrl;
3127     HRESULT hr;
3128     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3129
3130     hr = cctrl_create_new(This, dwIDCtl, pszText, WC_EDITW, ES_AUTOHSCROLL, WS_EX_CLIENTEDGE,
3131                           This->cctrl_def_height, &ctrl);
3132     if(SUCCEEDED(hr))
3133         ctrl->type = IDLG_CCTRL_EDITBOX;
3134
3135     return hr;
3136 }
3137
3138 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
3139                                                           DWORD dwIDCtl)
3140 {
3141     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3142     customctrl *ctrl;
3143     HRESULT hr;
3144     TRACE("%p (%d)\n", This, dwIDCtl);
3145
3146     hr = cctrl_create_new(This, dwIDCtl, NULL, WC_STATICW, SS_ETCHEDHORZ, 0,
3147                           GetSystemMetrics(SM_CYEDGE), &ctrl);
3148     if(SUCCEEDED(hr))
3149         ctrl->type = IDLG_CCTRL_SEPARATOR;
3150
3151     return hr;
3152 }
3153
3154 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
3155                                                      DWORD dwIDCtl,
3156                                                      LPCWSTR pszText)
3157 {
3158     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3159     customctrl *ctrl;
3160     HRESULT hr;
3161     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3162
3163     hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
3164                           This->cctrl_def_height, &ctrl);
3165     if(SUCCEEDED(hr))
3166         ctrl->type = IDLG_CCTRL_TEXT;
3167
3168     return hr;
3169 }
3170
3171 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
3172                                                              DWORD dwIDCtl,
3173                                                              LPCWSTR pszLabel)
3174 {
3175     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3176     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3177     TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3178
3179     if(!ctrl) return E_INVALIDARG;
3180
3181     switch(ctrl->type)
3182     {
3183     case IDLG_CCTRL_MENU:
3184     case IDLG_CCTRL_PUSHBUTTON:
3185     case IDLG_CCTRL_CHECKBUTTON:
3186     case IDLG_CCTRL_TEXT:
3187         SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
3188         break;
3189     default:
3190         break;
3191     }
3192
3193     return S_OK;
3194 }
3195
3196 static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomize *iface,
3197                                                              DWORD dwIDCtl,
3198                                                              CDCONTROLSTATEF *pdwState)
3199 {
3200     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3201     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3202     TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
3203
3204     if(!ctrl) return E_NOTIMPL;
3205
3206     *pdwState = ctrl->cdcstate;
3207     return S_OK;
3208 }
3209
3210 static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomize *iface,
3211                                                              DWORD dwIDCtl,
3212                                                              CDCONTROLSTATEF dwState)
3213 {
3214     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3215     customctrl *ctrl = get_cctrl(This,dwIDCtl);
3216     TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
3217
3218     if(ctrl)
3219     {
3220         LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
3221
3222         if(dwState & CDCS_ENABLED)
3223             wndstyle &= ~(WS_DISABLED);
3224         else
3225             wndstyle |= WS_DISABLED;
3226
3227         if(dwState & CDCS_VISIBLE)
3228             wndstyle |= WS_VISIBLE;
3229         else
3230             wndstyle &= ~(WS_VISIBLE);
3231
3232         SetWindowLongW(ctrl->hwnd, GWL_STYLE, wndstyle);
3233
3234         /* We save the state separately since at least one application
3235          * relies on being able to hide a control. */
3236         ctrl->cdcstate = dwState;
3237     }
3238
3239     return S_OK;
3240 }
3241
3242 static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize *iface,
3243                                                             DWORD dwIDCtl,
3244                                                             WCHAR **ppszText)
3245 {
3246     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3247     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3248     WCHAR len, *text;
3249     TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
3250
3251     if(!ctrl || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
3252         return E_FAIL;
3253
3254     text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
3255     if(!text) return E_FAIL;
3256
3257     SendMessageW(ctrl->hwnd, WM_GETTEXT, len+1, (LPARAM)text);
3258     *ppszText = text;
3259     return S_OK;
3260 }
3261
3262 static HRESULT WINAPI IFileDialogCustomize_fnSetEditBoxText(IFileDialogCustomize *iface,
3263                                                             DWORD dwIDCtl,
3264                                                             LPCWSTR pszText)
3265 {
3266     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3267     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3268     TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
3269
3270     if(!ctrl || ctrl->type != IDLG_CCTRL_EDITBOX)
3271         return E_FAIL;
3272
3273     SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
3274     return S_OK;
3275 }
3276
3277 static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCustomize *iface,
3278                                                                  DWORD dwIDCtl,
3279                                                                  BOOL *pbChecked)
3280 {
3281     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3282     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3283     TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
3284
3285     if(ctrl)
3286         *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
3287
3288     return S_OK;
3289 }
3290
3291 static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCustomize *iface,
3292                                                                  DWORD dwIDCtl,
3293                                                                  BOOL bChecked)
3294 {
3295     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3296     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3297     TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
3298
3299     if(ctrl)
3300         SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
3301
3302     return S_OK;
3303 }
3304
3305 static UINT get_combobox_index_from_id(HWND cb_hwnd, DWORD dwIDItem)
3306 {
3307     UINT count = SendMessageW(cb_hwnd, CB_GETCOUNT, 0, 0);
3308     UINT i;
3309     if(!count || (count == CB_ERR))
3310         return -1;
3311
3312     for(i = 0; i < count; i++)
3313         if(SendMessageW(cb_hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
3314             return i;
3315
3316     TRACE("Item with id %d not found in combobox %p (item count: %d)\n", dwIDItem, cb_hwnd, count);
3317     return -1;
3318 }
3319
3320 static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize *iface,
3321                                                             DWORD dwIDCtl,
3322                                                             DWORD dwIDItem,
3323                                                             LPCWSTR pszLabel)
3324 {
3325     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3326     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3327     TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
3328
3329     if(!ctrl) return E_FAIL;
3330
3331     switch(ctrl->type)
3332     {
3333     case IDLG_CCTRL_COMBOBOX:
3334     {
3335         UINT index;
3336
3337         if(get_combobox_index_from_id(ctrl->hwnd, dwIDItem) != -1)
3338             return E_INVALIDARG;
3339
3340         index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
3341         SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
3342
3343         return S_OK;
3344     }
3345     case IDLG_CCTRL_MENU:
3346     {
3347         TBBUTTON tbb;
3348         SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
3349
3350         if(GetMenuState((HMENU)tbb.dwData, dwIDItem, MF_BYCOMMAND) != -1)
3351             return E_INVALIDARG;
3352
3353         AppendMenuW((HMENU)tbb.dwData, MF_STRING, dwIDItem, pszLabel);
3354         return S_OK;
3355     }
3356     default:
3357         break;
3358     }
3359
3360     return E_NOINTERFACE; /* win7 */
3361 }
3362
3363 static HRESULT WINAPI IFileDialogCustomize_fnRemoveControlItem(IFileDialogCustomize *iface,
3364                                                                DWORD dwIDCtl,
3365                                                                DWORD dwIDItem)
3366 {
3367     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3368     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3369     TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
3370
3371     if(!ctrl) return E_FAIL;
3372
3373     switch(ctrl->type)
3374     {
3375     case IDLG_CCTRL_COMBOBOX:
3376     {
3377         UINT i, count = SendMessageW(ctrl->hwnd, CB_GETCOUNT, 0, 0);
3378         if(!count || (count == CB_ERR))
3379             return E_FAIL;
3380
3381         for(i = 0; i < count; i++)
3382             if(SendMessageW(ctrl->hwnd, CB_GETITEMDATA, 0, 0) == dwIDItem)
3383             {
3384                 if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, i, 0) == CB_ERR)
3385                     return E_FAIL;
3386                 return S_OK;
3387             }
3388
3389         return E_UNEXPECTED;
3390     }
3391     case IDLG_CCTRL_MENU:
3392     {
3393         TBBUTTON tbb;
3394         HMENU hmenu;
3395         SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
3396         hmenu = (HMENU)tbb.dwData;
3397
3398         if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
3399             return E_UNEXPECTED;
3400
3401         return S_OK;
3402     }
3403     default:
3404         break;
3405     }
3406
3407     return E_FAIL;
3408 }
3409
3410 static HRESULT WINAPI IFileDialogCustomize_fnRemoveAllControlItems(IFileDialogCustomize *iface,
3411                                                                    DWORD dwIDCtl)
3412 {
3413     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3414     TRACE("%p (%d)\n", This, dwIDCtl);
3415
3416     /* Not implemented by native */
3417     return E_NOTIMPL;
3418 }
3419
3420 static HRESULT WINAPI IFileDialogCustomize_fnGetControlItemState(IFileDialogCustomize *iface,
3421                                                                  DWORD dwIDCtl,
3422                                                                  DWORD dwIDItem,
3423                                                                  CDCONTROLSTATEF *pdwState)
3424 {
3425     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3426     FIXME("stub - %p\n", This);
3427     return E_NOTIMPL;
3428 }
3429
3430 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCustomize *iface,
3431                                                                  DWORD dwIDCtl,
3432                                                                  DWORD dwIDItem,
3433                                                                  CDCONTROLSTATEF dwState)
3434 {
3435     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3436     FIXME("stub - %p\n", This);
3437     return E_NOTIMPL;
3438 }
3439
3440 static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogCustomize *iface,
3441                                                                     DWORD dwIDCtl,
3442                                                                     DWORD *pdwIDItem)
3443 {
3444     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3445     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3446     TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwIDItem);
3447
3448     if(!ctrl) return E_FAIL;
3449
3450     switch(ctrl->type)
3451     {
3452     case IDLG_CCTRL_COMBOBOX:
3453     {
3454         UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
3455         if(index == CB_ERR)
3456             return E_FAIL;
3457
3458         *pdwIDItem = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
3459         return S_OK;
3460     }
3461     default:
3462         FIXME("Unsupported control type %d\n", ctrl->type);
3463     }
3464
3465     return E_NOTIMPL;
3466 }
3467
3468 static HRESULT WINAPI IFileDialogCustomize_fnSetSelectedControlItem(IFileDialogCustomize *iface,
3469                                                                     DWORD dwIDCtl,
3470                                                                     DWORD dwIDItem)
3471 {
3472     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3473     customctrl *ctrl = get_cctrl(This, dwIDCtl);
3474     TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
3475
3476     if(!ctrl) return E_INVALIDARG;
3477
3478     switch(ctrl->type)
3479     {
3480     case IDLG_CCTRL_COMBOBOX:
3481     {
3482         UINT index = get_combobox_index_from_id(ctrl->hwnd, dwIDItem);
3483
3484         if(index == -1)
3485             return E_INVALIDARG;
3486
3487         if(SendMessageW(ctrl->hwnd, CB_SETCURSEL, index, 0) == CB_ERR)
3488             return E_FAIL;
3489
3490         return S_OK;
3491     }
3492     default:
3493         FIXME("Unsupported control type %d\n", ctrl->type);
3494     }
3495
3496     return E_INVALIDARG;
3497 }
3498
3499 static HRESULT WINAPI IFileDialogCustomize_fnStartVisualGroup(IFileDialogCustomize *iface,
3500                                                               DWORD dwIDCtl,
3501                                                               LPCWSTR pszLabel)
3502 {
3503     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3504     FIXME("stub - %p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
3505     return E_NOTIMPL;
3506 }
3507
3508 static HRESULT WINAPI IFileDialogCustomize_fnEndVisualGroup(IFileDialogCustomize *iface)
3509 {
3510     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3511     FIXME("stub - %p\n", This);
3512     return E_NOTIMPL;
3513 }
3514
3515 static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize *iface,
3516                                                            DWORD dwIDCtl)
3517 {
3518     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3519     FIXME("stub - %p (%d)\n", This, dwIDCtl);
3520     return E_NOTIMPL;
3521 }
3522
3523 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCustomize *iface,
3524                                                                 DWORD dwIDCtl,
3525                                                                 DWORD dwIDItem,
3526                                                                 LPCWSTR pszLabel)
3527 {
3528     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3529     FIXME("stub - %p (%d, %d, %p)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
3530     return E_NOTIMPL;
3531 }
3532
3533 static const IFileDialogCustomizeVtbl vt_IFileDialogCustomize = {
3534     IFileDialogCustomize_fnQueryInterface,
3535     IFileDialogCustomize_fnAddRef,
3536     IFileDialogCustomize_fnRelease,
3537     IFileDialogCustomize_fnEnableOpenDropDown,
3538     IFileDialogCustomize_fnAddMenu,
3539     IFileDialogCustomize_fnAddPushButton,
3540     IFileDialogCustomize_fnAddComboBox,
3541     IFileDialogCustomize_fnAddRadioButtonList,
3542     IFileDialogCustomize_fnAddCheckButton,
3543     IFileDialogCustomize_fnAddEditBox,
3544     IFileDialogCustomize_fnAddSeparator,
3545     IFileDialogCustomize_fnAddText,
3546     IFileDialogCustomize_fnSetControlLabel,
3547     IFileDialogCustomize_fnGetControlState,
3548     IFileDialogCustomize_fnSetControlState,
3549     IFileDialogCustomize_fnGetEditBoxText,
3550     IFileDialogCustomize_fnSetEditBoxText,
3551     IFileDialogCustomize_fnGetCheckButtonState,
3552     IFileDialogCustomize_fnSetCheckButtonState,
3553     IFileDialogCustomize_fnAddControlItem,
3554     IFileDialogCustomize_fnRemoveControlItem,
3555     IFileDialogCustomize_fnRemoveAllControlItems,
3556     IFileDialogCustomize_fnGetControlItemState,
3557     IFileDialogCustomize_fnSetControlItemState,
3558     IFileDialogCustomize_fnGetSelectedControlItem,
3559     IFileDialogCustomize_fnSetSelectedControlItem,
3560     IFileDialogCustomize_fnStartVisualGroup,
3561     IFileDialogCustomize_fnEndVisualGroup,
3562     IFileDialogCustomize_fnMakeProminent,
3563     IFileDialogCustomize_fnSetControlItemText
3564 };
3565
3566 static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv, enum ITEMDLG_TYPE type)
3567 {
3568     FileDialogImpl *fdimpl;
3569     HRESULT hr;
3570     IShellFolder *psf;
3571     TRACE("%p, %s, %p\n", pUnkOuter, debugstr_guid(riid), ppv);
3572
3573     if(!ppv)
3574         return E_POINTER;
3575     if(pUnkOuter)
3576         return CLASS_E_NOAGGREGATION;
3577
3578     fdimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(FileDialogImpl));
3579     if(!fdimpl)
3580         return E_OUTOFMEMORY;
3581
3582     fdimpl->ref = 1;
3583     fdimpl->IFileDialog2_iface.lpVtbl = &vt_IFileDialog2;
3584     fdimpl->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
3585     fdimpl->IServiceProvider_iface.lpVtbl = &vt_IServiceProvider;
3586     fdimpl->ICommDlgBrowser3_iface.lpVtbl = &vt_ICommDlgBrowser3;
3587     fdimpl->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
3588     fdimpl->IFileDialogCustomize_iface.lpVtbl = &vt_IFileDialogCustomize;
3589
3590     if(type == ITEMDLG_TYPE_OPEN)
3591     {
3592         fdimpl->dlg_type = ITEMDLG_TYPE_OPEN;
3593         fdimpl->u.IFileOpenDialog_iface.lpVtbl = &vt_IFileOpenDialog;
3594         fdimpl->options = FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
3595         fdimpl->custom_title = fdimpl->custom_okbutton = NULL;
3596     }
3597     else
3598     {
3599         WCHAR buf[16];
3600         fdimpl->dlg_type = ITEMDLG_TYPE_SAVE;
3601         fdimpl->u.IFileSaveDialog_iface.lpVtbl = &vt_IFileSaveDialog;
3602         fdimpl->options = FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
3603
3604         LoadStringW(COMDLG32_hInstance, IDS_SAVE, buf, sizeof(buf)/sizeof(WCHAR));
3605         fdimpl->custom_title = StrDupW(buf);
3606         fdimpl->custom_okbutton = StrDupW(buf);
3607     }
3608
3609     fdimpl->filterspecs = NULL;
3610     fdimpl->filterspec_count = 0;
3611     fdimpl->filetypeindex = 0;
3612
3613     fdimpl->psia_selection = fdimpl->psia_results = NULL;
3614     fdimpl->psi_setfolder = fdimpl->psi_folder = NULL;
3615
3616     list_init(&fdimpl->events_clients);
3617     fdimpl->events_next_cookie = 0;
3618
3619     fdimpl->dlg_hwnd = NULL;
3620     fdimpl->peb = NULL;
3621
3622     fdimpl->set_filename = NULL;
3623     fdimpl->default_ext = NULL;
3624     fdimpl->custom_cancelbutton = fdimpl->custom_filenamelabel = NULL;
3625
3626     /* FIXME: The default folder setting should be restored for the
3627      * application if it was previously set. */
3628     SHGetDesktopFolder(&psf);
3629     SHGetItemFromObject((IUnknown*)psf, &IID_IShellItem, (void**)&fdimpl->psi_defaultfolder);
3630     IShellFolder_Release(psf);
3631
3632     hr = init_custom_controls(fdimpl);
3633     if(FAILED(hr))
3634     {
3635         ERR("Failed to initialize custom controls (0x%08x).\n", hr);
3636         IUnknown_Release((IUnknown*)fdimpl);
3637         return E_FAIL;
3638     }
3639
3640     hr = IUnknown_QueryInterface((IUnknown*)fdimpl, riid, ppv);
3641     IUnknown_Release((IUnknown*)fdimpl);
3642     return hr;
3643 }
3644
3645 HRESULT FileOpenDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
3646 {
3647     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_OPEN);
3648 }
3649
3650 HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
3651 {
3652     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);
3653 }