ddraw/tests: Recognise E_NOTIMPL returned from UpdateOverlay() on VMware as broken.
[wine.git] / dlls / comdlg32 / itemdlg.c
blob309601785733b33ff179162b0871dfc513467401
1 /*
2 * Common Item Dialog
4 * Copyright 2010,2011 David Hedberg
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.
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.
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
21 #include <stdarg.h>
23 #define COBJMACROS
24 #define NONAMELESSUNION
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "wingdi.h"
30 #include "winreg.h"
31 #include "shlwapi.h"
33 #include "commdlg.h"
34 #include "cdlg.h"
35 #include "filedlgbrowser.h"
37 #include "wine/debug.h"
38 #include "wine/list.h"
40 #define IDC_NAV_TOOLBAR 200
41 #define IDC_NAVBACK 201
42 #define IDC_NAVFORWARD 202
44 #include <initguid.h>
45 /* This seems to be another version of IID_IFileDialogCustomize. If
46 * there is any difference I have yet to find it. */
47 DEFINE_GUID(IID_IFileDialogCustomizeAlt, 0x8016B7B3, 0x3D49, 0x4504, 0xA0,0xAA, 0x2A,0x37,0x49,0x4E,0x60,0x6F);
49 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
51 static const WCHAR notifysink_childW[] = {'n','f','s','_','c','h','i','l','d',0};
52 static const WCHAR floatnotifysinkW[] = {'F','l','o','a','t','N','o','t','i','f','y','S','i','n','k',0};
53 static const WCHAR radiobuttonlistW[] = {'R','a','d','i','o','B','u','t','t','o','n','L','i','s','t',0};
55 enum ITEMDLG_TYPE {
56 ITEMDLG_TYPE_OPEN,
57 ITEMDLG_TYPE_SAVE
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 IDLG_CCTRL_OPENDROPDOWN,
70 IDLG_CCTRL_VISUALGROUP
73 typedef struct cctrl_item {
74 DWORD id, parent_id;
75 LPWSTR label;
76 CDCONTROLSTATEF cdcstate;
77 HWND hwnd;
78 struct list entry;
79 } cctrl_item;
81 typedef struct {
82 HWND hwnd, wrapper_hwnd;
83 UINT id, dlgid;
84 enum ITEMDLG_CCTRL_TYPE type;
85 CDCONTROLSTATEF cdcstate;
86 struct list entry;
88 struct list sub_cctrls;
89 struct list sub_cctrls_entry;
90 struct list sub_items;
91 } customctrl;
93 typedef struct {
94 struct list entry;
95 IFileDialogEvents *pfde;
96 DWORD cookie;
97 } events_client;
99 typedef struct FileDialogImpl {
100 IFileDialog2 IFileDialog2_iface;
101 union {
102 IFileOpenDialog IFileOpenDialog_iface;
103 IFileSaveDialog IFileSaveDialog_iface;
104 } u;
105 enum ITEMDLG_TYPE dlg_type;
106 IExplorerBrowserEvents IExplorerBrowserEvents_iface;
107 IServiceProvider IServiceProvider_iface;
108 ICommDlgBrowser3 ICommDlgBrowser3_iface;
109 IOleWindow IOleWindow_iface;
110 IFileDialogCustomize IFileDialogCustomize_iface;
111 LONG ref;
113 FILEOPENDIALOGOPTIONS options;
114 COMDLG_FILTERSPEC *filterspecs;
115 UINT filterspec_count;
116 UINT filetypeindex;
118 struct list events_clients;
119 DWORD events_next_cookie;
121 IShellItemArray *psia_selection;
122 IShellItemArray *psia_results;
123 IShellItem *psi_defaultfolder;
124 IShellItem *psi_setfolder;
125 IShellItem *psi_folder;
127 HWND dlg_hwnd;
128 IExplorerBrowser *peb;
129 DWORD ebevents_cookie;
131 LPWSTR set_filename;
132 LPWSTR default_ext;
133 LPWSTR custom_title;
134 LPWSTR custom_okbutton;
135 LPWSTR custom_cancelbutton;
136 LPWSTR custom_filenamelabel;
138 UINT cctrl_width, cctrl_def_height, cctrls_cols;
139 UINT cctrl_indent, dpi_x, dpi_y;
140 HWND cctrls_hwnd;
141 struct list cctrls;
142 UINT_PTR cctrl_next_dlgid;
143 customctrl *cctrl_active_vg;
145 HMENU hmenu_opendropdown;
146 customctrl cctrl_opendropdown;
147 HFONT hfont_opendropdown;
148 BOOL opendropdown_has_selection;
149 DWORD opendropdown_selection;
151 GUID client_guid;
152 } FileDialogImpl;
154 /**************************************************************************
155 * Event wrappers.
157 static HRESULT events_OnFileOk(FileDialogImpl *This)
159 events_client *cursor;
160 HRESULT hr = S_OK;
161 TRACE("%p\n", This);
163 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
165 TRACE("Notifying %p\n", cursor);
166 hr = IFileDialogEvents_OnFileOk(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
167 if(FAILED(hr) && hr != E_NOTIMPL)
168 break;
171 if(hr == E_NOTIMPL)
172 hr = S_OK;
174 return hr;
177 static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder)
179 events_client *cursor;
180 HRESULT hr = S_OK;
181 TRACE("%p (%p)\n", This, folder);
183 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
185 TRACE("Notifying %p\n", cursor);
186 hr = IFileDialogEvents_OnFolderChanging(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, folder);
187 if(FAILED(hr) && hr != E_NOTIMPL)
188 break;
191 if(hr == E_NOTIMPL)
192 hr = S_OK;
194 return hr;
197 static void events_OnFolderChange(FileDialogImpl *This)
199 events_client *cursor;
200 TRACE("%p\n", This);
202 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
204 TRACE("Notifying %p\n", cursor);
205 IFileDialogEvents_OnFolderChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
209 static void events_OnSelectionChange(FileDialogImpl *This)
211 events_client *cursor;
212 TRACE("%p\n", This);
214 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
216 TRACE("Notifying %p\n", cursor);
217 IFileDialogEvents_OnSelectionChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
221 static void events_OnTypeChange(FileDialogImpl *This)
223 events_client *cursor;
224 TRACE("%p\n", This);
226 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
228 TRACE("Notifying %p\n", cursor);
229 IFileDialogEvents_OnTypeChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
233 static HRESULT events_OnOverwrite(FileDialogImpl *This, IShellItem *shellitem)
235 events_client *cursor;
236 HRESULT hr = S_OK;
237 FDE_OVERWRITE_RESPONSE response = FDEOR_DEFAULT;
238 TRACE("%p %p\n", This, shellitem);
240 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
242 TRACE("Notifying %p\n", cursor);
243 hr = IFileDialogEvents_OnOverwrite(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, shellitem, &response);
244 TRACE("<-- hr=%x response=%u\n", hr, response);
245 if(FAILED(hr) && hr != E_NOTIMPL)
246 break;
249 if(hr == E_NOTIMPL)
250 hr = S_OK;
252 if(SUCCEEDED(hr))
254 if (response == FDEOR_DEFAULT)
256 WCHAR buf[100];
257 int answer;
259 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, buf, 100);
260 answer = MessageBoxW(This->dlg_hwnd, buf, This->custom_title,
261 MB_YESNO | MB_ICONEXCLAMATION);
262 if (answer == IDNO || answer == IDCANCEL)
264 hr = E_FAIL;
267 else if (response == FDEOR_REFUSE)
268 hr = E_FAIL;
271 return hr;
274 static inline HRESULT get_cctrl_event(IFileDialogEvents *pfde, IFileDialogControlEvents **pfdce)
276 return IFileDialogEvents_QueryInterface(pfde, &IID_IFileDialogControlEvents, (void**)pfdce);
279 static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id)
281 events_client *cursor;
282 TRACE("%p\n", This);
284 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
286 IFileDialogControlEvents *pfdce;
287 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
289 TRACE("Notifying %p\n", cursor);
290 IFileDialogControlEvents_OnButtonClicked(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
291 IFileDialogControlEvents_Release(pfdce);
295 return S_OK;
298 static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DWORD item_id)
300 events_client *cursor;
301 TRACE("%p %i %i\n", This, ctl_id, item_id);
303 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
305 IFileDialogControlEvents *pfdce;
306 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
308 TRACE("Notifying %p\n", cursor);
309 IFileDialogControlEvents_OnItemSelected(pfdce, &This->IFileDialogCustomize_iface, ctl_id, item_id);
310 IFileDialogControlEvents_Release(pfdce);
314 return S_OK;
317 static HRESULT cctrl_event_OnCheckButtonToggled(FileDialogImpl *This, DWORD ctl_id, BOOL checked)
319 events_client *cursor;
320 TRACE("%p\n", This);
322 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
324 IFileDialogControlEvents *pfdce;
325 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
327 TRACE("Notifying %p\n", cursor);
328 IFileDialogControlEvents_OnCheckButtonToggled(pfdce, &This->IFileDialogCustomize_iface, ctl_id, checked);
329 IFileDialogControlEvents_Release(pfdce);
333 return S_OK;
336 static HRESULT cctrl_event_OnControlActivating(FileDialogImpl *This,
337 DWORD ctl_id)
339 events_client *cursor;
340 TRACE("%p\n", This);
342 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
344 IFileDialogControlEvents *pfdce;
345 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
347 TRACE("Notifying %p\n", cursor);
348 IFileDialogControlEvents_OnControlActivating(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
349 IFileDialogControlEvents_Release(pfdce);
353 return S_OK;
356 /**************************************************************************
357 * Helper functions.
359 static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
361 HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
362 UINT len;
364 if(!hwnd_edit)
366 if(This->set_filename)
368 len = lstrlenW(This->set_filename);
369 *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
370 lstrcpyW(*str, This->set_filename);
371 return len;
373 return 0;
376 len = SendMessageW(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);
377 *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
378 if(!*str)
379 return 0;
381 SendMessageW(hwnd_edit, WM_GETTEXT, len+1, (LPARAM)*str);
382 return len;
385 static BOOL set_file_name(FileDialogImpl *This, LPCWSTR str)
387 if(This->set_filename)
388 LocalFree(This->set_filename);
390 This->set_filename = str ? StrDupW(str) : NULL;
392 return SetDlgItemTextW(This->dlg_hwnd, IDC_FILENAME, This->set_filename);
395 static void fill_filename_from_selection(FileDialogImpl *This)
397 IShellItem *psi;
398 LPWSTR *names;
399 HRESULT hr;
400 UINT item_count, valid_count;
401 UINT len_total, i;
403 if(!This->psia_selection)
404 return;
406 hr = IShellItemArray_GetCount(This->psia_selection, &item_count);
407 if(FAILED(hr) || !item_count)
408 return;
410 names = HeapAlloc(GetProcessHeap(), 0, item_count*sizeof(LPWSTR));
412 /* Get names of the selected items */
413 valid_count = 0; len_total = 0;
414 for(i = 0; i < item_count; i++)
416 hr = IShellItemArray_GetItemAt(This->psia_selection, i, &psi);
417 if(SUCCEEDED(hr))
419 UINT attr;
421 hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &attr);
422 if(SUCCEEDED(hr) &&
423 (( (This->options & FOS_PICKFOLDERS) && !(attr & SFGAO_FOLDER)) ||
424 (!(This->options & FOS_PICKFOLDERS) && (attr & SFGAO_FOLDER))))
425 continue;
427 hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &names[valid_count]);
428 if(SUCCEEDED(hr))
430 len_total += lstrlenW(names[valid_count]) + 3;
431 valid_count++;
433 IShellItem_Release(psi);
437 if(valid_count == 1)
439 set_file_name(This, names[0]);
440 CoTaskMemFree(names[0]);
442 else if(valid_count > 1)
444 LPWSTR string = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len_total);
445 LPWSTR cur_point = string;
447 for(i = 0; i < valid_count; i++)
449 LPWSTR file = names[i];
450 *cur_point++ = '\"';
451 lstrcpyW(cur_point, file);
452 cur_point += lstrlenW(file);
453 *cur_point++ = '\"';
454 *cur_point++ = ' ';
455 CoTaskMemFree(file);
457 *(cur_point-1) = '\0';
459 set_file_name(This, string);
460 HeapFree(GetProcessHeap(), 0, string);
463 HeapFree(GetProcessHeap(), 0, names);
464 return;
467 static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec)
469 WCHAR *endpos, *ext;
471 lstrcpyW(buf, spec);
472 if( (endpos = StrChrW(buf, ';')) )
473 *endpos = '\0';
475 ext = PathFindExtensionW(buf);
476 if(StrChrW(ext, '*'))
477 return NULL;
479 return ext;
482 static BOOL shell_item_exists(IShellItem* shellitem)
484 LPWSTR filename;
485 HRESULT hr;
486 BOOL result;
488 hr = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &filename);
489 if (SUCCEEDED(hr))
491 /* FIXME: Implement SFGAO_VALIDATE in Wine and use it instead. */
492 result = (GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES);
493 CoTaskMemFree(filename);
495 else
497 SFGAOF attributes;
498 result = SUCCEEDED(IShellItem_GetAttributes(shellitem, SFGAO_VALIDATE, &attributes));
501 return result;
504 static HRESULT on_default_action(FileDialogImpl *This)
506 IShellFolder *psf_parent, *psf_desktop;
507 LPITEMIDLIST *pidla;
508 LPITEMIDLIST current_folder;
509 LPWSTR fn_iter, files = NULL, tmp_files;
510 UINT file_count = 0, len, i;
511 int open_action;
512 HRESULT hr, ret = E_FAIL;
514 len = get_file_name(This, &tmp_files);
515 if(len)
517 UINT size_used;
518 file_count = COMDLG32_SplitFileNames(tmp_files, len, &files, &size_used);
519 CoTaskMemFree(tmp_files);
521 if(!file_count) return E_FAIL;
523 hr = SHGetIDListFromObject((IUnknown*)This->psi_folder, &current_folder);
524 if(FAILED(hr))
526 ERR("Failed to get pidl for current directory.\n");
527 HeapFree(GetProcessHeap(), 0, files);
528 return hr;
531 TRACE("Acting on %d file(s).\n", file_count);
533 pidla = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST) * file_count);
534 open_action = ONOPEN_OPEN;
535 fn_iter = files;
537 for(i = 0; i < file_count && open_action == ONOPEN_OPEN; i++)
539 WCHAR canon_filename[MAX_PATH];
540 psf_parent = NULL;
542 COMDLG32_GetCanonicalPath(current_folder, fn_iter, canon_filename);
544 if( (This->options & FOS_NOVALIDATE) &&
545 !(This->options & FOS_FILEMUSTEXIST) )
546 open_action = ONOPEN_OPEN;
547 else
548 open_action = ONOPEN_BROWSE;
550 open_action = FILEDLG95_ValidatePathAction(canon_filename, &psf_parent, This->dlg_hwnd,
551 This->options & ~FOS_FILEMUSTEXIST,
552 (This->dlg_type == ITEMDLG_TYPE_SAVE),
553 open_action);
555 /* Add the proper extension */
556 if(open_action == ONOPEN_OPEN)
558 static const WCHAR dotW[] = {'.',0};
560 if(This->dlg_type == ITEMDLG_TYPE_SAVE)
562 WCHAR extbuf[MAX_PATH], *newext = NULL;
564 if(This->filterspec_count)
566 newext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
568 else if(This->default_ext)
570 lstrcpyW(extbuf, dotW);
571 lstrcatW(extbuf, This->default_ext);
572 newext = extbuf;
575 if(newext)
577 WCHAR *ext = PathFindExtensionW(canon_filename);
578 if(lstrcmpW(ext, newext))
579 lstrcatW(canon_filename, newext);
582 else
584 if( !(This->options & FOS_NOVALIDATE) && (This->options & FOS_FILEMUSTEXIST) &&
585 !PathFileExistsW(canon_filename))
587 if(This->default_ext)
589 lstrcatW(canon_filename, dotW);
590 lstrcatW(canon_filename, This->default_ext);
592 if(!PathFileExistsW(canon_filename))
594 FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
595 open_action = ONOPEN_BROWSE;
598 else
600 FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
601 open_action = ONOPEN_BROWSE;
607 pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename);
609 if(psf_parent && !(open_action == ONOPEN_BROWSE))
610 IShellFolder_Release(psf_parent);
612 fn_iter += (WCHAR)lstrlenW(fn_iter) + 1;
615 HeapFree(GetProcessHeap(), 0, files);
616 ILFree(current_folder);
618 if((This->options & FOS_PICKFOLDERS) && open_action == ONOPEN_BROWSE)
619 open_action = ONOPEN_OPEN; /* FIXME: Multiple folders? */
621 switch(open_action)
623 case ONOPEN_SEARCH:
624 FIXME("Filtering not implemented.\n");
625 break;
627 case ONOPEN_BROWSE:
628 hr = IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psf_parent, SBSP_DEFBROWSER);
629 if(FAILED(hr))
630 ERR("Failed to browse to directory: %08x\n", hr);
632 IShellFolder_Release(psf_parent);
633 break;
635 case ONOPEN_OPEN:
636 hr = SHGetDesktopFolder(&psf_desktop);
637 if(SUCCEEDED(hr))
639 if(This->psia_results)
641 IShellItemArray_Release(This->psia_results);
642 This->psia_results = NULL;
645 hr = SHCreateShellItemArray(NULL, psf_desktop, file_count, (PCUITEMID_CHILD_ARRAY)pidla,
646 &This->psia_results);
648 IShellFolder_Release(psf_desktop);
650 if(FAILED(hr))
651 break;
653 if(This->options & FOS_PICKFOLDERS)
655 SFGAOF attributes;
656 hr = IShellItemArray_GetAttributes(This->psia_results, SIATTRIBFLAGS_AND, SFGAO_FOLDER, &attributes);
657 if(hr != S_OK)
659 WCHAR buf[64];
660 LoadStringW(COMDLG32_hInstance, IDS_INVALID_FOLDERNAME, buf, sizeof(buf)/sizeof(WCHAR));
662 MessageBoxW(This->dlg_hwnd, buf, This->custom_title, MB_OK | MB_ICONEXCLAMATION);
664 IShellItemArray_Release(This->psia_results);
665 This->psia_results = NULL;
666 break;
670 if((This->options & FOS_OVERWRITEPROMPT) && This->dlg_type == ITEMDLG_TYPE_SAVE)
672 IShellItem *shellitem;
674 for (i=0; SUCCEEDED(hr) && i<file_count; i++)
676 hr = IShellItemArray_GetItemAt(This->psia_results, i, &shellitem);
677 if (SUCCEEDED(hr))
679 if (shell_item_exists(shellitem))
680 hr = events_OnOverwrite(This, shellitem);
682 IShellItem_Release(shellitem);
686 if (FAILED(hr))
687 break;
690 if(events_OnFileOk(This) == S_OK)
691 ret = S_OK;
693 break;
695 default:
696 ERR("Failed.\n");
697 break;
700 /* Clean up */
701 for(i = 0; i < file_count; i++)
702 ILFree(pidla[i]);
703 HeapFree(GetProcessHeap(), 0, pidla);
705 /* Success closes the dialog */
706 return ret;
709 static void show_opendropdown(FileDialogImpl *This)
711 HWND open_hwnd;
712 RECT open_rc;
713 MSG msg;
715 open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
717 GetWindowRect(open_hwnd, &open_rc);
719 if (TrackPopupMenu(This->hmenu_opendropdown, 0, open_rc.left, open_rc.bottom, 0, This->dlg_hwnd, NULL) &&
720 PeekMessageW(&msg, This->dlg_hwnd, WM_MENUCOMMAND, WM_MENUCOMMAND, PM_REMOVE))
722 MENUITEMINFOW mii;
724 This->opendropdown_has_selection = TRUE;
726 mii.cbSize = sizeof(mii);
727 mii.fMask = MIIM_ID;
728 GetMenuItemInfoW((HMENU)msg.lParam, msg.wParam, TRUE, &mii);
729 This->opendropdown_selection = mii.wID;
731 if(SUCCEEDED(on_default_action(This)))
732 EndDialog(This->dlg_hwnd, S_OK);
733 else
734 This->opendropdown_has_selection = FALSE;
738 /**************************************************************************
739 * Control item functions.
742 static void item_free(cctrl_item *item)
744 DestroyWindow(item->hwnd);
745 HeapFree(GetProcessHeap(), 0, item->label);
746 HeapFree(GetProcessHeap(), 0, item);
749 static cctrl_item* get_item(customctrl* parent, DWORD itemid, CDCONTROLSTATEF visible_flags, DWORD* position)
751 DWORD dummy;
752 cctrl_item* item;
754 if (!position) position = &dummy;
756 *position = 0;
758 LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
760 if (item->id == itemid)
761 return item;
763 if ((item->cdcstate & visible_flags) == visible_flags)
764 (*position)++;
767 return NULL;
770 static cctrl_item* get_first_item(customctrl* parent)
772 cctrl_item* item;
774 LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
776 if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
777 return item;
780 return NULL;
783 static HRESULT add_item(customctrl* parent, DWORD itemid, LPCWSTR label, cctrl_item** result)
785 cctrl_item* item;
786 LPWSTR label_copy;
788 if (get_item(parent, itemid, 0, NULL))
789 return E_INVALIDARG;
791 item = HeapAlloc(GetProcessHeap(), 0, sizeof(*item));
792 label_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(label)+1)*sizeof(WCHAR));
794 if (!item || !label_copy)
796 HeapFree(GetProcessHeap(), 0, item);
797 HeapFree(GetProcessHeap(), 0, label_copy);
798 return E_OUTOFMEMORY;
801 item->id = itemid;
802 item->parent_id = parent->id;
803 lstrcpyW(label_copy, label);
804 item->label = label_copy;
805 item->cdcstate = CDCS_VISIBLE|CDCS_ENABLED;
806 item->hwnd = NULL;
807 list_add_tail(&parent->sub_items, &item->entry);
809 *result = item;
811 return S_OK;
814 /**************************************************************************
815 * Control functions.
817 static inline customctrl *get_cctrl_from_dlgid(FileDialogImpl *This, DWORD dlgid)
819 customctrl *ctrl, *sub_ctrl;
821 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
823 if(ctrl->dlgid == dlgid)
824 return ctrl;
826 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
827 if(sub_ctrl->dlgid == dlgid)
828 return sub_ctrl;
831 ERR("Failed to find control with dialog id %d\n", dlgid);
832 return NULL;
835 static inline customctrl *get_cctrl(FileDialogImpl *This, DWORD ctlid)
837 customctrl *ctrl, *sub_ctrl;
839 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
841 if(ctrl->id == ctlid)
842 return ctrl;
844 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
845 if(sub_ctrl->id == ctlid)
846 return sub_ctrl;
849 if (This->hmenu_opendropdown && This->cctrl_opendropdown.id == ctlid)
850 return &This->cctrl_opendropdown;
852 TRACE("No existing control with control id %d\n", ctlid);
853 return NULL;
856 static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multiline)
858 LPWSTR text;
859 UINT len, final_width;
860 UINT lines, final_height;
861 SIZE size;
862 RECT rc;
863 HDC hdc;
864 WCHAR *c;
865 HFONT font;
867 TRACE("\n");
869 len = SendMessageW(hctrl, WM_GETTEXTLENGTH, 0, 0);
870 text = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
871 if(!text) return;
872 SendMessageW(hctrl, WM_GETTEXT, len+1, (LPARAM)text);
874 hdc = GetDC(hctrl);
875 font = (HFONT)SendMessageW(hctrl, WM_GETFONT, 0, 0);
876 font = SelectObject(hdc, font);
877 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &size);
878 SelectObject(hdc, font);
879 ReleaseDC(hctrl, hdc);
881 if(len && multiline)
883 /* FIXME: line-wrap */
884 for(lines = 1, c = text; *c != '\0'; c++)
885 if(*c == '\n') lines++;
887 final_height = size.cy*lines + 2*4;
889 else
891 GetWindowRect(hctrl, &rc);
892 final_height = rc.bottom - rc.top;
895 final_width = min(max(size.cx, min_width) + 4, max_width);
896 SetWindowPos(hctrl, NULL, 0, 0, final_width, final_height,
897 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
899 HeapFree(GetProcessHeap(), 0, text);
902 static UINT ctrl_get_height(customctrl *ctrl) {
903 RECT rc;
904 GetWindowRect(ctrl->wrapper_hwnd, &rc);
905 return rc.bottom - rc.top;
908 static void ctrl_free(customctrl *ctrl)
910 customctrl *sub_cur1, *sub_cur2;
911 cctrl_item *item_cur1, *item_cur2;
913 TRACE("Freeing control %p\n", ctrl);
914 if(ctrl->type == IDLG_CCTRL_MENU)
916 TBBUTTON tbb;
917 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
918 DestroyMenu((HMENU)tbb.dwData);
921 LIST_FOR_EACH_ENTRY_SAFE(sub_cur1, sub_cur2, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
923 list_remove(&sub_cur1->sub_cctrls_entry);
924 ctrl_free(sub_cur1);
927 LIST_FOR_EACH_ENTRY_SAFE(item_cur1, item_cur2, &ctrl->sub_items, cctrl_item, entry)
929 list_remove(&item_cur1->entry);
930 item_free(item_cur1);
933 DestroyWindow(ctrl->hwnd);
934 HeapFree(GetProcessHeap(), 0, ctrl);
937 static void customctrl_resize(FileDialogImpl *This, customctrl *ctrl)
939 RECT rc;
940 UINT total_height;
941 UINT max_width, size;
942 customctrl *sub_ctrl;
944 switch(ctrl->type)
946 case IDLG_CCTRL_PUSHBUTTON:
947 case IDLG_CCTRL_COMBOBOX:
948 case IDLG_CCTRL_CHECKBUTTON:
949 case IDLG_CCTRL_TEXT:
950 size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
951 ctrl_resize(ctrl->hwnd, size, size, TRUE);
952 GetWindowRect(ctrl->hwnd, &rc);
953 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
954 SWP_NOZORDER|SWP_NOMOVE);
955 break;
956 case IDLG_CCTRL_VISUALGROUP:
957 total_height = 0;
958 ctrl_resize(ctrl->hwnd, 0, This->cctrl_indent, TRUE);
960 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
962 customctrl_resize(This, sub_ctrl);
963 SetWindowPos(sub_ctrl->wrapper_hwnd, NULL, This->cctrl_indent, total_height, 0, 0,
964 SWP_NOZORDER|SWP_NOSIZE);
966 total_height += ctrl_get_height(sub_ctrl);
969 /* The label should be right adjusted */
971 UINT width, height;
973 GetWindowRect(ctrl->hwnd, &rc);
974 width = rc.right - rc.left;
975 height = rc.bottom - rc.top;
977 SetWindowPos(ctrl->hwnd, NULL, This->cctrl_indent - width, 0, width, height, SWP_NOZORDER);
980 /* Resize the wrapper window to fit all the sub controls */
981 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, This->cctrl_width + This->cctrl_indent, total_height,
982 SWP_NOZORDER|SWP_NOMOVE);
983 break;
984 case IDLG_CCTRL_RADIOBUTTONLIST:
986 cctrl_item* item;
988 total_height = 0;
989 max_width = 0;
991 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
993 size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
994 ctrl_resize(item->hwnd, size, size, TRUE);
995 SetWindowPos(item->hwnd, NULL, 0, total_height, 0, 0,
996 SWP_NOZORDER|SWP_NOSIZE);
998 GetWindowRect(item->hwnd, &rc);
1000 total_height += rc.bottom - rc.top;
1001 max_width = max(rc.right - rc.left, max_width);
1004 SetWindowPos(ctrl->hwnd, NULL, 0, 0, max_width, total_height,
1005 SWP_NOZORDER|SWP_NOMOVE);
1007 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, max_width, total_height,
1008 SWP_NOZORDER|SWP_NOMOVE);
1010 break;
1012 case IDLG_CCTRL_EDITBOX:
1013 case IDLG_CCTRL_SEPARATOR:
1014 case IDLG_CCTRL_MENU:
1015 case IDLG_CCTRL_OPENDROPDOWN:
1016 /* Nothing */
1017 break;
1021 static LRESULT notifysink_on_create(HWND hwnd, CREATESTRUCTW *crs)
1023 FileDialogImpl *This = crs->lpCreateParams;
1024 TRACE("%p\n", This);
1026 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1027 return TRUE;
1030 static LRESULT notifysink_on_bn_clicked(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
1032 customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
1034 TRACE("%p, %lx\n", This, wparam);
1036 if(ctrl)
1038 if(ctrl->type == IDLG_CCTRL_CHECKBUTTON)
1040 BOOL checked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
1041 cctrl_event_OnCheckButtonToggled(This, ctrl->id, checked);
1043 else
1044 cctrl_event_OnButtonClicked(This, ctrl->id);
1047 return TRUE;
1050 static LRESULT notifysink_on_cbn_selchange(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
1052 customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
1053 TRACE("%p, %p (%lx)\n", This, ctrl, wparam);
1055 if(ctrl)
1057 UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
1058 UINT selid = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
1060 cctrl_event_OnItemSelected(This, ctrl->id, selid);
1062 return TRUE;
1065 static LRESULT notifysink_on_tvn_dropdown(FileDialogImpl *This, LPARAM lparam)
1067 NMTOOLBARW *nmtb = (NMTOOLBARW*)lparam;
1068 customctrl *ctrl = get_cctrl_from_dlgid(This, GetDlgCtrlID(nmtb->hdr.hwndFrom));
1069 POINT pt = { 0, nmtb->rcButton.bottom };
1070 TBBUTTON tbb;
1071 UINT idcmd;
1073 TRACE("%p, %p (%lx)\n", This, ctrl, lparam);
1075 if(ctrl)
1077 cctrl_event_OnControlActivating(This,ctrl->id);
1079 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
1080 ClientToScreen(ctrl->hwnd, &pt);
1081 idcmd = TrackPopupMenu((HMENU)tbb.dwData, TPM_RETURNCMD, pt.x, pt.y, 0, This->dlg_hwnd, NULL);
1082 if(idcmd)
1083 cctrl_event_OnItemSelected(This, ctrl->id, idcmd);
1086 return TBDDRET_DEFAULT;
1089 static LRESULT notifysink_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1091 switch(HIWORD(wparam))
1093 case BN_CLICKED: return notifysink_on_bn_clicked(This, hwnd, wparam);
1094 case CBN_SELCHANGE: return notifysink_on_cbn_selchange(This, hwnd, wparam);
1097 return FALSE;
1100 static LRESULT notifysink_on_wm_notify(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1102 NMHDR *nmhdr = (NMHDR*)lparam;
1104 switch(nmhdr->code)
1106 case TBN_DROPDOWN: return notifysink_on_tvn_dropdown(This, lparam);
1109 return FALSE;
1112 static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
1114 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1115 customctrl *ctrl;
1116 HWND hwnd_child;
1117 RECT rc;
1119 switch(message)
1121 case WM_NCCREATE: return notifysink_on_create(hwnd, (CREATESTRUCTW*)lparam);
1122 case WM_COMMAND: return notifysink_on_wm_command(This, hwnd, wparam, lparam);
1123 case WM_NOTIFY: return notifysink_on_wm_notify(This, hwnd, wparam, lparam);
1124 case WM_SIZE:
1125 hwnd_child = GetPropW(hwnd, notifysink_childW);
1126 ctrl = (customctrl*)GetWindowLongPtrW(hwnd_child, GWLP_USERDATA);
1127 if(ctrl && ctrl->type != IDLG_CCTRL_VISUALGROUP)
1129 GetClientRect(hwnd, &rc);
1130 SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
1132 return TRUE;
1135 return DefWindowProcW(hwnd, message, wparam, lparam);
1138 static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
1139 LPCWSTR text, LPCWSTR wndclass, DWORD ctrl_wsflags,
1140 DWORD ctrl_exflags, UINT height, customctrl **ppctrl)
1142 HWND ns_hwnd, control_hwnd, parent_hwnd;
1143 DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
1144 customctrl *ctrl;
1146 if(get_cctrl(This, id))
1147 return E_UNEXPECTED; /* Duplicate id */
1149 if(This->cctrl_active_vg)
1150 parent_hwnd = This->cctrl_active_vg->wrapper_hwnd;
1151 else
1152 parent_hwnd = This->cctrls_hwnd;
1154 ns_hwnd = CreateWindowExW(0, floatnotifysinkW, NULL, wsflags,
1155 0, 0, This->cctrl_width, height, parent_hwnd,
1156 (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
1157 control_hwnd = CreateWindowExW(ctrl_exflags, wndclass, text, wsflags | ctrl_wsflags,
1158 0, 0, This->cctrl_width, height, ns_hwnd,
1159 (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, 0);
1161 if(!ns_hwnd || !control_hwnd)
1163 ERR("Failed to create wrapper (%p) or control (%p)\n", ns_hwnd, control_hwnd);
1164 DestroyWindow(ns_hwnd);
1165 DestroyWindow(control_hwnd);
1167 return E_FAIL;
1170 SetPropW(ns_hwnd, notifysink_childW, control_hwnd);
1172 ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(customctrl));
1173 if(!ctrl)
1174 return E_OUTOFMEMORY;
1176 ctrl->hwnd = control_hwnd;
1177 ctrl->wrapper_hwnd = ns_hwnd;
1178 ctrl->id = id;
1179 ctrl->dlgid = This->cctrl_next_dlgid;
1180 ctrl->cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
1181 list_init(&ctrl->sub_cctrls);
1182 list_init(&ctrl->sub_items);
1184 if(This->cctrl_active_vg)
1185 list_add_tail(&This->cctrl_active_vg->sub_cctrls, &ctrl->sub_cctrls_entry);
1186 else
1187 list_add_tail(&This->cctrls, &ctrl->entry);
1189 SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)ctrl);
1191 if(ppctrl) *ppctrl = ctrl;
1193 This->cctrl_next_dlgid++;
1194 return S_OK;
1197 /**************************************************************************
1198 * Container functions.
1200 static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
1202 UINT container_height;
1203 UINT column_width;
1204 UINT nr_of_cols;
1205 UINT max_control_height, total_height = 0;
1206 UINT cur_col_pos, cur_row_pos;
1207 customctrl *ctrl;
1208 BOOL fits_height;
1209 UINT cspacing = MulDiv(90, This->dpi_x, USER_DEFAULT_SCREEN_DPI); /* Columns are spaced with 90px */
1210 UINT rspacing = MulDiv(4, This->dpi_y, USER_DEFAULT_SCREEN_DPI); /* Rows are spaced with 4 px. */
1212 /* Given the new width of the container, this function determines the
1213 * needed height of the container and places the controls according to
1214 * the new layout. Returns the new height.
1217 TRACE("%p\n", This);
1219 column_width = This->cctrl_width + cspacing;
1220 nr_of_cols = (container_width - This->cctrl_indent + cspacing) / column_width;
1222 /* We don't need to do anything unless the number of visible columns has changed. */
1223 if(nr_of_cols == This->cctrls_cols)
1225 RECT rc;
1226 GetWindowRect(This->cctrls_hwnd, &rc);
1227 return rc.bottom - rc.top;
1230 This->cctrls_cols = nr_of_cols;
1232 /* Get the size of the tallest control, and the total size of
1233 * all the controls to figure out the number of slots we need.
1235 max_control_height = 0;
1236 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1238 if(ctrl->cdcstate & CDCS_VISIBLE)
1240 UINT control_height = ctrl_get_height(ctrl);
1241 max_control_height = max(max_control_height, control_height);
1243 total_height += control_height + rspacing;
1247 if(!total_height)
1248 return 0;
1250 container_height = max(total_height / nr_of_cols, max_control_height + rspacing);
1251 TRACE("Guess: container_height: %d\n",container_height);
1253 /* Incrementally increase container_height until all the controls
1254 * fit.
1256 do {
1257 UINT columns_needed = 1;
1258 cur_row_pos = 0;
1260 fits_height = TRUE;
1261 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1263 if(ctrl->cdcstate & CDCS_VISIBLE)
1265 UINT control_height = ctrl_get_height(ctrl);
1267 if(cur_row_pos + control_height > container_height)
1269 if(++columns_needed > nr_of_cols)
1271 container_height += 1;
1272 fits_height = FALSE;
1273 break;
1275 cur_row_pos = 0;
1278 cur_row_pos += control_height + rspacing;
1281 } while(!fits_height);
1283 TRACE("Final container height: %d\n", container_height);
1285 /* Move the controls to their final destination
1287 cur_col_pos = 0, cur_row_pos = 0;
1288 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1290 if(ctrl->cdcstate & CDCS_VISIBLE)
1292 RECT rc;
1293 UINT control_height, control_indent;
1294 GetWindowRect(ctrl->wrapper_hwnd, &rc);
1295 control_height = rc.bottom - rc.top;
1297 if(cur_row_pos + control_height > container_height)
1299 cur_row_pos = 0;
1300 cur_col_pos += This->cctrl_width + cspacing;
1304 if(ctrl->type == IDLG_CCTRL_VISUALGROUP)
1305 control_indent = 0;
1306 else
1307 control_indent = This->cctrl_indent;
1309 SetWindowPos(ctrl->wrapper_hwnd, NULL, cur_col_pos + control_indent, cur_row_pos, 0, 0,
1310 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
1312 cur_row_pos += control_height + rspacing;
1316 /* Sanity check */
1317 if(cur_row_pos + This->cctrl_width > container_width)
1318 ERR("-- Failed to place controls properly.\n");
1320 return container_height;
1323 static void ctrl_set_font(customctrl *ctrl, HFONT font)
1325 customctrl *sub_ctrl;
1326 cctrl_item* item;
1328 SendMessageW(ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1330 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
1332 ctrl_set_font(sub_ctrl, font);
1335 if (ctrl->type == IDLG_CCTRL_RADIOBUTTONLIST)
1337 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
1339 SendMessageW(item->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1344 static void ctrl_container_reparent(FileDialogImpl *This, HWND parent)
1346 LONG wndstyle;
1348 if(parent)
1350 customctrl *ctrl;
1351 HFONT font;
1353 wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1354 wndstyle &= ~(WS_POPUP);
1355 wndstyle |= WS_CHILD;
1356 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1358 SetParent(This->cctrls_hwnd, parent);
1359 ShowWindow(This->cctrls_hwnd, TRUE);
1361 /* Set the fonts to match the dialog font. */
1362 font = (HFONT)SendMessageW(parent, WM_GETFONT, 0, 0);
1363 if(!font)
1364 ERR("Failed to get font handle from dialog.\n");
1366 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1368 if(font) ctrl_set_font(ctrl, font);
1369 customctrl_resize(This, ctrl);
1372 else
1374 ShowWindow(This->cctrls_hwnd, FALSE);
1376 wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1377 wndstyle &= ~(WS_CHILD);
1378 wndstyle |= WS_POPUP;
1379 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1381 SetParent(This->cctrls_hwnd, NULL);
1385 static LRESULT ctrl_container_on_create(HWND hwnd, CREATESTRUCTW *crs)
1387 FileDialogImpl *This = crs->lpCreateParams;
1388 TRACE("%p\n", This);
1390 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1391 return TRUE;
1394 static LRESULT ctrl_container_on_wm_destroy(FileDialogImpl *This)
1396 customctrl *cur1, *cur2;
1397 TRACE("%p\n", This);
1399 LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->cctrls, customctrl, entry)
1401 list_remove(&cur1->entry);
1402 ctrl_free(cur1);
1405 return TRUE;
1408 static LRESULT CALLBACK ctrl_container_wndproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1410 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1412 switch(umessage)
1414 case WM_NCCREATE: return ctrl_container_on_create(hwnd, (CREATESTRUCTW*)lparam);
1415 case WM_DESTROY: return ctrl_container_on_wm_destroy(This);
1416 default: return DefWindowProcW(hwnd, umessage, wparam, lparam);
1419 return FALSE;
1422 static void radiobuttonlist_set_selected_item(FileDialogImpl *This, customctrl *ctrl, cctrl_item *item)
1424 cctrl_item *cursor;
1426 LIST_FOR_EACH_ENTRY(cursor, &ctrl->sub_items, cctrl_item, entry)
1428 SendMessageW(cursor->hwnd, BM_SETCHECK, (cursor == item) ? BST_CHECKED : BST_UNCHECKED, 0);
1432 static LRESULT radiobuttonlist_on_bn_clicked(FileDialogImpl *This, HWND hwnd, HWND child)
1434 DWORD ctrl_id = (DWORD)GetWindowLongPtrW(hwnd, GWLP_ID);
1435 customctrl *ctrl;
1436 cctrl_item *item;
1437 BOOL found_item=FALSE;
1439 ctrl = get_cctrl_from_dlgid(This, ctrl_id);
1441 if (!ctrl)
1443 ERR("Can't find this control\n");
1444 return 0;
1447 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
1449 if (item->hwnd == child)
1451 found_item = TRUE;
1452 break;
1456 if (!found_item)
1458 ERR("Can't find control item\n");
1459 return 0;
1462 radiobuttonlist_set_selected_item(This, ctrl, item);
1464 cctrl_event_OnItemSelected(This, ctrl->id, item->id);
1466 return 0;
1469 static LRESULT radiobuttonlist_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1471 switch(HIWORD(wparam))
1473 case BN_CLICKED: return radiobuttonlist_on_bn_clicked(This, hwnd, (HWND)lparam);
1476 return FALSE;
1479 static LRESULT CALLBACK radiobuttonlist_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
1481 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1483 switch(message)
1485 case WM_COMMAND: return radiobuttonlist_on_wm_command(This, hwnd, wparam, lparam);
1488 return DefWindowProcW(hwnd, message, wparam, lparam);
1491 static HRESULT init_custom_controls(FileDialogImpl *This)
1493 WNDCLASSW wc;
1494 HDC hdc;
1495 static const WCHAR ctrl_container_classname[] =
1496 {'i','d','l','g','_','c','o','n','t','a','i','n','e','r','_','p','a','n','e',0};
1498 InitCommonControlsEx(NULL);
1500 if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
1502 wc.style = CS_HREDRAW | CS_VREDRAW;
1503 wc.lpfnWndProc = ctrl_container_wndproc;
1504 wc.cbClsExtra = 0;
1505 wc.cbWndExtra = 0;
1506 wc.hInstance = COMDLG32_hInstance;
1507 wc.hIcon = 0;
1508 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1509 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1510 wc.lpszMenuName = NULL;
1511 wc.lpszClassName = ctrl_container_classname;
1513 if(!RegisterClassW(&wc)) return E_FAIL;
1516 This->cctrls_hwnd = CreateWindowExW(0, ctrl_container_classname, NULL,
1517 WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1518 0, 0, 0, 0, NULL, 0,
1519 COMDLG32_hInstance, This);
1520 if(!This->cctrls_hwnd)
1521 return E_FAIL;
1523 hdc = GetDC(This->cctrls_hwnd);
1524 This->dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
1525 This->dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
1526 ReleaseDC(This->cctrls_hwnd, hdc);
1528 This->cctrl_width = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI); /* Controls have a fixed width */
1529 This->cctrl_indent = MulDiv(100, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1530 This->cctrl_def_height = MulDiv(23, This->dpi_y, USER_DEFAULT_SCREEN_DPI);
1531 This->cctrls_cols = 0;
1533 This->cctrl_next_dlgid = 0x2000;
1534 list_init(&This->cctrls);
1535 This->cctrl_active_vg = NULL;
1537 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, WS_TABSTOP);
1539 /* Register class for */
1540 if( !GetClassInfoW(COMDLG32_hInstance, floatnotifysinkW, &wc) ||
1541 wc.hInstance != COMDLG32_hInstance)
1543 wc.style = CS_HREDRAW | CS_VREDRAW;
1544 wc.lpfnWndProc = notifysink_proc;
1545 wc.cbClsExtra = 0;
1546 wc.cbWndExtra = 0;
1547 wc.hInstance = COMDLG32_hInstance;
1548 wc.hIcon = 0;
1549 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1550 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1551 wc.lpszMenuName = NULL;
1552 wc.lpszClassName = floatnotifysinkW;
1554 if (!RegisterClassW(&wc))
1555 ERR("Failed to register FloatNotifySink window class.\n");
1558 if( !GetClassInfoW(COMDLG32_hInstance, radiobuttonlistW, &wc) ||
1559 wc.hInstance != COMDLG32_hInstance)
1561 wc.style = CS_HREDRAW | CS_VREDRAW;
1562 wc.lpfnWndProc = radiobuttonlist_proc;
1563 wc.cbClsExtra = 0;
1564 wc.cbWndExtra = 0;
1565 wc.hInstance = COMDLG32_hInstance;
1566 wc.hIcon = 0;
1567 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1568 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1569 wc.lpszMenuName = NULL;
1570 wc.lpszClassName = radiobuttonlistW;
1572 if (!RegisterClassW(&wc))
1573 ERR("Failed to register RadioButtonList window class.\n");
1576 return S_OK;
1579 /**************************************************************************
1580 * Window related functions.
1582 static BOOL update_open_dropdown(FileDialogImpl *This)
1584 /* Show or hide the open dropdown button as appropriate */
1585 BOOL show=FALSE, showing;
1586 HWND open_hwnd, dropdown_hwnd;
1588 if (This->hmenu_opendropdown)
1590 INT num_visible_items=0;
1591 cctrl_item* item;
1593 LIST_FOR_EACH_ENTRY(item, &This->cctrl_opendropdown.sub_items, cctrl_item, entry)
1595 if (item->cdcstate & CDCS_VISIBLE)
1597 num_visible_items++;
1598 if (num_visible_items >= 2)
1600 show = TRUE;
1601 break;
1607 open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1608 dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
1610 showing = (GetWindowLongPtrW(dropdown_hwnd, GWL_STYLE) & WS_VISIBLE) != 0;
1612 if (showing != show)
1614 RECT open_rc, dropdown_rc;
1616 GetWindowRect(open_hwnd, &open_rc);
1617 GetWindowRect(dropdown_hwnd, &dropdown_rc);
1619 if (show)
1621 ShowWindow(dropdown_hwnd, SW_SHOW);
1623 SetWindowPos(open_hwnd, NULL, 0, 0,
1624 (open_rc.right - open_rc.left) - (dropdown_rc.right - dropdown_rc.left),
1625 open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1627 else
1629 ShowWindow(dropdown_hwnd, SW_HIDE);
1631 SetWindowPos(open_hwnd, NULL, 0, 0,
1632 (open_rc.right - open_rc.left) + (dropdown_rc.right - dropdown_rc.left),
1633 open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1637 return show;
1640 static void update_layout(FileDialogImpl *This)
1642 HDWP hdwp;
1643 HWND hwnd;
1644 RECT dialog_rc;
1645 RECT cancel_rc, dropdown_rc, open_rc;
1646 RECT filetype_rc, filename_rc, filenamelabel_rc;
1647 RECT toolbar_rc, ebrowser_rc, customctrls_rc;
1648 static const UINT vspacing = 4, hspacing = 4;
1649 static const UINT min_width = 320, min_height = 200;
1650 BOOL show_dropdown;
1652 if (!GetClientRect(This->dlg_hwnd, &dialog_rc))
1654 TRACE("Invalid dialog window, not updating layout\n");
1655 return;
1658 if(dialog_rc.right < min_width || dialog_rc.bottom < min_height)
1660 TRACE("Dialog size (%d, %d) too small, not updating layout\n", dialog_rc.right, dialog_rc.bottom);
1661 return;
1664 /****
1665 * Calculate the size of the dialog and all the parts.
1668 /* Cancel button */
1669 hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL);
1670 if(hwnd)
1672 int cancel_width, cancel_height;
1673 GetWindowRect(hwnd, &cancel_rc);
1674 cancel_width = cancel_rc.right - cancel_rc.left;
1675 cancel_height = cancel_rc.bottom - cancel_rc.top;
1677 cancel_rc.left = dialog_rc.right - cancel_width - hspacing;
1678 cancel_rc.top = dialog_rc.bottom - cancel_height - vspacing;
1679 cancel_rc.right = cancel_rc.left + cancel_width;
1680 cancel_rc.bottom = cancel_rc.top + cancel_height;
1683 /* Open/Save dropdown */
1684 show_dropdown = update_open_dropdown(This);
1686 if(show_dropdown)
1688 int dropdown_width, dropdown_height;
1689 hwnd = GetDlgItem(This->dlg_hwnd, psh1);
1691 GetWindowRect(hwnd, &dropdown_rc);
1692 dropdown_width = dropdown_rc.right - dropdown_rc.left;
1693 dropdown_height = dropdown_rc.bottom - dropdown_rc.top;
1695 dropdown_rc.left = cancel_rc.left - dropdown_width - hspacing;
1696 dropdown_rc.top = cancel_rc.top;
1697 dropdown_rc.right = dropdown_rc.left + dropdown_width;
1698 dropdown_rc.bottom = dropdown_rc.top + dropdown_height;
1700 else
1702 dropdown_rc.left = dropdown_rc.right = cancel_rc.left - hspacing;
1703 dropdown_rc.top = cancel_rc.top;
1704 dropdown_rc.bottom = cancel_rc.bottom;
1707 /* Open/Save button */
1708 hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1709 if(hwnd)
1711 int open_width, open_height;
1712 GetWindowRect(hwnd, &open_rc);
1713 open_width = open_rc.right - open_rc.left;
1714 open_height = open_rc.bottom - open_rc.top;
1716 open_rc.left = dropdown_rc.left - open_width;
1717 open_rc.top = dropdown_rc.top;
1718 open_rc.right = open_rc.left + open_width;
1719 open_rc.bottom = open_rc.top + open_height;
1722 /* The filetype combobox. */
1723 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1724 if(hwnd)
1726 int filetype_width, filetype_height;
1727 GetWindowRect(hwnd, &filetype_rc);
1729 filetype_width = filetype_rc.right - filetype_rc.left;
1730 filetype_height = filetype_rc.bottom - filetype_rc.top;
1732 filetype_rc.right = cancel_rc.right;
1734 filetype_rc.left = filetype_rc.right - filetype_width;
1735 filetype_rc.top = cancel_rc.top - filetype_height - vspacing;
1736 filetype_rc.bottom = filetype_rc.top + filetype_height;
1738 if(!This->filterspec_count)
1739 filetype_rc.left = filetype_rc.right;
1742 /* Filename label. */
1743 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC);
1744 if(hwnd)
1746 int filetypelabel_width, filetypelabel_height;
1747 GetWindowRect(hwnd, &filenamelabel_rc);
1749 filetypelabel_width = filenamelabel_rc.right - filenamelabel_rc.left;
1750 filetypelabel_height = filenamelabel_rc.bottom - filenamelabel_rc.top;
1752 filenamelabel_rc.left = 160; /* FIXME */
1753 filenamelabel_rc.top = filetype_rc.top;
1754 filenamelabel_rc.right = filenamelabel_rc.left + filetypelabel_width;
1755 filenamelabel_rc.bottom = filenamelabel_rc.top + filetypelabel_height;
1758 /* Filename edit box. */
1759 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
1760 if(hwnd)
1762 int filename_width, filename_height;
1763 GetWindowRect(hwnd, &filename_rc);
1765 filename_width = filetype_rc.left - filenamelabel_rc.right - hspacing*2;
1766 filename_height = filename_rc.bottom - filename_rc.top;
1768 filename_rc.left = filenamelabel_rc.right + hspacing;
1769 filename_rc.top = filetype_rc.top;
1770 filename_rc.right = filename_rc.left + filename_width;
1771 filename_rc.bottom = filename_rc.top + filename_height;
1774 hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR);
1775 if(hwnd)
1777 GetWindowRect(hwnd, &toolbar_rc);
1778 MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2);
1781 /* The custom controls */
1782 customctrls_rc.left = dialog_rc.left + hspacing;
1783 customctrls_rc.right = dialog_rc.right - hspacing;
1784 customctrls_rc.bottom = filename_rc.top - vspacing;
1785 customctrls_rc.top = customctrls_rc.bottom -
1786 ctrl_container_resize(This, customctrls_rc.right - customctrls_rc.left);
1788 /* The ExplorerBrowser control. */
1789 ebrowser_rc.left = dialog_rc.left + hspacing;
1790 ebrowser_rc.top = toolbar_rc.bottom + vspacing;
1791 ebrowser_rc.right = dialog_rc.right - hspacing;
1792 ebrowser_rc.bottom = customctrls_rc.top - vspacing;
1794 /****
1795 * Move everything to the right place.
1798 /* FIXME: The Save Dialog uses a slightly different layout. */
1799 hdwp = BeginDeferWindowPos(7);
1801 if(hdwp && This->peb)
1802 IExplorerBrowser_SetRect(This->peb, &hdwp, ebrowser_rc);
1804 if(hdwp && This->cctrls_hwnd)
1805 DeferWindowPos(hdwp, This->cctrls_hwnd, NULL,
1806 customctrls_rc.left, customctrls_rc.top,
1807 customctrls_rc.right - customctrls_rc.left, customctrls_rc.bottom - customctrls_rc.top,
1808 SWP_NOZORDER | SWP_NOACTIVATE);
1810 /* The default controls */
1811 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE)) )
1812 DeferWindowPos(hdwp, hwnd, NULL, filetype_rc.left, filetype_rc.top, 0, 0,
1813 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1815 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
1816 DeferWindowPos(hdwp, hwnd, NULL, filename_rc.left, filename_rc.top,
1817 filename_rc.right - filename_rc.left, filename_rc.bottom - filename_rc.top,
1818 SWP_NOZORDER | SWP_NOACTIVATE);
1820 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)) )
1821 DeferWindowPos(hdwp, hwnd, NULL, filenamelabel_rc.left, filenamelabel_rc.top, 0, 0,
1822 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1824 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDOK)) )
1825 DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
1826 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1828 if(hdwp && This->hmenu_opendropdown && (hwnd = GetDlgItem(This->dlg_hwnd, psh1)))
1829 DeferWindowPos(hdwp, hwnd, NULL, dropdown_rc.left, dropdown_rc.top, 0, 0,
1830 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1832 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
1833 DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
1834 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1836 if(hdwp)
1837 EndDeferWindowPos(hdwp);
1838 else
1839 ERR("Failed to position dialog controls.\n");
1841 return;
1844 static HRESULT init_explorerbrowser(FileDialogImpl *This)
1846 IShellItem *psi_folder;
1847 IObjectWithSite *client;
1848 FOLDERSETTINGS fos;
1849 RECT rc = {0};
1850 HRESULT hr;
1852 /* Create ExplorerBrowser instance */
1853 OleInitialize(NULL);
1855 hr = CoCreateInstance(&CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER,
1856 &IID_IExplorerBrowser, (void**)&This->peb);
1857 if(FAILED(hr))
1859 ERR("Failed to instantiate ExplorerBrowser control.\n");
1860 return hr;
1863 IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES | EBO_NOBORDER);
1865 hr = IExplorerBrowser_Initialize(This->peb, This->dlg_hwnd, &rc, NULL);
1866 if(FAILED(hr))
1868 ERR("Failed to initialize the ExplorerBrowser control.\n");
1869 IExplorerBrowser_Release(This->peb);
1870 This->peb = NULL;
1871 return hr;
1873 hr = IExplorerBrowser_Advise(This->peb, &This->IExplorerBrowserEvents_iface, &This->ebevents_cookie);
1874 if(FAILED(hr))
1875 ERR("Advise (ExplorerBrowser) failed.\n");
1877 /* Get previous options? */
1878 fos.ViewMode = fos.fFlags = 0;
1879 if(!(This->options & FOS_ALLOWMULTISELECT))
1880 fos.fFlags |= FWF_SINGLESEL;
1882 IExplorerBrowser_SetFolderSettings(This->peb, &fos);
1884 hr = IExplorerBrowser_QueryInterface(This->peb, &IID_IObjectWithSite, (void**)&client);
1885 if (hr == S_OK)
1887 hr = IObjectWithSite_SetSite(client, (IUnknown*)&This->IFileDialog2_iface);
1888 IObjectWithSite_Release(client);
1889 if(FAILED(hr))
1890 ERR("SetSite failed, 0x%08x\n", hr);
1893 /* Browse somewhere */
1894 psi_folder = This->psi_setfolder ? This->psi_setfolder : This->psi_defaultfolder;
1895 IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psi_folder, SBSP_DEFBROWSER);
1897 return S_OK;
1900 static void init_toolbar(FileDialogImpl *This, HWND hwnd)
1902 HWND htoolbar;
1903 TBADDBITMAP tbab;
1904 TBBUTTON button[2];
1906 htoolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE,
1907 0, 0, 0, 0,
1908 hwnd, (HMENU)IDC_NAV_TOOLBAR, NULL, NULL);
1910 tbab.hInst = HINST_COMMCTRL;
1911 tbab.nID = IDB_HIST_LARGE_COLOR;
1912 SendMessageW(htoolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
1914 button[0].iBitmap = HIST_BACK;
1915 button[0].idCommand = IDC_NAVBACK;
1916 button[0].fsState = TBSTATE_ENABLED;
1917 button[0].fsStyle = BTNS_BUTTON;
1918 button[0].dwData = 0;
1919 button[0].iString = 0;
1921 button[1].iBitmap = HIST_FORWARD;
1922 button[1].idCommand = IDC_NAVFORWARD;
1923 button[1].fsState = TBSTATE_ENABLED;
1924 button[1].fsStyle = BTNS_BUTTON;
1925 button[1].dwData = 0;
1926 button[1].iString = 0;
1928 SendMessageW(htoolbar, TB_ADDBUTTONSW, 2, (LPARAM)button);
1929 SendMessageW(htoolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(24,24));
1930 SendMessageW(htoolbar, TB_AUTOSIZE, 0, 0);
1933 static void update_control_text(FileDialogImpl *This)
1935 HWND hitem;
1936 LPCWSTR custom_okbutton;
1937 cctrl_item* item;
1938 UINT min_width = MulDiv(50, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1939 UINT max_width = MulDiv(250, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1941 if(This->custom_title)
1942 SetWindowTextW(This->dlg_hwnd, This->custom_title);
1944 if(This->hmenu_opendropdown && (item = get_first_item(&This->cctrl_opendropdown)))
1945 custom_okbutton = item->label;
1946 else
1947 custom_okbutton = This->custom_okbutton;
1949 if(custom_okbutton &&
1950 (hitem = GetDlgItem(This->dlg_hwnd, IDOK)))
1952 SetWindowTextW(hitem, custom_okbutton);
1953 ctrl_resize(hitem, min_width, max_width, FALSE);
1956 if(This->custom_cancelbutton &&
1957 (hitem = GetDlgItem(This->dlg_hwnd, IDCANCEL)))
1959 SetWindowTextW(hitem, This->custom_cancelbutton);
1960 ctrl_resize(hitem, min_width, max_width, FALSE);
1963 if(This->custom_filenamelabel &&
1964 (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)))
1966 SetWindowTextW(hitem, This->custom_filenamelabel);
1967 ctrl_resize(hitem, min_width, max_width, FALSE);
1971 static LRESULT CALLBACK dropdown_subclass_proc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1973 static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
1974 static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
1976 if (umessage == WM_LBUTTONDOWN)
1978 FileDialogImpl *This = GetPropW(hwnd, prop_this);
1980 SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
1981 show_opendropdown(This);
1982 SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
1984 return 0;
1987 return CallWindowProcW((WNDPROC)GetPropW(hwnd, prop_oldwndproc), hwnd, umessage, wparam, lparam);
1990 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
1992 FileDialogImpl *This = (FileDialogImpl*)lParam;
1993 HWND hitem;
1995 TRACE("(%p, %p)\n", This, hwnd);
1997 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1998 This->dlg_hwnd = hwnd;
2000 hitem = GetDlgItem(This->dlg_hwnd, pshHelp);
2001 if(hitem) ShowWindow(hitem, SW_HIDE);
2003 hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPESTATIC);
2004 if(hitem) ShowWindow(hitem, SW_HIDE);
2006 /* Fill filetypes combobox, or hide it. */
2007 hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
2008 if(This->filterspec_count)
2010 HDC hdc;
2011 HFONT font;
2012 SIZE size;
2013 UINT i, maxwidth = 0;
2015 hdc = GetDC(hitem);
2016 font = (HFONT)SendMessageW(hitem, WM_GETFONT, 0, 0);
2017 SelectObject(hdc, font);
2019 for(i = 0; i < This->filterspec_count; i++)
2021 SendMessageW(hitem, CB_ADDSTRING, 0, (LPARAM)This->filterspecs[i].pszName);
2023 if(GetTextExtentPoint32W(hdc, This->filterspecs[i].pszName, lstrlenW(This->filterspecs[i].pszName), &size))
2024 maxwidth = max(maxwidth, size.cx);
2026 ReleaseDC(hitem, hdc);
2028 if(maxwidth > 0)
2030 maxwidth += GetSystemMetrics(SM_CXVSCROLL) + 4;
2031 SendMessageW(hitem, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0);
2033 else
2034 ERR("Failed to calculate width of filetype dropdown\n");
2036 SendMessageW(hitem, CB_SETCURSEL, This->filetypeindex, 0);
2038 else
2039 ShowWindow(hitem, SW_HIDE);
2041 if(This->set_filename &&
2042 (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
2043 SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
2045 if(This->hmenu_opendropdown)
2047 HWND dropdown_hwnd;
2048 LOGFONTW lfw, lfw_marlett;
2049 HFONT dialog_font;
2050 static const WCHAR marlett[] = {'M','a','r','l','e','t','t',0};
2051 static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
2052 static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
2054 dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
2056 /* Change dropdown button font to Marlett */
2057 dialog_font = (HFONT)SendMessageW(dropdown_hwnd, WM_GETFONT, 0, 0);
2059 GetObjectW(dialog_font, sizeof(lfw), &lfw);
2061 memset(&lfw_marlett, 0, sizeof(lfw_marlett));
2062 lstrcpyW(lfw_marlett.lfFaceName, marlett);
2063 lfw_marlett.lfHeight = lfw.lfHeight;
2064 lfw_marlett.lfCharSet = SYMBOL_CHARSET;
2066 This->hfont_opendropdown = CreateFontIndirectW(&lfw_marlett);
2068 SendMessageW(dropdown_hwnd, WM_SETFONT, (LPARAM)This->hfont_opendropdown, 0);
2070 /* Subclass button so we can handle LBUTTONDOWN */
2071 SetPropW(dropdown_hwnd, prop_this, This);
2072 SetPropW(dropdown_hwnd, prop_oldwndproc,
2073 (HANDLE)SetWindowLongPtrW(dropdown_hwnd, GWLP_WNDPROC, (LONG_PTR)dropdown_subclass_proc));
2076 ctrl_container_reparent(This, This->dlg_hwnd);
2077 init_explorerbrowser(This);
2078 init_toolbar(This, hwnd);
2079 update_control_text(This);
2080 update_layout(This);
2082 if(This->filterspec_count)
2083 events_OnTypeChange(This);
2085 if ((hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)))
2086 SetFocus(hitem);
2088 return FALSE;
2091 static LRESULT on_wm_size(FileDialogImpl *This)
2093 update_layout(This);
2094 return FALSE;
2097 static LRESULT on_wm_getminmaxinfo(FileDialogImpl *This, LPARAM lparam)
2099 MINMAXINFO *mmi = (MINMAXINFO*)lparam;
2100 TRACE("%p (%p)\n", This, mmi);
2102 /* FIXME */
2103 mmi->ptMinTrackSize.x = 640;
2104 mmi->ptMinTrackSize.y = 480;
2106 return FALSE;
2109 static LRESULT on_wm_destroy(FileDialogImpl *This)
2111 TRACE("%p\n", This);
2113 if(This->peb)
2115 IExplorerBrowser_Destroy(This->peb);
2116 IExplorerBrowser_Release(This->peb);
2117 This->peb = NULL;
2120 ctrl_container_reparent(This, NULL);
2121 This->dlg_hwnd = NULL;
2123 DeleteObject(This->hfont_opendropdown);
2124 This->hfont_opendropdown = NULL;
2126 return TRUE;
2129 static LRESULT on_idok(FileDialogImpl *This)
2131 TRACE("%p\n", This);
2133 if(SUCCEEDED(on_default_action(This)))
2134 EndDialog(This->dlg_hwnd, S_OK);
2136 return FALSE;
2139 static LRESULT on_idcancel(FileDialogImpl *This)
2141 TRACE("%p\n", This);
2143 EndDialog(This->dlg_hwnd, HRESULT_FROM_WIN32(ERROR_CANCELLED));
2145 return FALSE;
2148 static LRESULT on_command_opendropdown(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2150 if(HIWORD(wparam) == BN_CLICKED)
2152 HWND hwnd = (HWND)lparam;
2153 SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
2154 show_opendropdown(This);
2155 SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
2158 return FALSE;
2161 static LRESULT on_browse_back(FileDialogImpl *This)
2163 TRACE("%p\n", This);
2164 IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEBACK);
2165 return FALSE;
2168 static LRESULT on_browse_forward(FileDialogImpl *This)
2170 TRACE("%p\n", This);
2171 IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEFORWARD);
2172 return FALSE;
2175 static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2177 if(HIWORD(wparam) == CBN_SELCHANGE)
2179 IShellView *psv;
2180 HRESULT hr;
2181 LPWSTR filename;
2182 UINT prev_index = This->filetypeindex;
2184 This->filetypeindex = SendMessageW((HWND)lparam, CB_GETCURSEL, 0, 0);
2185 TRACE("File type selection changed to %d.\n", This->filetypeindex);
2187 if(prev_index == This->filetypeindex)
2188 return FALSE;
2190 hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&psv);
2191 if(SUCCEEDED(hr))
2193 IShellView_Refresh(psv);
2194 IShellView_Release(psv);
2197 if(This->dlg_type == ITEMDLG_TYPE_SAVE && get_file_name(This, &filename))
2199 WCHAR buf[MAX_PATH], extbuf[MAX_PATH], *ext;
2201 ext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
2202 if(ext)
2204 lstrcpyW(buf, filename);
2206 if(PathMatchSpecW(buf, This->filterspecs[prev_index].pszSpec))
2207 PathRemoveExtensionW(buf);
2209 lstrcatW(buf, ext);
2210 set_file_name(This, buf);
2212 CoTaskMemFree(filename);
2215 /* The documentation claims that OnTypeChange is called only
2216 * when the dialog is opened, but this is obviously not the
2217 * case. */
2218 events_OnTypeChange(This);
2221 return FALSE;
2224 static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2226 switch(LOWORD(wparam))
2228 case IDOK: return on_idok(This);
2229 case IDCANCEL: return on_idcancel(This);
2230 case psh1: return on_command_opendropdown(This, wparam, lparam);
2231 case IDC_NAVBACK: return on_browse_back(This);
2232 case IDC_NAVFORWARD: return on_browse_forward(This);
2233 case IDC_FILETYPE: return on_command_filetype(This, wparam, lparam);
2234 default: TRACE("Unknown command.\n");
2236 return FALSE;
2239 static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
2241 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
2243 switch(umessage)
2245 case WM_INITDIALOG: return on_wm_initdialog(hwnd, lparam);
2246 case WM_COMMAND: return on_wm_command(This, wparam, lparam);
2247 case WM_SIZE: return on_wm_size(This);
2248 case WM_GETMINMAXINFO: return on_wm_getminmaxinfo(This, lparam);
2249 case WM_DESTROY: return on_wm_destroy(This);
2252 return FALSE;
2255 static HRESULT create_dialog(FileDialogImpl *This, HWND parent)
2257 INT_PTR res;
2259 SetLastError(0);
2260 res = DialogBoxParamW(COMDLG32_hInstance,
2261 MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
2262 parent, itemdlg_dlgproc, (LPARAM)This);
2263 This->dlg_hwnd = NULL;
2264 if(res == -1)
2266 ERR("Failed to show dialog (LastError: %d)\n", GetLastError());
2267 return E_FAIL;
2270 TRACE("Returning 0x%08x\n", (HRESULT)res);
2271 return (HRESULT)res;
2274 /**************************************************************************
2275 * IFileDialog implementation
2277 static inline FileDialogImpl *impl_from_IFileDialog2(IFileDialog2 *iface)
2279 return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialog2_iface);
2282 static HRESULT WINAPI IFileDialog2_fnQueryInterface(IFileDialog2 *iface,
2283 REFIID riid,
2284 void **ppvObject)
2286 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2287 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
2289 *ppvObject = NULL;
2290 if(IsEqualGUID(riid, &IID_IUnknown) ||
2291 IsEqualGUID(riid, &IID_IFileDialog) ||
2292 IsEqualGUID(riid, &IID_IFileDialog2))
2294 *ppvObject = iface;
2296 else if(IsEqualGUID(riid, &IID_IFileOpenDialog) && This->dlg_type == ITEMDLG_TYPE_OPEN)
2298 *ppvObject = &This->u.IFileOpenDialog_iface;
2300 else if(IsEqualGUID(riid, &IID_IFileSaveDialog) && This->dlg_type == ITEMDLG_TYPE_SAVE)
2302 *ppvObject = &This->u.IFileSaveDialog_iface;
2304 else if(IsEqualGUID(riid, &IID_IExplorerBrowserEvents))
2306 *ppvObject = &This->IExplorerBrowserEvents_iface;
2308 else if(IsEqualGUID(riid, &IID_IServiceProvider))
2310 *ppvObject = &This->IServiceProvider_iface;
2312 else if(IsEqualGUID(&IID_ICommDlgBrowser3, riid) ||
2313 IsEqualGUID(&IID_ICommDlgBrowser2, riid) ||
2314 IsEqualGUID(&IID_ICommDlgBrowser, riid))
2316 *ppvObject = &This->ICommDlgBrowser3_iface;
2318 else if(IsEqualGUID(&IID_IOleWindow, riid))
2320 *ppvObject = &This->IOleWindow_iface;
2322 else if(IsEqualGUID(riid, &IID_IFileDialogCustomize) ||
2323 IsEqualGUID(riid, &IID_IFileDialogCustomizeAlt))
2325 *ppvObject = &This->IFileDialogCustomize_iface;
2327 else
2328 FIXME("Unknown interface requested: %s.\n", debugstr_guid(riid));
2330 if(*ppvObject)
2332 IUnknown_AddRef((IUnknown*)*ppvObject);
2333 return S_OK;
2336 return E_NOINTERFACE;
2339 static ULONG WINAPI IFileDialog2_fnAddRef(IFileDialog2 *iface)
2341 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2342 LONG ref = InterlockedIncrement(&This->ref);
2343 TRACE("%p - ref %d\n", This, ref);
2345 return ref;
2348 static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface)
2350 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2351 LONG ref = InterlockedDecrement(&This->ref);
2352 TRACE("%p - ref %d\n", This, ref);
2354 if(!ref)
2356 UINT i;
2357 for(i = 0; i < This->filterspec_count; i++)
2359 LocalFree((void*)This->filterspecs[i].pszName);
2360 LocalFree((void*)This->filterspecs[i].pszSpec);
2362 HeapFree(GetProcessHeap(), 0, This->filterspecs);
2364 DestroyWindow(This->cctrls_hwnd);
2366 if(This->psi_defaultfolder) IShellItem_Release(This->psi_defaultfolder);
2367 if(This->psi_setfolder) IShellItem_Release(This->psi_setfolder);
2368 if(This->psi_folder) IShellItem_Release(This->psi_folder);
2369 if(This->psia_selection) IShellItemArray_Release(This->psia_selection);
2370 if(This->psia_results) IShellItemArray_Release(This->psia_results);
2372 LocalFree(This->set_filename);
2373 LocalFree(This->default_ext);
2374 LocalFree(This->custom_title);
2375 LocalFree(This->custom_okbutton);
2376 LocalFree(This->custom_cancelbutton);
2377 LocalFree(This->custom_filenamelabel);
2379 DestroyMenu(This->hmenu_opendropdown);
2380 DeleteObject(This->hfont_opendropdown);
2382 HeapFree(GetProcessHeap(), 0, This);
2385 return ref;
2388 static HRESULT WINAPI IFileDialog2_fnShow(IFileDialog2 *iface, HWND hwndOwner)
2390 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2391 TRACE("%p (%p)\n", iface, hwndOwner);
2393 This->opendropdown_has_selection = FALSE;
2395 return create_dialog(This, hwndOwner);
2398 static HRESULT WINAPI IFileDialog2_fnSetFileTypes(IFileDialog2 *iface, UINT cFileTypes,
2399 const COMDLG_FILTERSPEC *rgFilterSpec)
2401 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2402 UINT i;
2403 TRACE("%p (%d, %p)\n", This, cFileTypes, rgFilterSpec);
2405 if(This->filterspecs)
2406 return E_UNEXPECTED;
2408 if(!rgFilterSpec)
2409 return E_INVALIDARG;
2411 if(!cFileTypes)
2412 return S_OK;
2414 This->filterspecs = HeapAlloc(GetProcessHeap(), 0, sizeof(COMDLG_FILTERSPEC)*cFileTypes);
2415 for(i = 0; i < cFileTypes; i++)
2417 This->filterspecs[i].pszName = StrDupW(rgFilterSpec[i].pszName);
2418 This->filterspecs[i].pszSpec = StrDupW(rgFilterSpec[i].pszSpec);
2420 This->filterspec_count = cFileTypes;
2422 return S_OK;
2425 static HRESULT WINAPI IFileDialog2_fnSetFileTypeIndex(IFileDialog2 *iface, UINT iFileType)
2427 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2428 TRACE("%p (%d)\n", This, iFileType);
2430 if(!This->filterspecs)
2431 return E_FAIL;
2433 iFileType = max(iFileType, 1);
2434 iFileType = min(iFileType, This->filterspec_count);
2435 This->filetypeindex = iFileType-1;
2437 return S_OK;
2440 static HRESULT WINAPI IFileDialog2_fnGetFileTypeIndex(IFileDialog2 *iface, UINT *piFileType)
2442 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2443 TRACE("%p (%p)\n", This, piFileType);
2445 if(!piFileType)
2446 return E_INVALIDARG;
2448 if(This->filterspec_count == 0)
2449 *piFileType = 0;
2450 else
2451 *piFileType = This->filetypeindex + 1;
2453 return S_OK;
2456 static HRESULT WINAPI IFileDialog2_fnAdvise(IFileDialog2 *iface, IFileDialogEvents *pfde, DWORD *pdwCookie)
2458 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2459 events_client *client;
2460 TRACE("%p (%p, %p)\n", This, pfde, pdwCookie);
2462 if(!pfde || !pdwCookie)
2463 return E_INVALIDARG;
2465 client = HeapAlloc(GetProcessHeap(), 0, sizeof(events_client));
2466 client->pfde = pfde;
2467 client->cookie = ++This->events_next_cookie;
2469 IFileDialogEvents_AddRef(pfde);
2470 *pdwCookie = client->cookie;
2472 list_add_tail(&This->events_clients, &client->entry);
2474 return S_OK;
2477 static HRESULT WINAPI IFileDialog2_fnUnadvise(IFileDialog2 *iface, DWORD dwCookie)
2479 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2480 events_client *client, *found = NULL;
2481 TRACE("%p (%d)\n", This, dwCookie);
2483 LIST_FOR_EACH_ENTRY(client, &This->events_clients, events_client, entry)
2485 if(client->cookie == dwCookie)
2487 found = client;
2488 break;
2492 if(found)
2494 list_remove(&found->entry);
2495 IFileDialogEvents_Release(found->pfde);
2496 HeapFree(GetProcessHeap(), 0, found);
2497 return S_OK;
2500 return E_INVALIDARG;
2503 static HRESULT WINAPI IFileDialog2_fnSetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS fos)
2505 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2506 TRACE("%p (0x%x)\n", This, fos);
2508 if( !(This->options & FOS_PICKFOLDERS) && (fos & FOS_PICKFOLDERS) )
2510 WCHAR buf[30];
2511 LoadStringW(COMDLG32_hInstance, IDS_SELECT_FOLDER, buf, sizeof(buf)/sizeof(WCHAR));
2512 IFileDialog2_SetTitle(iface, buf);
2515 This->options = fos;
2517 return S_OK;
2520 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
2522 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2523 TRACE("%p (%p)\n", This, pfos);
2525 if(!pfos)
2526 return E_INVALIDARG;
2528 *pfos = This->options;
2530 return S_OK;
2533 static HRESULT WINAPI IFileDialog2_fnSetDefaultFolder(IFileDialog2 *iface, IShellItem *psi)
2535 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2536 TRACE("%p (%p)\n", This, psi);
2537 if(This->psi_defaultfolder)
2538 IShellItem_Release(This->psi_defaultfolder);
2540 This->psi_defaultfolder = psi;
2542 if(This->psi_defaultfolder)
2543 IShellItem_AddRef(This->psi_defaultfolder);
2545 return S_OK;
2548 static HRESULT WINAPI IFileDialog2_fnSetFolder(IFileDialog2 *iface, IShellItem *psi)
2550 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2551 TRACE("%p (%p)\n", This, psi);
2552 if(This->psi_setfolder)
2553 IShellItem_Release(This->psi_setfolder);
2555 This->psi_setfolder = psi;
2557 if(This->psi_setfolder)
2558 IShellItem_AddRef(This->psi_setfolder);
2560 return S_OK;
2563 static HRESULT WINAPI IFileDialog2_fnGetFolder(IFileDialog2 *iface, IShellItem **ppsi)
2565 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2566 TRACE("%p (%p)\n", This, ppsi);
2567 if(!ppsi)
2568 return E_INVALIDARG;
2570 /* FIXME:
2571 If the dialog is shown, return the current(ly selected) folder. */
2573 *ppsi = NULL;
2574 if(This->psi_folder)
2575 *ppsi = This->psi_folder;
2576 else if(This->psi_setfolder)
2577 *ppsi = This->psi_setfolder;
2578 else if(This->psi_defaultfolder)
2579 *ppsi = This->psi_defaultfolder;
2581 if(*ppsi)
2583 IShellItem_AddRef(*ppsi);
2584 return S_OK;
2587 return E_FAIL;
2590 static HRESULT WINAPI IFileDialog2_fnGetCurrentSelection(IFileDialog2 *iface, IShellItem **ppsi)
2592 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2593 HRESULT hr;
2594 TRACE("%p (%p)\n", This, ppsi);
2596 if(!ppsi)
2597 return E_INVALIDARG;
2599 if(This->psia_selection)
2601 /* FIXME: Check filename edit box */
2602 hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
2603 return hr;
2606 return E_FAIL;
2609 static HRESULT WINAPI IFileDialog2_fnSetFileName(IFileDialog2 *iface, LPCWSTR pszName)
2611 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2612 TRACE("%p (%s)\n", iface, debugstr_w(pszName));
2614 set_file_name(This, pszName);
2616 return S_OK;
2619 static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *pszName)
2621 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2622 TRACE("%p (%p)\n", iface, pszName);
2624 if(!pszName)
2625 return E_INVALIDARG;
2627 *pszName = NULL;
2628 get_file_name(This, pszName);
2629 return *pszName ? S_OK : E_FAIL;
2632 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
2634 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2635 TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
2637 LocalFree(This->custom_title);
2638 This->custom_title = StrDupW(pszTitle);
2639 update_control_text(This);
2641 return S_OK;
2644 static HRESULT WINAPI IFileDialog2_fnSetOkButtonLabel(IFileDialog2 *iface, LPCWSTR pszText)
2646 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2647 TRACE("%p (%s)\n", This, debugstr_w(pszText));
2649 LocalFree(This->custom_okbutton);
2650 This->custom_okbutton = StrDupW(pszText);
2651 update_control_text(This);
2652 update_layout(This);
2654 return S_OK;
2657 static HRESULT WINAPI IFileDialog2_fnSetFileNameLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2659 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2660 TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2662 LocalFree(This->custom_filenamelabel);
2663 This->custom_filenamelabel = StrDupW(pszLabel);
2664 update_control_text(This);
2665 update_layout(This);
2667 return S_OK;
2670 static HRESULT WINAPI IFileDialog2_fnGetResult(IFileDialog2 *iface, IShellItem **ppsi)
2672 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2673 HRESULT hr;
2674 TRACE("%p (%p)\n", This, ppsi);
2676 if(!ppsi)
2677 return E_INVALIDARG;
2679 if(This->psia_results)
2681 UINT item_count;
2682 hr = IShellItemArray_GetCount(This->psia_results, &item_count);
2683 if(SUCCEEDED(hr))
2685 if(item_count != 1)
2686 return E_FAIL;
2688 /* Adds a reference. */
2689 hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
2692 return hr;
2695 return E_UNEXPECTED;
2698 static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *psi, FDAP fdap)
2700 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2701 FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
2702 return S_OK;
2705 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
2707 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2708 TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
2710 LocalFree(This->default_ext);
2711 This->default_ext = StrDupW(pszDefaultExtension);
2713 return S_OK;
2716 static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
2718 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2719 TRACE("%p (0x%08x)\n", This, hr);
2721 if(This->dlg_hwnd)
2722 EndDialog(This->dlg_hwnd, hr);
2724 return S_OK;
2727 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
2729 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2730 TRACE("%p (%s)\n", This, debugstr_guid(guid));
2731 This->client_guid = *guid;
2732 return S_OK;
2735 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
2737 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2738 FIXME("stub - %p\n", This);
2739 return E_NOTIMPL;
2742 static HRESULT WINAPI IFileDialog2_fnSetFilter(IFileDialog2 *iface, IShellItemFilter *pFilter)
2744 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2745 FIXME("stub - %p (%p)\n", This, pFilter);
2746 return E_NOTIMPL;
2749 static HRESULT WINAPI IFileDialog2_fnSetCancelButtonLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2751 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2752 TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2754 LocalFree(This->custom_cancelbutton);
2755 This->custom_cancelbutton = StrDupW(pszLabel);
2756 update_control_text(This);
2757 update_layout(This);
2759 return S_OK;
2762 static HRESULT WINAPI IFileDialog2_fnSetNavigationRoot(IFileDialog2 *iface, IShellItem *psi)
2764 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2765 FIXME("stub - %p (%p)\n", This, psi);
2766 return E_NOTIMPL;
2769 static const IFileDialog2Vtbl vt_IFileDialog2 = {
2770 IFileDialog2_fnQueryInterface,
2771 IFileDialog2_fnAddRef,
2772 IFileDialog2_fnRelease,
2773 IFileDialog2_fnShow,
2774 IFileDialog2_fnSetFileTypes,
2775 IFileDialog2_fnSetFileTypeIndex,
2776 IFileDialog2_fnGetFileTypeIndex,
2777 IFileDialog2_fnAdvise,
2778 IFileDialog2_fnUnadvise,
2779 IFileDialog2_fnSetOptions,
2780 IFileDialog2_fnGetOptions,
2781 IFileDialog2_fnSetDefaultFolder,
2782 IFileDialog2_fnSetFolder,
2783 IFileDialog2_fnGetFolder,
2784 IFileDialog2_fnGetCurrentSelection,
2785 IFileDialog2_fnSetFileName,
2786 IFileDialog2_fnGetFileName,
2787 IFileDialog2_fnSetTitle,
2788 IFileDialog2_fnSetOkButtonLabel,
2789 IFileDialog2_fnSetFileNameLabel,
2790 IFileDialog2_fnGetResult,
2791 IFileDialog2_fnAddPlace,
2792 IFileDialog2_fnSetDefaultExtension,
2793 IFileDialog2_fnClose,
2794 IFileDialog2_fnSetClientGuid,
2795 IFileDialog2_fnClearClientData,
2796 IFileDialog2_fnSetFilter,
2797 IFileDialog2_fnSetCancelButtonLabel,
2798 IFileDialog2_fnSetNavigationRoot
2801 /**************************************************************************
2802 * IFileOpenDialog
2804 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
2806 return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
2809 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
2810 REFIID riid, void **ppvObject)
2812 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2813 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2816 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
2818 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2819 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2822 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
2824 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2825 return IFileDialog2_Release(&This->IFileDialog2_iface);
2828 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
2830 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2831 return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2834 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
2835 const COMDLG_FILTERSPEC *rgFilterSpec)
2837 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2838 return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2841 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
2843 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2844 return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2847 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
2849 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2850 return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2853 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
2854 DWORD *pdwCookie)
2856 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2857 return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2860 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
2862 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2863 return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2866 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
2868 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2869 return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2872 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2874 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2875 return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2878 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
2880 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2881 return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2884 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
2886 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2887 return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2890 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
2892 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2893 return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2896 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
2898 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2899 return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2902 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
2904 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2905 return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2908 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
2910 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2911 return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2914 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
2916 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2917 return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2920 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
2922 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2923 return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2926 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
2928 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2929 return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2932 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
2934 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2935 return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2938 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
2940 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2941 return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2944 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultExtension(IFileOpenDialog *iface,
2945 LPCWSTR pszDefaultExtension)
2947 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2948 return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2951 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
2953 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2954 return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2957 static HRESULT WINAPI IFileOpenDialog_fnSetClientGuid(IFileOpenDialog *iface, REFGUID guid)
2959 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2960 return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2963 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
2965 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2966 return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2969 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
2971 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2972 return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2975 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
2977 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2978 TRACE("%p (%p)\n", This, ppenum);
2980 *ppenum = This->psia_results;
2982 if(*ppenum)
2984 IShellItemArray_AddRef(*ppenum);
2985 return S_OK;
2988 return E_FAIL;
2991 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
2993 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2994 TRACE("%p (%p)\n", This, ppsai);
2996 if(This->psia_selection)
2998 *ppsai = This->psia_selection;
2999 IShellItemArray_AddRef(*ppsai);
3000 return S_OK;
3003 return E_FAIL;
3006 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
3007 IFileOpenDialog_fnQueryInterface,
3008 IFileOpenDialog_fnAddRef,
3009 IFileOpenDialog_fnRelease,
3010 IFileOpenDialog_fnShow,
3011 IFileOpenDialog_fnSetFileTypes,
3012 IFileOpenDialog_fnSetFileTypeIndex,
3013 IFileOpenDialog_fnGetFileTypeIndex,
3014 IFileOpenDialog_fnAdvise,
3015 IFileOpenDialog_fnUnadvise,
3016 IFileOpenDialog_fnSetOptions,
3017 IFileOpenDialog_fnGetOptions,
3018 IFileOpenDialog_fnSetDefaultFolder,
3019 IFileOpenDialog_fnSetFolder,
3020 IFileOpenDialog_fnGetFolder,
3021 IFileOpenDialog_fnGetCurrentSelection,
3022 IFileOpenDialog_fnSetFileName,
3023 IFileOpenDialog_fnGetFileName,
3024 IFileOpenDialog_fnSetTitle,
3025 IFileOpenDialog_fnSetOkButtonLabel,
3026 IFileOpenDialog_fnSetFileNameLabel,
3027 IFileOpenDialog_fnGetResult,
3028 IFileOpenDialog_fnAddPlace,
3029 IFileOpenDialog_fnSetDefaultExtension,
3030 IFileOpenDialog_fnClose,
3031 IFileOpenDialog_fnSetClientGuid,
3032 IFileOpenDialog_fnClearClientData,
3033 IFileOpenDialog_fnSetFilter,
3034 IFileOpenDialog_fnGetResults,
3035 IFileOpenDialog_fnGetSelectedItems
3038 /**************************************************************************
3039 * IFileSaveDialog
3041 static inline FileDialogImpl *impl_from_IFileSaveDialog(IFileSaveDialog *iface)
3043 return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
3046 static HRESULT WINAPI IFileSaveDialog_fnQueryInterface(IFileSaveDialog *iface,
3047 REFIID riid,
3048 void **ppvObject)
3050 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3051 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3054 static ULONG WINAPI IFileSaveDialog_fnAddRef(IFileSaveDialog *iface)
3056 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3057 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3060 static ULONG WINAPI IFileSaveDialog_fnRelease(IFileSaveDialog *iface)
3062 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3063 return IFileDialog2_Release(&This->IFileDialog2_iface);
3066 static HRESULT WINAPI IFileSaveDialog_fnShow(IFileSaveDialog *iface, HWND hwndOwner)
3068 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3069 return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
3072 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypes(IFileSaveDialog *iface, UINT cFileTypes,
3073 const COMDLG_FILTERSPEC *rgFilterSpec)
3075 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3076 return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
3079 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypeIndex(IFileSaveDialog *iface, UINT iFileType)
3081 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3082 return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
3085 static HRESULT WINAPI IFileSaveDialog_fnGetFileTypeIndex(IFileSaveDialog *iface, UINT *piFileType)
3087 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3088 return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
3091 static HRESULT WINAPI IFileSaveDialog_fnAdvise(IFileSaveDialog *iface, IFileDialogEvents *pfde,
3092 DWORD *pdwCookie)
3094 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3095 return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
3098 static HRESULT WINAPI IFileSaveDialog_fnUnadvise(IFileSaveDialog *iface, DWORD dwCookie)
3100 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3101 return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
3104 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
3106 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3107 return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
3110 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
3112 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3113 return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
3116 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultFolder(IFileSaveDialog *iface, IShellItem *psi)
3118 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3119 return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
3122 static HRESULT WINAPI IFileSaveDialog_fnSetFolder(IFileSaveDialog *iface, IShellItem *psi)
3124 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3125 return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
3128 static HRESULT WINAPI IFileSaveDialog_fnGetFolder(IFileSaveDialog *iface, IShellItem **ppsi)
3130 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3131 return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
3134 static HRESULT WINAPI IFileSaveDialog_fnGetCurrentSelection(IFileSaveDialog *iface, IShellItem **ppsi)
3136 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3137 return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
3140 static HRESULT WINAPI IFileSaveDialog_fnSetFileName(IFileSaveDialog *iface, LPCWSTR pszName)
3142 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3143 return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
3146 static HRESULT WINAPI IFileSaveDialog_fnGetFileName(IFileSaveDialog *iface, LPWSTR *pszName)
3148 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3149 return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
3152 static HRESULT WINAPI IFileSaveDialog_fnSetTitle(IFileSaveDialog *iface, LPCWSTR pszTitle)
3154 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3155 return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
3158 static HRESULT WINAPI IFileSaveDialog_fnSetOkButtonLabel(IFileSaveDialog *iface, LPCWSTR pszText)
3160 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3161 return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
3164 static HRESULT WINAPI IFileSaveDialog_fnSetFileNameLabel(IFileSaveDialog *iface, LPCWSTR pszLabel)
3166 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3167 return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
3170 static HRESULT WINAPI IFileSaveDialog_fnGetResult(IFileSaveDialog *iface, IShellItem **ppsi)
3172 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3173 return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
3176 static HRESULT WINAPI IFileSaveDialog_fnAddPlace(IFileSaveDialog *iface, IShellItem *psi, FDAP fdap)
3178 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3179 return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
3182 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultExtension(IFileSaveDialog *iface,
3183 LPCWSTR pszDefaultExtension)
3185 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3186 return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
3189 static HRESULT WINAPI IFileSaveDialog_fnClose(IFileSaveDialog *iface, HRESULT hr)
3191 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3192 return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
3195 static HRESULT WINAPI IFileSaveDialog_fnSetClientGuid(IFileSaveDialog *iface, REFGUID guid)
3197 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3198 return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
3201 static HRESULT WINAPI IFileSaveDialog_fnClearClientData(IFileSaveDialog *iface)
3203 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3204 return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
3207 static HRESULT WINAPI IFileSaveDialog_fnSetFilter(IFileSaveDialog *iface, IShellItemFilter *pFilter)
3209 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3210 return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
3213 static HRESULT WINAPI IFileSaveDialog_fnSetSaveAsItem(IFileSaveDialog* iface, IShellItem *psi)
3215 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3216 FIXME("stub - %p (%p)\n", This, psi);
3217 return E_NOTIMPL;
3220 static HRESULT WINAPI IFileSaveDialog_fnSetProperties(IFileSaveDialog* iface, IPropertyStore *pStore)
3222 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3223 FIXME("stub - %p (%p)\n", This, pStore);
3224 return E_NOTIMPL;
3227 static HRESULT WINAPI IFileSaveDialog_fnSetCollectedProperties(IFileSaveDialog* iface,
3228 IPropertyDescriptionList *pList,
3229 BOOL fAppendDefault)
3231 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3232 FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
3233 return E_NOTIMPL;
3236 static HRESULT WINAPI IFileSaveDialog_fnGetProperties(IFileSaveDialog* iface, IPropertyStore **ppStore)
3238 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3239 FIXME("stub - %p (%p)\n", This, ppStore);
3240 return E_NOTIMPL;
3243 static HRESULT WINAPI IFileSaveDialog_fnApplyProperties(IFileSaveDialog* iface,
3244 IShellItem *psi,
3245 IPropertyStore *pStore,
3246 HWND hwnd,
3247 IFileOperationProgressSink *pSink)
3249 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3250 FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
3251 return E_NOTIMPL;
3254 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
3255 IFileSaveDialog_fnQueryInterface,
3256 IFileSaveDialog_fnAddRef,
3257 IFileSaveDialog_fnRelease,
3258 IFileSaveDialog_fnShow,
3259 IFileSaveDialog_fnSetFileTypes,
3260 IFileSaveDialog_fnSetFileTypeIndex,
3261 IFileSaveDialog_fnGetFileTypeIndex,
3262 IFileSaveDialog_fnAdvise,
3263 IFileSaveDialog_fnUnadvise,
3264 IFileSaveDialog_fnSetOptions,
3265 IFileSaveDialog_fnGetOptions,
3266 IFileSaveDialog_fnSetDefaultFolder,
3267 IFileSaveDialog_fnSetFolder,
3268 IFileSaveDialog_fnGetFolder,
3269 IFileSaveDialog_fnGetCurrentSelection,
3270 IFileSaveDialog_fnSetFileName,
3271 IFileSaveDialog_fnGetFileName,
3272 IFileSaveDialog_fnSetTitle,
3273 IFileSaveDialog_fnSetOkButtonLabel,
3274 IFileSaveDialog_fnSetFileNameLabel,
3275 IFileSaveDialog_fnGetResult,
3276 IFileSaveDialog_fnAddPlace,
3277 IFileSaveDialog_fnSetDefaultExtension,
3278 IFileSaveDialog_fnClose,
3279 IFileSaveDialog_fnSetClientGuid,
3280 IFileSaveDialog_fnClearClientData,
3281 IFileSaveDialog_fnSetFilter,
3282 IFileSaveDialog_fnSetSaveAsItem,
3283 IFileSaveDialog_fnSetProperties,
3284 IFileSaveDialog_fnSetCollectedProperties,
3285 IFileSaveDialog_fnGetProperties,
3286 IFileSaveDialog_fnApplyProperties
3289 /**************************************************************************
3290 * IExplorerBrowserEvents implementation
3292 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
3294 return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
3297 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
3298 REFIID riid, void **ppvObject)
3300 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3301 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
3303 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3306 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
3308 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3309 TRACE("%p\n", This);
3310 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3313 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
3315 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3316 TRACE("%p\n", This);
3317 return IFileDialog2_Release(&This->IFileDialog2_iface);
3320 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
3321 PCIDLIST_ABSOLUTE pidlFolder)
3323 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3324 IShellItem *psi;
3325 HRESULT hr;
3326 TRACE("%p (%p)\n", This, pidlFolder);
3328 hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
3329 if(SUCCEEDED(hr))
3331 hr = events_OnFolderChanging(This, psi);
3332 IShellItem_Release(psi);
3334 /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
3335 if(hr == S_FALSE)
3336 hr = E_FAIL;
3338 return hr;
3340 else
3341 ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
3343 return S_OK;
3346 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
3347 IShellView *psv)
3349 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3350 TRACE("%p (%p)\n", This, psv);
3351 return S_OK;
3354 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
3355 PCIDLIST_ABSOLUTE pidlFolder)
3357 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3358 HRESULT hr;
3359 TRACE("%p (%p)\n", This, pidlFolder);
3361 if(This->psi_folder)
3362 IShellItem_Release(This->psi_folder);
3364 hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
3365 if(FAILED(hr))
3367 ERR("Failed to get the current folder.\n");
3368 This->psi_folder = NULL;
3371 events_OnFolderChange(This);
3373 return S_OK;
3376 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
3377 PCIDLIST_ABSOLUTE pidlFolder)
3379 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3380 TRACE("%p (%p)\n", This, pidlFolder);
3381 return S_OK;
3384 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
3385 IExplorerBrowserEvents_fnQueryInterface,
3386 IExplorerBrowserEvents_fnAddRef,
3387 IExplorerBrowserEvents_fnRelease,
3388 IExplorerBrowserEvents_fnOnNavigationPending,
3389 IExplorerBrowserEvents_fnOnViewCreated,
3390 IExplorerBrowserEvents_fnOnNavigationComplete,
3391 IExplorerBrowserEvents_fnOnNavigationFailed
3394 /**************************************************************************
3395 * IServiceProvider implementation
3397 static inline FileDialogImpl *impl_from_IServiceProvider(IServiceProvider *iface)
3399 return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
3402 static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface,
3403 REFIID riid, void **ppvObject)
3405 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3406 TRACE("%p\n", This);
3407 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3410 static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface)
3412 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3413 TRACE("%p\n", This);
3414 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3417 static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface)
3419 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3420 TRACE("%p\n", This);
3421 return IFileDialog2_Release(&This->IFileDialog2_iface);
3424 static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
3425 REFGUID guidService,
3426 REFIID riid, void **ppv)
3428 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3429 HRESULT hr = E_NOTIMPL;
3430 TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
3432 *ppv = NULL;
3433 if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
3434 hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
3435 else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
3436 hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
3437 else
3438 FIXME("Interface %s requested from unknown service %s\n",
3439 debugstr_guid(riid), debugstr_guid(guidService));
3441 return hr;
3444 static const IServiceProviderVtbl vt_IServiceProvider = {
3445 IServiceProvider_fnQueryInterface,
3446 IServiceProvider_fnAddRef,
3447 IServiceProvider_fnRelease,
3448 IServiceProvider_fnQueryService
3451 /**************************************************************************
3452 * ICommDlgBrowser3 implementation
3454 static inline FileDialogImpl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface)
3456 return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
3459 static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface,
3460 REFIID riid, void **ppvObject)
3462 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3463 TRACE("%p\n", This);
3464 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3467 static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface)
3469 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3470 TRACE("%p\n", This);
3471 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3474 static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface)
3476 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3477 TRACE("%p\n", This);
3478 return IFileDialog2_Release(&This->IFileDialog2_iface);
3481 static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3 *iface,
3482 IShellView *shv)
3484 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3485 HRESULT hr;
3486 TRACE("%p (%p)\n", This, shv);
3488 hr = on_default_action(This);
3490 if(SUCCEEDED(hr))
3491 EndDialog(This->dlg_hwnd, S_OK);
3493 return S_OK;
3496 static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange(ICommDlgBrowser3 *iface,
3497 IShellView *shv, ULONG uChange )
3499 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3500 IDataObject *new_selection;
3501 HRESULT hr;
3502 TRACE("%p (%p, %x)\n", This, shv, uChange);
3504 switch(uChange)
3506 case CDBOSC_SELCHANGE:
3507 if(This->psia_selection)
3509 IShellItemArray_Release(This->psia_selection);
3510 This->psia_selection = NULL;
3513 hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
3514 if(SUCCEEDED(hr))
3516 hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
3517 (void**)&This->psia_selection);
3518 if(SUCCEEDED(hr))
3520 fill_filename_from_selection(This);
3521 events_OnSelectionChange(This);
3524 IDataObject_Release(new_selection);
3526 break;
3527 default:
3528 TRACE("Unhandled state change\n");
3530 return S_OK;
3533 static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
3534 IShellView *shv, LPCITEMIDLIST pidl)
3536 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3537 IShellItem *psi;
3538 LPWSTR filename;
3539 LPITEMIDLIST parent_pidl;
3540 HRESULT hr;
3541 ULONG attr;
3542 TRACE("%p (%p, %p)\n", This, shv, pidl);
3544 if(!This->filterspec_count && !(This->options & FOS_PICKFOLDERS))
3545 return S_OK;
3547 hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
3548 if(SUCCEEDED(hr))
3550 LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
3551 hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
3552 ILFree(parent_pidl);
3553 ILFree(full_pidl);
3555 if(FAILED(hr))
3557 ERR("Failed to get shellitem (%08x).\n", hr);
3558 return S_OK;
3561 hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
3562 if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
3564 IShellItem_Release(psi);
3565 return S_OK;
3568 if((This->options & FOS_PICKFOLDERS) && !(attr & (SFGAO_FOLDER | SFGAO_LINK)))
3570 IShellItem_Release(psi);
3571 return S_FALSE;
3574 hr = S_OK;
3575 if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
3577 if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
3578 hr = S_FALSE;
3579 CoTaskMemFree(filename);
3582 IShellItem_Release(psi);
3583 return hr;
3586 static HRESULT WINAPI ICommDlgBrowser3_fnNotify(ICommDlgBrowser3 *iface,
3587 IShellView *ppshv, DWORD dwNotifyType)
3589 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3590 FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
3591 return E_NOTIMPL;
3594 static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText(ICommDlgBrowser3 *iface,
3595 IShellView *pshv,
3596 LPWSTR pszText, int cchMax)
3598 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3599 FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
3600 return E_NOTIMPL;
3603 static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags(ICommDlgBrowser3 *iface, DWORD *pdwFlags)
3605 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3606 FIXME("Stub: %p (%p)\n", This, pdwFlags);
3607 return E_NOTIMPL;
3610 static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked(ICommDlgBrowser3 *iface,
3611 IShellView *pshv, int iColumn)
3613 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3614 FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
3615 return E_NOTIMPL;
3618 static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter(ICommDlgBrowser3 *iface,
3619 LPWSTR pszFileSpec, int cchFileSpec)
3621 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3622 FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
3623 return E_NOTIMPL;
3626 static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated(ICommDlgBrowser3 *iface,
3627 IShellView *pshv)
3629 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3630 FIXME("Stub: %p (%p)\n", This, pshv);
3631 return E_NOTIMPL;
3634 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
3635 ICommDlgBrowser3_fnQueryInterface,
3636 ICommDlgBrowser3_fnAddRef,
3637 ICommDlgBrowser3_fnRelease,
3638 ICommDlgBrowser3_fnOnDefaultCommand,
3639 ICommDlgBrowser3_fnOnStateChange,
3640 ICommDlgBrowser3_fnIncludeObject,
3641 ICommDlgBrowser3_fnNotify,
3642 ICommDlgBrowser3_fnGetDefaultMenuText,
3643 ICommDlgBrowser3_fnGetViewFlags,
3644 ICommDlgBrowser3_fnOnColumnClicked,
3645 ICommDlgBrowser3_fnGetCurrentFilter,
3646 ICommDlgBrowser3_fnOnPreviewCreated
3649 /**************************************************************************
3650 * IOleWindow implementation
3652 static inline FileDialogImpl *impl_from_IOleWindow(IOleWindow *iface)
3654 return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
3657 static HRESULT WINAPI IOleWindow_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
3659 FileDialogImpl *This = impl_from_IOleWindow(iface);
3660 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3663 static ULONG WINAPI IOleWindow_fnAddRef(IOleWindow *iface)
3665 FileDialogImpl *This = impl_from_IOleWindow(iface);
3666 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3669 static ULONG WINAPI IOleWindow_fnRelease(IOleWindow *iface)
3671 FileDialogImpl *This = impl_from_IOleWindow(iface);
3672 return IFileDialog2_Release(&This->IFileDialog2_iface);
3675 static HRESULT WINAPI IOleWindow_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMOde)
3677 FileDialogImpl *This = impl_from_IOleWindow(iface);
3678 FIXME("Stub: %p (%d)\n", This, fEnterMOde);
3679 return E_NOTIMPL;
3682 static HRESULT WINAPI IOleWindow_fnGetWindow(IOleWindow *iface, HWND *phwnd)
3684 FileDialogImpl *This = impl_from_IOleWindow(iface);
3685 TRACE("%p (%p)\n", This, phwnd);
3686 *phwnd = This->dlg_hwnd;
3687 return S_OK;
3690 static const IOleWindowVtbl vt_IOleWindow = {
3691 IOleWindow_fnQueryInterface,
3692 IOleWindow_fnAddRef,
3693 IOleWindow_fnRelease,
3694 IOleWindow_fnGetWindow,
3695 IOleWindow_fnContextSensitiveHelp
3698 /**************************************************************************
3699 * IFileDialogCustomize implementation
3701 static inline FileDialogImpl *impl_from_IFileDialogCustomize(IFileDialogCustomize *iface)
3703 return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialogCustomize_iface);
3706 static HRESULT WINAPI IFileDialogCustomize_fnQueryInterface(IFileDialogCustomize *iface,
3707 REFIID riid, void **ppvObject)
3709 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3710 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3713 static ULONG WINAPI IFileDialogCustomize_fnAddRef(IFileDialogCustomize *iface)
3715 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3716 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3719 static ULONG WINAPI IFileDialogCustomize_fnRelease(IFileDialogCustomize *iface)
3721 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3722 return IFileDialog2_Release(&This->IFileDialog2_iface);
3725 static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCustomize *iface,
3726 DWORD dwIDCtl)
3728 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3729 MENUINFO mi;
3730 TRACE("%p (%d)\n", This, dwIDCtl);
3732 if (This->hmenu_opendropdown || get_cctrl(This, dwIDCtl))
3733 return E_UNEXPECTED;
3735 This->hmenu_opendropdown = CreatePopupMenu();
3737 if (!This->hmenu_opendropdown)
3738 return E_OUTOFMEMORY;
3740 mi.cbSize = sizeof(mi);
3741 mi.fMask = MIM_STYLE;
3742 mi.dwStyle = MNS_NOTIFYBYPOS;
3743 SetMenuInfo(This->hmenu_opendropdown, &mi);
3745 This->cctrl_opendropdown.hwnd = NULL;
3746 This->cctrl_opendropdown.wrapper_hwnd = NULL;
3747 This->cctrl_opendropdown.id = dwIDCtl;
3748 This->cctrl_opendropdown.dlgid = 0;
3749 This->cctrl_opendropdown.type = IDLG_CCTRL_OPENDROPDOWN;
3750 This->cctrl_opendropdown.cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
3751 list_init(&This->cctrl_opendropdown.sub_cctrls);
3752 list_init(&This->cctrl_opendropdown.sub_items);
3754 return S_OK;
3757 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
3758 DWORD dwIDCtl,
3759 LPCWSTR pszLabel)
3761 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3762 customctrl *ctrl;
3763 TBBUTTON tbb;
3764 HRESULT hr;
3765 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3767 hr = cctrl_create_new(This, dwIDCtl, NULL, TOOLBARCLASSNAMEW,
3768 TBSTYLE_FLAT | CCS_NODIVIDER, 0,
3769 This->cctrl_def_height, &ctrl);
3770 if(SUCCEEDED(hr))
3772 SendMessageW(ctrl->hwnd, TB_BUTTONSTRUCTSIZE, sizeof(tbb), 0);
3773 ctrl->type = IDLG_CCTRL_MENU;
3775 /* Add the actual button with a popup menu. */
3776 tbb.iBitmap = I_IMAGENONE;
3777 tbb.dwData = (DWORD_PTR)CreatePopupMenu();
3778 tbb.iString = (DWORD_PTR)pszLabel;
3779 tbb.fsState = TBSTATE_ENABLED;
3780 tbb.fsStyle = BTNS_WHOLEDROPDOWN;
3781 tbb.idCommand = 1;
3783 SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
3786 return hr;
3789 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
3790 DWORD dwIDCtl,
3791 LPCWSTR pszLabel)
3793 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3794 customctrl *ctrl;
3795 HRESULT hr;
3796 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3798 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
3799 This->cctrl_def_height, &ctrl);
3800 if(SUCCEEDED(hr))
3801 ctrl->type = IDLG_CCTRL_PUSHBUTTON;
3803 return hr;
3806 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
3807 DWORD dwIDCtl)
3809 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3810 customctrl *ctrl;
3811 HRESULT hr;
3812 TRACE("%p (%d)\n", This, dwIDCtl);
3814 hr = cctrl_create_new(This, dwIDCtl, NULL, WC_COMBOBOXW, CBS_DROPDOWNLIST, 0,
3815 This->cctrl_def_height, &ctrl);
3816 if(SUCCEEDED(hr))
3817 ctrl->type = IDLG_CCTRL_COMBOBOX;
3819 return hr;
3822 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
3823 DWORD dwIDCtl)
3825 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3826 customctrl *ctrl;
3827 HRESULT hr;
3828 TRACE("%p (%d)\n", This, dwIDCtl);
3830 hr = cctrl_create_new(This, dwIDCtl, NULL, radiobuttonlistW, 0, 0, 0, &ctrl);
3831 if(SUCCEEDED(hr))
3833 ctrl->type = IDLG_CCTRL_RADIOBUTTONLIST;
3834 SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)This);
3837 return hr;
3840 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
3841 DWORD dwIDCtl,
3842 LPCWSTR pszLabel,
3843 BOOL bChecked)
3845 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3846 customctrl *ctrl;
3847 HRESULT hr;
3848 TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
3850 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX|BS_MULTILINE, 0,
3851 This->cctrl_def_height, &ctrl);
3852 if(SUCCEEDED(hr))
3854 ctrl->type = IDLG_CCTRL_CHECKBUTTON;
3855 SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
3858 return hr;
3861 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
3862 DWORD dwIDCtl,
3863 LPCWSTR pszText)
3865 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3866 customctrl *ctrl;
3867 HRESULT hr;
3868 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3870 hr = cctrl_create_new(This, dwIDCtl, pszText, WC_EDITW, ES_AUTOHSCROLL, WS_EX_CLIENTEDGE,
3871 This->cctrl_def_height, &ctrl);
3872 if(SUCCEEDED(hr))
3873 ctrl->type = IDLG_CCTRL_EDITBOX;
3875 return hr;
3878 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
3879 DWORD dwIDCtl)
3881 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3882 customctrl *ctrl;
3883 HRESULT hr;
3884 TRACE("%p (%d)\n", This, dwIDCtl);
3886 hr = cctrl_create_new(This, dwIDCtl, NULL, WC_STATICW, SS_ETCHEDHORZ, 0,
3887 GetSystemMetrics(SM_CYEDGE), &ctrl);
3888 if(SUCCEEDED(hr))
3889 ctrl->type = IDLG_CCTRL_SEPARATOR;
3891 return hr;
3894 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
3895 DWORD dwIDCtl,
3896 LPCWSTR pszText)
3898 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3899 customctrl *ctrl;
3900 HRESULT hr;
3901 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3903 hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
3904 This->cctrl_def_height, &ctrl);
3905 if(SUCCEEDED(hr))
3906 ctrl->type = IDLG_CCTRL_TEXT;
3908 return hr;
3911 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
3912 DWORD dwIDCtl,
3913 LPCWSTR pszLabel)
3915 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3916 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3917 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3919 if(!ctrl) return E_INVALIDARG;
3921 switch(ctrl->type)
3923 case IDLG_CCTRL_MENU:
3924 case IDLG_CCTRL_PUSHBUTTON:
3925 case IDLG_CCTRL_CHECKBUTTON:
3926 case IDLG_CCTRL_TEXT:
3927 case IDLG_CCTRL_VISUALGROUP:
3928 SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
3929 break;
3930 case IDLG_CCTRL_OPENDROPDOWN:
3931 return E_NOTIMPL;
3932 default:
3933 break;
3936 return S_OK;
3939 static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomize *iface,
3940 DWORD dwIDCtl,
3941 CDCONTROLSTATEF *pdwState)
3943 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3944 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3945 TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
3947 if(!ctrl || ctrl->type == IDLG_CCTRL_OPENDROPDOWN) return E_NOTIMPL;
3949 *pdwState = ctrl->cdcstate;
3950 return S_OK;
3953 static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomize *iface,
3954 DWORD dwIDCtl,
3955 CDCONTROLSTATEF dwState)
3957 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3958 customctrl *ctrl = get_cctrl(This,dwIDCtl);
3959 TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
3961 if(ctrl && ctrl->hwnd)
3963 LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
3965 if(dwState & CDCS_ENABLED)
3966 wndstyle &= ~(WS_DISABLED);
3967 else
3968 wndstyle |= WS_DISABLED;
3970 if(dwState & CDCS_VISIBLE)
3971 wndstyle |= WS_VISIBLE;
3972 else
3973 wndstyle &= ~(WS_VISIBLE);
3975 SetWindowLongW(ctrl->hwnd, GWL_STYLE, wndstyle);
3977 /* We save the state separately since at least one application
3978 * relies on being able to hide a control. */
3979 ctrl->cdcstate = dwState;
3982 return S_OK;
3985 static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize *iface,
3986 DWORD dwIDCtl,
3987 WCHAR **ppszText)
3989 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3990 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3991 WCHAR len, *text;
3992 TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
3994 if(!ctrl || !ctrl->hwnd || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
3995 return E_FAIL;
3997 text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
3998 if(!text) return E_FAIL;
4000 SendMessageW(ctrl->hwnd, WM_GETTEXT, len+1, (LPARAM)text);
4001 *ppszText = text;
4002 return S_OK;
4005 static HRESULT WINAPI IFileDialogCustomize_fnSetEditBoxText(IFileDialogCustomize *iface,
4006 DWORD dwIDCtl,
4007 LPCWSTR pszText)
4009 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4010 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4011 TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
4013 if(!ctrl || ctrl->type != IDLG_CCTRL_EDITBOX)
4014 return E_FAIL;
4016 SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
4017 return S_OK;
4020 static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCustomize *iface,
4021 DWORD dwIDCtl,
4022 BOOL *pbChecked)
4024 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4025 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4026 TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
4028 if(ctrl && ctrl->hwnd)
4029 *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
4031 return S_OK;
4034 static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCustomize *iface,
4035 DWORD dwIDCtl,
4036 BOOL bChecked)
4038 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4039 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4040 TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
4042 if(ctrl && ctrl->hwnd)
4043 SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
4045 return S_OK;
4048 static UINT get_combobox_index_from_id(HWND cb_hwnd, DWORD dwIDItem)
4050 UINT count = SendMessageW(cb_hwnd, CB_GETCOUNT, 0, 0);
4051 UINT i;
4052 if(!count || (count == CB_ERR))
4053 return -1;
4055 for(i = 0; i < count; i++)
4056 if(SendMessageW(cb_hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
4057 return i;
4059 TRACE("Item with id %d not found in combobox %p (item count: %d)\n", dwIDItem, cb_hwnd, count);
4060 return -1;
4063 static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize *iface,
4064 DWORD dwIDCtl,
4065 DWORD dwIDItem,
4066 LPCWSTR pszLabel)
4068 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4069 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4070 HRESULT hr;
4071 TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4073 if(!ctrl) return E_FAIL;
4075 switch(ctrl->type)
4077 case IDLG_CCTRL_COMBOBOX:
4079 UINT index;
4080 cctrl_item* item;
4082 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4084 if (FAILED(hr)) return hr;
4086 index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
4087 SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
4089 return S_OK;
4091 case IDLG_CCTRL_MENU:
4092 case IDLG_CCTRL_OPENDROPDOWN:
4094 cctrl_item* item;
4095 HMENU hmenu;
4097 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4099 if (FAILED(hr)) return hr;
4101 if (ctrl->type == IDLG_CCTRL_MENU)
4103 TBBUTTON tbb;
4104 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4105 hmenu = (HMENU)tbb.dwData;
4107 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4108 hmenu = This->hmenu_opendropdown;
4110 AppendMenuW(hmenu, MF_STRING, dwIDItem, pszLabel);
4111 return S_OK;
4113 case IDLG_CCTRL_RADIOBUTTONLIST:
4115 cctrl_item* item;
4117 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4119 if (SUCCEEDED(hr))
4121 item->hwnd = CreateWindowExW(0, WC_BUTTONW, pszLabel,
4122 WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|BS_RADIOBUTTON|BS_MULTILINE,
4123 0, 0, 0, 0, ctrl->hwnd, ULongToHandle(dwIDItem), COMDLG32_hInstance, 0);
4125 if (!item->hwnd)
4127 ERR("Failed to create radio button\n");
4128 list_remove(&item->entry);
4129 item_free(item);
4130 return E_FAIL;
4134 return hr;
4136 default:
4137 break;
4140 return E_NOINTERFACE; /* win7 */
4143 static HRESULT WINAPI IFileDialogCustomize_fnRemoveControlItem(IFileDialogCustomize *iface,
4144 DWORD dwIDCtl,
4145 DWORD dwIDItem)
4147 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4148 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4149 TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4151 if(!ctrl) return E_FAIL;
4153 switch(ctrl->type)
4155 case IDLG_CCTRL_COMBOBOX:
4157 cctrl_item* item;
4158 DWORD position;
4160 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4162 if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
4164 if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0) == CB_ERR)
4165 return E_FAIL;
4168 list_remove(&item->entry);
4169 item_free(item);
4171 return S_OK;
4173 case IDLG_CCTRL_MENU:
4174 case IDLG_CCTRL_OPENDROPDOWN:
4176 HMENU hmenu;
4177 cctrl_item* item;
4179 item = get_item(ctrl, dwIDItem, 0, NULL);
4181 if (!item)
4182 return E_UNEXPECTED;
4184 if (item->cdcstate & CDCS_VISIBLE)
4186 if (ctrl->type == IDLG_CCTRL_MENU)
4188 TBBUTTON tbb;
4189 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4190 hmenu = (HMENU)tbb.dwData;
4192 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4193 hmenu = This->hmenu_opendropdown;
4195 if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
4196 return E_UNEXPECTED;
4199 list_remove(&item->entry);
4200 item_free(item);
4202 return S_OK;
4204 case IDLG_CCTRL_RADIOBUTTONLIST:
4206 cctrl_item* item;
4208 item = get_item(ctrl, dwIDItem, 0, NULL);
4210 if (!item)
4211 return E_UNEXPECTED;
4213 list_remove(&item->entry);
4214 item_free(item);
4216 return S_OK;
4218 default:
4219 break;
4222 return E_FAIL;
4225 static HRESULT WINAPI IFileDialogCustomize_fnRemoveAllControlItems(IFileDialogCustomize *iface,
4226 DWORD dwIDCtl)
4228 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4229 TRACE("%p (%d)\n", This, dwIDCtl);
4231 /* Not implemented by native */
4232 return E_NOTIMPL;
4235 static HRESULT WINAPI IFileDialogCustomize_fnGetControlItemState(IFileDialogCustomize *iface,
4236 DWORD dwIDCtl,
4237 DWORD dwIDItem,
4238 CDCONTROLSTATEF *pdwState)
4240 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4241 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4242 TRACE("%p (%d, %d, %p)\n", This, dwIDCtl, dwIDItem, pdwState);
4244 if(!ctrl) return E_FAIL;
4246 switch(ctrl->type)
4248 case IDLG_CCTRL_COMBOBOX:
4249 case IDLG_CCTRL_MENU:
4250 case IDLG_CCTRL_OPENDROPDOWN:
4251 case IDLG_CCTRL_RADIOBUTTONLIST:
4253 cctrl_item* item;
4255 item = get_item(ctrl, dwIDItem, 0, NULL);
4257 if (!item)
4258 return E_UNEXPECTED;
4260 *pdwState = item->cdcstate;
4262 return S_OK;
4264 default:
4265 break;
4268 return E_FAIL;
4271 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCustomize *iface,
4272 DWORD dwIDCtl,
4273 DWORD dwIDItem,
4274 CDCONTROLSTATEF dwState)
4276 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4277 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4278 TRACE("%p (%d, %d, %x)\n", This, dwIDCtl, dwIDItem, dwState);
4280 if(!ctrl) return E_FAIL;
4282 switch(ctrl->type)
4284 case IDLG_CCTRL_COMBOBOX:
4286 cctrl_item* item;
4287 BOOL visible, was_visible;
4288 DWORD position;
4290 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4292 if (!item)
4293 return E_UNEXPECTED;
4295 visible = ((dwState & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4296 was_visible = ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4298 if (visible && !was_visible)
4300 SendMessageW(ctrl->hwnd, CB_INSERTSTRING, position, (LPARAM)item->label);
4301 SendMessageW(ctrl->hwnd, CB_SETITEMDATA, position, dwIDItem);
4303 else if (!visible && was_visible)
4305 SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0);
4308 item->cdcstate = dwState;
4310 return S_OK;
4312 case IDLG_CCTRL_MENU:
4313 case IDLG_CCTRL_OPENDROPDOWN:
4315 HMENU hmenu;
4316 cctrl_item* item;
4317 CDCONTROLSTATEF prev_state;
4318 DWORD position;
4320 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, &position);
4322 if (!item)
4323 return E_UNEXPECTED;
4325 prev_state = item->cdcstate;
4327 if (ctrl->type == IDLG_CCTRL_MENU)
4329 TBBUTTON tbb;
4330 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4331 hmenu = (HMENU)tbb.dwData;
4333 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4334 hmenu = This->hmenu_opendropdown;
4336 if (dwState & CDCS_VISIBLE)
4338 if (prev_state & CDCS_VISIBLE)
4340 /* change state */
4341 EnableMenuItem(hmenu, dwIDItem,
4342 MF_BYCOMMAND|((dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED));
4344 else
4346 /* show item */
4347 MENUITEMINFOW mii;
4349 mii.cbSize = sizeof(mii);
4350 mii.fMask = MIIM_ID|MIIM_STATE|MIIM_STRING;
4351 mii.fState = (dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED;
4352 mii.wID = dwIDItem;
4353 mii.dwTypeData = item->label;
4355 InsertMenuItemW(hmenu, position, TRUE, &mii);
4358 else if (prev_state & CDCS_VISIBLE)
4360 /* hide item */
4361 DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND);
4364 item->cdcstate = dwState;
4366 if (ctrl->type == IDLG_CCTRL_OPENDROPDOWN)
4368 update_control_text(This);
4369 update_layout(This);
4372 return S_OK;
4374 case IDLG_CCTRL_RADIOBUTTONLIST:
4376 cctrl_item* item;
4378 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, NULL);
4380 if (!item)
4381 return E_UNEXPECTED;
4383 /* Oddly, native allows setting this but doesn't seem to do anything with it. */
4384 item->cdcstate = dwState;
4386 return S_OK;
4388 default:
4389 break;
4392 return E_FAIL;
4395 static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogCustomize *iface,
4396 DWORD dwIDCtl,
4397 DWORD *pdwIDItem)
4399 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4400 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4401 TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwIDItem);
4403 if(!ctrl) return E_FAIL;
4405 switch(ctrl->type)
4407 case IDLG_CCTRL_COMBOBOX:
4409 UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
4410 if(index == CB_ERR)
4411 return E_FAIL;
4413 *pdwIDItem = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
4414 return S_OK;
4416 case IDLG_CCTRL_OPENDROPDOWN:
4417 if (This->opendropdown_has_selection)
4419 *pdwIDItem = This->opendropdown_selection;
4420 return S_OK;
4422 else
4424 /* Return first enabled item. */
4425 cctrl_item* item = get_first_item(ctrl);
4427 if (item)
4429 *pdwIDItem = item->id;
4430 return S_OK;
4433 WARN("no enabled items in open dropdown\n");
4434 return E_FAIL;
4436 case IDLG_CCTRL_RADIOBUTTONLIST:
4438 cctrl_item* item;
4440 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
4442 if (SendMessageW(item->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED)
4444 *pdwIDItem = item->id;
4445 return S_OK;
4449 WARN("no checked items in radio button list\n");
4450 return E_FAIL;
4452 default:
4453 FIXME("Unsupported control type %d\n", ctrl->type);
4456 return E_NOTIMPL;
4459 static HRESULT WINAPI IFileDialogCustomize_fnSetSelectedControlItem(IFileDialogCustomize *iface,
4460 DWORD dwIDCtl,
4461 DWORD dwIDItem)
4463 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4464 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4465 TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4467 if(!ctrl) return E_INVALIDARG;
4469 switch(ctrl->type)
4471 case IDLG_CCTRL_COMBOBOX:
4473 UINT index = get_combobox_index_from_id(ctrl->hwnd, dwIDItem);
4475 if(index == -1)
4476 return E_INVALIDARG;
4478 if(SendMessageW(ctrl->hwnd, CB_SETCURSEL, index, 0) == CB_ERR)
4479 return E_FAIL;
4481 return S_OK;
4483 case IDLG_CCTRL_RADIOBUTTONLIST:
4485 cctrl_item* item;
4487 item = get_item(ctrl, dwIDItem, 0, NULL);
4489 if (item)
4491 radiobuttonlist_set_selected_item(This, ctrl, item);
4492 return S_OK;
4495 return E_INVALIDARG;
4497 default:
4498 FIXME("Unsupported control type %d\n", ctrl->type);
4501 return E_INVALIDARG;
4504 static HRESULT WINAPI IFileDialogCustomize_fnStartVisualGroup(IFileDialogCustomize *iface,
4505 DWORD dwIDCtl,
4506 LPCWSTR pszLabel)
4508 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4509 customctrl *vg;
4510 HRESULT hr;
4511 TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
4513 if(This->cctrl_active_vg)
4514 return E_UNEXPECTED;
4516 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_STATICW, 0, 0,
4517 This->cctrl_def_height, &vg);
4518 if(SUCCEEDED(hr))
4520 vg->type = IDLG_CCTRL_VISUALGROUP;
4521 This->cctrl_active_vg = vg;
4524 return hr;
4527 static HRESULT WINAPI IFileDialogCustomize_fnEndVisualGroup(IFileDialogCustomize *iface)
4529 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4530 TRACE("%p\n", This);
4532 This->cctrl_active_vg = NULL;
4534 return S_OK;
4537 static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize *iface,
4538 DWORD dwIDCtl)
4540 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4541 FIXME("stub - %p (%d)\n", This, dwIDCtl);
4542 return S_OK;
4545 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCustomize *iface,
4546 DWORD dwIDCtl,
4547 DWORD dwIDItem,
4548 LPCWSTR pszLabel)
4550 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4551 FIXME("stub - %p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4552 return E_NOTIMPL;
4555 static const IFileDialogCustomizeVtbl vt_IFileDialogCustomize = {
4556 IFileDialogCustomize_fnQueryInterface,
4557 IFileDialogCustomize_fnAddRef,
4558 IFileDialogCustomize_fnRelease,
4559 IFileDialogCustomize_fnEnableOpenDropDown,
4560 IFileDialogCustomize_fnAddMenu,
4561 IFileDialogCustomize_fnAddPushButton,
4562 IFileDialogCustomize_fnAddComboBox,
4563 IFileDialogCustomize_fnAddRadioButtonList,
4564 IFileDialogCustomize_fnAddCheckButton,
4565 IFileDialogCustomize_fnAddEditBox,
4566 IFileDialogCustomize_fnAddSeparator,
4567 IFileDialogCustomize_fnAddText,
4568 IFileDialogCustomize_fnSetControlLabel,
4569 IFileDialogCustomize_fnGetControlState,
4570 IFileDialogCustomize_fnSetControlState,
4571 IFileDialogCustomize_fnGetEditBoxText,
4572 IFileDialogCustomize_fnSetEditBoxText,
4573 IFileDialogCustomize_fnGetCheckButtonState,
4574 IFileDialogCustomize_fnSetCheckButtonState,
4575 IFileDialogCustomize_fnAddControlItem,
4576 IFileDialogCustomize_fnRemoveControlItem,
4577 IFileDialogCustomize_fnRemoveAllControlItems,
4578 IFileDialogCustomize_fnGetControlItemState,
4579 IFileDialogCustomize_fnSetControlItemState,
4580 IFileDialogCustomize_fnGetSelectedControlItem,
4581 IFileDialogCustomize_fnSetSelectedControlItem,
4582 IFileDialogCustomize_fnStartVisualGroup,
4583 IFileDialogCustomize_fnEndVisualGroup,
4584 IFileDialogCustomize_fnMakeProminent,
4585 IFileDialogCustomize_fnSetControlItemText
4588 static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv, enum ITEMDLG_TYPE type)
4590 FileDialogImpl *fdimpl;
4591 HRESULT hr;
4592 IShellFolder *psf;
4593 TRACE("%p, %s, %p\n", pUnkOuter, debugstr_guid(riid), ppv);
4595 if(!ppv)
4596 return E_POINTER;
4597 if(pUnkOuter)
4598 return CLASS_E_NOAGGREGATION;
4600 fdimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(FileDialogImpl));
4601 if(!fdimpl)
4602 return E_OUTOFMEMORY;
4604 fdimpl->ref = 1;
4605 fdimpl->IFileDialog2_iface.lpVtbl = &vt_IFileDialog2;
4606 fdimpl->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
4607 fdimpl->IServiceProvider_iface.lpVtbl = &vt_IServiceProvider;
4608 fdimpl->ICommDlgBrowser3_iface.lpVtbl = &vt_ICommDlgBrowser3;
4609 fdimpl->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
4610 fdimpl->IFileDialogCustomize_iface.lpVtbl = &vt_IFileDialogCustomize;
4612 if(type == ITEMDLG_TYPE_OPEN)
4614 fdimpl->dlg_type = ITEMDLG_TYPE_OPEN;
4615 fdimpl->u.IFileOpenDialog_iface.lpVtbl = &vt_IFileOpenDialog;
4616 fdimpl->options = FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
4617 fdimpl->custom_title = fdimpl->custom_okbutton = NULL;
4619 else
4621 WCHAR buf[16];
4622 fdimpl->dlg_type = ITEMDLG_TYPE_SAVE;
4623 fdimpl->u.IFileSaveDialog_iface.lpVtbl = &vt_IFileSaveDialog;
4624 fdimpl->options = FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
4626 LoadStringW(COMDLG32_hInstance, IDS_SAVE, buf, sizeof(buf)/sizeof(WCHAR));
4627 fdimpl->custom_title = StrDupW(buf);
4628 fdimpl->custom_okbutton = StrDupW(buf);
4631 fdimpl->filterspecs = NULL;
4632 fdimpl->filterspec_count = 0;
4633 fdimpl->filetypeindex = 0;
4635 fdimpl->psia_selection = fdimpl->psia_results = NULL;
4636 fdimpl->psi_setfolder = fdimpl->psi_folder = NULL;
4638 list_init(&fdimpl->events_clients);
4639 fdimpl->events_next_cookie = 0;
4641 fdimpl->dlg_hwnd = NULL;
4642 fdimpl->peb = NULL;
4644 fdimpl->set_filename = NULL;
4645 fdimpl->default_ext = NULL;
4646 fdimpl->custom_cancelbutton = fdimpl->custom_filenamelabel = NULL;
4648 fdimpl->client_guid = GUID_NULL;
4650 fdimpl->hmenu_opendropdown = NULL;
4651 fdimpl->hfont_opendropdown = NULL;
4653 /* FIXME: The default folder setting should be restored for the
4654 * application if it was previously set. */
4655 SHGetDesktopFolder(&psf);
4656 SHGetItemFromObject((IUnknown*)psf, &IID_IShellItem, (void**)&fdimpl->psi_defaultfolder);
4657 IShellFolder_Release(psf);
4659 hr = init_custom_controls(fdimpl);
4660 if(FAILED(hr))
4662 ERR("Failed to initialize custom controls (0x%08x).\n", hr);
4663 IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4664 return E_FAIL;
4667 hr = IFileDialog2_QueryInterface(&fdimpl->IFileDialog2_iface, riid, ppv);
4668 IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4669 return hr;
4672 HRESULT FileOpenDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4674 return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_OPEN);
4677 HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4679 return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);