include: Add a private header for Unix libraries definitions.
[wine.git] / dlls / comdlg32 / itemdlg.c
blobeb85aa10d643b6fff0b263a0eccd736e21b4f05f
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 enum ITEMDLG_TYPE {
52 ITEMDLG_TYPE_OPEN,
53 ITEMDLG_TYPE_SAVE
56 enum ITEMDLG_CCTRL_TYPE {
57 IDLG_CCTRL_MENU,
58 IDLG_CCTRL_PUSHBUTTON,
59 IDLG_CCTRL_COMBOBOX,
60 IDLG_CCTRL_RADIOBUTTONLIST,
61 IDLG_CCTRL_CHECKBUTTON,
62 IDLG_CCTRL_EDITBOX,
63 IDLG_CCTRL_SEPARATOR,
64 IDLG_CCTRL_TEXT,
65 IDLG_CCTRL_OPENDROPDOWN,
66 IDLG_CCTRL_VISUALGROUP
69 typedef struct cctrl_item {
70 DWORD id, parent_id;
71 LPWSTR label;
72 CDCONTROLSTATEF cdcstate;
73 HWND hwnd;
74 struct list entry;
75 } cctrl_item;
77 typedef struct {
78 HWND hwnd, wrapper_hwnd;
79 UINT id, dlgid;
80 enum ITEMDLG_CCTRL_TYPE type;
81 CDCONTROLSTATEF cdcstate;
82 struct list entry;
84 struct list sub_cctrls;
85 struct list sub_cctrls_entry;
86 struct list sub_items;
87 } customctrl;
89 typedef struct {
90 struct list entry;
91 IFileDialogEvents *pfde;
92 DWORD cookie;
93 } events_client;
95 typedef struct FileDialogImpl {
96 IFileDialog2 IFileDialog2_iface;
97 union {
98 IFileOpenDialog IFileOpenDialog_iface;
99 IFileSaveDialog IFileSaveDialog_iface;
100 } u;
101 enum ITEMDLG_TYPE dlg_type;
102 IExplorerBrowserEvents IExplorerBrowserEvents_iface;
103 IServiceProvider IServiceProvider_iface;
104 ICommDlgBrowser3 ICommDlgBrowser3_iface;
105 IOleWindow IOleWindow_iface;
106 IFileDialogCustomize IFileDialogCustomize_iface;
107 LONG ref;
109 FILEOPENDIALOGOPTIONS options;
110 COMDLG_FILTERSPEC *filterspecs;
111 UINT filterspec_count;
112 UINT filetypeindex;
114 struct list events_clients;
115 DWORD events_next_cookie;
117 IShellItemArray *psia_selection;
118 IShellItemArray *psia_results;
119 IShellItem *psi_defaultfolder;
120 IShellItem *psi_setfolder;
121 IShellItem *psi_folder;
123 HWND dlg_hwnd;
124 IExplorerBrowser *peb;
125 DWORD ebevents_cookie;
127 LPWSTR set_filename;
128 LPWSTR default_ext;
129 LPWSTR custom_title;
130 LPWSTR custom_okbutton;
131 LPWSTR custom_cancelbutton;
132 LPWSTR custom_filenamelabel;
134 UINT cctrl_width, cctrl_def_height, cctrls_cols;
135 UINT cctrl_indent, dpi_x, dpi_y;
136 HWND cctrls_hwnd;
137 struct list cctrls;
138 UINT_PTR cctrl_next_dlgid;
139 customctrl *cctrl_active_vg;
141 HMENU hmenu_opendropdown;
142 customctrl cctrl_opendropdown;
143 HFONT hfont_opendropdown;
144 BOOL opendropdown_has_selection;
145 DWORD opendropdown_selection;
147 GUID client_guid;
148 } FileDialogImpl;
150 /**************************************************************************
151 * Event wrappers.
153 static HRESULT events_OnFileOk(FileDialogImpl *This)
155 events_client *cursor;
156 HRESULT hr = S_OK;
157 TRACE("%p\n", This);
159 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
161 TRACE("Notifying %p\n", cursor);
162 hr = IFileDialogEvents_OnFileOk(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
163 if(FAILED(hr) && hr != E_NOTIMPL)
164 break;
167 if(hr == E_NOTIMPL)
168 hr = S_OK;
170 return hr;
173 static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder)
175 events_client *cursor;
176 HRESULT hr = S_OK;
177 TRACE("%p (%p)\n", This, folder);
179 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
181 TRACE("Notifying %p\n", cursor);
182 hr = IFileDialogEvents_OnFolderChanging(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, folder);
183 if(FAILED(hr) && hr != E_NOTIMPL)
184 break;
187 if(hr == E_NOTIMPL)
188 hr = S_OK;
190 return hr;
193 static void events_OnFolderChange(FileDialogImpl *This)
195 events_client *cursor;
196 TRACE("%p\n", This);
198 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
200 TRACE("Notifying %p\n", cursor);
201 IFileDialogEvents_OnFolderChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
205 static void events_OnSelectionChange(FileDialogImpl *This)
207 events_client *cursor;
208 TRACE("%p\n", This);
210 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
212 TRACE("Notifying %p\n", cursor);
213 IFileDialogEvents_OnSelectionChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
217 static void events_OnTypeChange(FileDialogImpl *This)
219 events_client *cursor;
220 TRACE("%p\n", This);
222 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
224 TRACE("Notifying %p\n", cursor);
225 IFileDialogEvents_OnTypeChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
229 static HRESULT events_OnOverwrite(FileDialogImpl *This, IShellItem *shellitem)
231 events_client *cursor;
232 HRESULT hr = S_OK;
233 FDE_OVERWRITE_RESPONSE response = FDEOR_DEFAULT;
234 TRACE("%p %p\n", This, shellitem);
236 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
238 TRACE("Notifying %p\n", cursor);
239 hr = IFileDialogEvents_OnOverwrite(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, shellitem, &response);
240 TRACE("<-- hr=%x response=%u\n", hr, response);
241 if(FAILED(hr) && hr != E_NOTIMPL)
242 break;
245 if(hr == E_NOTIMPL)
246 hr = S_OK;
248 if(SUCCEEDED(hr))
250 if (response == FDEOR_DEFAULT)
252 WCHAR buf[100];
253 int answer;
255 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, buf, 100);
256 answer = MessageBoxW(This->dlg_hwnd, buf, This->custom_title,
257 MB_YESNO | MB_ICONEXCLAMATION);
258 if (answer == IDNO || answer == IDCANCEL)
260 hr = E_FAIL;
263 else if (response == FDEOR_REFUSE)
264 hr = E_FAIL;
267 return hr;
270 static inline HRESULT get_cctrl_event(IFileDialogEvents *pfde, IFileDialogControlEvents **pfdce)
272 return IFileDialogEvents_QueryInterface(pfde, &IID_IFileDialogControlEvents, (void**)pfdce);
275 static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id)
277 events_client *cursor;
278 TRACE("%p\n", This);
280 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
282 IFileDialogControlEvents *pfdce;
283 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
285 TRACE("Notifying %p\n", cursor);
286 IFileDialogControlEvents_OnButtonClicked(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
287 IFileDialogControlEvents_Release(pfdce);
291 return S_OK;
294 static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DWORD item_id)
296 events_client *cursor;
297 TRACE("%p %i %i\n", This, ctl_id, item_id);
299 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
301 IFileDialogControlEvents *pfdce;
302 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
304 TRACE("Notifying %p\n", cursor);
305 IFileDialogControlEvents_OnItemSelected(pfdce, &This->IFileDialogCustomize_iface, ctl_id, item_id);
306 IFileDialogControlEvents_Release(pfdce);
310 return S_OK;
313 static HRESULT cctrl_event_OnCheckButtonToggled(FileDialogImpl *This, DWORD ctl_id, BOOL checked)
315 events_client *cursor;
316 TRACE("%p\n", This);
318 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
320 IFileDialogControlEvents *pfdce;
321 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
323 TRACE("Notifying %p\n", cursor);
324 IFileDialogControlEvents_OnCheckButtonToggled(pfdce, &This->IFileDialogCustomize_iface, ctl_id, checked);
325 IFileDialogControlEvents_Release(pfdce);
329 return S_OK;
332 static HRESULT cctrl_event_OnControlActivating(FileDialogImpl *This,
333 DWORD ctl_id)
335 events_client *cursor;
336 TRACE("%p\n", This);
338 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
340 IFileDialogControlEvents *pfdce;
341 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
343 TRACE("Notifying %p\n", cursor);
344 IFileDialogControlEvents_OnControlActivating(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
345 IFileDialogControlEvents_Release(pfdce);
349 return S_OK;
352 /**************************************************************************
353 * Helper functions.
355 static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
357 HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
358 UINT len;
360 if(!hwnd_edit)
362 if(This->set_filename)
364 len = lstrlenW(This->set_filename);
365 *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
366 lstrcpyW(*str, This->set_filename);
367 return len;
369 return 0;
372 len = SendMessageW(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);
373 *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
374 if(!*str)
375 return 0;
377 SendMessageW(hwnd_edit, WM_GETTEXT, len+1, (LPARAM)*str);
378 return len;
381 static BOOL set_file_name(FileDialogImpl *This, LPCWSTR str)
383 if(This->set_filename)
384 LocalFree(This->set_filename);
386 This->set_filename = str ? StrDupW(str) : NULL;
388 return SetDlgItemTextW(This->dlg_hwnd, IDC_FILENAME, This->set_filename);
391 static void fill_filename_from_selection(FileDialogImpl *This)
393 IShellItem *psi;
394 LPWSTR *names;
395 HRESULT hr;
396 UINT item_count, valid_count;
397 UINT len_total, i;
399 if(!This->psia_selection)
400 return;
402 hr = IShellItemArray_GetCount(This->psia_selection, &item_count);
403 if(FAILED(hr) || !item_count)
404 return;
406 names = HeapAlloc(GetProcessHeap(), 0, item_count*sizeof(LPWSTR));
408 /* Get names of the selected items */
409 valid_count = 0; len_total = 0;
410 for(i = 0; i < item_count; i++)
412 hr = IShellItemArray_GetItemAt(This->psia_selection, i, &psi);
413 if(SUCCEEDED(hr))
415 UINT attr;
417 hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &attr);
418 if(SUCCEEDED(hr) &&
419 (( (This->options & FOS_PICKFOLDERS) && !(attr & SFGAO_FOLDER)) ||
420 (!(This->options & FOS_PICKFOLDERS) && (attr & SFGAO_FOLDER))))
421 continue;
423 hr = IShellItem_GetDisplayName(psi, (This->options & FOS_PICKFOLDERS) ? SIGDN_FILESYSPATH : SIGDN_PARENTRELATIVEPARSING, &names[valid_count]);
424 if(SUCCEEDED(hr))
426 len_total += lstrlenW(names[valid_count]) + 3;
427 valid_count++;
429 IShellItem_Release(psi);
433 if(valid_count == 1)
435 set_file_name(This, names[0]);
436 CoTaskMemFree(names[0]);
438 else if(valid_count > 1)
440 LPWSTR string = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len_total);
441 LPWSTR cur_point = string;
443 for(i = 0; i < valid_count; i++)
445 LPWSTR file = names[i];
446 *cur_point++ = '\"';
447 lstrcpyW(cur_point, file);
448 cur_point += lstrlenW(file);
449 *cur_point++ = '\"';
450 *cur_point++ = ' ';
451 CoTaskMemFree(file);
453 *(cur_point-1) = '\0';
455 set_file_name(This, string);
456 HeapFree(GetProcessHeap(), 0, string);
459 HeapFree(GetProcessHeap(), 0, names);
460 return;
463 static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec)
465 WCHAR *endpos, *ext;
467 lstrcpyW(buf, spec);
468 if( (endpos = StrChrW(buf, ';')) )
469 *endpos = '\0';
471 ext = PathFindExtensionW(buf);
472 if(StrChrW(ext, '*'))
473 return NULL;
475 return ext;
478 static BOOL shell_item_exists(IShellItem* shellitem)
480 LPWSTR filename;
481 HRESULT hr;
482 BOOL result;
484 hr = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &filename);
485 if (SUCCEEDED(hr))
487 /* FIXME: Implement SFGAO_VALIDATE in Wine and use it instead. */
488 result = (GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES);
489 CoTaskMemFree(filename);
491 else
493 SFGAOF attributes;
494 result = SUCCEEDED(IShellItem_GetAttributes(shellitem, SFGAO_VALIDATE, &attributes));
497 return result;
500 static HRESULT on_default_action(FileDialogImpl *This)
502 IShellFolder *psf_parent, *psf_desktop;
503 LPITEMIDLIST *pidla;
504 LPITEMIDLIST current_folder;
505 LPWSTR fn_iter, files = NULL, tmp_files;
506 UINT file_count = 0, len, i;
507 int open_action;
508 HRESULT hr, ret = E_FAIL;
510 len = get_file_name(This, &tmp_files);
511 if(len)
513 UINT size_used;
514 file_count = COMDLG32_SplitFileNames(tmp_files, len, &files, &size_used);
515 CoTaskMemFree(tmp_files);
517 if(!file_count) return E_FAIL;
519 hr = SHGetIDListFromObject((IUnknown*)This->psi_folder, &current_folder);
520 if(FAILED(hr))
522 ERR("Failed to get pidl for current directory.\n");
523 HeapFree(GetProcessHeap(), 0, files);
524 return hr;
527 TRACE("Acting on %d file(s).\n", file_count);
529 pidla = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST) * file_count);
530 open_action = ONOPEN_OPEN;
531 fn_iter = files;
533 for(i = 0; i < file_count && open_action == ONOPEN_OPEN; i++)
535 WCHAR canon_filename[MAX_PATH];
536 psf_parent = NULL;
538 COMDLG32_GetCanonicalPath(current_folder, fn_iter, canon_filename);
540 if( (This->options & FOS_NOVALIDATE) &&
541 !(This->options & FOS_FILEMUSTEXIST) )
542 open_action = ONOPEN_OPEN;
543 else
544 open_action = ONOPEN_BROWSE;
546 open_action = FILEDLG95_ValidatePathAction(canon_filename, &psf_parent, This->dlg_hwnd,
547 This->options & ~FOS_FILEMUSTEXIST,
548 (This->dlg_type == ITEMDLG_TYPE_SAVE),
549 open_action);
551 /* Add the proper extension */
552 if(open_action == ONOPEN_OPEN)
554 if(This->dlg_type == ITEMDLG_TYPE_SAVE)
556 WCHAR extbuf[MAX_PATH], *newext = NULL;
558 if(This->filterspec_count)
560 newext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
562 else if(This->default_ext)
564 lstrcpyW(extbuf, L".");
565 lstrcatW(extbuf, This->default_ext);
566 newext = extbuf;
569 if(newext)
571 WCHAR *ext = PathFindExtensionW(canon_filename);
572 if(lstrcmpW(ext, newext))
573 lstrcatW(canon_filename, newext);
576 else
578 if( !(This->options & FOS_NOVALIDATE) && (This->options & FOS_FILEMUSTEXIST) &&
579 !PathFileExistsW(canon_filename))
581 if(This->default_ext)
583 lstrcatW(canon_filename, L".");
584 lstrcatW(canon_filename, This->default_ext);
586 if(!PathFileExistsW(canon_filename))
588 FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
589 open_action = ONOPEN_BROWSE;
592 else
594 FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
595 open_action = ONOPEN_BROWSE;
601 pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename);
603 if(psf_parent && !(open_action == ONOPEN_BROWSE))
604 IShellFolder_Release(psf_parent);
606 fn_iter += (WCHAR)lstrlenW(fn_iter) + 1;
609 HeapFree(GetProcessHeap(), 0, files);
610 ILFree(current_folder);
612 if((This->options & FOS_PICKFOLDERS) && open_action == ONOPEN_BROWSE)
613 open_action = ONOPEN_OPEN; /* FIXME: Multiple folders? */
615 switch(open_action)
617 case ONOPEN_SEARCH:
618 FIXME("Filtering not implemented.\n");
619 break;
621 case ONOPEN_BROWSE:
622 hr = IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psf_parent, SBSP_DEFBROWSER);
623 if(FAILED(hr))
624 ERR("Failed to browse to directory: %08x\n", hr);
626 IShellFolder_Release(psf_parent);
627 break;
629 case ONOPEN_OPEN:
630 hr = SHGetDesktopFolder(&psf_desktop);
631 if(SUCCEEDED(hr))
633 if(This->psia_results)
635 IShellItemArray_Release(This->psia_results);
636 This->psia_results = NULL;
639 hr = SHCreateShellItemArray(NULL, psf_desktop, file_count, (PCUITEMID_CHILD_ARRAY)pidla,
640 &This->psia_results);
642 IShellFolder_Release(psf_desktop);
644 if(FAILED(hr))
645 break;
647 if(This->options & FOS_PICKFOLDERS)
649 SFGAOF attributes;
650 hr = IShellItemArray_GetAttributes(This->psia_results, SIATTRIBFLAGS_AND, SFGAO_FOLDER, &attributes);
651 if(hr != S_OK)
653 WCHAR buf[64];
654 LoadStringW(COMDLG32_hInstance, IDS_INVALID_FOLDERNAME, buf, ARRAY_SIZE(buf));
656 MessageBoxW(This->dlg_hwnd, buf, This->custom_title, MB_OK | MB_ICONEXCLAMATION);
658 IShellItemArray_Release(This->psia_results);
659 This->psia_results = NULL;
660 break;
664 if((This->options & FOS_OVERWRITEPROMPT) && This->dlg_type == ITEMDLG_TYPE_SAVE)
666 IShellItem *shellitem;
668 for (i=0; SUCCEEDED(hr) && i<file_count; i++)
670 hr = IShellItemArray_GetItemAt(This->psia_results, i, &shellitem);
671 if (SUCCEEDED(hr))
673 if (shell_item_exists(shellitem))
674 hr = events_OnOverwrite(This, shellitem);
676 IShellItem_Release(shellitem);
680 if (FAILED(hr))
681 break;
684 if(events_OnFileOk(This) == S_OK)
685 ret = S_OK;
687 break;
689 default:
690 ERR("Failed.\n");
691 break;
694 /* Clean up */
695 for(i = 0; i < file_count; i++)
696 ILFree(pidla[i]);
697 HeapFree(GetProcessHeap(), 0, pidla);
699 /* Success closes the dialog */
700 return ret;
703 static void show_opendropdown(FileDialogImpl *This)
705 HWND open_hwnd;
706 RECT open_rc;
707 MSG msg;
709 open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
711 GetWindowRect(open_hwnd, &open_rc);
713 if (TrackPopupMenu(This->hmenu_opendropdown, 0, open_rc.left, open_rc.bottom, 0, This->dlg_hwnd, NULL) &&
714 PeekMessageW(&msg, This->dlg_hwnd, WM_MENUCOMMAND, WM_MENUCOMMAND, PM_REMOVE))
716 MENUITEMINFOW mii;
718 This->opendropdown_has_selection = TRUE;
720 mii.cbSize = sizeof(mii);
721 mii.fMask = MIIM_ID;
722 GetMenuItemInfoW((HMENU)msg.lParam, msg.wParam, TRUE, &mii);
723 This->opendropdown_selection = mii.wID;
725 if(SUCCEEDED(on_default_action(This)))
726 EndDialog(This->dlg_hwnd, S_OK);
727 else
728 This->opendropdown_has_selection = FALSE;
732 /**************************************************************************
733 * Control item functions.
736 static void item_free(cctrl_item *item)
738 DestroyWindow(item->hwnd);
739 HeapFree(GetProcessHeap(), 0, item->label);
740 HeapFree(GetProcessHeap(), 0, item);
743 static cctrl_item* get_item(customctrl* parent, DWORD itemid, CDCONTROLSTATEF visible_flags, DWORD* position)
745 DWORD dummy;
746 cctrl_item* item;
748 if (!position) position = &dummy;
750 *position = 0;
752 LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
754 if (item->id == itemid)
755 return item;
757 if ((item->cdcstate & visible_flags) == visible_flags)
758 (*position)++;
761 return NULL;
764 static cctrl_item* get_first_item(customctrl* parent)
766 cctrl_item* item;
768 LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
770 if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
771 return item;
774 return NULL;
777 static HRESULT add_item(customctrl* parent, DWORD itemid, LPCWSTR label, cctrl_item** result)
779 cctrl_item* item;
780 LPWSTR label_copy;
782 if (get_item(parent, itemid, 0, NULL))
783 return E_INVALIDARG;
785 item = HeapAlloc(GetProcessHeap(), 0, sizeof(*item));
786 label_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(label)+1)*sizeof(WCHAR));
788 if (!item || !label_copy)
790 HeapFree(GetProcessHeap(), 0, item);
791 HeapFree(GetProcessHeap(), 0, label_copy);
792 return E_OUTOFMEMORY;
795 item->id = itemid;
796 item->parent_id = parent->id;
797 lstrcpyW(label_copy, label);
798 item->label = label_copy;
799 item->cdcstate = CDCS_VISIBLE|CDCS_ENABLED;
800 item->hwnd = NULL;
801 list_add_tail(&parent->sub_items, &item->entry);
803 *result = item;
805 return S_OK;
808 /**************************************************************************
809 * Control functions.
811 static inline customctrl *get_cctrl_from_dlgid(FileDialogImpl *This, DWORD dlgid)
813 customctrl *ctrl, *sub_ctrl;
815 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
817 if(ctrl->dlgid == dlgid)
818 return ctrl;
820 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
821 if(sub_ctrl->dlgid == dlgid)
822 return sub_ctrl;
825 ERR("Failed to find control with dialog id %d\n", dlgid);
826 return NULL;
829 static inline customctrl *get_cctrl(FileDialogImpl *This, DWORD ctlid)
831 customctrl *ctrl, *sub_ctrl;
833 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
835 if(ctrl->id == ctlid)
836 return ctrl;
838 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
839 if(sub_ctrl->id == ctlid)
840 return sub_ctrl;
843 if (This->hmenu_opendropdown && This->cctrl_opendropdown.id == ctlid)
844 return &This->cctrl_opendropdown;
846 TRACE("No existing control with control id %d\n", ctlid);
847 return NULL;
850 static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multiline)
852 LPWSTR text;
853 UINT len, final_width;
854 UINT lines, final_height;
855 SIZE size;
856 RECT rc;
857 HDC hdc;
858 WCHAR *c;
859 HFONT font;
861 TRACE("\n");
863 len = SendMessageW(hctrl, WM_GETTEXTLENGTH, 0, 0);
864 text = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
865 if(!text) return;
866 SendMessageW(hctrl, WM_GETTEXT, len+1, (LPARAM)text);
868 hdc = GetDC(hctrl);
869 font = (HFONT)SendMessageW(hctrl, WM_GETFONT, 0, 0);
870 font = SelectObject(hdc, font);
871 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &size);
872 SelectObject(hdc, font);
873 ReleaseDC(hctrl, hdc);
875 if(len && multiline)
877 /* FIXME: line-wrap */
878 for(lines = 1, c = text; *c != '\0'; c++)
879 if(*c == '\n') lines++;
881 final_height = size.cy*lines + 2*4;
883 else
885 GetWindowRect(hctrl, &rc);
886 final_height = rc.bottom - rc.top;
889 final_width = min(max(size.cx, min_width) + 4, max_width);
890 SetWindowPos(hctrl, NULL, 0, 0, final_width, final_height,
891 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
893 HeapFree(GetProcessHeap(), 0, text);
896 static UINT ctrl_get_height(customctrl *ctrl) {
897 RECT rc;
898 GetWindowRect(ctrl->wrapper_hwnd, &rc);
899 return rc.bottom - rc.top;
902 static void ctrl_free(customctrl *ctrl)
904 customctrl *sub_cur1, *sub_cur2;
905 cctrl_item *item_cur1, *item_cur2;
907 TRACE("Freeing control %p\n", ctrl);
908 if(ctrl->type == IDLG_CCTRL_MENU)
910 TBBUTTON tbb;
911 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
912 DestroyMenu((HMENU)tbb.dwData);
915 LIST_FOR_EACH_ENTRY_SAFE(sub_cur1, sub_cur2, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
917 list_remove(&sub_cur1->sub_cctrls_entry);
918 ctrl_free(sub_cur1);
921 LIST_FOR_EACH_ENTRY_SAFE(item_cur1, item_cur2, &ctrl->sub_items, cctrl_item, entry)
923 list_remove(&item_cur1->entry);
924 item_free(item_cur1);
927 DestroyWindow(ctrl->hwnd);
928 HeapFree(GetProcessHeap(), 0, ctrl);
931 static void customctrl_resize(FileDialogImpl *This, customctrl *ctrl)
933 RECT rc;
934 UINT total_height;
935 UINT max_width, size;
936 customctrl *sub_ctrl;
938 switch(ctrl->type)
940 case IDLG_CCTRL_PUSHBUTTON:
941 case IDLG_CCTRL_COMBOBOX:
942 case IDLG_CCTRL_CHECKBUTTON:
943 case IDLG_CCTRL_TEXT:
944 size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
945 ctrl_resize(ctrl->hwnd, size, size, TRUE);
946 GetWindowRect(ctrl->hwnd, &rc);
947 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
948 SWP_NOZORDER|SWP_NOMOVE);
949 break;
950 case IDLG_CCTRL_VISUALGROUP:
951 total_height = 0;
952 ctrl_resize(ctrl->hwnd, 0, This->cctrl_indent, TRUE);
954 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
956 customctrl_resize(This, sub_ctrl);
957 SetWindowPos(sub_ctrl->wrapper_hwnd, NULL, This->cctrl_indent, total_height, 0, 0,
958 SWP_NOZORDER|SWP_NOSIZE);
960 total_height += ctrl_get_height(sub_ctrl);
963 /* The label should be right adjusted */
965 UINT width, height;
967 GetWindowRect(ctrl->hwnd, &rc);
968 width = rc.right - rc.left;
969 height = rc.bottom - rc.top;
971 SetWindowPos(ctrl->hwnd, NULL, This->cctrl_indent - width, 0, width, height, SWP_NOZORDER);
974 /* Resize the wrapper window to fit all the sub controls */
975 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, This->cctrl_width + This->cctrl_indent, total_height,
976 SWP_NOZORDER|SWP_NOMOVE);
977 break;
978 case IDLG_CCTRL_RADIOBUTTONLIST:
980 cctrl_item* item;
982 total_height = 0;
983 max_width = 0;
985 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
987 size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
988 ctrl_resize(item->hwnd, size, size, TRUE);
989 SetWindowPos(item->hwnd, NULL, 0, total_height, 0, 0,
990 SWP_NOZORDER|SWP_NOSIZE);
992 GetWindowRect(item->hwnd, &rc);
994 total_height += rc.bottom - rc.top;
995 max_width = max(rc.right - rc.left, max_width);
998 SetWindowPos(ctrl->hwnd, NULL, 0, 0, max_width, total_height,
999 SWP_NOZORDER|SWP_NOMOVE);
1001 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, max_width, total_height,
1002 SWP_NOZORDER|SWP_NOMOVE);
1004 break;
1006 case IDLG_CCTRL_EDITBOX:
1007 case IDLG_CCTRL_SEPARATOR:
1008 case IDLG_CCTRL_MENU:
1009 case IDLG_CCTRL_OPENDROPDOWN:
1010 /* Nothing */
1011 break;
1015 static LRESULT notifysink_on_create(HWND hwnd, CREATESTRUCTW *crs)
1017 FileDialogImpl *This = crs->lpCreateParams;
1018 TRACE("%p\n", This);
1020 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1021 return TRUE;
1024 static LRESULT notifysink_on_bn_clicked(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
1026 customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
1028 TRACE("%p, %lx\n", This, wparam);
1030 if(ctrl)
1032 if(ctrl->type == IDLG_CCTRL_CHECKBUTTON)
1034 BOOL checked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
1035 cctrl_event_OnCheckButtonToggled(This, ctrl->id, checked);
1037 else
1038 cctrl_event_OnButtonClicked(This, ctrl->id);
1041 return TRUE;
1044 static LRESULT notifysink_on_cbn_selchange(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
1046 customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
1047 TRACE("%p, %p (%lx)\n", This, ctrl, wparam);
1049 if(ctrl)
1051 UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
1052 UINT selid = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
1054 cctrl_event_OnItemSelected(This, ctrl->id, selid);
1056 return TRUE;
1059 static LRESULT notifysink_on_tvn_dropdown(FileDialogImpl *This, LPARAM lparam)
1061 NMTOOLBARW *nmtb = (NMTOOLBARW*)lparam;
1062 customctrl *ctrl = get_cctrl_from_dlgid(This, GetDlgCtrlID(nmtb->hdr.hwndFrom));
1063 POINT pt = { 0, nmtb->rcButton.bottom };
1064 TBBUTTON tbb;
1065 UINT idcmd;
1067 TRACE("%p, %p (%lx)\n", This, ctrl, lparam);
1069 if(ctrl)
1071 cctrl_event_OnControlActivating(This,ctrl->id);
1073 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
1074 ClientToScreen(ctrl->hwnd, &pt);
1075 idcmd = TrackPopupMenu((HMENU)tbb.dwData, TPM_RETURNCMD, pt.x, pt.y, 0, This->dlg_hwnd, NULL);
1076 if(idcmd)
1077 cctrl_event_OnItemSelected(This, ctrl->id, idcmd);
1080 return TBDDRET_DEFAULT;
1083 static LRESULT notifysink_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1085 switch(HIWORD(wparam))
1087 case BN_CLICKED: return notifysink_on_bn_clicked(This, hwnd, wparam);
1088 case CBN_SELCHANGE: return notifysink_on_cbn_selchange(This, hwnd, wparam);
1091 return FALSE;
1094 static LRESULT notifysink_on_wm_notify(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1096 NMHDR *nmhdr = (NMHDR*)lparam;
1098 switch(nmhdr->code)
1100 case TBN_DROPDOWN: return notifysink_on_tvn_dropdown(This, lparam);
1103 return FALSE;
1106 static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
1108 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1109 customctrl *ctrl;
1110 HWND hwnd_child;
1111 RECT rc;
1113 switch(message)
1115 case WM_NCCREATE: return notifysink_on_create(hwnd, (CREATESTRUCTW*)lparam);
1116 case WM_COMMAND: return notifysink_on_wm_command(This, hwnd, wparam, lparam);
1117 case WM_NOTIFY: return notifysink_on_wm_notify(This, hwnd, wparam, lparam);
1118 case WM_SIZE:
1119 hwnd_child = GetPropW(hwnd, L"nfs_child");
1120 ctrl = (customctrl*)GetWindowLongPtrW(hwnd_child, GWLP_USERDATA);
1121 if(ctrl && ctrl->type != IDLG_CCTRL_VISUALGROUP)
1123 GetClientRect(hwnd, &rc);
1124 SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
1126 return TRUE;
1129 return DefWindowProcW(hwnd, message, wparam, lparam);
1132 static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
1133 LPCWSTR text, LPCWSTR wndclass, DWORD ctrl_wsflags,
1134 DWORD ctrl_exflags, UINT height, customctrl **ppctrl)
1136 HWND ns_hwnd, control_hwnd, parent_hwnd;
1137 DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
1138 customctrl *ctrl;
1140 if(get_cctrl(This, id))
1141 return E_UNEXPECTED; /* Duplicate id */
1143 if(This->cctrl_active_vg)
1144 parent_hwnd = This->cctrl_active_vg->wrapper_hwnd;
1145 else
1146 parent_hwnd = This->cctrls_hwnd;
1148 ns_hwnd = CreateWindowExW(0, L"FloatNotifySink", NULL, wsflags,
1149 0, 0, This->cctrl_width, height, parent_hwnd,
1150 (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
1151 control_hwnd = CreateWindowExW(ctrl_exflags, wndclass, text, wsflags | ctrl_wsflags,
1152 0, 0, This->cctrl_width, height, ns_hwnd,
1153 (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, 0);
1155 if(!ns_hwnd || !control_hwnd)
1157 ERR("Failed to create wrapper (%p) or control (%p)\n", ns_hwnd, control_hwnd);
1158 DestroyWindow(ns_hwnd);
1159 DestroyWindow(control_hwnd);
1161 return E_FAIL;
1164 SetPropW(ns_hwnd, L"nfs_child", control_hwnd);
1166 ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(customctrl));
1167 if(!ctrl)
1168 return E_OUTOFMEMORY;
1170 ctrl->hwnd = control_hwnd;
1171 ctrl->wrapper_hwnd = ns_hwnd;
1172 ctrl->id = id;
1173 ctrl->dlgid = This->cctrl_next_dlgid;
1174 ctrl->cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
1175 list_init(&ctrl->sub_cctrls);
1176 list_init(&ctrl->sub_items);
1178 if(This->cctrl_active_vg)
1179 list_add_tail(&This->cctrl_active_vg->sub_cctrls, &ctrl->sub_cctrls_entry);
1180 else
1181 list_add_tail(&This->cctrls, &ctrl->entry);
1183 SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)ctrl);
1185 if(ppctrl) *ppctrl = ctrl;
1187 This->cctrl_next_dlgid++;
1188 return S_OK;
1191 /**************************************************************************
1192 * Container functions.
1194 static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
1196 UINT container_height;
1197 UINT column_width;
1198 UINT nr_of_cols;
1199 UINT max_control_height, total_height = 0;
1200 UINT cur_col_pos, cur_row_pos;
1201 customctrl *ctrl;
1202 BOOL fits_height;
1203 UINT cspacing = MulDiv(90, This->dpi_x, USER_DEFAULT_SCREEN_DPI); /* Columns are spaced with 90px */
1204 UINT rspacing = MulDiv(4, This->dpi_y, USER_DEFAULT_SCREEN_DPI); /* Rows are spaced with 4 px. */
1206 /* Given the new width of the container, this function determines the
1207 * needed height of the container and places the controls according to
1208 * the new layout. Returns the new height.
1211 TRACE("%p\n", This);
1213 column_width = This->cctrl_width + cspacing;
1214 nr_of_cols = (container_width - This->cctrl_indent + cspacing) / column_width;
1216 /* We don't need to do anything unless the number of visible columns has changed. */
1217 if(nr_of_cols == This->cctrls_cols)
1219 RECT rc;
1220 GetWindowRect(This->cctrls_hwnd, &rc);
1221 return rc.bottom - rc.top;
1224 This->cctrls_cols = nr_of_cols;
1226 /* Get the size of the tallest control, and the total size of
1227 * all the controls to figure out the number of slots we need.
1229 max_control_height = 0;
1230 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1232 if(ctrl->cdcstate & CDCS_VISIBLE)
1234 UINT control_height = ctrl_get_height(ctrl);
1235 max_control_height = max(max_control_height, control_height);
1237 total_height += control_height + rspacing;
1241 if(!total_height)
1242 return 0;
1244 container_height = max(total_height / nr_of_cols, max_control_height + rspacing);
1245 TRACE("Guess: container_height: %d\n",container_height);
1247 /* Incrementally increase container_height until all the controls
1248 * fit.
1250 do {
1251 UINT columns_needed = 1;
1252 cur_row_pos = 0;
1254 fits_height = TRUE;
1255 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1257 if(ctrl->cdcstate & CDCS_VISIBLE)
1259 UINT control_height = ctrl_get_height(ctrl);
1261 if(cur_row_pos + control_height > container_height)
1263 if(++columns_needed > nr_of_cols)
1265 container_height += 1;
1266 fits_height = FALSE;
1267 break;
1269 cur_row_pos = 0;
1272 cur_row_pos += control_height + rspacing;
1275 } while(!fits_height);
1277 TRACE("Final container height: %d\n", container_height);
1279 /* Move the controls to their final destination
1281 cur_col_pos = 0; cur_row_pos = 0;
1282 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1284 if(ctrl->cdcstate & CDCS_VISIBLE)
1286 RECT rc;
1287 UINT control_height, control_indent;
1288 GetWindowRect(ctrl->wrapper_hwnd, &rc);
1289 control_height = rc.bottom - rc.top;
1291 if(cur_row_pos + control_height > container_height)
1293 cur_row_pos = 0;
1294 cur_col_pos += This->cctrl_width + cspacing;
1298 if(ctrl->type == IDLG_CCTRL_VISUALGROUP)
1299 control_indent = 0;
1300 else
1301 control_indent = This->cctrl_indent;
1303 SetWindowPos(ctrl->wrapper_hwnd, NULL, cur_col_pos + control_indent, cur_row_pos, 0, 0,
1304 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
1306 cur_row_pos += control_height + rspacing;
1310 /* Sanity check */
1311 if(cur_row_pos + This->cctrl_width > container_width)
1312 ERR("-- Failed to place controls properly.\n");
1314 return container_height;
1317 static void ctrl_set_font(customctrl *ctrl, HFONT font)
1319 customctrl *sub_ctrl;
1320 cctrl_item* item;
1322 SendMessageW(ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1324 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
1326 ctrl_set_font(sub_ctrl, font);
1329 if (ctrl->type == IDLG_CCTRL_RADIOBUTTONLIST)
1331 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
1333 SendMessageW(item->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1338 static void ctrl_container_reparent(FileDialogImpl *This, HWND parent)
1340 LONG wndstyle;
1342 if(parent)
1344 customctrl *ctrl;
1345 HFONT font;
1347 wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1348 wndstyle &= ~(WS_POPUP);
1349 wndstyle |= WS_CHILD;
1350 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1352 SetParent(This->cctrls_hwnd, parent);
1353 ShowWindow(This->cctrls_hwnd, TRUE);
1355 /* Set the fonts to match the dialog font. */
1356 font = (HFONT)SendMessageW(parent, WM_GETFONT, 0, 0);
1357 if(!font)
1358 ERR("Failed to get font handle from dialog.\n");
1360 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1362 if(font) ctrl_set_font(ctrl, font);
1363 customctrl_resize(This, ctrl);
1366 else
1368 ShowWindow(This->cctrls_hwnd, FALSE);
1370 wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1371 wndstyle &= ~(WS_CHILD);
1372 wndstyle |= WS_POPUP;
1373 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1375 SetParent(This->cctrls_hwnd, NULL);
1379 static LRESULT ctrl_container_on_create(HWND hwnd, CREATESTRUCTW *crs)
1381 FileDialogImpl *This = crs->lpCreateParams;
1382 TRACE("%p\n", This);
1384 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1385 return TRUE;
1388 static LRESULT ctrl_container_on_wm_destroy(FileDialogImpl *This)
1390 customctrl *cur1, *cur2;
1391 TRACE("%p\n", This);
1393 LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->cctrls, customctrl, entry)
1395 list_remove(&cur1->entry);
1396 ctrl_free(cur1);
1399 return TRUE;
1402 static LRESULT CALLBACK ctrl_container_wndproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1404 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1406 switch(umessage)
1408 case WM_NCCREATE: return ctrl_container_on_create(hwnd, (CREATESTRUCTW*)lparam);
1409 case WM_DESTROY: return ctrl_container_on_wm_destroy(This);
1410 default: return DefWindowProcW(hwnd, umessage, wparam, lparam);
1413 return FALSE;
1416 static void radiobuttonlist_set_selected_item(FileDialogImpl *This, customctrl *ctrl, cctrl_item *item)
1418 cctrl_item *cursor;
1420 LIST_FOR_EACH_ENTRY(cursor, &ctrl->sub_items, cctrl_item, entry)
1422 SendMessageW(cursor->hwnd, BM_SETCHECK, (cursor == item) ? BST_CHECKED : BST_UNCHECKED, 0);
1426 static LRESULT radiobuttonlist_on_bn_clicked(FileDialogImpl *This, HWND hwnd, HWND child)
1428 DWORD ctrl_id = (DWORD)GetWindowLongPtrW(hwnd, GWLP_ID);
1429 customctrl *ctrl;
1430 cctrl_item *item;
1431 BOOL found_item=FALSE;
1433 ctrl = get_cctrl_from_dlgid(This, ctrl_id);
1435 if (!ctrl)
1437 ERR("Can't find this control\n");
1438 return 0;
1441 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
1443 if (item->hwnd == child)
1445 found_item = TRUE;
1446 break;
1450 if (!found_item)
1452 ERR("Can't find control item\n");
1453 return 0;
1456 radiobuttonlist_set_selected_item(This, ctrl, item);
1458 cctrl_event_OnItemSelected(This, ctrl->id, item->id);
1460 return 0;
1463 static LRESULT radiobuttonlist_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1465 switch(HIWORD(wparam))
1467 case BN_CLICKED: return radiobuttonlist_on_bn_clicked(This, hwnd, (HWND)lparam);
1470 return FALSE;
1473 static LRESULT CALLBACK radiobuttonlist_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
1475 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1477 switch(message)
1479 case WM_COMMAND: return radiobuttonlist_on_wm_command(This, hwnd, wparam, lparam);
1482 return DefWindowProcW(hwnd, message, wparam, lparam);
1485 static HRESULT init_custom_controls(FileDialogImpl *This)
1487 WNDCLASSW wc;
1488 HDC hdc;
1489 static const WCHAR ctrl_container_classname[] = L"idlg_container_pane";
1491 InitCommonControlsEx(NULL);
1493 if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
1495 wc.style = CS_HREDRAW | CS_VREDRAW;
1496 wc.lpfnWndProc = ctrl_container_wndproc;
1497 wc.cbClsExtra = 0;
1498 wc.cbWndExtra = 0;
1499 wc.hInstance = COMDLG32_hInstance;
1500 wc.hIcon = 0;
1501 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1502 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1503 wc.lpszMenuName = NULL;
1504 wc.lpszClassName = ctrl_container_classname;
1506 if(!RegisterClassW(&wc)) return E_FAIL;
1509 This->cctrls_hwnd = CreateWindowExW(0, ctrl_container_classname, NULL,
1510 WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1511 0, 0, 0, 0, NULL, 0,
1512 COMDLG32_hInstance, This);
1513 if(!This->cctrls_hwnd)
1514 return E_FAIL;
1516 hdc = GetDC(This->cctrls_hwnd);
1517 This->dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
1518 This->dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
1519 ReleaseDC(This->cctrls_hwnd, hdc);
1521 This->cctrl_width = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI); /* Controls have a fixed width */
1522 This->cctrl_indent = MulDiv(100, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1523 This->cctrl_def_height = MulDiv(23, This->dpi_y, USER_DEFAULT_SCREEN_DPI);
1524 This->cctrls_cols = 0;
1526 This->cctrl_next_dlgid = 0x2000;
1527 list_init(&This->cctrls);
1528 This->cctrl_active_vg = NULL;
1530 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, WS_TABSTOP);
1532 /* Register class for */
1533 if (!GetClassInfoW(COMDLG32_hInstance, L"FloatNotifySink", &wc) ||
1534 wc.hInstance != COMDLG32_hInstance)
1536 wc.style = CS_HREDRAW | CS_VREDRAW;
1537 wc.lpfnWndProc = notifysink_proc;
1538 wc.cbClsExtra = 0;
1539 wc.cbWndExtra = 0;
1540 wc.hInstance = COMDLG32_hInstance;
1541 wc.hIcon = 0;
1542 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1543 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1544 wc.lpszMenuName = NULL;
1545 wc.lpszClassName = L"FloatNotifySink";
1547 if (!RegisterClassW(&wc))
1548 ERR("Failed to register FloatNotifySink window class.\n");
1551 if (!GetClassInfoW(COMDLG32_hInstance, L"RadioButtonList", &wc) ||
1552 wc.hInstance != COMDLG32_hInstance)
1554 wc.style = CS_HREDRAW | CS_VREDRAW;
1555 wc.lpfnWndProc = radiobuttonlist_proc;
1556 wc.cbClsExtra = 0;
1557 wc.cbWndExtra = 0;
1558 wc.hInstance = COMDLG32_hInstance;
1559 wc.hIcon = 0;
1560 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1561 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1562 wc.lpszMenuName = NULL;
1563 wc.lpszClassName = L"RadioButtonList";
1565 if (!RegisterClassW(&wc))
1566 ERR("Failed to register RadioButtonList window class.\n");
1569 return S_OK;
1572 /**************************************************************************
1573 * Window related functions.
1575 static BOOL update_open_dropdown(FileDialogImpl *This)
1577 /* Show or hide the open dropdown button as appropriate */
1578 BOOL show=FALSE, showing;
1579 HWND open_hwnd, dropdown_hwnd;
1581 if (This->hmenu_opendropdown)
1583 INT num_visible_items=0;
1584 cctrl_item* item;
1586 LIST_FOR_EACH_ENTRY(item, &This->cctrl_opendropdown.sub_items, cctrl_item, entry)
1588 if (item->cdcstate & CDCS_VISIBLE)
1590 num_visible_items++;
1591 if (num_visible_items >= 2)
1593 show = TRUE;
1594 break;
1600 open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1601 dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
1603 showing = (GetWindowLongPtrW(dropdown_hwnd, GWL_STYLE) & WS_VISIBLE) != 0;
1605 if (showing != show)
1607 RECT open_rc, dropdown_rc;
1609 GetWindowRect(open_hwnd, &open_rc);
1610 GetWindowRect(dropdown_hwnd, &dropdown_rc);
1612 if (show)
1614 ShowWindow(dropdown_hwnd, SW_SHOW);
1616 SetWindowPos(open_hwnd, NULL, 0, 0,
1617 (open_rc.right - open_rc.left) - (dropdown_rc.right - dropdown_rc.left),
1618 open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1620 else
1622 ShowWindow(dropdown_hwnd, SW_HIDE);
1624 SetWindowPos(open_hwnd, NULL, 0, 0,
1625 (open_rc.right - open_rc.left) + (dropdown_rc.right - dropdown_rc.left),
1626 open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1630 return show;
1633 static void update_layout(FileDialogImpl *This)
1635 HDWP hdwp;
1636 HWND hwnd;
1637 RECT dialog_rc;
1638 RECT cancel_rc, dropdown_rc, open_rc;
1639 RECT filetype_rc, filename_rc, filenamelabel_rc;
1640 RECT toolbar_rc, ebrowser_rc, customctrls_rc;
1641 static const UINT vspacing = 4, hspacing = 4;
1642 static const UINT min_width = 320, min_height = 200;
1643 BOOL show_dropdown;
1645 if (!GetClientRect(This->dlg_hwnd, &dialog_rc))
1647 TRACE("Invalid dialog window, not updating layout\n");
1648 return;
1651 if(dialog_rc.right < min_width || dialog_rc.bottom < min_height)
1653 TRACE("Dialog size (%d, %d) too small, not updating layout\n", dialog_rc.right, dialog_rc.bottom);
1654 return;
1657 /****
1658 * Calculate the size of the dialog and all the parts.
1661 /* Cancel button */
1662 hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL);
1663 if(hwnd)
1665 int cancel_width, cancel_height;
1666 GetWindowRect(hwnd, &cancel_rc);
1667 cancel_width = cancel_rc.right - cancel_rc.left;
1668 cancel_height = cancel_rc.bottom - cancel_rc.top;
1670 cancel_rc.left = dialog_rc.right - cancel_width - hspacing;
1671 cancel_rc.top = dialog_rc.bottom - cancel_height - vspacing;
1672 cancel_rc.right = cancel_rc.left + cancel_width;
1673 cancel_rc.bottom = cancel_rc.top + cancel_height;
1676 /* Open/Save dropdown */
1677 show_dropdown = update_open_dropdown(This);
1679 if(show_dropdown)
1681 int dropdown_width, dropdown_height;
1682 hwnd = GetDlgItem(This->dlg_hwnd, psh1);
1684 GetWindowRect(hwnd, &dropdown_rc);
1685 dropdown_width = dropdown_rc.right - dropdown_rc.left;
1686 dropdown_height = dropdown_rc.bottom - dropdown_rc.top;
1688 dropdown_rc.left = cancel_rc.left - dropdown_width - hspacing;
1689 dropdown_rc.top = cancel_rc.top;
1690 dropdown_rc.right = dropdown_rc.left + dropdown_width;
1691 dropdown_rc.bottom = dropdown_rc.top + dropdown_height;
1693 else
1695 dropdown_rc.left = dropdown_rc.right = cancel_rc.left - hspacing;
1696 dropdown_rc.top = cancel_rc.top;
1697 dropdown_rc.bottom = cancel_rc.bottom;
1700 /* Open/Save button */
1701 hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1702 if(hwnd)
1704 int open_width, open_height;
1705 GetWindowRect(hwnd, &open_rc);
1706 open_width = open_rc.right - open_rc.left;
1707 open_height = open_rc.bottom - open_rc.top;
1709 open_rc.left = dropdown_rc.left - open_width;
1710 open_rc.top = dropdown_rc.top;
1711 open_rc.right = open_rc.left + open_width;
1712 open_rc.bottom = open_rc.top + open_height;
1715 /* The filetype combobox. */
1716 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1717 if(hwnd)
1719 int filetype_width, filetype_height;
1720 GetWindowRect(hwnd, &filetype_rc);
1722 filetype_width = filetype_rc.right - filetype_rc.left;
1723 filetype_height = filetype_rc.bottom - filetype_rc.top;
1725 filetype_rc.right = cancel_rc.right;
1727 filetype_rc.left = filetype_rc.right - filetype_width;
1728 filetype_rc.top = cancel_rc.top - filetype_height - vspacing;
1729 filetype_rc.bottom = filetype_rc.top + filetype_height;
1731 if(!This->filterspec_count)
1732 filetype_rc.left = filetype_rc.right;
1735 /* Filename label. */
1736 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC);
1737 if(hwnd)
1739 int filetypelabel_width, filetypelabel_height;
1740 GetWindowRect(hwnd, &filenamelabel_rc);
1742 filetypelabel_width = filenamelabel_rc.right - filenamelabel_rc.left;
1743 filetypelabel_height = filenamelabel_rc.bottom - filenamelabel_rc.top;
1745 filenamelabel_rc.left = 160; /* FIXME */
1746 filenamelabel_rc.top = filetype_rc.top;
1747 filenamelabel_rc.right = filenamelabel_rc.left + filetypelabel_width;
1748 filenamelabel_rc.bottom = filenamelabel_rc.top + filetypelabel_height;
1751 /* Filename edit box. */
1752 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
1753 if(hwnd)
1755 int filename_width, filename_height;
1756 GetWindowRect(hwnd, &filename_rc);
1758 filename_width = filetype_rc.left - filenamelabel_rc.right - hspacing*2;
1759 filename_height = filename_rc.bottom - filename_rc.top;
1761 filename_rc.left = filenamelabel_rc.right + hspacing;
1762 filename_rc.top = filetype_rc.top;
1763 filename_rc.right = filename_rc.left + filename_width;
1764 filename_rc.bottom = filename_rc.top + filename_height;
1767 hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR);
1768 if(hwnd)
1770 GetWindowRect(hwnd, &toolbar_rc);
1771 MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2);
1774 /* The custom controls */
1775 customctrls_rc.left = dialog_rc.left + hspacing;
1776 customctrls_rc.right = dialog_rc.right - hspacing;
1777 customctrls_rc.bottom = filename_rc.top - vspacing;
1778 customctrls_rc.top = customctrls_rc.bottom -
1779 ctrl_container_resize(This, customctrls_rc.right - customctrls_rc.left);
1781 /* The ExplorerBrowser control. */
1782 ebrowser_rc.left = dialog_rc.left + hspacing;
1783 ebrowser_rc.top = toolbar_rc.bottom + vspacing;
1784 ebrowser_rc.right = dialog_rc.right - hspacing;
1785 ebrowser_rc.bottom = customctrls_rc.top - vspacing;
1787 /****
1788 * Move everything to the right place.
1791 /* FIXME: The Save Dialog uses a slightly different layout. */
1792 hdwp = BeginDeferWindowPos(7);
1794 if(hdwp && This->peb)
1795 IExplorerBrowser_SetRect(This->peb, &hdwp, ebrowser_rc);
1797 if(hdwp && This->cctrls_hwnd)
1798 DeferWindowPos(hdwp, This->cctrls_hwnd, NULL,
1799 customctrls_rc.left, customctrls_rc.top,
1800 customctrls_rc.right - customctrls_rc.left, customctrls_rc.bottom - customctrls_rc.top,
1801 SWP_NOZORDER | SWP_NOACTIVATE);
1803 /* The default controls */
1804 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE)) )
1805 DeferWindowPos(hdwp, hwnd, NULL, filetype_rc.left, filetype_rc.top, 0, 0,
1806 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1808 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
1809 DeferWindowPos(hdwp, hwnd, NULL, filename_rc.left, filename_rc.top,
1810 filename_rc.right - filename_rc.left, filename_rc.bottom - filename_rc.top,
1811 SWP_NOZORDER | SWP_NOACTIVATE);
1813 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)) )
1814 DeferWindowPos(hdwp, hwnd, NULL, filenamelabel_rc.left, filenamelabel_rc.top, 0, 0,
1815 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1817 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDOK)) )
1818 DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
1819 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1821 if(hdwp && This->hmenu_opendropdown && (hwnd = GetDlgItem(This->dlg_hwnd, psh1)))
1822 DeferWindowPos(hdwp, hwnd, NULL, dropdown_rc.left, dropdown_rc.top, 0, 0,
1823 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1825 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
1826 DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
1827 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1829 if(hdwp)
1830 EndDeferWindowPos(hdwp);
1831 else
1832 ERR("Failed to position dialog controls.\n");
1834 return;
1837 static HRESULT init_explorerbrowser(FileDialogImpl *This)
1839 IShellItem *psi_folder;
1840 IObjectWithSite *client;
1841 FOLDERSETTINGS fos;
1842 RECT rc = {0};
1843 HRESULT hr;
1845 /* Create ExplorerBrowser instance */
1846 OleInitialize(NULL);
1848 hr = CoCreateInstance(&CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER,
1849 &IID_IExplorerBrowser, (void**)&This->peb);
1850 if(FAILED(hr))
1852 ERR("Failed to instantiate ExplorerBrowser control.\n");
1853 return hr;
1856 IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES | EBO_NOBORDER);
1858 hr = IExplorerBrowser_Initialize(This->peb, This->dlg_hwnd, &rc, NULL);
1859 if(FAILED(hr))
1861 ERR("Failed to initialize the ExplorerBrowser control.\n");
1862 IExplorerBrowser_Release(This->peb);
1863 This->peb = NULL;
1864 return hr;
1866 hr = IExplorerBrowser_Advise(This->peb, &This->IExplorerBrowserEvents_iface, &This->ebevents_cookie);
1867 if(FAILED(hr))
1868 ERR("Advise (ExplorerBrowser) failed.\n");
1870 /* Get previous options? */
1871 fos.ViewMode = fos.fFlags = 0;
1872 if(!(This->options & FOS_ALLOWMULTISELECT))
1873 fos.fFlags |= FWF_SINGLESEL;
1875 IExplorerBrowser_SetFolderSettings(This->peb, &fos);
1877 hr = IExplorerBrowser_QueryInterface(This->peb, &IID_IObjectWithSite, (void**)&client);
1878 if (hr == S_OK)
1880 hr = IObjectWithSite_SetSite(client, (IUnknown*)&This->IFileDialog2_iface);
1881 IObjectWithSite_Release(client);
1882 if(FAILED(hr))
1883 ERR("SetSite failed, 0x%08x\n", hr);
1886 /* Browse somewhere */
1887 psi_folder = This->psi_setfolder ? This->psi_setfolder : This->psi_defaultfolder;
1888 IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psi_folder, SBSP_DEFBROWSER);
1890 return S_OK;
1893 static void init_toolbar(FileDialogImpl *This, HWND hwnd)
1895 HWND htoolbar;
1896 TBADDBITMAP tbab;
1897 TBBUTTON button[2];
1899 htoolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE,
1900 0, 0, 0, 0,
1901 hwnd, (HMENU)IDC_NAV_TOOLBAR, NULL, NULL);
1903 tbab.hInst = HINST_COMMCTRL;
1904 tbab.nID = IDB_HIST_LARGE_COLOR;
1905 SendMessageW(htoolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
1907 button[0].iBitmap = HIST_BACK;
1908 button[0].idCommand = IDC_NAVBACK;
1909 button[0].fsState = TBSTATE_ENABLED;
1910 button[0].fsStyle = BTNS_BUTTON;
1911 button[0].dwData = 0;
1912 button[0].iString = 0;
1914 button[1].iBitmap = HIST_FORWARD;
1915 button[1].idCommand = IDC_NAVFORWARD;
1916 button[1].fsState = TBSTATE_ENABLED;
1917 button[1].fsStyle = BTNS_BUTTON;
1918 button[1].dwData = 0;
1919 button[1].iString = 0;
1921 SendMessageW(htoolbar, TB_ADDBUTTONSW, 2, (LPARAM)button);
1922 SendMessageW(htoolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(24,24));
1923 SendMessageW(htoolbar, TB_AUTOSIZE, 0, 0);
1926 static void update_control_text(FileDialogImpl *This)
1928 HWND hitem;
1929 LPCWSTR custom_okbutton;
1930 cctrl_item* item;
1931 UINT min_width = MulDiv(50, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1932 UINT max_width = MulDiv(250, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1934 if(This->custom_title)
1935 SetWindowTextW(This->dlg_hwnd, This->custom_title);
1937 if(This->hmenu_opendropdown && (item = get_first_item(&This->cctrl_opendropdown)))
1938 custom_okbutton = item->label;
1939 else
1940 custom_okbutton = This->custom_okbutton;
1942 if(custom_okbutton &&
1943 (hitem = GetDlgItem(This->dlg_hwnd, IDOK)))
1945 SetWindowTextW(hitem, custom_okbutton);
1946 ctrl_resize(hitem, min_width, max_width, FALSE);
1949 if(This->custom_cancelbutton &&
1950 (hitem = GetDlgItem(This->dlg_hwnd, IDCANCEL)))
1952 SetWindowTextW(hitem, This->custom_cancelbutton);
1953 ctrl_resize(hitem, min_width, max_width, FALSE);
1956 if(This->custom_filenamelabel &&
1957 (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)))
1959 SetWindowTextW(hitem, This->custom_filenamelabel);
1960 ctrl_resize(hitem, min_width, max_width, FALSE);
1964 static LRESULT CALLBACK dropdown_subclass_proc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1966 if (umessage == WM_LBUTTONDOWN)
1968 FileDialogImpl *This = GetPropW(hwnd, L"itemdlg_This");
1970 SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
1971 show_opendropdown(This);
1972 SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
1974 return 0;
1977 return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"itemdlg_oldwndproc"), hwnd, umessage, wparam, lparam);
1980 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
1982 FileDialogImpl *This = (FileDialogImpl*)lParam;
1983 HWND hitem;
1985 TRACE("(%p, %p)\n", This, hwnd);
1987 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1988 This->dlg_hwnd = hwnd;
1990 hitem = GetDlgItem(This->dlg_hwnd, pshHelp);
1991 if(hitem) ShowWindow(hitem, SW_HIDE);
1993 hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPESTATIC);
1994 if(hitem) ShowWindow(hitem, SW_HIDE);
1996 /* Fill filetypes combobox, or hide it. */
1997 hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1998 if(This->filterspec_count)
2000 HDC hdc;
2001 HFONT font;
2002 SIZE size;
2003 UINT i, maxwidth = 0;
2005 hdc = GetDC(hitem);
2006 font = (HFONT)SendMessageW(hitem, WM_GETFONT, 0, 0);
2007 SelectObject(hdc, font);
2009 for(i = 0; i < This->filterspec_count; i++)
2011 SendMessageW(hitem, CB_ADDSTRING, 0, (LPARAM)This->filterspecs[i].pszName);
2013 if(GetTextExtentPoint32W(hdc, This->filterspecs[i].pszName, lstrlenW(This->filterspecs[i].pszName), &size))
2014 maxwidth = max(maxwidth, size.cx);
2016 ReleaseDC(hitem, hdc);
2018 if(maxwidth > 0)
2020 maxwidth += GetSystemMetrics(SM_CXVSCROLL) + 4;
2021 SendMessageW(hitem, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0);
2023 else
2024 ERR("Failed to calculate width of filetype dropdown\n");
2026 SendMessageW(hitem, CB_SETCURSEL, This->filetypeindex, 0);
2028 else
2029 ShowWindow(hitem, SW_HIDE);
2031 if(This->set_filename &&
2032 (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
2033 SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
2035 if(This->hmenu_opendropdown)
2037 HWND dropdown_hwnd;
2038 LOGFONTW lfw, lfw_marlett;
2039 HFONT dialog_font;
2041 dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
2043 /* Change dropdown button font to Marlett */
2044 dialog_font = (HFONT)SendMessageW(dropdown_hwnd, WM_GETFONT, 0, 0);
2046 GetObjectW(dialog_font, sizeof(lfw), &lfw);
2048 memset(&lfw_marlett, 0, sizeof(lfw_marlett));
2049 lstrcpyW(lfw_marlett.lfFaceName, L"Marlett");
2050 lfw_marlett.lfHeight = lfw.lfHeight;
2051 lfw_marlett.lfCharSet = SYMBOL_CHARSET;
2053 This->hfont_opendropdown = CreateFontIndirectW(&lfw_marlett);
2055 SendMessageW(dropdown_hwnd, WM_SETFONT, (LPARAM)This->hfont_opendropdown, 0);
2057 /* Subclass button so we can handle LBUTTONDOWN */
2058 SetPropW(dropdown_hwnd, L"itemdlg_This", This);
2059 SetPropW(dropdown_hwnd, L"itemdlg_oldwndproc",
2060 (HANDLE)SetWindowLongPtrW(dropdown_hwnd, GWLP_WNDPROC, (LONG_PTR)dropdown_subclass_proc));
2063 ctrl_container_reparent(This, This->dlg_hwnd);
2064 init_explorerbrowser(This);
2065 init_toolbar(This, hwnd);
2066 update_control_text(This);
2067 update_layout(This);
2069 if(This->filterspec_count)
2070 events_OnTypeChange(This);
2072 if ((hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)))
2073 SetFocus(hitem);
2075 return FALSE;
2078 static LRESULT on_wm_size(FileDialogImpl *This)
2080 update_layout(This);
2081 return FALSE;
2084 static LRESULT on_wm_getminmaxinfo(FileDialogImpl *This, LPARAM lparam)
2086 MINMAXINFO *mmi = (MINMAXINFO*)lparam;
2087 TRACE("%p (%p)\n", This, mmi);
2089 /* FIXME */
2090 mmi->ptMinTrackSize.x = 640;
2091 mmi->ptMinTrackSize.y = 480;
2093 return FALSE;
2096 static LRESULT on_wm_destroy(FileDialogImpl *This)
2098 TRACE("%p\n", This);
2100 if(This->peb)
2102 IExplorerBrowser_Destroy(This->peb);
2103 IExplorerBrowser_Release(This->peb);
2104 This->peb = NULL;
2107 ctrl_container_reparent(This, NULL);
2108 This->dlg_hwnd = NULL;
2110 DeleteObject(This->hfont_opendropdown);
2111 This->hfont_opendropdown = NULL;
2113 return TRUE;
2116 static LRESULT on_idok(FileDialogImpl *This)
2118 TRACE("%p\n", This);
2120 if(SUCCEEDED(on_default_action(This)))
2121 EndDialog(This->dlg_hwnd, S_OK);
2123 return FALSE;
2126 static LRESULT on_idcancel(FileDialogImpl *This)
2128 TRACE("%p\n", This);
2130 EndDialog(This->dlg_hwnd, HRESULT_FROM_WIN32(ERROR_CANCELLED));
2132 return FALSE;
2135 static LRESULT on_command_opendropdown(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2137 if(HIWORD(wparam) == BN_CLICKED)
2139 HWND hwnd = (HWND)lparam;
2140 SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
2141 show_opendropdown(This);
2142 SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
2145 return FALSE;
2148 static LRESULT on_browse_back(FileDialogImpl *This)
2150 TRACE("%p\n", This);
2151 IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEBACK);
2152 return FALSE;
2155 static LRESULT on_browse_forward(FileDialogImpl *This)
2157 TRACE("%p\n", This);
2158 IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEFORWARD);
2159 return FALSE;
2162 static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2164 if(HIWORD(wparam) == CBN_SELCHANGE)
2166 IShellView *psv;
2167 HRESULT hr;
2168 LPWSTR filename;
2169 UINT prev_index = This->filetypeindex;
2171 This->filetypeindex = SendMessageW((HWND)lparam, CB_GETCURSEL, 0, 0);
2172 TRACE("File type selection changed to %d.\n", This->filetypeindex);
2174 if(prev_index == This->filetypeindex)
2175 return FALSE;
2177 hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&psv);
2178 if(SUCCEEDED(hr))
2180 IShellView_Refresh(psv);
2181 IShellView_Release(psv);
2184 if(This->dlg_type == ITEMDLG_TYPE_SAVE && get_file_name(This, &filename))
2186 WCHAR buf[MAX_PATH], extbuf[MAX_PATH], *ext;
2188 ext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
2189 if(ext)
2191 lstrcpyW(buf, filename);
2193 if(PathMatchSpecW(buf, This->filterspecs[prev_index].pszSpec))
2194 PathRemoveExtensionW(buf);
2196 lstrcatW(buf, ext);
2197 set_file_name(This, buf);
2199 CoTaskMemFree(filename);
2202 /* The documentation claims that OnTypeChange is called only
2203 * when the dialog is opened, but this is obviously not the
2204 * case. */
2205 events_OnTypeChange(This);
2208 return FALSE;
2211 static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2213 switch(LOWORD(wparam))
2215 case IDOK: return on_idok(This);
2216 case IDCANCEL: return on_idcancel(This);
2217 case psh1: return on_command_opendropdown(This, wparam, lparam);
2218 case IDC_NAVBACK: return on_browse_back(This);
2219 case IDC_NAVFORWARD: return on_browse_forward(This);
2220 case IDC_FILETYPE: return on_command_filetype(This, wparam, lparam);
2221 default: TRACE("Unknown command.\n");
2223 return FALSE;
2226 static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
2228 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
2230 switch(umessage)
2232 case WM_INITDIALOG: return on_wm_initdialog(hwnd, lparam);
2233 case WM_COMMAND: return on_wm_command(This, wparam, lparam);
2234 case WM_SIZE: return on_wm_size(This);
2235 case WM_GETMINMAXINFO: return on_wm_getminmaxinfo(This, lparam);
2236 case WM_DESTROY: return on_wm_destroy(This);
2239 return FALSE;
2242 static HRESULT create_dialog(FileDialogImpl *This, HWND parent)
2244 INT_PTR res;
2246 SetLastError(0);
2247 res = DialogBoxParamW(COMDLG32_hInstance,
2248 MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
2249 parent, itemdlg_dlgproc, (LPARAM)This);
2250 This->dlg_hwnd = NULL;
2251 if(res == -1)
2253 ERR("Failed to show dialog (LastError: %d)\n", GetLastError());
2254 return E_FAIL;
2257 TRACE("Returning 0x%08x\n", (HRESULT)res);
2258 return (HRESULT)res;
2261 /**************************************************************************
2262 * IFileDialog implementation
2264 static inline FileDialogImpl *impl_from_IFileDialog2(IFileDialog2 *iface)
2266 return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialog2_iface);
2269 static HRESULT WINAPI IFileDialog2_fnQueryInterface(IFileDialog2 *iface,
2270 REFIID riid,
2271 void **ppvObject)
2273 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2274 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
2276 *ppvObject = NULL;
2277 if(IsEqualGUID(riid, &IID_IUnknown) ||
2278 IsEqualGUID(riid, &IID_IFileDialog) ||
2279 IsEqualGUID(riid, &IID_IFileDialog2))
2281 *ppvObject = iface;
2283 else if(IsEqualGUID(riid, &IID_IFileOpenDialog) && This->dlg_type == ITEMDLG_TYPE_OPEN)
2285 *ppvObject = &This->u.IFileOpenDialog_iface;
2287 else if(IsEqualGUID(riid, &IID_IFileSaveDialog) && This->dlg_type == ITEMDLG_TYPE_SAVE)
2289 *ppvObject = &This->u.IFileSaveDialog_iface;
2291 else if(IsEqualGUID(riid, &IID_IExplorerBrowserEvents))
2293 *ppvObject = &This->IExplorerBrowserEvents_iface;
2295 else if(IsEqualGUID(riid, &IID_IServiceProvider))
2297 *ppvObject = &This->IServiceProvider_iface;
2299 else if(IsEqualGUID(&IID_ICommDlgBrowser3, riid) ||
2300 IsEqualGUID(&IID_ICommDlgBrowser2, riid) ||
2301 IsEqualGUID(&IID_ICommDlgBrowser, riid))
2303 *ppvObject = &This->ICommDlgBrowser3_iface;
2305 else if(IsEqualGUID(&IID_IOleWindow, riid))
2307 *ppvObject = &This->IOleWindow_iface;
2309 else if(IsEqualGUID(riid, &IID_IFileDialogCustomize) ||
2310 IsEqualGUID(riid, &IID_IFileDialogCustomizeAlt))
2312 *ppvObject = &This->IFileDialogCustomize_iface;
2314 else
2315 FIXME("Unknown interface requested: %s.\n", debugstr_guid(riid));
2317 if(*ppvObject)
2319 IUnknown_AddRef((IUnknown*)*ppvObject);
2320 return S_OK;
2323 return E_NOINTERFACE;
2326 static ULONG WINAPI IFileDialog2_fnAddRef(IFileDialog2 *iface)
2328 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2329 LONG ref = InterlockedIncrement(&This->ref);
2330 TRACE("%p - ref %d\n", This, ref);
2332 return ref;
2335 static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface)
2337 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2338 LONG ref = InterlockedDecrement(&This->ref);
2339 TRACE("%p - ref %d\n", This, ref);
2341 if(!ref)
2343 UINT i;
2344 for(i = 0; i < This->filterspec_count; i++)
2346 LocalFree((void*)This->filterspecs[i].pszName);
2347 LocalFree((void*)This->filterspecs[i].pszSpec);
2349 HeapFree(GetProcessHeap(), 0, This->filterspecs);
2351 DestroyWindow(This->cctrls_hwnd);
2353 if(This->psi_defaultfolder) IShellItem_Release(This->psi_defaultfolder);
2354 if(This->psi_setfolder) IShellItem_Release(This->psi_setfolder);
2355 if(This->psi_folder) IShellItem_Release(This->psi_folder);
2356 if(This->psia_selection) IShellItemArray_Release(This->psia_selection);
2357 if(This->psia_results) IShellItemArray_Release(This->psia_results);
2359 LocalFree(This->set_filename);
2360 LocalFree(This->default_ext);
2361 LocalFree(This->custom_title);
2362 LocalFree(This->custom_okbutton);
2363 LocalFree(This->custom_cancelbutton);
2364 LocalFree(This->custom_filenamelabel);
2366 DestroyMenu(This->hmenu_opendropdown);
2367 DeleteObject(This->hfont_opendropdown);
2369 HeapFree(GetProcessHeap(), 0, This);
2372 return ref;
2375 static HRESULT WINAPI IFileDialog2_fnShow(IFileDialog2 *iface, HWND hwndOwner)
2377 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2378 TRACE("%p (%p)\n", iface, hwndOwner);
2380 This->opendropdown_has_selection = FALSE;
2382 return create_dialog(This, hwndOwner);
2385 static HRESULT WINAPI IFileDialog2_fnSetFileTypes(IFileDialog2 *iface, UINT cFileTypes,
2386 const COMDLG_FILTERSPEC *rgFilterSpec)
2388 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2389 UINT i;
2390 TRACE("%p (%d, %p)\n", This, cFileTypes, rgFilterSpec);
2392 if(!rgFilterSpec)
2393 return E_INVALIDARG;
2395 if(This->filterspecs)
2396 return E_UNEXPECTED;
2398 if(!cFileTypes)
2399 return S_OK;
2401 This->filterspecs = HeapAlloc(GetProcessHeap(), 0, sizeof(COMDLG_FILTERSPEC)*cFileTypes);
2402 for(i = 0; i < cFileTypes; i++)
2404 This->filterspecs[i].pszName = StrDupW(rgFilterSpec[i].pszName);
2405 This->filterspecs[i].pszSpec = StrDupW(rgFilterSpec[i].pszSpec);
2407 This->filterspec_count = cFileTypes;
2409 return S_OK;
2412 static HRESULT WINAPI IFileDialog2_fnSetFileTypeIndex(IFileDialog2 *iface, UINT iFileType)
2414 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2415 TRACE("%p (%d)\n", This, iFileType);
2417 if(!This->filterspecs)
2418 return E_FAIL;
2420 iFileType = max(iFileType, 1);
2421 iFileType = min(iFileType, This->filterspec_count);
2422 This->filetypeindex = iFileType-1;
2424 return S_OK;
2427 static HRESULT WINAPI IFileDialog2_fnGetFileTypeIndex(IFileDialog2 *iface, UINT *piFileType)
2429 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2430 TRACE("%p (%p)\n", This, piFileType);
2432 if(!piFileType)
2433 return E_INVALIDARG;
2435 if(This->filterspec_count == 0)
2436 *piFileType = 0;
2437 else
2438 *piFileType = This->filetypeindex + 1;
2440 return S_OK;
2443 static HRESULT WINAPI IFileDialog2_fnAdvise(IFileDialog2 *iface, IFileDialogEvents *pfde, DWORD *pdwCookie)
2445 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2446 events_client *client;
2447 TRACE("%p (%p, %p)\n", This, pfde, pdwCookie);
2449 if(!pfde || !pdwCookie)
2450 return E_INVALIDARG;
2452 client = HeapAlloc(GetProcessHeap(), 0, sizeof(events_client));
2453 client->pfde = pfde;
2454 client->cookie = ++This->events_next_cookie;
2456 IFileDialogEvents_AddRef(pfde);
2457 *pdwCookie = client->cookie;
2459 list_add_tail(&This->events_clients, &client->entry);
2461 return S_OK;
2464 static HRESULT WINAPI IFileDialog2_fnUnadvise(IFileDialog2 *iface, DWORD dwCookie)
2466 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2467 events_client *client, *found = NULL;
2468 TRACE("%p (%d)\n", This, dwCookie);
2470 LIST_FOR_EACH_ENTRY(client, &This->events_clients, events_client, entry)
2472 if(client->cookie == dwCookie)
2474 found = client;
2475 break;
2479 if(found)
2481 list_remove(&found->entry);
2482 IFileDialogEvents_Release(found->pfde);
2483 HeapFree(GetProcessHeap(), 0, found);
2484 return S_OK;
2487 return E_INVALIDARG;
2490 static HRESULT WINAPI IFileDialog2_fnSetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS fos)
2492 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2493 TRACE("%p (0x%x)\n", This, fos);
2495 if (fos & ~(FOS_OVERWRITEPROMPT | FOS_STRICTFILETYPES | FOS_NOCHANGEDIR | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM
2496 | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_ALLOWMULTISELECT | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST
2497 | FOS_CREATEPROMPT | FOS_SHAREAWARE | FOS_NOREADONLYRETURN | FOS_NOTESTFILECREATE | FOS_HIDEMRUPLACES
2498 | FOS_HIDEPINNEDPLACES | FOS_NODEREFERENCELINKS | FOS_DONTADDTORECENT | FOS_FORCESHOWHIDDEN
2499 | FOS_DEFAULTNOMINIMODE | FOS_FORCEPREVIEWPANEON | FOS_SUPPORTSTREAMABLEITEMS))
2501 WARN("Invalid option %#x\n", fos);
2502 return E_INVALIDARG;
2505 if( !(This->options & FOS_PICKFOLDERS) && (fos & FOS_PICKFOLDERS) )
2507 WCHAR buf[30];
2508 LoadStringW(COMDLG32_hInstance, IDS_SELECT_FOLDER, buf, ARRAY_SIZE(buf));
2509 IFileDialog2_SetTitle(iface, buf);
2512 This->options = fos;
2514 return S_OK;
2517 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
2519 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2520 TRACE("%p (%p)\n", This, pfos);
2522 if(!pfos)
2523 return E_INVALIDARG;
2525 *pfos = This->options;
2527 return S_OK;
2530 static HRESULT WINAPI IFileDialog2_fnSetDefaultFolder(IFileDialog2 *iface, IShellItem *psi)
2532 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2533 TRACE("%p (%p)\n", This, psi);
2534 if(This->psi_defaultfolder)
2535 IShellItem_Release(This->psi_defaultfolder);
2537 This->psi_defaultfolder = psi;
2539 if(This->psi_defaultfolder)
2540 IShellItem_AddRef(This->psi_defaultfolder);
2542 return S_OK;
2545 static HRESULT WINAPI IFileDialog2_fnSetFolder(IFileDialog2 *iface, IShellItem *psi)
2547 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2548 TRACE("%p (%p)\n", This, psi);
2549 if(This->psi_setfolder)
2550 IShellItem_Release(This->psi_setfolder);
2552 This->psi_setfolder = psi;
2554 if(This->psi_setfolder)
2555 IShellItem_AddRef(This->psi_setfolder);
2557 return S_OK;
2560 static HRESULT WINAPI IFileDialog2_fnGetFolder(IFileDialog2 *iface, IShellItem **ppsi)
2562 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2563 TRACE("%p (%p)\n", This, ppsi);
2564 if(!ppsi)
2565 return E_INVALIDARG;
2567 /* FIXME:
2568 If the dialog is shown, return the current(ly selected) folder. */
2570 *ppsi = NULL;
2571 if(This->psi_folder)
2572 *ppsi = This->psi_folder;
2573 else if(This->psi_setfolder)
2574 *ppsi = This->psi_setfolder;
2575 else if(This->psi_defaultfolder)
2576 *ppsi = This->psi_defaultfolder;
2578 if(*ppsi)
2580 IShellItem_AddRef(*ppsi);
2581 return S_OK;
2584 return E_FAIL;
2587 static HRESULT WINAPI IFileDialog2_fnGetCurrentSelection(IFileDialog2 *iface, IShellItem **ppsi)
2589 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2590 HRESULT hr;
2591 TRACE("%p (%p)\n", This, ppsi);
2593 if(!ppsi)
2594 return E_INVALIDARG;
2596 if(This->psia_selection)
2598 /* FIXME: Check filename edit box */
2599 hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
2600 return hr;
2603 return E_FAIL;
2606 static HRESULT WINAPI IFileDialog2_fnSetFileName(IFileDialog2 *iface, LPCWSTR pszName)
2608 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2609 TRACE("%p (%s)\n", iface, debugstr_w(pszName));
2611 set_file_name(This, pszName);
2613 return S_OK;
2616 static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *pszName)
2618 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2619 TRACE("%p (%p)\n", iface, pszName);
2621 if(!pszName)
2622 return E_INVALIDARG;
2624 *pszName = NULL;
2625 get_file_name(This, pszName);
2626 return *pszName ? S_OK : E_FAIL;
2629 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
2631 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2632 TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
2634 LocalFree(This->custom_title);
2635 This->custom_title = StrDupW(pszTitle);
2636 update_control_text(This);
2638 return S_OK;
2641 static HRESULT WINAPI IFileDialog2_fnSetOkButtonLabel(IFileDialog2 *iface, LPCWSTR pszText)
2643 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2644 TRACE("%p (%s)\n", This, debugstr_w(pszText));
2646 LocalFree(This->custom_okbutton);
2647 This->custom_okbutton = StrDupW(pszText);
2648 update_control_text(This);
2649 update_layout(This);
2651 return S_OK;
2654 static HRESULT WINAPI IFileDialog2_fnSetFileNameLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2656 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2657 TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2659 LocalFree(This->custom_filenamelabel);
2660 This->custom_filenamelabel = StrDupW(pszLabel);
2661 update_control_text(This);
2662 update_layout(This);
2664 return S_OK;
2667 static HRESULT WINAPI IFileDialog2_fnGetResult(IFileDialog2 *iface, IShellItem **ppsi)
2669 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2670 HRESULT hr;
2671 TRACE("%p (%p)\n", This, ppsi);
2673 if(!ppsi)
2674 return E_INVALIDARG;
2676 if(This->psia_results)
2678 UINT item_count;
2679 hr = IShellItemArray_GetCount(This->psia_results, &item_count);
2680 if(SUCCEEDED(hr))
2682 if(item_count != 1)
2683 return E_FAIL;
2685 /* Adds a reference. */
2686 hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
2689 return hr;
2692 return E_UNEXPECTED;
2695 static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *psi, FDAP fdap)
2697 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2698 FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
2699 return S_OK;
2702 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
2704 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2705 TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
2707 LocalFree(This->default_ext);
2708 This->default_ext = StrDupW(pszDefaultExtension);
2710 return S_OK;
2713 static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
2715 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2716 TRACE("%p (0x%08x)\n", This, hr);
2718 if(This->dlg_hwnd)
2719 EndDialog(This->dlg_hwnd, hr);
2721 return S_OK;
2724 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
2726 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2727 TRACE("%p (%s)\n", This, debugstr_guid(guid));
2728 This->client_guid = *guid;
2729 return S_OK;
2732 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
2734 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2735 FIXME("stub - %p\n", This);
2736 return E_NOTIMPL;
2739 static HRESULT WINAPI IFileDialog2_fnSetFilter(IFileDialog2 *iface, IShellItemFilter *pFilter)
2741 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2742 FIXME("stub - %p (%p)\n", This, pFilter);
2743 return E_NOTIMPL;
2746 static HRESULT WINAPI IFileDialog2_fnSetCancelButtonLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2748 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2749 TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2751 LocalFree(This->custom_cancelbutton);
2752 This->custom_cancelbutton = StrDupW(pszLabel);
2753 update_control_text(This);
2754 update_layout(This);
2756 return S_OK;
2759 static HRESULT WINAPI IFileDialog2_fnSetNavigationRoot(IFileDialog2 *iface, IShellItem *psi)
2761 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2762 FIXME("stub - %p (%p)\n", This, psi);
2763 return E_NOTIMPL;
2766 static const IFileDialog2Vtbl vt_IFileDialog2 = {
2767 IFileDialog2_fnQueryInterface,
2768 IFileDialog2_fnAddRef,
2769 IFileDialog2_fnRelease,
2770 IFileDialog2_fnShow,
2771 IFileDialog2_fnSetFileTypes,
2772 IFileDialog2_fnSetFileTypeIndex,
2773 IFileDialog2_fnGetFileTypeIndex,
2774 IFileDialog2_fnAdvise,
2775 IFileDialog2_fnUnadvise,
2776 IFileDialog2_fnSetOptions,
2777 IFileDialog2_fnGetOptions,
2778 IFileDialog2_fnSetDefaultFolder,
2779 IFileDialog2_fnSetFolder,
2780 IFileDialog2_fnGetFolder,
2781 IFileDialog2_fnGetCurrentSelection,
2782 IFileDialog2_fnSetFileName,
2783 IFileDialog2_fnGetFileName,
2784 IFileDialog2_fnSetTitle,
2785 IFileDialog2_fnSetOkButtonLabel,
2786 IFileDialog2_fnSetFileNameLabel,
2787 IFileDialog2_fnGetResult,
2788 IFileDialog2_fnAddPlace,
2789 IFileDialog2_fnSetDefaultExtension,
2790 IFileDialog2_fnClose,
2791 IFileDialog2_fnSetClientGuid,
2792 IFileDialog2_fnClearClientData,
2793 IFileDialog2_fnSetFilter,
2794 IFileDialog2_fnSetCancelButtonLabel,
2795 IFileDialog2_fnSetNavigationRoot
2798 /**************************************************************************
2799 * IFileOpenDialog
2801 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
2803 return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
2806 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
2807 REFIID riid, void **ppvObject)
2809 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2810 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2813 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
2815 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2816 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2819 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
2821 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2822 return IFileDialog2_Release(&This->IFileDialog2_iface);
2825 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
2827 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2828 return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2831 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
2832 const COMDLG_FILTERSPEC *rgFilterSpec)
2834 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2835 return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2838 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
2840 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2841 return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2844 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
2846 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2847 return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2850 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
2851 DWORD *pdwCookie)
2853 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2854 return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2857 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
2859 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2860 return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2863 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
2865 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2866 return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2869 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2871 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2872 return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2875 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
2877 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2878 return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2881 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
2883 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2884 return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2887 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
2889 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2890 return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2893 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
2895 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2896 return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2899 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
2901 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2902 return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2905 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
2907 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2908 return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2911 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
2913 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2914 return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2917 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
2919 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2920 return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2923 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
2925 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2926 return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2929 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
2931 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2932 return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2935 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
2937 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2938 return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2941 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultExtension(IFileOpenDialog *iface,
2942 LPCWSTR pszDefaultExtension)
2944 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2945 return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2948 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
2950 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2951 return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2954 static HRESULT WINAPI IFileOpenDialog_fnSetClientGuid(IFileOpenDialog *iface, REFGUID guid)
2956 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2957 return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2960 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
2962 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2963 return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2966 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
2968 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2969 return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2972 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
2974 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2975 TRACE("%p (%p)\n", This, ppenum);
2977 *ppenum = This->psia_results;
2979 if(*ppenum)
2981 IShellItemArray_AddRef(*ppenum);
2982 return S_OK;
2985 return E_FAIL;
2988 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
2990 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2991 TRACE("%p (%p)\n", This, ppsai);
2993 if(This->psia_selection)
2995 *ppsai = This->psia_selection;
2996 IShellItemArray_AddRef(*ppsai);
2997 return S_OK;
3000 return E_FAIL;
3003 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
3004 IFileOpenDialog_fnQueryInterface,
3005 IFileOpenDialog_fnAddRef,
3006 IFileOpenDialog_fnRelease,
3007 IFileOpenDialog_fnShow,
3008 IFileOpenDialog_fnSetFileTypes,
3009 IFileOpenDialog_fnSetFileTypeIndex,
3010 IFileOpenDialog_fnGetFileTypeIndex,
3011 IFileOpenDialog_fnAdvise,
3012 IFileOpenDialog_fnUnadvise,
3013 IFileOpenDialog_fnSetOptions,
3014 IFileOpenDialog_fnGetOptions,
3015 IFileOpenDialog_fnSetDefaultFolder,
3016 IFileOpenDialog_fnSetFolder,
3017 IFileOpenDialog_fnGetFolder,
3018 IFileOpenDialog_fnGetCurrentSelection,
3019 IFileOpenDialog_fnSetFileName,
3020 IFileOpenDialog_fnGetFileName,
3021 IFileOpenDialog_fnSetTitle,
3022 IFileOpenDialog_fnSetOkButtonLabel,
3023 IFileOpenDialog_fnSetFileNameLabel,
3024 IFileOpenDialog_fnGetResult,
3025 IFileOpenDialog_fnAddPlace,
3026 IFileOpenDialog_fnSetDefaultExtension,
3027 IFileOpenDialog_fnClose,
3028 IFileOpenDialog_fnSetClientGuid,
3029 IFileOpenDialog_fnClearClientData,
3030 IFileOpenDialog_fnSetFilter,
3031 IFileOpenDialog_fnGetResults,
3032 IFileOpenDialog_fnGetSelectedItems
3035 /**************************************************************************
3036 * IFileSaveDialog
3038 static inline FileDialogImpl *impl_from_IFileSaveDialog(IFileSaveDialog *iface)
3040 return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
3043 static HRESULT WINAPI IFileSaveDialog_fnQueryInterface(IFileSaveDialog *iface,
3044 REFIID riid,
3045 void **ppvObject)
3047 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3048 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3051 static ULONG WINAPI IFileSaveDialog_fnAddRef(IFileSaveDialog *iface)
3053 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3054 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3057 static ULONG WINAPI IFileSaveDialog_fnRelease(IFileSaveDialog *iface)
3059 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3060 return IFileDialog2_Release(&This->IFileDialog2_iface);
3063 static HRESULT WINAPI IFileSaveDialog_fnShow(IFileSaveDialog *iface, HWND hwndOwner)
3065 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3066 return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
3069 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypes(IFileSaveDialog *iface, UINT cFileTypes,
3070 const COMDLG_FILTERSPEC *rgFilterSpec)
3072 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3073 return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
3076 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypeIndex(IFileSaveDialog *iface, UINT iFileType)
3078 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3079 return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
3082 static HRESULT WINAPI IFileSaveDialog_fnGetFileTypeIndex(IFileSaveDialog *iface, UINT *piFileType)
3084 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3085 return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
3088 static HRESULT WINAPI IFileSaveDialog_fnAdvise(IFileSaveDialog *iface, IFileDialogEvents *pfde,
3089 DWORD *pdwCookie)
3091 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3092 return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
3095 static HRESULT WINAPI IFileSaveDialog_fnUnadvise(IFileSaveDialog *iface, DWORD dwCookie)
3097 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3098 return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
3101 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
3103 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3104 return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
3107 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
3109 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3110 return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
3113 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultFolder(IFileSaveDialog *iface, IShellItem *psi)
3115 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3116 return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
3119 static HRESULT WINAPI IFileSaveDialog_fnSetFolder(IFileSaveDialog *iface, IShellItem *psi)
3121 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3122 return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
3125 static HRESULT WINAPI IFileSaveDialog_fnGetFolder(IFileSaveDialog *iface, IShellItem **ppsi)
3127 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3128 return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
3131 static HRESULT WINAPI IFileSaveDialog_fnGetCurrentSelection(IFileSaveDialog *iface, IShellItem **ppsi)
3133 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3134 return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
3137 static HRESULT WINAPI IFileSaveDialog_fnSetFileName(IFileSaveDialog *iface, LPCWSTR pszName)
3139 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3140 return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
3143 static HRESULT WINAPI IFileSaveDialog_fnGetFileName(IFileSaveDialog *iface, LPWSTR *pszName)
3145 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3146 return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
3149 static HRESULT WINAPI IFileSaveDialog_fnSetTitle(IFileSaveDialog *iface, LPCWSTR pszTitle)
3151 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3152 return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
3155 static HRESULT WINAPI IFileSaveDialog_fnSetOkButtonLabel(IFileSaveDialog *iface, LPCWSTR pszText)
3157 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3158 return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
3161 static HRESULT WINAPI IFileSaveDialog_fnSetFileNameLabel(IFileSaveDialog *iface, LPCWSTR pszLabel)
3163 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3164 return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
3167 static HRESULT WINAPI IFileSaveDialog_fnGetResult(IFileSaveDialog *iface, IShellItem **ppsi)
3169 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3170 return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
3173 static HRESULT WINAPI IFileSaveDialog_fnAddPlace(IFileSaveDialog *iface, IShellItem *psi, FDAP fdap)
3175 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3176 return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
3179 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultExtension(IFileSaveDialog *iface,
3180 LPCWSTR pszDefaultExtension)
3182 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3183 return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
3186 static HRESULT WINAPI IFileSaveDialog_fnClose(IFileSaveDialog *iface, HRESULT hr)
3188 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3189 return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
3192 static HRESULT WINAPI IFileSaveDialog_fnSetClientGuid(IFileSaveDialog *iface, REFGUID guid)
3194 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3195 return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
3198 static HRESULT WINAPI IFileSaveDialog_fnClearClientData(IFileSaveDialog *iface)
3200 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3201 return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
3204 static HRESULT WINAPI IFileSaveDialog_fnSetFilter(IFileSaveDialog *iface, IShellItemFilter *pFilter)
3206 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3207 return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
3210 static HRESULT WINAPI IFileSaveDialog_fnSetSaveAsItem(IFileSaveDialog* iface, IShellItem *psi)
3212 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3213 FIXME("stub - %p (%p)\n", This, psi);
3214 return E_NOTIMPL;
3217 static HRESULT WINAPI IFileSaveDialog_fnSetProperties(IFileSaveDialog* iface, IPropertyStore *pStore)
3219 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3220 FIXME("stub - %p (%p)\n", This, pStore);
3221 return E_NOTIMPL;
3224 static HRESULT WINAPI IFileSaveDialog_fnSetCollectedProperties(IFileSaveDialog* iface,
3225 IPropertyDescriptionList *pList,
3226 BOOL fAppendDefault)
3228 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3229 FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
3230 return E_NOTIMPL;
3233 static HRESULT WINAPI IFileSaveDialog_fnGetProperties(IFileSaveDialog* iface, IPropertyStore **ppStore)
3235 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3236 FIXME("stub - %p (%p)\n", This, ppStore);
3237 return E_NOTIMPL;
3240 static HRESULT WINAPI IFileSaveDialog_fnApplyProperties(IFileSaveDialog* iface,
3241 IShellItem *psi,
3242 IPropertyStore *pStore,
3243 HWND hwnd,
3244 IFileOperationProgressSink *pSink)
3246 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3247 FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
3248 return E_NOTIMPL;
3251 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
3252 IFileSaveDialog_fnQueryInterface,
3253 IFileSaveDialog_fnAddRef,
3254 IFileSaveDialog_fnRelease,
3255 IFileSaveDialog_fnShow,
3256 IFileSaveDialog_fnSetFileTypes,
3257 IFileSaveDialog_fnSetFileTypeIndex,
3258 IFileSaveDialog_fnGetFileTypeIndex,
3259 IFileSaveDialog_fnAdvise,
3260 IFileSaveDialog_fnUnadvise,
3261 IFileSaveDialog_fnSetOptions,
3262 IFileSaveDialog_fnGetOptions,
3263 IFileSaveDialog_fnSetDefaultFolder,
3264 IFileSaveDialog_fnSetFolder,
3265 IFileSaveDialog_fnGetFolder,
3266 IFileSaveDialog_fnGetCurrentSelection,
3267 IFileSaveDialog_fnSetFileName,
3268 IFileSaveDialog_fnGetFileName,
3269 IFileSaveDialog_fnSetTitle,
3270 IFileSaveDialog_fnSetOkButtonLabel,
3271 IFileSaveDialog_fnSetFileNameLabel,
3272 IFileSaveDialog_fnGetResult,
3273 IFileSaveDialog_fnAddPlace,
3274 IFileSaveDialog_fnSetDefaultExtension,
3275 IFileSaveDialog_fnClose,
3276 IFileSaveDialog_fnSetClientGuid,
3277 IFileSaveDialog_fnClearClientData,
3278 IFileSaveDialog_fnSetFilter,
3279 IFileSaveDialog_fnSetSaveAsItem,
3280 IFileSaveDialog_fnSetProperties,
3281 IFileSaveDialog_fnSetCollectedProperties,
3282 IFileSaveDialog_fnGetProperties,
3283 IFileSaveDialog_fnApplyProperties
3286 /**************************************************************************
3287 * IExplorerBrowserEvents implementation
3289 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
3291 return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
3294 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
3295 REFIID riid, void **ppvObject)
3297 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3298 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
3300 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3303 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
3305 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3306 TRACE("%p\n", This);
3307 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3310 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
3312 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3313 TRACE("%p\n", This);
3314 return IFileDialog2_Release(&This->IFileDialog2_iface);
3317 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
3318 PCIDLIST_ABSOLUTE pidlFolder)
3320 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3321 IShellItem *psi;
3322 HRESULT hr;
3323 TRACE("%p (%p)\n", This, pidlFolder);
3325 hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
3326 if(SUCCEEDED(hr))
3328 hr = events_OnFolderChanging(This, psi);
3329 IShellItem_Release(psi);
3331 /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
3332 if(hr == S_FALSE)
3333 hr = E_FAIL;
3335 return hr;
3337 else
3338 ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
3340 return S_OK;
3343 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
3344 IShellView *psv)
3346 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3347 TRACE("%p (%p)\n", This, psv);
3348 return S_OK;
3351 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
3352 PCIDLIST_ABSOLUTE pidlFolder)
3354 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3355 HRESULT hr;
3356 TRACE("%p (%p)\n", This, pidlFolder);
3358 if(This->psi_folder)
3359 IShellItem_Release(This->psi_folder);
3361 hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
3362 if(FAILED(hr))
3364 ERR("Failed to get the current folder.\n");
3365 This->psi_folder = NULL;
3368 events_OnFolderChange(This);
3370 return S_OK;
3373 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
3374 PCIDLIST_ABSOLUTE pidlFolder)
3376 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3377 TRACE("%p (%p)\n", This, pidlFolder);
3378 return S_OK;
3381 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
3382 IExplorerBrowserEvents_fnQueryInterface,
3383 IExplorerBrowserEvents_fnAddRef,
3384 IExplorerBrowserEvents_fnRelease,
3385 IExplorerBrowserEvents_fnOnNavigationPending,
3386 IExplorerBrowserEvents_fnOnViewCreated,
3387 IExplorerBrowserEvents_fnOnNavigationComplete,
3388 IExplorerBrowserEvents_fnOnNavigationFailed
3391 /**************************************************************************
3392 * IServiceProvider implementation
3394 static inline FileDialogImpl *impl_from_IServiceProvider(IServiceProvider *iface)
3396 return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
3399 static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface,
3400 REFIID riid, void **ppvObject)
3402 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3403 TRACE("%p\n", This);
3404 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3407 static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface)
3409 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3410 TRACE("%p\n", This);
3411 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3414 static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface)
3416 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3417 TRACE("%p\n", This);
3418 return IFileDialog2_Release(&This->IFileDialog2_iface);
3421 static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
3422 REFGUID guidService,
3423 REFIID riid, void **ppv)
3425 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3426 HRESULT hr = E_NOTIMPL;
3427 TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
3429 *ppv = NULL;
3430 if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
3431 hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
3432 else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
3433 hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
3434 else
3435 FIXME("Interface %s requested from unknown service %s\n",
3436 debugstr_guid(riid), debugstr_guid(guidService));
3438 return hr;
3441 static const IServiceProviderVtbl vt_IServiceProvider = {
3442 IServiceProvider_fnQueryInterface,
3443 IServiceProvider_fnAddRef,
3444 IServiceProvider_fnRelease,
3445 IServiceProvider_fnQueryService
3448 /**************************************************************************
3449 * ICommDlgBrowser3 implementation
3451 static inline FileDialogImpl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface)
3453 return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
3456 static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface,
3457 REFIID riid, void **ppvObject)
3459 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3460 TRACE("%p\n", This);
3461 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3464 static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface)
3466 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3467 TRACE("%p\n", This);
3468 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3471 static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface)
3473 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3474 TRACE("%p\n", This);
3475 return IFileDialog2_Release(&This->IFileDialog2_iface);
3478 static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3 *iface,
3479 IShellView *shv)
3481 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3482 HRESULT hr;
3483 TRACE("%p (%p)\n", This, shv);
3485 hr = on_default_action(This);
3487 if(SUCCEEDED(hr))
3488 EndDialog(This->dlg_hwnd, S_OK);
3490 return S_OK;
3493 static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange(ICommDlgBrowser3 *iface,
3494 IShellView *shv, ULONG uChange )
3496 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3497 IDataObject *new_selection;
3498 HRESULT hr;
3499 TRACE("%p (%p, %x)\n", This, shv, uChange);
3501 switch(uChange)
3503 case CDBOSC_SELCHANGE:
3504 if(This->psia_selection)
3506 IShellItemArray_Release(This->psia_selection);
3507 This->psia_selection = NULL;
3510 hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
3511 if(SUCCEEDED(hr))
3513 hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
3514 (void**)&This->psia_selection);
3515 if(SUCCEEDED(hr))
3517 fill_filename_from_selection(This);
3518 events_OnSelectionChange(This);
3521 IDataObject_Release(new_selection);
3523 break;
3524 default:
3525 TRACE("Unhandled state change\n");
3527 return S_OK;
3530 static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
3531 IShellView *shv, LPCITEMIDLIST pidl)
3533 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3534 IShellItem *psi;
3535 LPWSTR filename;
3536 LPITEMIDLIST parent_pidl;
3537 HRESULT hr;
3538 ULONG attr;
3539 TRACE("%p (%p, %p)\n", This, shv, pidl);
3541 if(!This->filterspec_count && !(This->options & FOS_PICKFOLDERS))
3542 return S_OK;
3544 hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
3545 if(SUCCEEDED(hr))
3547 LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
3548 hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
3549 ILFree(parent_pidl);
3550 ILFree(full_pidl);
3552 if(FAILED(hr))
3554 ERR("Failed to get shellitem (%08x).\n", hr);
3555 return S_OK;
3558 hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
3559 if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
3561 IShellItem_Release(psi);
3562 return S_OK;
3565 if((This->options & FOS_PICKFOLDERS) && !(attr & (SFGAO_FOLDER | SFGAO_LINK)))
3567 IShellItem_Release(psi);
3568 return S_FALSE;
3571 hr = S_OK;
3572 if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
3574 if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
3575 hr = S_FALSE;
3576 CoTaskMemFree(filename);
3579 IShellItem_Release(psi);
3580 return hr;
3583 static HRESULT WINAPI ICommDlgBrowser3_fnNotify(ICommDlgBrowser3 *iface,
3584 IShellView *ppshv, DWORD dwNotifyType)
3586 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3587 FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
3588 return E_NOTIMPL;
3591 static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText(ICommDlgBrowser3 *iface,
3592 IShellView *pshv,
3593 LPWSTR pszText, int cchMax)
3595 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3596 FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
3597 return E_NOTIMPL;
3600 static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags(ICommDlgBrowser3 *iface, DWORD *pdwFlags)
3602 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3603 FIXME("Stub: %p (%p)\n", This, pdwFlags);
3604 return E_NOTIMPL;
3607 static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked(ICommDlgBrowser3 *iface,
3608 IShellView *pshv, int iColumn)
3610 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3611 FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
3612 return E_NOTIMPL;
3615 static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter(ICommDlgBrowser3 *iface,
3616 LPWSTR pszFileSpec, int cchFileSpec)
3618 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3619 FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
3620 return E_NOTIMPL;
3623 static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated(ICommDlgBrowser3 *iface,
3624 IShellView *pshv)
3626 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3627 FIXME("Stub: %p (%p)\n", This, pshv);
3628 return E_NOTIMPL;
3631 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
3632 ICommDlgBrowser3_fnQueryInterface,
3633 ICommDlgBrowser3_fnAddRef,
3634 ICommDlgBrowser3_fnRelease,
3635 ICommDlgBrowser3_fnOnDefaultCommand,
3636 ICommDlgBrowser3_fnOnStateChange,
3637 ICommDlgBrowser3_fnIncludeObject,
3638 ICommDlgBrowser3_fnNotify,
3639 ICommDlgBrowser3_fnGetDefaultMenuText,
3640 ICommDlgBrowser3_fnGetViewFlags,
3641 ICommDlgBrowser3_fnOnColumnClicked,
3642 ICommDlgBrowser3_fnGetCurrentFilter,
3643 ICommDlgBrowser3_fnOnPreviewCreated
3646 /**************************************************************************
3647 * IOleWindow implementation
3649 static inline FileDialogImpl *impl_from_IOleWindow(IOleWindow *iface)
3651 return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
3654 static HRESULT WINAPI IOleWindow_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
3656 FileDialogImpl *This = impl_from_IOleWindow(iface);
3657 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3660 static ULONG WINAPI IOleWindow_fnAddRef(IOleWindow *iface)
3662 FileDialogImpl *This = impl_from_IOleWindow(iface);
3663 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3666 static ULONG WINAPI IOleWindow_fnRelease(IOleWindow *iface)
3668 FileDialogImpl *This = impl_from_IOleWindow(iface);
3669 return IFileDialog2_Release(&This->IFileDialog2_iface);
3672 static HRESULT WINAPI IOleWindow_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMOde)
3674 FileDialogImpl *This = impl_from_IOleWindow(iface);
3675 FIXME("Stub: %p (%d)\n", This, fEnterMOde);
3676 return E_NOTIMPL;
3679 static HRESULT WINAPI IOleWindow_fnGetWindow(IOleWindow *iface, HWND *phwnd)
3681 FileDialogImpl *This = impl_from_IOleWindow(iface);
3682 TRACE("%p (%p)\n", This, phwnd);
3683 *phwnd = This->dlg_hwnd;
3684 return S_OK;
3687 static const IOleWindowVtbl vt_IOleWindow = {
3688 IOleWindow_fnQueryInterface,
3689 IOleWindow_fnAddRef,
3690 IOleWindow_fnRelease,
3691 IOleWindow_fnGetWindow,
3692 IOleWindow_fnContextSensitiveHelp
3695 /**************************************************************************
3696 * IFileDialogCustomize implementation
3698 static inline FileDialogImpl *impl_from_IFileDialogCustomize(IFileDialogCustomize *iface)
3700 return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialogCustomize_iface);
3703 static HRESULT WINAPI IFileDialogCustomize_fnQueryInterface(IFileDialogCustomize *iface,
3704 REFIID riid, void **ppvObject)
3706 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3707 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3710 static ULONG WINAPI IFileDialogCustomize_fnAddRef(IFileDialogCustomize *iface)
3712 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3713 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3716 static ULONG WINAPI IFileDialogCustomize_fnRelease(IFileDialogCustomize *iface)
3718 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3719 return IFileDialog2_Release(&This->IFileDialog2_iface);
3722 static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCustomize *iface,
3723 DWORD dwIDCtl)
3725 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3726 MENUINFO mi;
3727 TRACE("%p (%d)\n", This, dwIDCtl);
3729 if (This->hmenu_opendropdown || get_cctrl(This, dwIDCtl))
3730 return E_UNEXPECTED;
3732 This->hmenu_opendropdown = CreatePopupMenu();
3734 if (!This->hmenu_opendropdown)
3735 return E_OUTOFMEMORY;
3737 mi.cbSize = sizeof(mi);
3738 mi.fMask = MIM_STYLE;
3739 mi.dwStyle = MNS_NOTIFYBYPOS;
3740 SetMenuInfo(This->hmenu_opendropdown, &mi);
3742 This->cctrl_opendropdown.hwnd = NULL;
3743 This->cctrl_opendropdown.wrapper_hwnd = NULL;
3744 This->cctrl_opendropdown.id = dwIDCtl;
3745 This->cctrl_opendropdown.dlgid = 0;
3746 This->cctrl_opendropdown.type = IDLG_CCTRL_OPENDROPDOWN;
3747 This->cctrl_opendropdown.cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
3748 list_init(&This->cctrl_opendropdown.sub_cctrls);
3749 list_init(&This->cctrl_opendropdown.sub_items);
3751 return S_OK;
3754 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
3755 DWORD dwIDCtl,
3756 LPCWSTR pszLabel)
3758 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3759 customctrl *ctrl;
3760 TBBUTTON tbb;
3761 HRESULT hr;
3762 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3764 hr = cctrl_create_new(This, dwIDCtl, NULL, TOOLBARCLASSNAMEW,
3765 TBSTYLE_FLAT | CCS_NODIVIDER, 0,
3766 This->cctrl_def_height, &ctrl);
3767 if(SUCCEEDED(hr))
3769 SendMessageW(ctrl->hwnd, TB_BUTTONSTRUCTSIZE, sizeof(tbb), 0);
3770 ctrl->type = IDLG_CCTRL_MENU;
3772 /* Add the actual button with a popup menu. */
3773 tbb.iBitmap = I_IMAGENONE;
3774 tbb.dwData = (DWORD_PTR)CreatePopupMenu();
3775 tbb.iString = (DWORD_PTR)pszLabel;
3776 tbb.fsState = TBSTATE_ENABLED;
3777 tbb.fsStyle = BTNS_WHOLEDROPDOWN;
3778 tbb.idCommand = 1;
3780 SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
3783 return hr;
3786 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
3787 DWORD dwIDCtl,
3788 LPCWSTR pszLabel)
3790 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3791 customctrl *ctrl;
3792 HRESULT hr;
3793 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3795 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
3796 This->cctrl_def_height, &ctrl);
3797 if(SUCCEEDED(hr))
3798 ctrl->type = IDLG_CCTRL_PUSHBUTTON;
3800 return hr;
3803 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
3804 DWORD dwIDCtl)
3806 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3807 customctrl *ctrl;
3808 HRESULT hr;
3809 TRACE("%p (%d)\n", This, dwIDCtl);
3811 hr = cctrl_create_new(This, dwIDCtl, NULL, WC_COMBOBOXW, CBS_DROPDOWNLIST, 0,
3812 This->cctrl_def_height, &ctrl);
3813 if(SUCCEEDED(hr))
3814 ctrl->type = IDLG_CCTRL_COMBOBOX;
3816 return hr;
3819 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
3820 DWORD dwIDCtl)
3822 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3823 customctrl *ctrl;
3824 HRESULT hr;
3825 TRACE("%p (%d)\n", This, dwIDCtl);
3827 hr = cctrl_create_new(This, dwIDCtl, NULL, L"RadioButtonList", 0, 0, 0, &ctrl);
3828 if(SUCCEEDED(hr))
3830 ctrl->type = IDLG_CCTRL_RADIOBUTTONLIST;
3831 SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)This);
3834 return hr;
3837 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
3838 DWORD dwIDCtl,
3839 LPCWSTR pszLabel,
3840 BOOL bChecked)
3842 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3843 customctrl *ctrl;
3844 HRESULT hr;
3845 TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
3847 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX|BS_MULTILINE, 0,
3848 This->cctrl_def_height, &ctrl);
3849 if(SUCCEEDED(hr))
3851 ctrl->type = IDLG_CCTRL_CHECKBUTTON;
3852 SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
3855 return hr;
3858 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
3859 DWORD dwIDCtl,
3860 LPCWSTR pszText)
3862 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3863 customctrl *ctrl;
3864 HRESULT hr;
3865 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3867 hr = cctrl_create_new(This, dwIDCtl, pszText, WC_EDITW, ES_AUTOHSCROLL, WS_EX_CLIENTEDGE,
3868 This->cctrl_def_height, &ctrl);
3869 if(SUCCEEDED(hr))
3870 ctrl->type = IDLG_CCTRL_EDITBOX;
3872 return hr;
3875 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
3876 DWORD dwIDCtl)
3878 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3879 customctrl *ctrl;
3880 HRESULT hr;
3881 TRACE("%p (%d)\n", This, dwIDCtl);
3883 hr = cctrl_create_new(This, dwIDCtl, NULL, WC_STATICW, SS_ETCHEDHORZ, 0,
3884 GetSystemMetrics(SM_CYEDGE), &ctrl);
3885 if(SUCCEEDED(hr))
3886 ctrl->type = IDLG_CCTRL_SEPARATOR;
3888 return hr;
3891 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
3892 DWORD dwIDCtl,
3893 LPCWSTR pszText)
3895 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3896 customctrl *ctrl;
3897 HRESULT hr;
3898 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3900 hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
3901 This->cctrl_def_height, &ctrl);
3902 if(SUCCEEDED(hr))
3903 ctrl->type = IDLG_CCTRL_TEXT;
3905 return hr;
3908 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
3909 DWORD dwIDCtl,
3910 LPCWSTR pszLabel)
3912 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3913 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3914 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3916 if(!ctrl) return E_INVALIDARG;
3918 switch(ctrl->type)
3920 case IDLG_CCTRL_MENU:
3921 case IDLG_CCTRL_PUSHBUTTON:
3922 case IDLG_CCTRL_CHECKBUTTON:
3923 case IDLG_CCTRL_TEXT:
3924 case IDLG_CCTRL_VISUALGROUP:
3925 SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
3926 break;
3927 case IDLG_CCTRL_OPENDROPDOWN:
3928 return E_NOTIMPL;
3929 default:
3930 break;
3933 return S_OK;
3936 static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomize *iface,
3937 DWORD dwIDCtl,
3938 CDCONTROLSTATEF *pdwState)
3940 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3941 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3942 TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
3944 if(!ctrl || ctrl->type == IDLG_CCTRL_OPENDROPDOWN) return E_NOTIMPL;
3946 *pdwState = ctrl->cdcstate;
3947 return S_OK;
3950 static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomize *iface,
3951 DWORD dwIDCtl,
3952 CDCONTROLSTATEF dwState)
3954 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3955 customctrl *ctrl = get_cctrl(This,dwIDCtl);
3956 TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
3958 if(ctrl && ctrl->hwnd)
3960 LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
3962 if(dwState & CDCS_ENABLED)
3963 wndstyle &= ~(WS_DISABLED);
3964 else
3965 wndstyle |= WS_DISABLED;
3967 if(dwState & CDCS_VISIBLE)
3968 wndstyle |= WS_VISIBLE;
3969 else
3970 wndstyle &= ~(WS_VISIBLE);
3972 SetWindowLongW(ctrl->hwnd, GWL_STYLE, wndstyle);
3974 /* We save the state separately since at least one application
3975 * relies on being able to hide a control. */
3976 ctrl->cdcstate = dwState;
3979 return S_OK;
3982 static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize *iface,
3983 DWORD dwIDCtl,
3984 WCHAR **ppszText)
3986 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3987 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3988 WCHAR len, *text;
3989 TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
3991 if(!ctrl || !ctrl->hwnd || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
3992 return E_FAIL;
3994 text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
3995 if(!text) return E_FAIL;
3997 SendMessageW(ctrl->hwnd, WM_GETTEXT, len+1, (LPARAM)text);
3998 *ppszText = text;
3999 return S_OK;
4002 static HRESULT WINAPI IFileDialogCustomize_fnSetEditBoxText(IFileDialogCustomize *iface,
4003 DWORD dwIDCtl,
4004 LPCWSTR pszText)
4006 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4007 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4008 TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
4010 if(!ctrl || ctrl->type != IDLG_CCTRL_EDITBOX)
4011 return E_FAIL;
4013 SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
4014 return S_OK;
4017 static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCustomize *iface,
4018 DWORD dwIDCtl,
4019 BOOL *pbChecked)
4021 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4022 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4023 TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
4025 if(ctrl && ctrl->hwnd)
4026 *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
4028 return S_OK;
4031 static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCustomize *iface,
4032 DWORD dwIDCtl,
4033 BOOL bChecked)
4035 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4036 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4037 TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
4039 if(ctrl && ctrl->hwnd)
4040 SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
4042 return S_OK;
4045 static UINT get_combobox_index_from_id(HWND cb_hwnd, DWORD dwIDItem)
4047 UINT count = SendMessageW(cb_hwnd, CB_GETCOUNT, 0, 0);
4048 UINT i;
4049 if(!count || (count == CB_ERR))
4050 return -1;
4052 for(i = 0; i < count; i++)
4053 if(SendMessageW(cb_hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
4054 return i;
4056 TRACE("Item with id %d not found in combobox %p (item count: %d)\n", dwIDItem, cb_hwnd, count);
4057 return -1;
4060 static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize *iface,
4061 DWORD dwIDCtl,
4062 DWORD dwIDItem,
4063 LPCWSTR pszLabel)
4065 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4066 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4067 HRESULT hr;
4068 TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4070 if(!ctrl) return E_FAIL;
4072 switch(ctrl->type)
4074 case IDLG_CCTRL_COMBOBOX:
4076 UINT index;
4077 cctrl_item* item;
4079 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4081 if (FAILED(hr)) return hr;
4083 index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
4084 SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
4086 return S_OK;
4088 case IDLG_CCTRL_MENU:
4089 case IDLG_CCTRL_OPENDROPDOWN:
4091 cctrl_item* item;
4092 HMENU hmenu;
4094 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4096 if (FAILED(hr)) return hr;
4098 if (ctrl->type == IDLG_CCTRL_MENU)
4100 TBBUTTON tbb;
4101 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4102 hmenu = (HMENU)tbb.dwData;
4104 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4105 hmenu = This->hmenu_opendropdown;
4107 AppendMenuW(hmenu, MF_STRING, dwIDItem, pszLabel);
4108 return S_OK;
4110 case IDLG_CCTRL_RADIOBUTTONLIST:
4112 cctrl_item* item;
4114 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4116 if (SUCCEEDED(hr))
4118 item->hwnd = CreateWindowExW(0, WC_BUTTONW, pszLabel,
4119 WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|BS_RADIOBUTTON|BS_MULTILINE,
4120 0, 0, 0, 0, ctrl->hwnd, ULongToHandle(dwIDItem), COMDLG32_hInstance, 0);
4122 if (!item->hwnd)
4124 ERR("Failed to create radio button\n");
4125 list_remove(&item->entry);
4126 item_free(item);
4127 return E_FAIL;
4131 return hr;
4133 default:
4134 break;
4137 return E_NOINTERFACE; /* win7 */
4140 static HRESULT WINAPI IFileDialogCustomize_fnRemoveControlItem(IFileDialogCustomize *iface,
4141 DWORD dwIDCtl,
4142 DWORD dwIDItem)
4144 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4145 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4146 TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4148 if(!ctrl) return E_FAIL;
4150 switch(ctrl->type)
4152 case IDLG_CCTRL_COMBOBOX:
4154 cctrl_item* item;
4155 DWORD position;
4157 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4159 if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
4161 if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0) == CB_ERR)
4162 return E_FAIL;
4165 list_remove(&item->entry);
4166 item_free(item);
4168 return S_OK;
4170 case IDLG_CCTRL_MENU:
4171 case IDLG_CCTRL_OPENDROPDOWN:
4173 HMENU hmenu;
4174 cctrl_item* item;
4176 item = get_item(ctrl, dwIDItem, 0, NULL);
4178 if (!item)
4179 return E_UNEXPECTED;
4181 if (item->cdcstate & CDCS_VISIBLE)
4183 if (ctrl->type == IDLG_CCTRL_MENU)
4185 TBBUTTON tbb;
4186 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4187 hmenu = (HMENU)tbb.dwData;
4189 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4190 hmenu = This->hmenu_opendropdown;
4192 if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
4193 return E_UNEXPECTED;
4196 list_remove(&item->entry);
4197 item_free(item);
4199 return S_OK;
4201 case IDLG_CCTRL_RADIOBUTTONLIST:
4203 cctrl_item* item;
4205 item = get_item(ctrl, dwIDItem, 0, NULL);
4207 if (!item)
4208 return E_UNEXPECTED;
4210 list_remove(&item->entry);
4211 item_free(item);
4213 return S_OK;
4215 default:
4216 break;
4219 return E_FAIL;
4222 static HRESULT WINAPI IFileDialogCustomize_fnRemoveAllControlItems(IFileDialogCustomize *iface,
4223 DWORD dwIDCtl)
4225 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4226 TRACE("%p (%d)\n", This, dwIDCtl);
4228 /* Not implemented by native */
4229 return E_NOTIMPL;
4232 static HRESULT WINAPI IFileDialogCustomize_fnGetControlItemState(IFileDialogCustomize *iface,
4233 DWORD dwIDCtl,
4234 DWORD dwIDItem,
4235 CDCONTROLSTATEF *pdwState)
4237 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4238 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4239 TRACE("%p (%d, %d, %p)\n", This, dwIDCtl, dwIDItem, pdwState);
4241 if(!ctrl) return E_FAIL;
4243 switch(ctrl->type)
4245 case IDLG_CCTRL_COMBOBOX:
4246 case IDLG_CCTRL_MENU:
4247 case IDLG_CCTRL_OPENDROPDOWN:
4248 case IDLG_CCTRL_RADIOBUTTONLIST:
4250 cctrl_item* item;
4252 item = get_item(ctrl, dwIDItem, 0, NULL);
4254 if (!item)
4255 return E_UNEXPECTED;
4257 *pdwState = item->cdcstate;
4259 return S_OK;
4261 default:
4262 break;
4265 return E_FAIL;
4268 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCustomize *iface,
4269 DWORD dwIDCtl,
4270 DWORD dwIDItem,
4271 CDCONTROLSTATEF dwState)
4273 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4274 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4275 TRACE("%p (%d, %d, %x)\n", This, dwIDCtl, dwIDItem, dwState);
4277 if(!ctrl) return E_FAIL;
4279 switch(ctrl->type)
4281 case IDLG_CCTRL_COMBOBOX:
4283 cctrl_item* item;
4284 BOOL visible, was_visible;
4285 DWORD position;
4287 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4289 if (!item)
4290 return E_UNEXPECTED;
4292 visible = ((dwState & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4293 was_visible = ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4295 if (visible && !was_visible)
4297 SendMessageW(ctrl->hwnd, CB_INSERTSTRING, position, (LPARAM)item->label);
4298 SendMessageW(ctrl->hwnd, CB_SETITEMDATA, position, dwIDItem);
4300 else if (!visible && was_visible)
4302 SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0);
4305 item->cdcstate = dwState;
4307 return S_OK;
4309 case IDLG_CCTRL_MENU:
4310 case IDLG_CCTRL_OPENDROPDOWN:
4312 HMENU hmenu;
4313 cctrl_item* item;
4314 CDCONTROLSTATEF prev_state;
4315 DWORD position;
4317 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, &position);
4319 if (!item)
4320 return E_UNEXPECTED;
4322 prev_state = item->cdcstate;
4324 if (ctrl->type == IDLG_CCTRL_MENU)
4326 TBBUTTON tbb;
4327 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4328 hmenu = (HMENU)tbb.dwData;
4330 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4331 hmenu = This->hmenu_opendropdown;
4333 if (dwState & CDCS_VISIBLE)
4335 if (prev_state & CDCS_VISIBLE)
4337 /* change state */
4338 EnableMenuItem(hmenu, dwIDItem,
4339 MF_BYCOMMAND|((dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED));
4341 else
4343 /* show item */
4344 MENUITEMINFOW mii;
4346 mii.cbSize = sizeof(mii);
4347 mii.fMask = MIIM_ID|MIIM_STATE|MIIM_STRING;
4348 mii.fState = (dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED;
4349 mii.wID = dwIDItem;
4350 mii.dwTypeData = item->label;
4352 InsertMenuItemW(hmenu, position, TRUE, &mii);
4355 else if (prev_state & CDCS_VISIBLE)
4357 /* hide item */
4358 DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND);
4361 item->cdcstate = dwState;
4363 if (ctrl->type == IDLG_CCTRL_OPENDROPDOWN)
4365 update_control_text(This);
4366 update_layout(This);
4369 return S_OK;
4371 case IDLG_CCTRL_RADIOBUTTONLIST:
4373 cctrl_item* item;
4375 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, NULL);
4377 if (!item)
4378 return E_UNEXPECTED;
4380 /* Oddly, native allows setting this but doesn't seem to do anything with it. */
4381 item->cdcstate = dwState;
4383 return S_OK;
4385 default:
4386 break;
4389 return E_FAIL;
4392 static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogCustomize *iface,
4393 DWORD dwIDCtl,
4394 DWORD *pdwIDItem)
4396 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4397 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4398 TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwIDItem);
4400 if(!ctrl) return E_FAIL;
4402 switch(ctrl->type)
4404 case IDLG_CCTRL_COMBOBOX:
4406 UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
4407 if(index == CB_ERR)
4408 return E_FAIL;
4410 *pdwIDItem = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
4411 return S_OK;
4413 case IDLG_CCTRL_OPENDROPDOWN:
4414 if (This->opendropdown_has_selection)
4416 *pdwIDItem = This->opendropdown_selection;
4417 return S_OK;
4419 else
4421 /* Return first enabled item. */
4422 cctrl_item* item = get_first_item(ctrl);
4424 if (item)
4426 *pdwIDItem = item->id;
4427 return S_OK;
4430 WARN("no enabled items in open dropdown\n");
4431 return E_FAIL;
4433 case IDLG_CCTRL_RADIOBUTTONLIST:
4435 cctrl_item* item;
4437 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
4439 if (SendMessageW(item->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED)
4441 *pdwIDItem = item->id;
4442 return S_OK;
4446 WARN("no checked items in radio button list\n");
4447 return E_FAIL;
4449 default:
4450 FIXME("Unsupported control type %d\n", ctrl->type);
4453 return E_NOTIMPL;
4456 static HRESULT WINAPI IFileDialogCustomize_fnSetSelectedControlItem(IFileDialogCustomize *iface,
4457 DWORD dwIDCtl,
4458 DWORD dwIDItem)
4460 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4461 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4462 TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4464 if(!ctrl) return E_INVALIDARG;
4466 switch(ctrl->type)
4468 case IDLG_CCTRL_COMBOBOX:
4470 UINT index = get_combobox_index_from_id(ctrl->hwnd, dwIDItem);
4472 if(index == -1)
4473 return E_INVALIDARG;
4475 if(SendMessageW(ctrl->hwnd, CB_SETCURSEL, index, 0) == CB_ERR)
4476 return E_FAIL;
4478 return S_OK;
4480 case IDLG_CCTRL_RADIOBUTTONLIST:
4482 cctrl_item* item;
4484 item = get_item(ctrl, dwIDItem, 0, NULL);
4486 if (item)
4488 radiobuttonlist_set_selected_item(This, ctrl, item);
4489 return S_OK;
4492 return E_INVALIDARG;
4494 default:
4495 FIXME("Unsupported control type %d\n", ctrl->type);
4498 return E_INVALIDARG;
4501 static HRESULT WINAPI IFileDialogCustomize_fnStartVisualGroup(IFileDialogCustomize *iface,
4502 DWORD dwIDCtl,
4503 LPCWSTR pszLabel)
4505 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4506 customctrl *vg;
4507 HRESULT hr;
4508 TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
4510 if(This->cctrl_active_vg)
4511 return E_UNEXPECTED;
4513 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_STATICW, 0, 0,
4514 This->cctrl_def_height, &vg);
4515 if(SUCCEEDED(hr))
4517 vg->type = IDLG_CCTRL_VISUALGROUP;
4518 This->cctrl_active_vg = vg;
4521 return hr;
4524 static HRESULT WINAPI IFileDialogCustomize_fnEndVisualGroup(IFileDialogCustomize *iface)
4526 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4527 TRACE("%p\n", This);
4529 This->cctrl_active_vg = NULL;
4531 return S_OK;
4534 static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize *iface,
4535 DWORD dwIDCtl)
4537 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4538 FIXME("stub - %p (%d)\n", This, dwIDCtl);
4539 return S_OK;
4542 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCustomize *iface,
4543 DWORD dwIDCtl,
4544 DWORD dwIDItem,
4545 LPCWSTR pszLabel)
4547 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4548 FIXME("stub - %p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4549 return E_NOTIMPL;
4552 static const IFileDialogCustomizeVtbl vt_IFileDialogCustomize = {
4553 IFileDialogCustomize_fnQueryInterface,
4554 IFileDialogCustomize_fnAddRef,
4555 IFileDialogCustomize_fnRelease,
4556 IFileDialogCustomize_fnEnableOpenDropDown,
4557 IFileDialogCustomize_fnAddMenu,
4558 IFileDialogCustomize_fnAddPushButton,
4559 IFileDialogCustomize_fnAddComboBox,
4560 IFileDialogCustomize_fnAddRadioButtonList,
4561 IFileDialogCustomize_fnAddCheckButton,
4562 IFileDialogCustomize_fnAddEditBox,
4563 IFileDialogCustomize_fnAddSeparator,
4564 IFileDialogCustomize_fnAddText,
4565 IFileDialogCustomize_fnSetControlLabel,
4566 IFileDialogCustomize_fnGetControlState,
4567 IFileDialogCustomize_fnSetControlState,
4568 IFileDialogCustomize_fnGetEditBoxText,
4569 IFileDialogCustomize_fnSetEditBoxText,
4570 IFileDialogCustomize_fnGetCheckButtonState,
4571 IFileDialogCustomize_fnSetCheckButtonState,
4572 IFileDialogCustomize_fnAddControlItem,
4573 IFileDialogCustomize_fnRemoveControlItem,
4574 IFileDialogCustomize_fnRemoveAllControlItems,
4575 IFileDialogCustomize_fnGetControlItemState,
4576 IFileDialogCustomize_fnSetControlItemState,
4577 IFileDialogCustomize_fnGetSelectedControlItem,
4578 IFileDialogCustomize_fnSetSelectedControlItem,
4579 IFileDialogCustomize_fnStartVisualGroup,
4580 IFileDialogCustomize_fnEndVisualGroup,
4581 IFileDialogCustomize_fnMakeProminent,
4582 IFileDialogCustomize_fnSetControlItemText
4585 static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv, enum ITEMDLG_TYPE type)
4587 FileDialogImpl *fdimpl;
4588 HRESULT hr;
4589 IShellFolder *psf;
4590 TRACE("%p, %s, %p\n", pUnkOuter, debugstr_guid(riid), ppv);
4592 if(!ppv)
4593 return E_POINTER;
4594 if(pUnkOuter)
4595 return CLASS_E_NOAGGREGATION;
4597 fdimpl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FileDialogImpl));
4598 if(!fdimpl)
4599 return E_OUTOFMEMORY;
4601 fdimpl->ref = 1;
4602 fdimpl->IFileDialog2_iface.lpVtbl = &vt_IFileDialog2;
4603 fdimpl->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
4604 fdimpl->IServiceProvider_iface.lpVtbl = &vt_IServiceProvider;
4605 fdimpl->ICommDlgBrowser3_iface.lpVtbl = &vt_ICommDlgBrowser3;
4606 fdimpl->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
4607 fdimpl->IFileDialogCustomize_iface.lpVtbl = &vt_IFileDialogCustomize;
4609 if(type == ITEMDLG_TYPE_OPEN)
4611 fdimpl->dlg_type = ITEMDLG_TYPE_OPEN;
4612 fdimpl->u.IFileOpenDialog_iface.lpVtbl = &vt_IFileOpenDialog;
4613 fdimpl->options = FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
4614 fdimpl->custom_title = fdimpl->custom_okbutton = NULL;
4616 else
4618 WCHAR buf[16];
4619 fdimpl->dlg_type = ITEMDLG_TYPE_SAVE;
4620 fdimpl->u.IFileSaveDialog_iface.lpVtbl = &vt_IFileSaveDialog;
4621 fdimpl->options = FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
4623 LoadStringW(COMDLG32_hInstance, IDS_SAVE, buf, ARRAY_SIZE(buf));
4624 fdimpl->custom_title = StrDupW(buf);
4625 fdimpl->custom_okbutton = StrDupW(buf);
4628 list_init(&fdimpl->events_clients);
4630 /* FIXME: The default folder setting should be restored for the
4631 * application if it was previously set. */
4632 SHGetDesktopFolder(&psf);
4633 SHGetItemFromObject((IUnknown*)psf, &IID_IShellItem, (void**)&fdimpl->psi_defaultfolder);
4634 IShellFolder_Release(psf);
4636 hr = init_custom_controls(fdimpl);
4637 if(FAILED(hr))
4639 ERR("Failed to initialize custom controls (0x%08x).\n", hr);
4640 IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4641 return E_FAIL;
4644 hr = IFileDialog2_QueryInterface(&fdimpl->IFileDialog2_iface, riid, ppv);
4645 IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4646 return hr;
4649 HRESULT FileOpenDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4651 return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_OPEN);
4654 HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4656 return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);