comdlg32: Improve handling of file extension changes in the Item Dialog.
[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 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
46
47 enum ITEMDLG_TYPE {
48     ITEMDLG_TYPE_OPEN,
49     ITEMDLG_TYPE_SAVE
50 };
51
52 typedef struct {
53     struct list entry;
54     IFileDialogEvents *pfde;
55     DWORD cookie;
56 } events_client;
57
58 typedef struct FileDialogImpl {
59     IFileDialog2 IFileDialog2_iface;
60     union {
61         IFileOpenDialog IFileOpenDialog_iface;
62         IFileSaveDialog IFileSaveDialog_iface;
63     } u;
64     enum ITEMDLG_TYPE dlg_type;
65     IExplorerBrowserEvents IExplorerBrowserEvents_iface;
66     IServiceProvider       IServiceProvider_iface;
67     ICommDlgBrowser3       ICommDlgBrowser3_iface;
68     IOleWindow             IOleWindow_iface;
69     LONG ref;
70
71     FILEOPENDIALOGOPTIONS options;
72     COMDLG_FILTERSPEC *filterspecs;
73     UINT filterspec_count;
74     UINT filetypeindex;
75
76     struct list events_clients;
77     DWORD events_next_cookie;
78
79     IShellItemArray *psia_selection;
80     IShellItemArray *psia_results;
81     IShellItem *psi_defaultfolder;
82     IShellItem *psi_setfolder;
83     IShellItem *psi_folder;
84
85     HWND dlg_hwnd;
86     IExplorerBrowser *peb;
87     DWORD ebevents_cookie;
88
89     LPWSTR set_filename;
90     LPWSTR default_ext;
91     LPWSTR custom_title;
92     LPWSTR custom_okbutton;
93     LPWSTR custom_cancelbutton;
94     LPWSTR custom_filenamelabel;
95 } FileDialogImpl;
96
97 /**************************************************************************
98  * Event wrappers.
99  */
100 static HRESULT events_OnFileOk(FileDialogImpl *This)
101 {
102     events_client *cursor;
103     HRESULT hr = S_OK;
104     TRACE("%p\n", This);
105
106     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
107     {
108         TRACE("Notifying %p\n", cursor);
109         hr = IFileDialogEvents_OnFileOk(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
110         if(FAILED(hr) && hr != E_NOTIMPL)
111             break;
112     }
113
114     if(hr == E_NOTIMPL)
115         hr = S_OK;
116
117     return hr;
118 }
119
120 static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder)
121 {
122     events_client *cursor;
123     HRESULT hr = S_OK;
124     TRACE("%p (%p)\n", This, folder);
125
126     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
127     {
128         TRACE("Notifying %p\n", cursor);
129         hr = IFileDialogEvents_OnFolderChanging(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, folder);
130         if(FAILED(hr) && hr != E_NOTIMPL)
131             break;
132     }
133
134     if(hr == E_NOTIMPL)
135         hr = S_OK;
136
137     return hr;
138 }
139
140 static void events_OnFolderChange(FileDialogImpl *This)
141 {
142     events_client *cursor;
143     TRACE("%p\n", This);
144
145     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
146     {
147         TRACE("Notifying %p\n", cursor);
148         IFileDialogEvents_OnFolderChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
149     }
150 }
151
152 static void events_OnSelectionChange(FileDialogImpl *This)
153 {
154     events_client *cursor;
155     TRACE("%p\n", This);
156
157     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
158     {
159         TRACE("Notifying %p\n", cursor);
160         IFileDialogEvents_OnSelectionChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
161     }
162 }
163
164 /**************************************************************************
165  * Helper functions.
166  */
167 static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
168 {
169     HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
170     UINT len;
171
172     if(!hwnd_edit)
173     {
174         if(This->set_filename)
175         {
176             len = lstrlenW(This->set_filename);
177             *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
178             lstrcpyW(*str, This->set_filename);
179             return len;
180         }
181         return FALSE;
182     }
183
184     len = SendMessageW(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);
185     *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
186     if(!*str)
187         return FALSE;
188
189     SendMessageW(hwnd_edit, WM_GETTEXT, len+1, (LPARAM)*str);
190     return len;
191 }
192
193 static BOOL set_file_name(FileDialogImpl *This, LPCWSTR str)
194 {
195     HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
196
197     if(This->set_filename)
198         LocalFree(This->set_filename);
199
200     This->set_filename = StrDupW(str);
201
202     return SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str);
203 }
204
205 static void fill_filename_from_selection(FileDialogImpl *This)
206 {
207     IShellItem *psi;
208     LPWSTR *names;
209     HRESULT hr;
210     UINT item_count, valid_count;
211     UINT len_total, i;
212
213     if(!This->psia_selection)
214         return;
215
216     hr = IShellItemArray_GetCount(This->psia_selection, &item_count);
217     if(FAILED(hr) || !item_count)
218         return;
219
220     names = HeapAlloc(GetProcessHeap(), 0, item_count*sizeof(LPWSTR));
221
222     /* Get names of the selected items */
223     valid_count = 0; len_total = 0;
224     for(i = 0; i < item_count; i++)
225     {
226         hr = IShellItemArray_GetItemAt(This->psia_selection, i, &psi);
227         if(SUCCEEDED(hr))
228         {
229             UINT attr;
230
231             hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &attr);
232             if(SUCCEEDED(hr) && (attr & SFGAO_FOLDER))
233                 continue; /* FIXME: FOS_PICKFOLDERS */
234
235             hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &names[valid_count]);
236             if(SUCCEEDED(hr))
237             {
238                 len_total += lstrlenW(names[valid_count]) + 3;
239                 valid_count++;
240             }
241             IShellItem_Release(psi);
242         }
243     }
244
245     if(valid_count == 1)
246     {
247         set_file_name(This, names[0]);
248         CoTaskMemFree(names[0]);
249     }
250     else if(valid_count > 1)
251     {
252         LPWSTR string = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len_total);
253         LPWSTR cur_point = string;
254
255         for(i = 0; i < valid_count; i++)
256         {
257             LPWSTR file = names[i];
258             *cur_point++ = '\"';
259             lstrcpyW(cur_point, file);
260             cur_point += lstrlenW(file);
261             *cur_point++ = '\"';
262             *cur_point++ = ' ';
263             CoTaskMemFree(file);
264         }
265         *(cur_point-1) = '\0';
266
267         set_file_name(This, string);
268         HeapFree(GetProcessHeap(), 0, string);
269     }
270
271     HeapFree(GetProcessHeap(), 0, names);
272     return;
273 }
274
275 static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec)
276 {
277     WCHAR *endpos, *ext;
278
279     lstrcpyW(buf, spec);
280     if( (endpos = StrChrW(buf, ';')) )
281         *endpos = '\0';
282
283     ext = PathFindExtensionW(buf);
284     if(StrChrW(ext, '*'))
285         return NULL;
286
287     return ext;
288 }
289
290 static HRESULT on_default_action(FileDialogImpl *This)
291 {
292     IShellFolder *psf_parent, *psf_desktop;
293     LPITEMIDLIST *pidla;
294     LPITEMIDLIST current_folder;
295     LPWSTR fn_iter, files, tmp_files;
296     UINT file_count = 0, len, i;
297     int open_action;
298     HRESULT hr, ret = E_FAIL;
299
300     len = get_file_name(This, &tmp_files);
301     if(len)
302     {
303         UINT size_used;
304         file_count = COMDLG32_SplitFileNames(tmp_files, len, &files, &size_used);
305     }
306     if(!file_count) return E_FAIL;
307
308     hr = SHGetIDListFromObject((IUnknown*)This->psi_folder, &current_folder);
309     if(FAILED(hr))
310     {
311         ERR("Failed to get pidl for current directory.\n");
312         return hr;
313     }
314
315     TRACE("Acting on %d file(s).\n", file_count);
316
317     pidla = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST) * file_count);
318     open_action = ONOPEN_OPEN;
319     fn_iter = files;
320
321     for(i = 0; i < file_count && open_action == ONOPEN_OPEN; i++)
322     {
323         WCHAR canon_filename[MAX_PATH];
324         psf_parent = NULL;
325
326         COMDLG32_GetCanonicalPath(current_folder, fn_iter, canon_filename);
327
328         if( (This->options & FOS_NOVALIDATE) &&
329             !(This->options & FOS_FILEMUSTEXIST) )
330             open_action = ONOPEN_OPEN;
331         else
332             open_action = ONOPEN_BROWSE;
333
334         open_action = FILEDLG95_ValidatePathAction(canon_filename, &psf_parent, This->dlg_hwnd,
335                                                    This->options & ~FOS_FILEMUSTEXIST,
336                                                    (This->dlg_type == ITEMDLG_TYPE_SAVE),
337                                                    open_action);
338
339         /* Add the proper extension */
340         if(open_action == ONOPEN_OPEN)
341         {
342             static const WCHAR dotW[] = {'.',0};
343
344             if(This->dlg_type == ITEMDLG_TYPE_SAVE)
345             {
346                 WCHAR extbuf[MAX_PATH], *newext = NULL;
347
348                 if(This->filterspec_count)
349                 {
350                     newext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
351                 }
352                 else if(This->default_ext)
353                 {
354                     lstrcpyW(extbuf, dotW);
355                     lstrcatW(extbuf, This->default_ext);
356                     newext = extbuf;
357                 }
358
359                 if(newext)
360                 {
361                     WCHAR *ext = PathFindExtensionW(canon_filename);
362                     if(lstrcmpW(ext, newext))
363                         lstrcatW(canon_filename, newext);
364                 }
365             }
366             else
367             {
368                 if( !(This->options & FOS_NOVALIDATE) && (This->options & FOS_FILEMUSTEXIST) &&
369                     !PathFileExistsW(canon_filename))
370                 {
371                     if(This->default_ext)
372                     {
373                         lstrcatW(canon_filename, dotW);
374                         lstrcatW(canon_filename, This->default_ext);
375
376                         if(!PathFileExistsW(canon_filename))
377                         {
378                             FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
379                             open_action = ONOPEN_BROWSE;
380                         }
381                     }
382                     else
383                     {
384                         FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
385                         open_action = ONOPEN_BROWSE;
386                     }
387                 }
388             }
389         }
390
391         pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename);
392
393         if(psf_parent && !(open_action == ONOPEN_BROWSE))
394             IShellItem_Release(psf_parent);
395
396         fn_iter += (WCHAR)lstrlenW(fn_iter) + 1;
397     }
398
399     HeapFree(GetProcessHeap(), 0, files);
400     ILFree(current_folder);
401
402     if((This->options & FOS_PICKFOLDERS) && open_action == ONOPEN_BROWSE)
403         open_action = ONOPEN_OPEN; /* FIXME: Multiple folders? */
404
405     switch(open_action)
406     {
407     case ONOPEN_SEARCH:
408         FIXME("Filtering not implemented.\n");
409         break;
410
411     case ONOPEN_BROWSE:
412         hr = IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psf_parent, SBSP_DEFBROWSER);
413         if(FAILED(hr))
414             ERR("Failed to browse to directory: %08x\n", hr);
415
416         IShellItem_Release(psf_parent);
417         break;
418
419     case ONOPEN_OPEN:
420         if(events_OnFileOk(This) != S_OK)
421             break;
422
423         hr = SHGetDesktopFolder(&psf_desktop);
424         if(SUCCEEDED(hr))
425         {
426             if(This->psia_results)
427                 IShellItemArray_Release(This->psia_results);
428
429             hr = SHCreateShellItemArray(NULL, psf_desktop, file_count, (PCUITEMID_CHILD_ARRAY)pidla,
430                                         &This->psia_results);
431             if(SUCCEEDED(hr))
432                 ret = S_OK;
433
434             IShellFolder_Release(psf_desktop);
435         }
436         break;
437
438     default:
439         ERR("Failed.\n");
440         break;
441     }
442
443     /* Clean up */
444     for(i = 0; i < file_count; i++)
445         ILFree(pidla[i]);
446     HeapFree(GetProcessHeap(), 0, pidla);
447
448     /* Success closes the dialog */
449     return ret;
450 }
451
452 /**************************************************************************
453  * Control functions.
454  */
455 static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width)
456 {
457     LPWSTR text;
458     UINT len, final_width;
459     SIZE size;
460     RECT rc;
461     HDC hdc;
462
463     TRACE("\n");
464
465     len = SendMessageW(hctrl, WM_GETTEXTLENGTH, 0, 0);
466     text = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
467     if(!text) return;
468     SendMessageW(hctrl, WM_GETTEXT, len+1, (LPARAM)text);
469
470     hdc = GetDC(hctrl);
471     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &size);
472     ReleaseDC(hctrl, hdc);
473
474     GetWindowRect(hctrl, &rc);
475     final_width = min(max(size.cx, min_width) + 4, max_width);
476     SetWindowPos(hctrl, NULL, 0, 0, final_width, rc.bottom - rc.top,
477                  SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
478
479     HeapFree(GetProcessHeap(), 0, text);
480 }
481
482 /**************************************************************************
483  * Window related functions.
484  */
485 static SIZE update_layout(FileDialogImpl *This)
486 {
487     HDWP hdwp;
488     HWND hwnd;
489     RECT dialog_rc;
490     RECT cancel_rc, open_rc;
491     RECT filetype_rc, filename_rc, filenamelabel_rc;
492     RECT toolbar_rc, ebrowser_rc;
493     int missing_width, missing_height;
494     static const UINT vspacing = 4, hspacing = 4;
495     SIZE ret;
496
497     GetClientRect(This->dlg_hwnd, &dialog_rc);
498
499     missing_width = max(0, 320 - dialog_rc.right);
500     missing_height = max(0, 200 - dialog_rc.bottom);
501
502     if(missing_width || missing_height)
503     {
504         TRACE("Missing (%d, %d)\n", missing_width, missing_height);
505         ret.cx = missing_width;
506         ret.cy = missing_height;
507         return ret;
508     }
509
510     /****
511      * Calculate the size of the dialog and all the parts.
512      */
513
514     /* Cancel button */
515     hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL);
516     if(hwnd)
517     {
518         int cancel_width, cancel_height;
519         GetWindowRect(hwnd, &cancel_rc);
520         cancel_width = cancel_rc.right - cancel_rc.left;
521         cancel_height = cancel_rc.bottom - cancel_rc.top;
522
523         cancel_rc.left = dialog_rc.right - cancel_width - hspacing;
524         cancel_rc.top = dialog_rc.bottom - cancel_height - vspacing;
525         cancel_rc.right = cancel_rc.left + cancel_width;
526         cancel_rc.bottom = cancel_rc.top + cancel_height;
527     }
528
529     /* Open/Save button */
530     hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
531     if(hwnd)
532     {
533         int open_width, open_height;
534         GetWindowRect(hwnd, &open_rc);
535         open_width = open_rc.right - open_rc.left;
536         open_height = open_rc.bottom - open_rc.top;
537
538         open_rc.left = cancel_rc.left - open_width - hspacing;
539         open_rc.top = cancel_rc.top;
540         open_rc.right = open_rc.left + open_width;
541         open_rc.bottom = open_rc.top + open_height;
542     }
543
544     /* The filetype combobox. */
545     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
546     if(hwnd)
547     {
548         int filetype_width, filetype_height;
549         GetWindowRect(hwnd, &filetype_rc);
550
551         filetype_width = filetype_rc.right - filetype_rc.left;
552         filetype_height = filetype_rc.bottom - filetype_rc.top;
553
554         filetype_rc.right = cancel_rc.right;
555
556         filetype_rc.left = filetype_rc.right - filetype_width;
557         filetype_rc.top = cancel_rc.top - filetype_height - vspacing;
558         filetype_rc.bottom = filetype_rc.top + filetype_height;
559
560         if(!This->filterspec_count)
561             filetype_rc.left = filetype_rc.right;
562     }
563
564     /* Filename label. */
565     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC);
566     if(hwnd)
567     {
568         int filetypelabel_width, filetypelabel_height;
569         GetWindowRect(hwnd, &filenamelabel_rc);
570
571         filetypelabel_width = filenamelabel_rc.right - filenamelabel_rc.left;
572         filetypelabel_height = filenamelabel_rc.bottom - filenamelabel_rc.top;
573
574         filenamelabel_rc.left = 160; /* FIXME */
575         filenamelabel_rc.top = filetype_rc.top;
576         filenamelabel_rc.right = filenamelabel_rc.left + filetypelabel_width;
577         filenamelabel_rc.bottom = filenamelabel_rc.top + filetypelabel_height;
578     }
579
580     /* Filename edit box. */
581     hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
582     if(hwnd)
583     {
584         int filename_width, filename_height;
585         GetWindowRect(hwnd, &filename_rc);
586
587         filename_width = filetype_rc.left - filenamelabel_rc.right - hspacing*2;
588         filename_height = filename_rc.bottom - filename_rc.top;
589
590         filename_rc.left = filenamelabel_rc.right + hspacing;
591         filename_rc.top = filetype_rc.top;
592         filename_rc.right = filename_rc.left + filename_width;
593         filename_rc.bottom = filename_rc.top + filename_height;
594     }
595
596     hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR);
597     if(hwnd)
598     {
599         GetWindowRect(hwnd, &toolbar_rc);
600         MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2);
601     }
602
603     /* The ExplorerBrowser control. */
604     ebrowser_rc.left = dialog_rc.left + vspacing;
605     ebrowser_rc.top = toolbar_rc.bottom + vspacing;
606     ebrowser_rc.right = dialog_rc.right - hspacing;
607     ebrowser_rc.bottom = filename_rc.top - hspacing;
608
609     /****
610      * Move everything to the right place.
611      */
612
613     /* FIXME: The Save Dialog uses a slightly different layout. */
614     hdwp = BeginDeferWindowPos(6);
615
616     if(hdwp && This->peb)
617         IExplorerBrowser_SetRect(This->peb, &hdwp, ebrowser_rc);
618
619     /* The default controls */
620     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE)) )
621         DeferWindowPos(hdwp, hwnd, NULL, filetype_rc.left, filetype_rc.top, 0, 0,
622                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
623
624     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
625         DeferWindowPos(hdwp, hwnd, NULL, filename_rc.left, filename_rc.top,
626                        filename_rc.right - filename_rc.left, filename_rc.bottom - filename_rc.top,
627                        SWP_NOZORDER | SWP_NOACTIVATE);
628
629     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)) )
630         DeferWindowPos(hdwp, hwnd, NULL, filenamelabel_rc.left, filenamelabel_rc.top, 0, 0,
631                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
632
633     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDOK)) )
634         DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
635                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
636
637     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
638         DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
639                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
640
641     if(hdwp)
642         EndDeferWindowPos(hdwp);
643     else
644         ERR("Failed to position dialog controls.\n");
645
646     ret.cx = 0; ret.cy = 0;
647     return ret;
648 }
649
650 static HRESULT init_explorerbrowser(FileDialogImpl *This)
651 {
652     IShellItem *psi_folder;
653     FOLDERSETTINGS fos;
654     RECT rc = {0};
655     HRESULT hr;
656
657     /* Create ExplorerBrowser instance */
658     OleInitialize(NULL);
659
660     hr = CoCreateInstance(&CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER,
661                           &IID_IExplorerBrowser, (void**)&This->peb);
662     if(FAILED(hr))
663     {
664         ERR("Failed to instantiate ExplorerBrowser control.\n");
665         return hr;
666     }
667
668     IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES);
669
670     hr = IExplorerBrowser_Initialize(This->peb, This->dlg_hwnd, &rc, NULL);
671     if(FAILED(hr))
672     {
673         ERR("Failed to initialize the ExplorerBrowser control.\n");
674         IExplorerBrowser_Release(This->peb);
675         This->peb = NULL;
676         return hr;
677     }
678     hr = IExplorerBrowser_Advise(This->peb, &This->IExplorerBrowserEvents_iface, &This->ebevents_cookie);
679     if(FAILED(hr))
680         ERR("Advise (ExplorerBrowser) failed.\n");
681
682     /* Get previous options? */
683     fos.ViewMode = fos.fFlags = 0;
684     if(!(This->options & FOS_ALLOWMULTISELECT))
685         fos.fFlags |= FWF_SINGLESEL;
686
687     IExplorerBrowser_SetFolderSettings(This->peb, &fos);
688
689     hr = IUnknown_SetSite((IUnknown*)This->peb, (IUnknown*)This);
690     if(FAILED(hr))
691         ERR("SetSite (ExplorerBrowser) failed.\n");
692
693     /* Browse somewhere */
694     psi_folder = This->psi_setfolder ? This->psi_setfolder : This->psi_defaultfolder;
695     IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psi_folder, SBSP_DEFBROWSER);
696
697     return S_OK;
698 }
699
700 static void init_toolbar(FileDialogImpl *This, HWND hwnd)
701 {
702     HWND htoolbar;
703     TBADDBITMAP tbab;
704     TBBUTTON button[2];
705
706     htoolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE,
707                                0, 0, 0, 0,
708                                hwnd, (HMENU)IDC_NAV_TOOLBAR, NULL, NULL);
709
710     tbab.hInst = HINST_COMMCTRL;
711     tbab.nID = IDB_HIST_LARGE_COLOR;
712     SendMessageW(htoolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
713
714     button[0].iBitmap = HIST_BACK;
715     button[0].idCommand = IDC_NAVBACK;
716     button[0].fsState = TBSTATE_ENABLED;
717     button[0].fsStyle = BTNS_BUTTON;
718     button[0].dwData = 0;
719     button[0].iString = 0;
720
721     button[1].iBitmap = HIST_FORWARD;
722     button[1].idCommand = IDC_NAVFORWARD;
723     button[1].fsState = TBSTATE_ENABLED;
724     button[1].fsStyle = BTNS_BUTTON;
725     button[1].dwData = 0;
726     button[1].iString = 0;
727
728     SendMessageW(htoolbar, TB_ADDBUTTONSW, 2, (LPARAM)&button);
729     SendMessageW(htoolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(24,24));
730     SendMessageW(htoolbar, TB_AUTOSIZE, 0, 0);
731 }
732
733 static void update_control_text(FileDialogImpl *This)
734 {
735     HWND hitem;
736     if(This->custom_title)
737         SetWindowTextW(This->dlg_hwnd, This->custom_title);
738
739     if(This->custom_okbutton &&
740        (hitem = GetDlgItem(This->dlg_hwnd, IDOK)))
741     {
742         SetWindowTextW(hitem, This->custom_okbutton);
743         ctrl_resize(hitem, 50, 250);
744     }
745
746     if(This->custom_cancelbutton &&
747        (hitem = GetDlgItem(This->dlg_hwnd, IDCANCEL)))
748     {
749         SetWindowTextW(hitem, This->custom_cancelbutton);
750         ctrl_resize(hitem, 50, 250);
751     }
752
753     if(This->custom_filenamelabel &&
754        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)))
755     {
756         SetWindowTextW(hitem, This->custom_filenamelabel);
757         ctrl_resize(hitem, 50, 250);
758     }
759 }
760
761 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
762 {
763     FileDialogImpl *This = (FileDialogImpl*)lParam;
764     HWND hitem;
765
766     TRACE("(%p, %p)\n", This, hwnd);
767
768     SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
769     This->dlg_hwnd = hwnd;
770
771     hitem = GetDlgItem(This->dlg_hwnd, pshHelp);
772     if(hitem) ShowWindow(hitem, SW_HIDE);
773
774     hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPESTATIC);
775     if(hitem) ShowWindow(hitem, SW_HIDE);
776
777     /* Fill filetypes combobox, or hide it. */
778     hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
779     if(This->filterspec_count)
780     {
781         UINT i;
782         for(i = 0; i < This->filterspec_count; i++)
783             SendMessageW(hitem, CB_ADDSTRING, 0, (LPARAM)This->filterspecs[i].pszName);
784
785         SendMessageW(hitem, CB_SETCURSEL, This->filetypeindex, 0);
786     }
787     else
788         ShowWindow(hitem, SW_HIDE);
789
790     if(This->set_filename &&
791        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
792         SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
793
794     init_explorerbrowser(This);
795     init_toolbar(This, hwnd);
796     update_control_text(This);
797     update_layout(This);
798
799     return TRUE;
800 }
801
802 static LRESULT on_wm_size(FileDialogImpl *This)
803 {
804     update_layout(This);
805     return FALSE;
806 }
807
808 static LRESULT on_wm_getminmaxinfo(FileDialogImpl *This, LPARAM lparam)
809 {
810     MINMAXINFO *mmi = (MINMAXINFO*)lparam;
811     TRACE("%p (%p)\n", This, mmi);
812
813     /* FIXME */
814     mmi->ptMinTrackSize.x = 640;
815     mmi->ptMinTrackSize.y = 480;
816
817     return FALSE;
818 }
819
820 static LRESULT on_wm_destroy(FileDialogImpl *This)
821 {
822     TRACE("%p\n", This);
823
824     if(This->peb)
825     {
826         IExplorerBrowser_Destroy(This->peb);
827         IExplorerBrowser_Release(This->peb);
828         This->peb = NULL;
829     }
830
831     This->dlg_hwnd = NULL;
832
833     return TRUE;
834 }
835
836 static LRESULT on_idok(FileDialogImpl *This)
837 {
838     TRACE("%p\n", This);
839
840     if(SUCCEEDED(on_default_action(This)))
841         EndDialog(This->dlg_hwnd, S_OK);
842
843     return FALSE;
844 }
845
846 static LRESULT on_idcancel(FileDialogImpl *This)
847 {
848     TRACE("%p\n", This);
849
850     EndDialog(This->dlg_hwnd, HRESULT_FROM_WIN32(ERROR_CANCELLED));
851
852     return FALSE;
853 }
854
855 static LRESULT on_browse_back(FileDialogImpl *This)
856 {
857     TRACE("%p\n", This);
858     IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEBACK);
859     return FALSE;
860 }
861
862 static LRESULT on_browse_forward(FileDialogImpl *This)
863 {
864     TRACE("%p\n", This);
865     IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEFORWARD);
866     return FALSE;
867 }
868
869 static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
870 {
871     if(HIWORD(wparam) == CBN_SELCHANGE)
872     {
873         IShellView *psv;
874         HRESULT hr;
875         LPWSTR filename;
876         UINT prev_index = This->filetypeindex;
877
878         This->filetypeindex = SendMessageW((HWND)lparam, CB_GETCURSEL, 0, 0);
879         TRACE("File type selection changed to %d.\n", This->filetypeindex);
880
881         if(prev_index == This->filetypeindex)
882             return FALSE;
883
884         hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&psv);
885         if(SUCCEEDED(hr))
886         {
887             IShellView_Refresh(psv);
888             IShellView_Release(psv);
889         }
890
891         if(This->dlg_type == ITEMDLG_TYPE_SAVE && get_file_name(This, &filename))
892         {
893             WCHAR buf[MAX_PATH], extbuf[MAX_PATH], *ext;
894
895             ext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
896             if(ext)
897             {
898                 lstrcpyW(buf, filename);
899
900                 if(PathMatchSpecW(buf, This->filterspecs[prev_index].pszSpec))
901                     PathRemoveExtensionW(buf);
902
903                 lstrcatW(buf, ext);
904                 set_file_name(This, buf);
905             }
906             CoTaskMemFree(filename);
907         }
908     }
909
910     return FALSE;
911 }
912
913 static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
914 {
915     switch(LOWORD(wparam))
916     {
917     case IDOK:                return on_idok(This);
918     case IDCANCEL:            return on_idcancel(This);
919     case IDC_NAVBACK:         return on_browse_back(This);
920     case IDC_NAVFORWARD:      return on_browse_forward(This);
921     case IDC_FILETYPE:        return on_command_filetype(This, wparam, lparam);
922     default:                  TRACE("Unknown command.\n");
923     }
924     return FALSE;
925 }
926
927 static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
928 {
929     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
930
931     switch(umessage)
932     {
933     case WM_INITDIALOG:       return on_wm_initdialog(hwnd, lparam);
934     case WM_COMMAND:          return on_wm_command(This, wparam, lparam);
935     case WM_SIZE:             return on_wm_size(This);
936     case WM_GETMINMAXINFO:    return on_wm_getminmaxinfo(This, lparam);
937     case WM_DESTROY:          return on_wm_destroy(This);
938     }
939
940     return FALSE;
941 }
942
943 static HRESULT create_dialog(FileDialogImpl *This, HWND parent)
944 {
945     INT_PTR res;
946
947     SetLastError(0);
948     res = DialogBoxParamW(COMDLG32_hInstance,
949                           MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
950                           parent, itemdlg_dlgproc, (LPARAM)This);
951     This->dlg_hwnd = NULL;
952     if(res == -1)
953     {
954         ERR("Failed to show dialog (LastError: %d)\n", GetLastError());
955         return E_FAIL;
956     }
957
958     TRACE("Returning 0x%08x\n", (HRESULT)res);
959     return (HRESULT)res;
960 }
961
962 /**************************************************************************
963  * IFileDialog implementation
964  */
965 static inline FileDialogImpl *impl_from_IFileDialog2(IFileDialog2 *iface)
966 {
967     return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialog2_iface);
968 }
969
970 static HRESULT WINAPI IFileDialog2_fnQueryInterface(IFileDialog2 *iface,
971                                                     REFIID riid,
972                                                     void **ppvObject)
973 {
974     FileDialogImpl *This = impl_from_IFileDialog2(iface);
975     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
976
977     *ppvObject = NULL;
978     if(IsEqualGUID(riid, &IID_IUnknown) ||
979        IsEqualGUID(riid, &IID_IFileDialog) ||
980        IsEqualGUID(riid, &IID_IFileDialog2))
981     {
982         *ppvObject = iface;
983     }
984     else if(IsEqualGUID(riid, &IID_IFileOpenDialog) && This->dlg_type == ITEMDLG_TYPE_OPEN)
985     {
986         *ppvObject = &This->u.IFileOpenDialog_iface;
987     }
988     else if(IsEqualGUID(riid, &IID_IFileSaveDialog) && This->dlg_type == ITEMDLG_TYPE_SAVE)
989     {
990         *ppvObject = &This->u.IFileSaveDialog_iface;
991     }
992     else if(IsEqualGUID(riid, &IID_IExplorerBrowserEvents))
993     {
994         *ppvObject = &This->IExplorerBrowserEvents_iface;
995     }
996     else if(IsEqualGUID(riid, &IID_IServiceProvider))
997     {
998         *ppvObject = &This->IServiceProvider_iface;
999     }
1000     else if(IsEqualGUID(&IID_ICommDlgBrowser3, riid) ||
1001             IsEqualGUID(&IID_ICommDlgBrowser2, riid) ||
1002             IsEqualGUID(&IID_ICommDlgBrowser, riid))
1003     {
1004         *ppvObject = &This->ICommDlgBrowser3_iface;
1005     }
1006     else if(IsEqualGUID(&IID_IOleWindow, riid))
1007     {
1008         *ppvObject = &This->IOleWindow_iface;
1009     }
1010     else
1011         FIXME("Unknown interface requested: %s.\n", debugstr_guid(riid));
1012
1013     if(*ppvObject)
1014     {
1015         IUnknown_AddRef((IUnknown*)*ppvObject);
1016         return S_OK;
1017     }
1018
1019     return E_NOINTERFACE;
1020 }
1021
1022 static ULONG WINAPI IFileDialog2_fnAddRef(IFileDialog2 *iface)
1023 {
1024     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1025     LONG ref = InterlockedIncrement(&This->ref);
1026     TRACE("%p - ref %d\n", This, ref);
1027
1028     return ref;
1029 }
1030
1031 static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface)
1032 {
1033     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1034     LONG ref = InterlockedDecrement(&This->ref);
1035     TRACE("%p - ref %d\n", This, ref);
1036
1037     if(!ref)
1038     {
1039         UINT i;
1040         for(i = 0; i < This->filterspec_count; i++)
1041         {
1042             LocalFree((void*)This->filterspecs[i].pszName);
1043             LocalFree((void*)This->filterspecs[i].pszSpec);
1044         }
1045         HeapFree(GetProcessHeap(), 0, This->filterspecs);
1046
1047         if(This->psi_defaultfolder) IShellItem_Release(This->psi_defaultfolder);
1048         if(This->psi_setfolder)     IShellItem_Release(This->psi_setfolder);
1049         if(This->psi_folder)        IShellItem_Release(This->psi_folder);
1050         if(This->psia_selection)    IShellItemArray_Release(This->psia_selection);
1051         if(This->psia_results)      IShellItemArray_Release(This->psia_results);
1052
1053         LocalFree(This->set_filename);
1054         LocalFree(This->default_ext);
1055         LocalFree(This->custom_title);
1056         LocalFree(This->custom_okbutton);
1057         LocalFree(This->custom_cancelbutton);
1058         LocalFree(This->custom_filenamelabel);
1059
1060         HeapFree(GetProcessHeap(), 0, This);
1061     }
1062
1063     return ref;
1064 }
1065
1066 static HRESULT WINAPI IFileDialog2_fnShow(IFileDialog2 *iface, HWND hwndOwner)
1067 {
1068     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1069     TRACE("%p (%p)\n", iface, hwndOwner);
1070
1071     return create_dialog(This, hwndOwner);
1072 }
1073
1074 static HRESULT WINAPI IFileDialog2_fnSetFileTypes(IFileDialog2 *iface, UINT cFileTypes,
1075                                                   const COMDLG_FILTERSPEC *rgFilterSpec)
1076 {
1077     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1078     UINT i;
1079     TRACE("%p (%d, %p)\n", This, cFileTypes, rgFilterSpec);
1080
1081     if(This->filterspecs)
1082         return E_UNEXPECTED;
1083
1084     if(!rgFilterSpec)
1085         return E_INVALIDARG;
1086
1087     if(!cFileTypes)
1088         return S_OK;
1089
1090     This->filterspecs = HeapAlloc(GetProcessHeap(), 0, sizeof(COMDLG_FILTERSPEC)*cFileTypes);
1091     for(i = 0; i < cFileTypes; i++)
1092     {
1093         This->filterspecs[i].pszName = StrDupW(rgFilterSpec[i].pszName);
1094         This->filterspecs[i].pszSpec = StrDupW(rgFilterSpec[i].pszSpec);
1095     }
1096     This->filterspec_count = cFileTypes;
1097
1098     return S_OK;
1099 }
1100
1101 static HRESULT WINAPI IFileDialog2_fnSetFileTypeIndex(IFileDialog2 *iface, UINT iFileType)
1102 {
1103     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1104     TRACE("%p (%d)\n", This, iFileType);
1105
1106     if(!This->filterspecs)
1107         return E_FAIL;
1108
1109     if(iFileType >= This->filterspec_count)
1110         This->filetypeindex = This->filterspec_count - 1;
1111     else
1112         This->filetypeindex = iFileType;
1113
1114     return S_OK;
1115 }
1116
1117 static HRESULT WINAPI IFileDialog2_fnGetFileTypeIndex(IFileDialog2 *iface, UINT *piFileType)
1118 {
1119     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1120     TRACE("%p (%p)\n", This, piFileType);
1121
1122     if(!piFileType)
1123         return E_INVALIDARG;
1124
1125     *piFileType = This->filetypeindex;
1126
1127     return S_OK;
1128 }
1129
1130 static HRESULT WINAPI IFileDialog2_fnAdvise(IFileDialog2 *iface, IFileDialogEvents *pfde, DWORD *pdwCookie)
1131 {
1132     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1133     events_client *client;
1134     TRACE("%p (%p, %p)\n", This, pfde, pdwCookie);
1135
1136     if(!pfde || !pdwCookie)
1137         return E_INVALIDARG;
1138
1139     client = HeapAlloc(GetProcessHeap(), 0, sizeof(events_client));
1140     client->pfde = pfde;
1141     client->cookie = ++This->events_next_cookie;
1142
1143     IFileDialogEvents_AddRef(pfde);
1144     *pdwCookie = client->cookie;
1145
1146     list_add_tail(&This->events_clients, &client->entry);
1147
1148     return S_OK;
1149 }
1150
1151 static HRESULT WINAPI IFileDialog2_fnUnadvise(IFileDialog2 *iface, DWORD dwCookie)
1152 {
1153     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1154     events_client *client, *found = NULL;
1155     TRACE("%p (%d)\n", This, dwCookie);
1156
1157     LIST_FOR_EACH_ENTRY(client, &This->events_clients, events_client, entry)
1158     {
1159         if(client->cookie == dwCookie)
1160         {
1161             found = client;
1162             break;
1163         }
1164     }
1165
1166     if(found)
1167     {
1168         list_remove(&found->entry);
1169         IFileDialogEvents_Release(found->pfde);
1170         HeapFree(GetProcessHeap(), 0, found);
1171         return S_OK;
1172     }
1173
1174     return E_INVALIDARG;
1175 }
1176
1177 static HRESULT WINAPI IFileDialog2_fnSetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS fos)
1178 {
1179     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1180     TRACE("%p (0x%x)\n", This, fos);
1181
1182     This->options = fos;
1183
1184     return S_OK;
1185 }
1186
1187 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
1188 {
1189     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1190     TRACE("%p (%p)\n", This, pfos);
1191
1192     if(!pfos)
1193         return E_INVALIDARG;
1194
1195     *pfos = This->options;
1196
1197     return S_OK;
1198 }
1199
1200 static HRESULT WINAPI IFileDialog2_fnSetDefaultFolder(IFileDialog2 *iface, IShellItem *psi)
1201 {
1202     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1203     TRACE("%p (%p)\n", This, psi);
1204     if(This->psi_defaultfolder)
1205         IShellItem_Release(This->psi_defaultfolder);
1206
1207     This->psi_defaultfolder = psi;
1208
1209     if(This->psi_defaultfolder)
1210         IShellItem_AddRef(This->psi_defaultfolder);
1211
1212     return S_OK;
1213 }
1214
1215 static HRESULT WINAPI IFileDialog2_fnSetFolder(IFileDialog2 *iface, IShellItem *psi)
1216 {
1217     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1218     TRACE("%p (%p)\n", This, psi);
1219     if(This->psi_setfolder)
1220         IShellItem_Release(This->psi_setfolder);
1221
1222     This->psi_setfolder = psi;
1223
1224     if(This->psi_setfolder)
1225         IShellItem_AddRef(This->psi_setfolder);
1226
1227     return S_OK;
1228 }
1229
1230 static HRESULT WINAPI IFileDialog2_fnGetFolder(IFileDialog2 *iface, IShellItem **ppsi)
1231 {
1232     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1233     TRACE("%p (%p)\n", This, ppsi);
1234     if(!ppsi)
1235         return E_INVALIDARG;
1236
1237     /* FIXME:
1238        If the dialog is shown, return the current(ly selected) folder. */
1239
1240     *ppsi = NULL;
1241     if(This->psi_folder)
1242         *ppsi = This->psi_folder;
1243     else if(This->psi_setfolder)
1244         *ppsi = This->psi_setfolder;
1245     else if(This->psi_defaultfolder)
1246         *ppsi = This->psi_defaultfolder;
1247
1248     if(*ppsi)
1249     {
1250         IShellItem_AddRef(*ppsi);
1251         return S_OK;
1252     }
1253
1254     return E_FAIL;
1255 }
1256
1257 static HRESULT WINAPI IFileDialog2_fnGetCurrentSelection(IFileDialog2 *iface, IShellItem **ppsi)
1258 {
1259     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1260     HRESULT hr;
1261     TRACE("%p (%p)\n", This, ppsi);
1262
1263     if(!ppsi)
1264         return E_INVALIDARG;
1265
1266     if(This->psia_selection)
1267     {
1268         /* FIXME: Check filename edit box */
1269         hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
1270         return hr;
1271     }
1272
1273     return E_FAIL;
1274 }
1275
1276 static HRESULT WINAPI IFileDialog2_fnSetFileName(IFileDialog2 *iface, LPCWSTR pszName)
1277 {
1278     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1279     TRACE("%p (%s)\n", iface, debugstr_w(pszName));
1280
1281     set_file_name(This, pszName);
1282
1283     return S_OK;
1284 }
1285
1286 static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *pszName)
1287 {
1288     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1289     TRACE("%p (%p)\n", iface, pszName);
1290
1291     if(!pszName)
1292         return E_INVALIDARG;
1293
1294     *pszName = NULL;
1295     if(get_file_name(This, pszName))
1296         return S_OK;
1297     else
1298         return E_FAIL;
1299 }
1300
1301 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
1302 {
1303     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1304     TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
1305
1306     LocalFree(This->custom_title);
1307     This->custom_title = StrDupW(pszTitle);
1308     update_control_text(This);
1309
1310     return S_OK;
1311 }
1312
1313 static HRESULT WINAPI IFileDialog2_fnSetOkButtonLabel(IFileDialog2 *iface, LPCWSTR pszText)
1314 {
1315     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1316     TRACE("%p (%s)\n", This, debugstr_w(pszText));
1317
1318     LocalFree(This->custom_okbutton);
1319     This->custom_okbutton = StrDupW(pszText);
1320     update_control_text(This);
1321     update_layout(This);
1322
1323     return S_OK;
1324 }
1325
1326 static HRESULT WINAPI IFileDialog2_fnSetFileNameLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
1327 {
1328     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1329     TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
1330
1331     LocalFree(This->custom_filenamelabel);
1332     This->custom_filenamelabel = StrDupW(pszLabel);
1333     update_control_text(This);
1334     update_layout(This);
1335
1336     return S_OK;
1337 }
1338
1339 static HRESULT WINAPI IFileDialog2_fnGetResult(IFileDialog2 *iface, IShellItem **ppsi)
1340 {
1341     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1342     HRESULT hr;
1343     TRACE("%p (%p)\n", This, ppsi);
1344
1345     if(!ppsi)
1346         return E_INVALIDARG;
1347
1348     if(This->psia_results)
1349     {
1350         UINT item_count;
1351         hr = IShellItemArray_GetCount(This->psia_results, &item_count);
1352         if(SUCCEEDED(hr))
1353         {
1354             if(item_count != 1)
1355                 return E_FAIL;
1356
1357             /* Adds a reference. */
1358             hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
1359         }
1360
1361         return hr;
1362     }
1363
1364     return E_UNEXPECTED;
1365 }
1366
1367 static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *psi, FDAP fdap)
1368 {
1369     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1370     FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
1371     return E_NOTIMPL;
1372 }
1373
1374 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
1375 {
1376     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1377     TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
1378
1379     LocalFree(This->default_ext);
1380     This->default_ext = StrDupW(pszDefaultExtension);
1381
1382     return S_OK;
1383 }
1384
1385 static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
1386 {
1387     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1388     TRACE("%p (0x%08x)\n", This, hr);
1389
1390     if(This->dlg_hwnd)
1391         EndDialog(This->dlg_hwnd, hr);
1392
1393     return S_OK;
1394 }
1395
1396 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
1397 {
1398     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1399     FIXME("stub - %p (%s)\n", This, debugstr_guid(guid));
1400     return E_NOTIMPL;
1401 }
1402
1403 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
1404 {
1405     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1406     FIXME("stub - %p\n", This);
1407     return E_NOTIMPL;
1408 }
1409
1410 static HRESULT WINAPI IFileDialog2_fnSetFilter(IFileDialog2 *iface, IShellItemFilter *pFilter)
1411 {
1412     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1413     FIXME("stub - %p (%p)\n", This, pFilter);
1414     return E_NOTIMPL;
1415 }
1416
1417 static HRESULT WINAPI IFileDialog2_fnSetCancelButtonLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
1418 {
1419     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1420     TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
1421
1422     LocalFree(This->custom_cancelbutton);
1423     This->custom_cancelbutton = StrDupW(pszLabel);
1424     update_control_text(This);
1425     update_layout(This);
1426
1427     return S_OK;
1428 }
1429
1430 static HRESULT WINAPI IFileDialog2_fnSetNavigationRoot(IFileDialog2 *iface, IShellItem *psi)
1431 {
1432     FileDialogImpl *This = impl_from_IFileDialog2(iface);
1433     FIXME("stub - %p (%p)\n", This, psi);
1434     return E_NOTIMPL;
1435 }
1436
1437 static const IFileDialog2Vtbl vt_IFileDialog2 = {
1438     IFileDialog2_fnQueryInterface,
1439     IFileDialog2_fnAddRef,
1440     IFileDialog2_fnRelease,
1441     IFileDialog2_fnShow,
1442     IFileDialog2_fnSetFileTypes,
1443     IFileDialog2_fnSetFileTypeIndex,
1444     IFileDialog2_fnGetFileTypeIndex,
1445     IFileDialog2_fnAdvise,
1446     IFileDialog2_fnUnadvise,
1447     IFileDialog2_fnSetOptions,
1448     IFileDialog2_fnGetOptions,
1449     IFileDialog2_fnSetDefaultFolder,
1450     IFileDialog2_fnSetFolder,
1451     IFileDialog2_fnGetFolder,
1452     IFileDialog2_fnGetCurrentSelection,
1453     IFileDialog2_fnSetFileName,
1454     IFileDialog2_fnGetFileName,
1455     IFileDialog2_fnSetTitle,
1456     IFileDialog2_fnSetOkButtonLabel,
1457     IFileDialog2_fnSetFileNameLabel,
1458     IFileDialog2_fnGetResult,
1459     IFileDialog2_fnAddPlace,
1460     IFileDialog2_fnSetDefaultExtension,
1461     IFileDialog2_fnClose,
1462     IFileDialog2_fnSetClientGuid,
1463     IFileDialog2_fnClearClientData,
1464     IFileDialog2_fnSetFilter,
1465     IFileDialog2_fnSetCancelButtonLabel,
1466     IFileDialog2_fnSetNavigationRoot
1467 };
1468
1469 /**************************************************************************
1470  * IFileOpenDialog
1471  */
1472 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
1473 {
1474     return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
1475 }
1476
1477 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
1478                                                        REFIID riid, void **ppvObject)
1479 {
1480     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1481     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
1482 }
1483
1484 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
1485 {
1486     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1487     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
1488 }
1489
1490 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
1491 {
1492     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1493     return IFileDialog2_Release(&This->IFileDialog2_iface);
1494 }
1495
1496 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
1497 {
1498     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1499     return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
1500 }
1501
1502 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
1503                                                      const COMDLG_FILTERSPEC *rgFilterSpec)
1504 {
1505     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1506     return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
1507 }
1508
1509 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
1510 {
1511     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1512     return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
1513 }
1514
1515 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
1516 {
1517     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1518     return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
1519 }
1520
1521 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
1522                                                DWORD *pdwCookie)
1523 {
1524     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1525     return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
1526 }
1527
1528 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
1529 {
1530     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1531     return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
1532 }
1533
1534 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
1535 {
1536     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1537     return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
1538 }
1539
1540 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
1541 {
1542     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1543     return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
1544 }
1545
1546 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
1547 {
1548     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1549     return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
1550 }
1551
1552 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
1553 {
1554     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1555     return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
1556 }
1557
1558 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
1559 {
1560     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1561     return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
1562 }
1563
1564 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
1565 {
1566     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1567     return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
1568 }
1569
1570 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
1571 {
1572     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1573     return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
1574 }
1575
1576 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
1577 {
1578     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1579     return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
1580 }
1581
1582 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
1583 {
1584     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1585     return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
1586 }
1587
1588 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
1589 {
1590     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1591     return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
1592 }
1593
1594 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
1595 {
1596     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1597     return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
1598 }
1599
1600 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
1601 {
1602     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1603     return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
1604 }
1605
1606 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
1607 {
1608     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1609     return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
1610 }
1611
1612 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultExtension(IFileOpenDialog *iface,
1613                                                             LPCWSTR pszDefaultExtension)
1614 {
1615     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1616     return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
1617 }
1618
1619 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
1620 {
1621     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1622     return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
1623 }
1624
1625 static HRESULT WINAPI IFileOpenDialog_fnSetClientGuid(IFileOpenDialog *iface, REFGUID guid)
1626 {
1627     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1628     return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
1629 }
1630
1631 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
1632 {
1633     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1634     return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
1635 }
1636
1637 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
1638 {
1639     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1640     return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
1641 }
1642
1643 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
1644 {
1645     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1646     TRACE("%p (%p)\n", This, ppenum);
1647
1648     *ppenum = This->psia_results;
1649
1650     if(*ppenum)
1651     {
1652         IShellItemArray_AddRef(*ppenum);
1653         return S_OK;
1654     }
1655
1656     return E_FAIL;
1657 }
1658
1659 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
1660 {
1661     FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
1662     TRACE("%p (%p)\n", This, ppsai);
1663
1664     if(This->psia_selection)
1665     {
1666         *ppsai = This->psia_selection;
1667         IShellItemArray_AddRef(*ppsai);
1668         return S_OK;
1669     }
1670
1671     return E_FAIL;
1672 }
1673
1674 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
1675     IFileOpenDialog_fnQueryInterface,
1676     IFileOpenDialog_fnAddRef,
1677     IFileOpenDialog_fnRelease,
1678     IFileOpenDialog_fnShow,
1679     IFileOpenDialog_fnSetFileTypes,
1680     IFileOpenDialog_fnSetFileTypeIndex,
1681     IFileOpenDialog_fnGetFileTypeIndex,
1682     IFileOpenDialog_fnAdvise,
1683     IFileOpenDialog_fnUnadvise,
1684     IFileOpenDialog_fnSetOptions,
1685     IFileOpenDialog_fnGetOptions,
1686     IFileOpenDialog_fnSetDefaultFolder,
1687     IFileOpenDialog_fnSetFolder,
1688     IFileOpenDialog_fnGetFolder,
1689     IFileOpenDialog_fnGetCurrentSelection,
1690     IFileOpenDialog_fnSetFileName,
1691     IFileOpenDialog_fnGetFileName,
1692     IFileOpenDialog_fnSetTitle,
1693     IFileOpenDialog_fnSetOkButtonLabel,
1694     IFileOpenDialog_fnSetFileNameLabel,
1695     IFileOpenDialog_fnGetResult,
1696     IFileOpenDialog_fnAddPlace,
1697     IFileOpenDialog_fnSetDefaultExtension,
1698     IFileOpenDialog_fnClose,
1699     IFileOpenDialog_fnSetClientGuid,
1700     IFileOpenDialog_fnClearClientData,
1701     IFileOpenDialog_fnSetFilter,
1702     IFileOpenDialog_fnGetResults,
1703     IFileOpenDialog_fnGetSelectedItems
1704 };
1705
1706 /**************************************************************************
1707  * IFileSaveDialog
1708  */
1709 static inline FileDialogImpl *impl_from_IFileSaveDialog(IFileSaveDialog *iface)
1710 {
1711     return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
1712 }
1713
1714 static HRESULT WINAPI IFileSaveDialog_fnQueryInterface(IFileSaveDialog *iface,
1715                                                        REFIID riid,
1716                                                        void **ppvObject)
1717 {
1718     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1719     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
1720 }
1721
1722 static ULONG WINAPI IFileSaveDialog_fnAddRef(IFileSaveDialog *iface)
1723 {
1724     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1725     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
1726 }
1727
1728 static ULONG WINAPI IFileSaveDialog_fnRelease(IFileSaveDialog *iface)
1729 {
1730     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1731     return IFileDialog2_Release(&This->IFileDialog2_iface);
1732 }
1733
1734 static HRESULT WINAPI IFileSaveDialog_fnShow(IFileSaveDialog *iface, HWND hwndOwner)
1735 {
1736     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1737     return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
1738 }
1739
1740 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypes(IFileSaveDialog *iface, UINT cFileTypes,
1741                                                      const COMDLG_FILTERSPEC *rgFilterSpec)
1742 {
1743     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1744     return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
1745 }
1746
1747 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypeIndex(IFileSaveDialog *iface, UINT iFileType)
1748 {
1749     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1750     return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
1751 }
1752
1753 static HRESULT WINAPI IFileSaveDialog_fnGetFileTypeIndex(IFileSaveDialog *iface, UINT *piFileType)
1754 {
1755     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1756     return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
1757 }
1758
1759 static HRESULT WINAPI IFileSaveDialog_fnAdvise(IFileSaveDialog *iface, IFileDialogEvents *pfde,
1760                                                DWORD *pdwCookie)
1761 {
1762     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1763     return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
1764 }
1765
1766 static HRESULT WINAPI IFileSaveDialog_fnUnadvise(IFileSaveDialog *iface, DWORD dwCookie)
1767 {
1768     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1769     return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
1770 }
1771
1772 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
1773 {
1774     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1775     return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
1776 }
1777
1778 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
1779 {
1780     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1781     return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
1782 }
1783
1784 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultFolder(IFileSaveDialog *iface, IShellItem *psi)
1785 {
1786     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1787     return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
1788 }
1789
1790 static HRESULT WINAPI IFileSaveDialog_fnSetFolder(IFileSaveDialog *iface, IShellItem *psi)
1791 {
1792     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1793     return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
1794 }
1795
1796 static HRESULT WINAPI IFileSaveDialog_fnGetFolder(IFileSaveDialog *iface, IShellItem **ppsi)
1797 {
1798     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1799     return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
1800 }
1801
1802 static HRESULT WINAPI IFileSaveDialog_fnGetCurrentSelection(IFileSaveDialog *iface, IShellItem **ppsi)
1803 {
1804     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1805     return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
1806 }
1807
1808 static HRESULT WINAPI IFileSaveDialog_fnSetFileName(IFileSaveDialog *iface, LPCWSTR pszName)
1809 {
1810     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1811     return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
1812 }
1813
1814 static HRESULT WINAPI IFileSaveDialog_fnGetFileName(IFileSaveDialog *iface, LPWSTR *pszName)
1815 {
1816     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1817     return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
1818 }
1819
1820 static HRESULT WINAPI IFileSaveDialog_fnSetTitle(IFileSaveDialog *iface, LPCWSTR pszTitle)
1821 {
1822     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1823     return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
1824 }
1825
1826 static HRESULT WINAPI IFileSaveDialog_fnSetOkButtonLabel(IFileSaveDialog *iface, LPCWSTR pszText)
1827 {
1828     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1829     return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
1830 }
1831
1832 static HRESULT WINAPI IFileSaveDialog_fnSetFileNameLabel(IFileSaveDialog *iface, LPCWSTR pszLabel)
1833 {
1834     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1835     return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
1836 }
1837
1838 static HRESULT WINAPI IFileSaveDialog_fnGetResult(IFileSaveDialog *iface, IShellItem **ppsi)
1839 {
1840     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1841     return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
1842 }
1843
1844 static HRESULT WINAPI IFileSaveDialog_fnAddPlace(IFileSaveDialog *iface, IShellItem *psi, FDAP fdap)
1845 {
1846     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1847     return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
1848 }
1849
1850 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultExtension(IFileSaveDialog *iface,
1851                                                             LPCWSTR pszDefaultExtension)
1852 {
1853     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1854     return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
1855 }
1856
1857 static HRESULT WINAPI IFileSaveDialog_fnClose(IFileSaveDialog *iface, HRESULT hr)
1858 {
1859     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1860     return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
1861 }
1862
1863 static HRESULT WINAPI IFileSaveDialog_fnSetClientGuid(IFileSaveDialog *iface, REFGUID guid)
1864 {
1865     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1866     return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
1867 }
1868
1869 static HRESULT WINAPI IFileSaveDialog_fnClearClientData(IFileSaveDialog *iface)
1870 {
1871     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1872     return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
1873 }
1874
1875 static HRESULT WINAPI IFileSaveDialog_fnSetFilter(IFileSaveDialog *iface, IShellItemFilter *pFilter)
1876 {
1877     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1878     return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
1879 }
1880
1881 static HRESULT WINAPI IFileSaveDialog_fnSetSaveAsItem(IFileSaveDialog* iface, IShellItem *psi)
1882 {
1883     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1884     FIXME("stub - %p (%p)\n", This, psi);
1885     return E_NOTIMPL;
1886 }
1887
1888 static HRESULT WINAPI IFileSaveDialog_fnSetProperties(IFileSaveDialog* iface, IPropertyStore *pStore)
1889 {
1890     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1891     FIXME("stub - %p (%p)\n", This, pStore);
1892     return E_NOTIMPL;
1893 }
1894
1895 static HRESULT WINAPI IFileSaveDialog_fnSetCollectedProperties(IFileSaveDialog* iface,
1896                                                                IPropertyDescriptionList *pList,
1897                                                                BOOL fAppendDefault)
1898 {
1899     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1900     FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
1901     return E_NOTIMPL;
1902 }
1903
1904 static HRESULT WINAPI IFileSaveDialog_fnGetProperties(IFileSaveDialog* iface, IPropertyStore **ppStore)
1905 {
1906     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1907     FIXME("stub - %p (%p)\n", This, ppStore);
1908     return E_NOTIMPL;
1909 }
1910
1911 static HRESULT WINAPI IFileSaveDialog_fnApplyProperties(IFileSaveDialog* iface,
1912                                                         IShellItem *psi,
1913                                                         IPropertyStore *pStore,
1914                                                         HWND hwnd,
1915                                                         IFileOperationProgressSink *pSink)
1916 {
1917     FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
1918     FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
1919     return E_NOTIMPL;
1920 }
1921
1922 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
1923     IFileSaveDialog_fnQueryInterface,
1924     IFileSaveDialog_fnAddRef,
1925     IFileSaveDialog_fnRelease,
1926     IFileSaveDialog_fnShow,
1927     IFileSaveDialog_fnSetFileTypes,
1928     IFileSaveDialog_fnSetFileTypeIndex,
1929     IFileSaveDialog_fnGetFileTypeIndex,
1930     IFileSaveDialog_fnAdvise,
1931     IFileSaveDialog_fnUnadvise,
1932     IFileSaveDialog_fnSetOptions,
1933     IFileSaveDialog_fnGetOptions,
1934     IFileSaveDialog_fnSetDefaultFolder,
1935     IFileSaveDialog_fnSetFolder,
1936     IFileSaveDialog_fnGetFolder,
1937     IFileSaveDialog_fnGetCurrentSelection,
1938     IFileSaveDialog_fnSetFileName,
1939     IFileSaveDialog_fnGetFileName,
1940     IFileSaveDialog_fnSetTitle,
1941     IFileSaveDialog_fnSetOkButtonLabel,
1942     IFileSaveDialog_fnSetFileNameLabel,
1943     IFileSaveDialog_fnGetResult,
1944     IFileSaveDialog_fnAddPlace,
1945     IFileSaveDialog_fnSetDefaultExtension,
1946     IFileSaveDialog_fnClose,
1947     IFileSaveDialog_fnSetClientGuid,
1948     IFileSaveDialog_fnClearClientData,
1949     IFileSaveDialog_fnSetFilter,
1950     IFileSaveDialog_fnSetSaveAsItem,
1951     IFileSaveDialog_fnSetProperties,
1952     IFileSaveDialog_fnSetCollectedProperties,
1953     IFileSaveDialog_fnGetProperties,
1954     IFileSaveDialog_fnApplyProperties
1955 };
1956
1957 /**************************************************************************
1958  * IExplorerBrowserEvents implementation
1959  */
1960 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
1961 {
1962     return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
1963 }
1964
1965 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
1966                                                               REFIID riid, void **ppvObject)
1967 {
1968     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
1969     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
1970
1971     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
1972 }
1973
1974 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
1975 {
1976     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
1977     TRACE("%p\n", This);
1978     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
1979 }
1980
1981 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
1982 {
1983     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
1984     TRACE("%p\n", This);
1985     return IFileDialog2_Release(&This->IFileDialog2_iface);
1986 }
1987
1988 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
1989                                                                    PCIDLIST_ABSOLUTE pidlFolder)
1990 {
1991     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
1992     IShellItem *psi;
1993     HRESULT hr;
1994     TRACE("%p (%p)\n", This, pidlFolder);
1995
1996     hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
1997     if(SUCCEEDED(hr))
1998     {
1999         hr = events_OnFolderChanging(This, psi);
2000         IShellItem_Release(psi);
2001
2002         /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
2003         if(hr == S_FALSE)
2004             hr = E_FAIL;
2005
2006         return hr;
2007     }
2008     else
2009         ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
2010
2011     return S_OK;
2012 }
2013
2014 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
2015                                                              IShellView *psv)
2016 {
2017     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2018     TRACE("%p (%p)\n", This, psv);
2019     return S_OK;
2020 }
2021
2022 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
2023                                                                     PCIDLIST_ABSOLUTE pidlFolder)
2024 {
2025     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2026     HRESULT hr;
2027     TRACE("%p (%p)\n", This, pidlFolder);
2028
2029     if(This->psi_folder)
2030         IShellItem_Release(This->psi_folder);
2031
2032     hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
2033     if(FAILED(hr))
2034     {
2035         ERR("Failed to get the current folder.\n");
2036         This->psi_folder = NULL;
2037     }
2038
2039     events_OnFolderChange(This);
2040
2041     return S_OK;
2042 }
2043
2044 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
2045                                                                   PCIDLIST_ABSOLUTE pidlFolder)
2046 {
2047     FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2048     TRACE("%p (%p)\n", This, pidlFolder);
2049     return S_OK;
2050 }
2051
2052 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
2053     IExplorerBrowserEvents_fnQueryInterface,
2054     IExplorerBrowserEvents_fnAddRef,
2055     IExplorerBrowserEvents_fnRelease,
2056     IExplorerBrowserEvents_fnOnNavigationPending,
2057     IExplorerBrowserEvents_fnOnViewCreated,
2058     IExplorerBrowserEvents_fnOnNavigationComplete,
2059     IExplorerBrowserEvents_fnOnNavigationFailed
2060 };
2061
2062 /**************************************************************************
2063  * IServiceProvider implementation
2064  */
2065 static inline FileDialogImpl *impl_from_IServiceProvider(IServiceProvider *iface)
2066 {
2067     return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
2068 }
2069
2070 static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface,
2071                                                         REFIID riid, void **ppvObject)
2072 {
2073     FileDialogImpl *This = impl_from_IServiceProvider(iface);
2074     TRACE("%p\n", This);
2075     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2076 }
2077
2078 static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface)
2079 {
2080     FileDialogImpl *This = impl_from_IServiceProvider(iface);
2081     TRACE("%p\n", This);
2082     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2083 }
2084
2085 static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface)
2086 {
2087     FileDialogImpl *This = impl_from_IServiceProvider(iface);
2088     TRACE("%p\n", This);
2089     return IFileDialog2_Release(&This->IFileDialog2_iface);
2090 }
2091
2092 static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
2093                                                       REFGUID guidService,
2094                                                       REFIID riid, void **ppv)
2095 {
2096     FileDialogImpl *This = impl_from_IServiceProvider(iface);
2097     HRESULT hr = E_FAIL;
2098     TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
2099
2100     *ppv = NULL;
2101     if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
2102         hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
2103     else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
2104         hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
2105     else
2106         FIXME("Interface %s requested from unknown service %s\n",
2107               debugstr_guid(riid), debugstr_guid(guidService));
2108
2109     return hr;
2110 }
2111
2112 static const IServiceProviderVtbl vt_IServiceProvider = {
2113     IServiceProvider_fnQueryInterface,
2114     IServiceProvider_fnAddRef,
2115     IServiceProvider_fnRelease,
2116     IServiceProvider_fnQueryService
2117 };
2118
2119 /**************************************************************************
2120  * ICommDlgBrowser3 implementation
2121  */
2122 static inline FileDialogImpl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface)
2123 {
2124     return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
2125 }
2126
2127 static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface,
2128                                                         REFIID riid, void **ppvObject)
2129 {
2130     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2131     TRACE("%p\n", This);
2132     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2133 }
2134
2135 static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface)
2136 {
2137     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2138     TRACE("%p\n", This);
2139     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2140 }
2141
2142 static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface)
2143 {
2144     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2145     TRACE("%p\n", This);
2146     return IFileDialog2_Release(&This->IFileDialog2_iface);
2147 }
2148
2149 static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3 *iface,
2150                                                           IShellView *shv)
2151 {
2152     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2153     HRESULT hr;
2154     TRACE("%p (%p)\n", This, shv);
2155
2156     hr = on_default_action(This);
2157
2158     if(SUCCEEDED(hr))
2159         EndDialog(This->dlg_hwnd, S_OK);
2160
2161     return S_OK;
2162 }
2163
2164 static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange(ICommDlgBrowser3 *iface,
2165                                                        IShellView *shv, ULONG uChange )
2166 {
2167     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2168     IDataObject *new_selection;
2169     HRESULT hr;
2170     TRACE("%p (%p, %x)\n", This, shv, uChange);
2171
2172     switch(uChange)
2173     {
2174     case CDBOSC_SELCHANGE:
2175         if(This->psia_selection)
2176         {
2177             IShellItemArray_Release(This->psia_selection);
2178             This->psia_selection = NULL;
2179         }
2180
2181         hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
2182         if(SUCCEEDED(hr))
2183         {
2184             hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
2185                                                       (void**)&This->psia_selection);
2186             if(SUCCEEDED(hr))
2187             {
2188                 fill_filename_from_selection(This);
2189                 events_OnSelectionChange(This);
2190             }
2191
2192             IDataObject_Release(new_selection);
2193         }
2194         break;
2195     default:
2196         TRACE("Unhandled state change\n");
2197     }
2198     return S_OK;
2199 }
2200
2201 static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
2202                                                        IShellView *shv, LPCITEMIDLIST pidl)
2203 {
2204     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2205     IShellItem *psi;
2206     LPWSTR filename;
2207     LPITEMIDLIST parent_pidl;
2208     HRESULT hr;
2209     ULONG attr;
2210     TRACE("%p (%p, %p)\n", This, shv, pidl);
2211
2212     if(!This->filterspec_count)
2213         return S_OK;
2214
2215     hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
2216     if(SUCCEEDED(hr))
2217     {
2218         LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
2219         hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
2220         ILFree(parent_pidl);
2221         ILFree(full_pidl);
2222     }
2223     if(FAILED(hr))
2224     {
2225         ERR("Failed to get shellitem (%08x).\n", hr);
2226         return S_OK;
2227     }
2228
2229     hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
2230     if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
2231     {
2232         IShellItem_Release(psi);
2233         return S_OK;
2234     }
2235
2236     hr = S_OK;
2237     if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
2238     {
2239         if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
2240             hr = S_FALSE;
2241         CoTaskMemFree(filename);
2242     }
2243
2244     IShellItem_Release(psi);
2245     return hr;
2246 }
2247
2248 static HRESULT WINAPI ICommDlgBrowser3_fnNotify(ICommDlgBrowser3 *iface,
2249                                                 IShellView *ppshv, DWORD dwNotifyType)
2250 {
2251     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2252     FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
2253     return E_NOTIMPL;
2254 }
2255
2256 static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText(ICommDlgBrowser3 *iface,
2257                                                             IShellView *pshv,
2258                                                             LPWSTR pszText, int cchMax)
2259 {
2260     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2261     FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
2262     return E_NOTIMPL;
2263 }
2264
2265 static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags(ICommDlgBrowser3 *iface, DWORD *pdwFlags)
2266 {
2267     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2268     FIXME("Stub: %p (%p)\n", This, pdwFlags);
2269     return E_NOTIMPL;
2270 }
2271
2272 static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked(ICommDlgBrowser3 *iface,
2273                                                          IShellView *pshv, int iColumn)
2274 {
2275     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2276     FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
2277     return E_NOTIMPL;
2278 }
2279
2280 static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter(ICommDlgBrowser3 *iface,
2281                                                           LPWSTR pszFileSpec, int cchFileSpec)
2282 {
2283     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2284     FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
2285     return E_NOTIMPL;
2286 }
2287
2288 static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated(ICommDlgBrowser3 *iface,
2289                                                           IShellView *pshv)
2290 {
2291     FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2292     FIXME("Stub: %p (%p)\n", This, pshv);
2293     return E_NOTIMPL;
2294 }
2295
2296 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
2297     ICommDlgBrowser3_fnQueryInterface,
2298     ICommDlgBrowser3_fnAddRef,
2299     ICommDlgBrowser3_fnRelease,
2300     ICommDlgBrowser3_fnOnDefaultCommand,
2301     ICommDlgBrowser3_fnOnStateChange,
2302     ICommDlgBrowser3_fnIncludeObject,
2303     ICommDlgBrowser3_fnNotify,
2304     ICommDlgBrowser3_fnGetDefaultMenuText,
2305     ICommDlgBrowser3_fnGetViewFlags,
2306     ICommDlgBrowser3_fnOnColumnClicked,
2307     ICommDlgBrowser3_fnGetCurrentFilter,
2308     ICommDlgBrowser3_fnOnPreviewCreated
2309 };
2310
2311 /**************************************************************************
2312  * IOleWindow implementation
2313  */
2314 static inline FileDialogImpl *impl_from_IOleWindow(IOleWindow *iface)
2315 {
2316     return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
2317 }
2318
2319 static HRESULT WINAPI IOleWindow_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
2320 {
2321     FileDialogImpl *This = impl_from_IOleWindow(iface);
2322     return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2323 }
2324
2325 static ULONG WINAPI IOleWindow_fnAddRef(IOleWindow *iface)
2326 {
2327     FileDialogImpl *This = impl_from_IOleWindow(iface);
2328     return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2329 }
2330
2331 static ULONG WINAPI IOleWindow_fnRelease(IOleWindow *iface)
2332 {
2333     FileDialogImpl *This = impl_from_IOleWindow(iface);
2334     return IFileDialog2_Release(&This->IFileDialog2_iface);
2335 }
2336
2337 static HRESULT WINAPI IOleWindow_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMOde)
2338 {
2339     FileDialogImpl *This = impl_from_IOleWindow(iface);
2340     FIXME("Stub: %p (%d)\n", This, fEnterMOde);
2341     return E_NOTIMPL;
2342 }
2343
2344 static HRESULT WINAPI IOleWindow_fnGetWindow(IOleWindow *iface, HWND *phwnd)
2345 {
2346     FileDialogImpl *This = impl_from_IOleWindow(iface);
2347     TRACE("%p (%p)\n", This, phwnd);
2348     *phwnd = This->dlg_hwnd;
2349     return S_OK;
2350 }
2351
2352 static const IOleWindowVtbl vt_IOleWindow = {
2353     IOleWindow_fnQueryInterface,
2354     IOleWindow_fnAddRef,
2355     IOleWindow_fnRelease,
2356     IOleWindow_fnGetWindow,
2357     IOleWindow_fnContextSensitiveHelp
2358 };
2359
2360 static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv, enum ITEMDLG_TYPE type)
2361 {
2362     FileDialogImpl *fdimpl;
2363     HRESULT hr;
2364     IShellFolder *psf;
2365     TRACE("%p, %s, %p\n", pUnkOuter, debugstr_guid(riid), ppv);
2366
2367     if(!ppv)
2368         return E_POINTER;
2369     if(pUnkOuter)
2370         return CLASS_E_NOAGGREGATION;
2371
2372     fdimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(FileDialogImpl));
2373     if(!fdimpl)
2374         return E_OUTOFMEMORY;
2375
2376     fdimpl->ref = 1;
2377     fdimpl->IFileDialog2_iface.lpVtbl = &vt_IFileDialog2;
2378     fdimpl->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
2379     fdimpl->IServiceProvider_iface.lpVtbl = &vt_IServiceProvider;
2380     fdimpl->ICommDlgBrowser3_iface.lpVtbl = &vt_ICommDlgBrowser3;
2381     fdimpl->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
2382
2383     if(type == ITEMDLG_TYPE_OPEN)
2384     {
2385         fdimpl->dlg_type = ITEMDLG_TYPE_OPEN;
2386         fdimpl->u.IFileOpenDialog_iface.lpVtbl = &vt_IFileOpenDialog;
2387         fdimpl->options = FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
2388         fdimpl->custom_title = fdimpl->custom_okbutton = NULL;
2389     }
2390     else
2391     {
2392         WCHAR buf[16];
2393         fdimpl->dlg_type = ITEMDLG_TYPE_SAVE;
2394         fdimpl->u.IFileSaveDialog_iface.lpVtbl = &vt_IFileSaveDialog;
2395         fdimpl->options = FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
2396
2397         LoadStringW(COMDLG32_hInstance, IDS_SAVE, buf, sizeof(buf)/sizeof(WCHAR));
2398         fdimpl->custom_title = StrDupW(buf);
2399         fdimpl->custom_okbutton = StrDupW(buf);
2400     }
2401
2402     fdimpl->filterspecs = NULL;
2403     fdimpl->filterspec_count = 0;
2404     fdimpl->filetypeindex = 0;
2405
2406     fdimpl->psia_selection = fdimpl->psia_results = NULL;
2407     fdimpl->psi_setfolder = fdimpl->psi_folder = NULL;
2408
2409     list_init(&fdimpl->events_clients);
2410     fdimpl->events_next_cookie = 0;
2411
2412     fdimpl->dlg_hwnd = NULL;
2413     fdimpl->peb = NULL;
2414
2415     fdimpl->set_filename = NULL;
2416     fdimpl->default_ext = NULL;
2417     fdimpl->custom_cancelbutton = fdimpl->custom_filenamelabel = NULL;
2418
2419     /* FIXME: The default folder setting should be restored for the
2420      * application if it was previously set. */
2421     SHGetDesktopFolder(&psf);
2422     SHGetItemFromObject((IUnknown*)psf, &IID_IShellItem, (void**)&fdimpl->psi_defaultfolder);
2423     IShellFolder_Release(psf);
2424
2425     hr = IUnknown_QueryInterface((IUnknown*)fdimpl, riid, ppv);
2426     IUnknown_Release((IUnknown*)fdimpl);
2427     return hr;
2428 }
2429
2430 HRESULT FileOpenDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
2431 {
2432     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_OPEN);
2433 }
2434
2435 HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
2436 {
2437     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);
2438 }