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