combase: Fix the trailing linefeed of a TRACE().
[wine/zf.git] / dlls / comdlg32 / itemdlg.c
blob1ae20ca782258304f0eaf9875b675379e4c0fefb
1 /*
2 * Common Item Dialog
4 * Copyright 2010,2011 David Hedberg
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
24 #define NONAMELESSUNION
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "wingdi.h"
30 #include "winreg.h"
31 #include "shlwapi.h"
33 #include "commdlg.h"
34 #include "cdlg.h"
35 #include "filedlgbrowser.h"
37 #include "wine/debug.h"
38 #include "wine/list.h"
40 #define IDC_NAV_TOOLBAR 200
41 #define IDC_NAVBACK 201
42 #define IDC_NAVFORWARD 202
44 #include <initguid.h>
45 /* This seems to be another version of IID_IFileDialogCustomize. If
46 * there is any difference I have yet to find it. */
47 DEFINE_GUID(IID_IFileDialogCustomizeAlt, 0x8016B7B3, 0x3D49, 0x4504, 0xA0,0xAA, 0x2A,0x37,0x49,0x4E,0x60,0x6F);
49 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
51 static const WCHAR notifysink_childW[] = {'n','f','s','_','c','h','i','l','d',0};
52 static const WCHAR floatnotifysinkW[] = {'F','l','o','a','t','N','o','t','i','f','y','S','i','n','k',0};
53 static const WCHAR radiobuttonlistW[] = {'R','a','d','i','o','B','u','t','t','o','n','L','i','s','t',0};
55 enum ITEMDLG_TYPE {
56 ITEMDLG_TYPE_OPEN,
57 ITEMDLG_TYPE_SAVE
60 enum ITEMDLG_CCTRL_TYPE {
61 IDLG_CCTRL_MENU,
62 IDLG_CCTRL_PUSHBUTTON,
63 IDLG_CCTRL_COMBOBOX,
64 IDLG_CCTRL_RADIOBUTTONLIST,
65 IDLG_CCTRL_CHECKBUTTON,
66 IDLG_CCTRL_EDITBOX,
67 IDLG_CCTRL_SEPARATOR,
68 IDLG_CCTRL_TEXT,
69 IDLG_CCTRL_OPENDROPDOWN,
70 IDLG_CCTRL_VISUALGROUP
73 typedef struct cctrl_item {
74 DWORD id, parent_id;
75 LPWSTR label;
76 CDCONTROLSTATEF cdcstate;
77 HWND hwnd;
78 struct list entry;
79 } cctrl_item;
81 typedef struct {
82 HWND hwnd, wrapper_hwnd;
83 UINT id, dlgid;
84 enum ITEMDLG_CCTRL_TYPE type;
85 CDCONTROLSTATEF cdcstate;
86 struct list entry;
88 struct list sub_cctrls;
89 struct list sub_cctrls_entry;
90 struct list sub_items;
91 } customctrl;
93 typedef struct {
94 struct list entry;
95 IFileDialogEvents *pfde;
96 DWORD cookie;
97 } events_client;
99 typedef struct FileDialogImpl {
100 IFileDialog2 IFileDialog2_iface;
101 union {
102 IFileOpenDialog IFileOpenDialog_iface;
103 IFileSaveDialog IFileSaveDialog_iface;
104 } u;
105 enum ITEMDLG_TYPE dlg_type;
106 IExplorerBrowserEvents IExplorerBrowserEvents_iface;
107 IServiceProvider IServiceProvider_iface;
108 ICommDlgBrowser3 ICommDlgBrowser3_iface;
109 IOleWindow IOleWindow_iface;
110 IFileDialogCustomize IFileDialogCustomize_iface;
111 LONG ref;
113 FILEOPENDIALOGOPTIONS options;
114 COMDLG_FILTERSPEC *filterspecs;
115 UINT filterspec_count;
116 UINT filetypeindex;
118 struct list events_clients;
119 DWORD events_next_cookie;
121 IShellItemArray *psia_selection;
122 IShellItemArray *psia_results;
123 IShellItem *psi_defaultfolder;
124 IShellItem *psi_setfolder;
125 IShellItem *psi_folder;
127 HWND dlg_hwnd;
128 IExplorerBrowser *peb;
129 DWORD ebevents_cookie;
131 LPWSTR set_filename;
132 LPWSTR default_ext;
133 LPWSTR custom_title;
134 LPWSTR custom_okbutton;
135 LPWSTR custom_cancelbutton;
136 LPWSTR custom_filenamelabel;
138 UINT cctrl_width, cctrl_def_height, cctrls_cols;
139 UINT cctrl_indent, dpi_x, dpi_y;
140 HWND cctrls_hwnd;
141 struct list cctrls;
142 UINT_PTR cctrl_next_dlgid;
143 customctrl *cctrl_active_vg;
145 HMENU hmenu_opendropdown;
146 customctrl cctrl_opendropdown;
147 HFONT hfont_opendropdown;
148 BOOL opendropdown_has_selection;
149 DWORD opendropdown_selection;
151 GUID client_guid;
152 } FileDialogImpl;
154 /**************************************************************************
155 * Event wrappers.
157 static HRESULT events_OnFileOk(FileDialogImpl *This)
159 events_client *cursor;
160 HRESULT hr = S_OK;
161 TRACE("%p\n", This);
163 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
165 TRACE("Notifying %p\n", cursor);
166 hr = IFileDialogEvents_OnFileOk(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
167 if(FAILED(hr) && hr != E_NOTIMPL)
168 break;
171 if(hr == E_NOTIMPL)
172 hr = S_OK;
174 return hr;
177 static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder)
179 events_client *cursor;
180 HRESULT hr = S_OK;
181 TRACE("%p (%p)\n", This, folder);
183 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
185 TRACE("Notifying %p\n", cursor);
186 hr = IFileDialogEvents_OnFolderChanging(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, folder);
187 if(FAILED(hr) && hr != E_NOTIMPL)
188 break;
191 if(hr == E_NOTIMPL)
192 hr = S_OK;
194 return hr;
197 static void events_OnFolderChange(FileDialogImpl *This)
199 events_client *cursor;
200 TRACE("%p\n", This);
202 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
204 TRACE("Notifying %p\n", cursor);
205 IFileDialogEvents_OnFolderChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
209 static void events_OnSelectionChange(FileDialogImpl *This)
211 events_client *cursor;
212 TRACE("%p\n", This);
214 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
216 TRACE("Notifying %p\n", cursor);
217 IFileDialogEvents_OnSelectionChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
221 static void events_OnTypeChange(FileDialogImpl *This)
223 events_client *cursor;
224 TRACE("%p\n", This);
226 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
228 TRACE("Notifying %p\n", cursor);
229 IFileDialogEvents_OnTypeChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
233 static HRESULT events_OnOverwrite(FileDialogImpl *This, IShellItem *shellitem)
235 events_client *cursor;
236 HRESULT hr = S_OK;
237 FDE_OVERWRITE_RESPONSE response = FDEOR_DEFAULT;
238 TRACE("%p %p\n", This, shellitem);
240 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
242 TRACE("Notifying %p\n", cursor);
243 hr = IFileDialogEvents_OnOverwrite(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, shellitem, &response);
244 TRACE("<-- hr=%x response=%u\n", hr, response);
245 if(FAILED(hr) && hr != E_NOTIMPL)
246 break;
249 if(hr == E_NOTIMPL)
250 hr = S_OK;
252 if(SUCCEEDED(hr))
254 if (response == FDEOR_DEFAULT)
256 WCHAR buf[100];
257 int answer;
259 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, buf, 100);
260 answer = MessageBoxW(This->dlg_hwnd, buf, This->custom_title,
261 MB_YESNO | MB_ICONEXCLAMATION);
262 if (answer == IDNO || answer == IDCANCEL)
264 hr = E_FAIL;
267 else if (response == FDEOR_REFUSE)
268 hr = E_FAIL;
271 return hr;
274 static inline HRESULT get_cctrl_event(IFileDialogEvents *pfde, IFileDialogControlEvents **pfdce)
276 return IFileDialogEvents_QueryInterface(pfde, &IID_IFileDialogControlEvents, (void**)pfdce);
279 static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id)
281 events_client *cursor;
282 TRACE("%p\n", This);
284 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
286 IFileDialogControlEvents *pfdce;
287 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
289 TRACE("Notifying %p\n", cursor);
290 IFileDialogControlEvents_OnButtonClicked(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
291 IFileDialogControlEvents_Release(pfdce);
295 return S_OK;
298 static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DWORD item_id)
300 events_client *cursor;
301 TRACE("%p %i %i\n", This, ctl_id, item_id);
303 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
305 IFileDialogControlEvents *pfdce;
306 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
308 TRACE("Notifying %p\n", cursor);
309 IFileDialogControlEvents_OnItemSelected(pfdce, &This->IFileDialogCustomize_iface, ctl_id, item_id);
310 IFileDialogControlEvents_Release(pfdce);
314 return S_OK;
317 static HRESULT cctrl_event_OnCheckButtonToggled(FileDialogImpl *This, DWORD ctl_id, BOOL checked)
319 events_client *cursor;
320 TRACE("%p\n", This);
322 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
324 IFileDialogControlEvents *pfdce;
325 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
327 TRACE("Notifying %p\n", cursor);
328 IFileDialogControlEvents_OnCheckButtonToggled(pfdce, &This->IFileDialogCustomize_iface, ctl_id, checked);
329 IFileDialogControlEvents_Release(pfdce);
333 return S_OK;
336 static HRESULT cctrl_event_OnControlActivating(FileDialogImpl *This,
337 DWORD ctl_id)
339 events_client *cursor;
340 TRACE("%p\n", This);
342 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
344 IFileDialogControlEvents *pfdce;
345 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
347 TRACE("Notifying %p\n", cursor);
348 IFileDialogControlEvents_OnControlActivating(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
349 IFileDialogControlEvents_Release(pfdce);
353 return S_OK;
356 /**************************************************************************
357 * Helper functions.
359 static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
361 HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
362 UINT len;
364 if(!hwnd_edit)
366 if(This->set_filename)
368 len = lstrlenW(This->set_filename);
369 *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
370 lstrcpyW(*str, This->set_filename);
371 return len;
373 return 0;
376 len = SendMessageW(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);
377 *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
378 if(!*str)
379 return 0;
381 SendMessageW(hwnd_edit, WM_GETTEXT, len+1, (LPARAM)*str);
382 return len;
385 static BOOL set_file_name(FileDialogImpl *This, LPCWSTR str)
387 if(This->set_filename)
388 LocalFree(This->set_filename);
390 This->set_filename = str ? StrDupW(str) : NULL;
392 return SetDlgItemTextW(This->dlg_hwnd, IDC_FILENAME, This->set_filename);
395 static void fill_filename_from_selection(FileDialogImpl *This)
397 IShellItem *psi;
398 LPWSTR *names;
399 HRESULT hr;
400 UINT item_count, valid_count;
401 UINT len_total, i;
403 if(!This->psia_selection)
404 return;
406 hr = IShellItemArray_GetCount(This->psia_selection, &item_count);
407 if(FAILED(hr) || !item_count)
408 return;
410 names = HeapAlloc(GetProcessHeap(), 0, item_count*sizeof(LPWSTR));
412 /* Get names of the selected items */
413 valid_count = 0; len_total = 0;
414 for(i = 0; i < item_count; i++)
416 hr = IShellItemArray_GetItemAt(This->psia_selection, i, &psi);
417 if(SUCCEEDED(hr))
419 UINT attr;
421 hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &attr);
422 if(SUCCEEDED(hr) &&
423 (( (This->options & FOS_PICKFOLDERS) && !(attr & SFGAO_FOLDER)) ||
424 (!(This->options & FOS_PICKFOLDERS) && (attr & SFGAO_FOLDER))))
425 continue;
427 hr = IShellItem_GetDisplayName(psi, (This->options & FOS_PICKFOLDERS) ? SIGDN_FILESYSPATH : SIGDN_PARENTRELATIVEPARSING, &names[valid_count]);
428 if(SUCCEEDED(hr))
430 len_total += lstrlenW(names[valid_count]) + 3;
431 valid_count++;
433 IShellItem_Release(psi);
437 if(valid_count == 1)
439 set_file_name(This, names[0]);
440 CoTaskMemFree(names[0]);
442 else if(valid_count > 1)
444 LPWSTR string = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len_total);
445 LPWSTR cur_point = string;
447 for(i = 0; i < valid_count; i++)
449 LPWSTR file = names[i];
450 *cur_point++ = '\"';
451 lstrcpyW(cur_point, file);
452 cur_point += lstrlenW(file);
453 *cur_point++ = '\"';
454 *cur_point++ = ' ';
455 CoTaskMemFree(file);
457 *(cur_point-1) = '\0';
459 set_file_name(This, string);
460 HeapFree(GetProcessHeap(), 0, string);
463 HeapFree(GetProcessHeap(), 0, names);
464 return;
467 static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec)
469 WCHAR *endpos, *ext;
471 lstrcpyW(buf, spec);
472 if( (endpos = StrChrW(buf, ';')) )
473 *endpos = '\0';
475 ext = PathFindExtensionW(buf);
476 if(StrChrW(ext, '*'))
477 return NULL;
479 return ext;
482 static BOOL shell_item_exists(IShellItem* shellitem)
484 LPWSTR filename;
485 HRESULT hr;
486 BOOL result;
488 hr = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &filename);
489 if (SUCCEEDED(hr))
491 /* FIXME: Implement SFGAO_VALIDATE in Wine and use it instead. */
492 result = (GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES);
493 CoTaskMemFree(filename);
495 else
497 SFGAOF attributes;
498 result = SUCCEEDED(IShellItem_GetAttributes(shellitem, SFGAO_VALIDATE, &attributes));
501 return result;
504 static HRESULT on_default_action(FileDialogImpl *This)
506 IShellFolder *psf_parent, *psf_desktop;
507 LPITEMIDLIST *pidla;
508 LPITEMIDLIST current_folder;
509 LPWSTR fn_iter, files = NULL, tmp_files;
510 UINT file_count = 0, len, i;
511 int open_action;
512 HRESULT hr, ret = E_FAIL;
514 len = get_file_name(This, &tmp_files);
515 if(len)
517 UINT size_used;
518 file_count = COMDLG32_SplitFileNames(tmp_files, len, &files, &size_used);
519 CoTaskMemFree(tmp_files);
521 if(!file_count) return E_FAIL;
523 hr = SHGetIDListFromObject((IUnknown*)This->psi_folder, &current_folder);
524 if(FAILED(hr))
526 ERR("Failed to get pidl for current directory.\n");
527 HeapFree(GetProcessHeap(), 0, files);
528 return hr;
531 TRACE("Acting on %d file(s).\n", file_count);
533 pidla = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST) * file_count);
534 open_action = ONOPEN_OPEN;
535 fn_iter = files;
537 for(i = 0; i < file_count && open_action == ONOPEN_OPEN; i++)
539 WCHAR canon_filename[MAX_PATH];
540 psf_parent = NULL;
542 COMDLG32_GetCanonicalPath(current_folder, fn_iter, canon_filename);
544 if( (This->options & FOS_NOVALIDATE) &&
545 !(This->options & FOS_FILEMUSTEXIST) )
546 open_action = ONOPEN_OPEN;
547 else
548 open_action = ONOPEN_BROWSE;
550 open_action = FILEDLG95_ValidatePathAction(canon_filename, &psf_parent, This->dlg_hwnd,
551 This->options & ~FOS_FILEMUSTEXIST,
552 (This->dlg_type == ITEMDLG_TYPE_SAVE),
553 open_action);
555 /* Add the proper extension */
556 if(open_action == ONOPEN_OPEN)
558 static const WCHAR dotW[] = {'.',0};
560 if(This->dlg_type == ITEMDLG_TYPE_SAVE)
562 WCHAR extbuf[MAX_PATH], *newext = NULL;
564 if(This->filterspec_count)
566 newext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
568 else if(This->default_ext)
570 lstrcpyW(extbuf, dotW);
571 lstrcatW(extbuf, This->default_ext);
572 newext = extbuf;
575 if(newext)
577 WCHAR *ext = PathFindExtensionW(canon_filename);
578 if(lstrcmpW(ext, newext))
579 lstrcatW(canon_filename, newext);
582 else
584 if( !(This->options & FOS_NOVALIDATE) && (This->options & FOS_FILEMUSTEXIST) &&
585 !PathFileExistsW(canon_filename))
587 if(This->default_ext)
589 lstrcatW(canon_filename, dotW);
590 lstrcatW(canon_filename, This->default_ext);
592 if(!PathFileExistsW(canon_filename))
594 FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
595 open_action = ONOPEN_BROWSE;
598 else
600 FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
601 open_action = ONOPEN_BROWSE;
607 pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename);
609 if(psf_parent && !(open_action == ONOPEN_BROWSE))
610 IShellFolder_Release(psf_parent);
612 fn_iter += (WCHAR)lstrlenW(fn_iter) + 1;
615 HeapFree(GetProcessHeap(), 0, files);
616 ILFree(current_folder);
618 if((This->options & FOS_PICKFOLDERS) && open_action == ONOPEN_BROWSE)
619 open_action = ONOPEN_OPEN; /* FIXME: Multiple folders? */
621 switch(open_action)
623 case ONOPEN_SEARCH:
624 FIXME("Filtering not implemented.\n");
625 break;
627 case ONOPEN_BROWSE:
628 hr = IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psf_parent, SBSP_DEFBROWSER);
629 if(FAILED(hr))
630 ERR("Failed to browse to directory: %08x\n", hr);
632 IShellFolder_Release(psf_parent);
633 break;
635 case ONOPEN_OPEN:
636 hr = SHGetDesktopFolder(&psf_desktop);
637 if(SUCCEEDED(hr))
639 if(This->psia_results)
641 IShellItemArray_Release(This->psia_results);
642 This->psia_results = NULL;
645 hr = SHCreateShellItemArray(NULL, psf_desktop, file_count, (PCUITEMID_CHILD_ARRAY)pidla,
646 &This->psia_results);
648 IShellFolder_Release(psf_desktop);
650 if(FAILED(hr))
651 break;
653 if(This->options & FOS_PICKFOLDERS)
655 SFGAOF attributes;
656 hr = IShellItemArray_GetAttributes(This->psia_results, SIATTRIBFLAGS_AND, SFGAO_FOLDER, &attributes);
657 if(hr != S_OK)
659 WCHAR buf[64];
660 LoadStringW(COMDLG32_hInstance, IDS_INVALID_FOLDERNAME, buf, ARRAY_SIZE(buf));
662 MessageBoxW(This->dlg_hwnd, buf, This->custom_title, MB_OK | MB_ICONEXCLAMATION);
664 IShellItemArray_Release(This->psia_results);
665 This->psia_results = NULL;
666 break;
670 if((This->options & FOS_OVERWRITEPROMPT) && This->dlg_type == ITEMDLG_TYPE_SAVE)
672 IShellItem *shellitem;
674 for (i=0; SUCCEEDED(hr) && i<file_count; i++)
676 hr = IShellItemArray_GetItemAt(This->psia_results, i, &shellitem);
677 if (SUCCEEDED(hr))
679 if (shell_item_exists(shellitem))
680 hr = events_OnOverwrite(This, shellitem);
682 IShellItem_Release(shellitem);
686 if (FAILED(hr))
687 break;
690 if(events_OnFileOk(This) == S_OK)
691 ret = S_OK;
693 break;
695 default:
696 ERR("Failed.\n");
697 break;
700 /* Clean up */
701 for(i = 0; i < file_count; i++)
702 ILFree(pidla[i]);
703 HeapFree(GetProcessHeap(), 0, pidla);
705 /* Success closes the dialog */
706 return ret;
709 static void show_opendropdown(FileDialogImpl *This)
711 HWND open_hwnd;
712 RECT open_rc;
713 MSG msg;
715 open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
717 GetWindowRect(open_hwnd, &open_rc);
719 if (TrackPopupMenu(This->hmenu_opendropdown, 0, open_rc.left, open_rc.bottom, 0, This->dlg_hwnd, NULL) &&
720 PeekMessageW(&msg, This->dlg_hwnd, WM_MENUCOMMAND, WM_MENUCOMMAND, PM_REMOVE))
722 MENUITEMINFOW mii;
724 This->opendropdown_has_selection = TRUE;
726 mii.cbSize = sizeof(mii);
727 mii.fMask = MIIM_ID;
728 GetMenuItemInfoW((HMENU)msg.lParam, msg.wParam, TRUE, &mii);
729 This->opendropdown_selection = mii.wID;
731 if(SUCCEEDED(on_default_action(This)))
732 EndDialog(This->dlg_hwnd, S_OK);
733 else
734 This->opendropdown_has_selection = FALSE;
738 /**************************************************************************
739 * Control item functions.
742 static void item_free(cctrl_item *item)
744 DestroyWindow(item->hwnd);
745 HeapFree(GetProcessHeap(), 0, item->label);
746 HeapFree(GetProcessHeap(), 0, item);
749 static cctrl_item* get_item(customctrl* parent, DWORD itemid, CDCONTROLSTATEF visible_flags, DWORD* position)
751 DWORD dummy;
752 cctrl_item* item;
754 if (!position) position = &dummy;
756 *position = 0;
758 LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
760 if (item->id == itemid)
761 return item;
763 if ((item->cdcstate & visible_flags) == visible_flags)
764 (*position)++;
767 return NULL;
770 static cctrl_item* get_first_item(customctrl* parent)
772 cctrl_item* item;
774 LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
776 if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
777 return item;
780 return NULL;
783 static HRESULT add_item(customctrl* parent, DWORD itemid, LPCWSTR label, cctrl_item** result)
785 cctrl_item* item;
786 LPWSTR label_copy;
788 if (get_item(parent, itemid, 0, NULL))
789 return E_INVALIDARG;
791 item = HeapAlloc(GetProcessHeap(), 0, sizeof(*item));
792 label_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(label)+1)*sizeof(WCHAR));
794 if (!item || !label_copy)
796 HeapFree(GetProcessHeap(), 0, item);
797 HeapFree(GetProcessHeap(), 0, label_copy);
798 return E_OUTOFMEMORY;
801 item->id = itemid;
802 item->parent_id = parent->id;
803 lstrcpyW(label_copy, label);
804 item->label = label_copy;
805 item->cdcstate = CDCS_VISIBLE|CDCS_ENABLED;
806 item->hwnd = NULL;
807 list_add_tail(&parent->sub_items, &item->entry);
809 *result = item;
811 return S_OK;
814 /**************************************************************************
815 * Control functions.
817 static inline customctrl *get_cctrl_from_dlgid(FileDialogImpl *This, DWORD dlgid)
819 customctrl *ctrl, *sub_ctrl;
821 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
823 if(ctrl->dlgid == dlgid)
824 return ctrl;
826 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
827 if(sub_ctrl->dlgid == dlgid)
828 return sub_ctrl;
831 ERR("Failed to find control with dialog id %d\n", dlgid);
832 return NULL;
835 static inline customctrl *get_cctrl(FileDialogImpl *This, DWORD ctlid)
837 customctrl *ctrl, *sub_ctrl;
839 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
841 if(ctrl->id == ctlid)
842 return ctrl;
844 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
845 if(sub_ctrl->id == ctlid)
846 return sub_ctrl;
849 if (This->hmenu_opendropdown && This->cctrl_opendropdown.id == ctlid)
850 return &This->cctrl_opendropdown;
852 TRACE("No existing control with control id %d\n", ctlid);
853 return NULL;
856 static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multiline)
858 LPWSTR text;
859 UINT len, final_width;
860 UINT lines, final_height;
861 SIZE size;
862 RECT rc;
863 HDC hdc;
864 WCHAR *c;
865 HFONT font;
867 TRACE("\n");
869 len = SendMessageW(hctrl, WM_GETTEXTLENGTH, 0, 0);
870 text = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
871 if(!text) return;
872 SendMessageW(hctrl, WM_GETTEXT, len+1, (LPARAM)text);
874 hdc = GetDC(hctrl);
875 font = (HFONT)SendMessageW(hctrl, WM_GETFONT, 0, 0);
876 font = SelectObject(hdc, font);
877 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &size);
878 SelectObject(hdc, font);
879 ReleaseDC(hctrl, hdc);
881 if(len && multiline)
883 /* FIXME: line-wrap */
884 for(lines = 1, c = text; *c != '\0'; c++)
885 if(*c == '\n') lines++;
887 final_height = size.cy*lines + 2*4;
889 else
891 GetWindowRect(hctrl, &rc);
892 final_height = rc.bottom - rc.top;
895 final_width = min(max(size.cx, min_width) + 4, max_width);
896 SetWindowPos(hctrl, NULL, 0, 0, final_width, final_height,
897 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
899 HeapFree(GetProcessHeap(), 0, text);
902 static UINT ctrl_get_height(customctrl *ctrl) {
903 RECT rc;
904 GetWindowRect(ctrl->wrapper_hwnd, &rc);
905 return rc.bottom - rc.top;
908 static void ctrl_free(customctrl *ctrl)
910 customctrl *sub_cur1, *sub_cur2;
911 cctrl_item *item_cur1, *item_cur2;
913 TRACE("Freeing control %p\n", ctrl);
914 if(ctrl->type == IDLG_CCTRL_MENU)
916 TBBUTTON tbb;
917 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
918 DestroyMenu((HMENU)tbb.dwData);
921 LIST_FOR_EACH_ENTRY_SAFE(sub_cur1, sub_cur2, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
923 list_remove(&sub_cur1->sub_cctrls_entry);
924 ctrl_free(sub_cur1);
927 LIST_FOR_EACH_ENTRY_SAFE(item_cur1, item_cur2, &ctrl->sub_items, cctrl_item, entry)
929 list_remove(&item_cur1->entry);
930 item_free(item_cur1);
933 DestroyWindow(ctrl->hwnd);
934 HeapFree(GetProcessHeap(), 0, ctrl);
937 static void customctrl_resize(FileDialogImpl *This, customctrl *ctrl)
939 RECT rc;
940 UINT total_height;
941 UINT max_width, size;
942 customctrl *sub_ctrl;
944 switch(ctrl->type)
946 case IDLG_CCTRL_PUSHBUTTON:
947 case IDLG_CCTRL_COMBOBOX:
948 case IDLG_CCTRL_CHECKBUTTON:
949 case IDLG_CCTRL_TEXT:
950 size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
951 ctrl_resize(ctrl->hwnd, size, size, TRUE);
952 GetWindowRect(ctrl->hwnd, &rc);
953 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
954 SWP_NOZORDER|SWP_NOMOVE);
955 break;
956 case IDLG_CCTRL_VISUALGROUP:
957 total_height = 0;
958 ctrl_resize(ctrl->hwnd, 0, This->cctrl_indent, TRUE);
960 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
962 customctrl_resize(This, sub_ctrl);
963 SetWindowPos(sub_ctrl->wrapper_hwnd, NULL, This->cctrl_indent, total_height, 0, 0,
964 SWP_NOZORDER|SWP_NOSIZE);
966 total_height += ctrl_get_height(sub_ctrl);
969 /* The label should be right adjusted */
971 UINT width, height;
973 GetWindowRect(ctrl->hwnd, &rc);
974 width = rc.right - rc.left;
975 height = rc.bottom - rc.top;
977 SetWindowPos(ctrl->hwnd, NULL, This->cctrl_indent - width, 0, width, height, SWP_NOZORDER);
980 /* Resize the wrapper window to fit all the sub controls */
981 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, This->cctrl_width + This->cctrl_indent, total_height,
982 SWP_NOZORDER|SWP_NOMOVE);
983 break;
984 case IDLG_CCTRL_RADIOBUTTONLIST:
986 cctrl_item* item;
988 total_height = 0;
989 max_width = 0;
991 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
993 size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
994 ctrl_resize(item->hwnd, size, size, TRUE);
995 SetWindowPos(item->hwnd, NULL, 0, total_height, 0, 0,
996 SWP_NOZORDER|SWP_NOSIZE);
998 GetWindowRect(item->hwnd, &rc);
1000 total_height += rc.bottom - rc.top;
1001 max_width = max(rc.right - rc.left, max_width);
1004 SetWindowPos(ctrl->hwnd, NULL, 0, 0, max_width, total_height,
1005 SWP_NOZORDER|SWP_NOMOVE);
1007 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, max_width, total_height,
1008 SWP_NOZORDER|SWP_NOMOVE);
1010 break;
1012 case IDLG_CCTRL_EDITBOX:
1013 case IDLG_CCTRL_SEPARATOR:
1014 case IDLG_CCTRL_MENU:
1015 case IDLG_CCTRL_OPENDROPDOWN:
1016 /* Nothing */
1017 break;
1021 static LRESULT notifysink_on_create(HWND hwnd, CREATESTRUCTW *crs)
1023 FileDialogImpl *This = crs->lpCreateParams;
1024 TRACE("%p\n", This);
1026 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1027 return TRUE;
1030 static LRESULT notifysink_on_bn_clicked(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
1032 customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
1034 TRACE("%p, %lx\n", This, wparam);
1036 if(ctrl)
1038 if(ctrl->type == IDLG_CCTRL_CHECKBUTTON)
1040 BOOL checked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
1041 cctrl_event_OnCheckButtonToggled(This, ctrl->id, checked);
1043 else
1044 cctrl_event_OnButtonClicked(This, ctrl->id);
1047 return TRUE;
1050 static LRESULT notifysink_on_cbn_selchange(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
1052 customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
1053 TRACE("%p, %p (%lx)\n", This, ctrl, wparam);
1055 if(ctrl)
1057 UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
1058 UINT selid = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
1060 cctrl_event_OnItemSelected(This, ctrl->id, selid);
1062 return TRUE;
1065 static LRESULT notifysink_on_tvn_dropdown(FileDialogImpl *This, LPARAM lparam)
1067 NMTOOLBARW *nmtb = (NMTOOLBARW*)lparam;
1068 customctrl *ctrl = get_cctrl_from_dlgid(This, GetDlgCtrlID(nmtb->hdr.hwndFrom));
1069 POINT pt = { 0, nmtb->rcButton.bottom };
1070 TBBUTTON tbb;
1071 UINT idcmd;
1073 TRACE("%p, %p (%lx)\n", This, ctrl, lparam);
1075 if(ctrl)
1077 cctrl_event_OnControlActivating(This,ctrl->id);
1079 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
1080 ClientToScreen(ctrl->hwnd, &pt);
1081 idcmd = TrackPopupMenu((HMENU)tbb.dwData, TPM_RETURNCMD, pt.x, pt.y, 0, This->dlg_hwnd, NULL);
1082 if(idcmd)
1083 cctrl_event_OnItemSelected(This, ctrl->id, idcmd);
1086 return TBDDRET_DEFAULT;
1089 static LRESULT notifysink_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1091 switch(HIWORD(wparam))
1093 case BN_CLICKED: return notifysink_on_bn_clicked(This, hwnd, wparam);
1094 case CBN_SELCHANGE: return notifysink_on_cbn_selchange(This, hwnd, wparam);
1097 return FALSE;
1100 static LRESULT notifysink_on_wm_notify(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1102 NMHDR *nmhdr = (NMHDR*)lparam;
1104 switch(nmhdr->code)
1106 case TBN_DROPDOWN: return notifysink_on_tvn_dropdown(This, lparam);
1109 return FALSE;
1112 static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
1114 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1115 customctrl *ctrl;
1116 HWND hwnd_child;
1117 RECT rc;
1119 switch(message)
1121 case WM_NCCREATE: return notifysink_on_create(hwnd, (CREATESTRUCTW*)lparam);
1122 case WM_COMMAND: return notifysink_on_wm_command(This, hwnd, wparam, lparam);
1123 case WM_NOTIFY: return notifysink_on_wm_notify(This, hwnd, wparam, lparam);
1124 case WM_SIZE:
1125 hwnd_child = GetPropW(hwnd, notifysink_childW);
1126 ctrl = (customctrl*)GetWindowLongPtrW(hwnd_child, GWLP_USERDATA);
1127 if(ctrl && ctrl->type != IDLG_CCTRL_VISUALGROUP)
1129 GetClientRect(hwnd, &rc);
1130 SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
1132 return TRUE;
1135 return DefWindowProcW(hwnd, message, wparam, lparam);
1138 static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
1139 LPCWSTR text, LPCWSTR wndclass, DWORD ctrl_wsflags,
1140 DWORD ctrl_exflags, UINT height, customctrl **ppctrl)
1142 HWND ns_hwnd, control_hwnd, parent_hwnd;
1143 DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
1144 customctrl *ctrl;
1146 if(get_cctrl(This, id))
1147 return E_UNEXPECTED; /* Duplicate id */
1149 if(This->cctrl_active_vg)
1150 parent_hwnd = This->cctrl_active_vg->wrapper_hwnd;
1151 else
1152 parent_hwnd = This->cctrls_hwnd;
1154 ns_hwnd = CreateWindowExW(0, floatnotifysinkW, NULL, wsflags,
1155 0, 0, This->cctrl_width, height, parent_hwnd,
1156 (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
1157 control_hwnd = CreateWindowExW(ctrl_exflags, wndclass, text, wsflags | ctrl_wsflags,
1158 0, 0, This->cctrl_width, height, ns_hwnd,
1159 (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, 0);
1161 if(!ns_hwnd || !control_hwnd)
1163 ERR("Failed to create wrapper (%p) or control (%p)\n", ns_hwnd, control_hwnd);
1164 DestroyWindow(ns_hwnd);
1165 DestroyWindow(control_hwnd);
1167 return E_FAIL;
1170 SetPropW(ns_hwnd, notifysink_childW, control_hwnd);
1172 ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(customctrl));
1173 if(!ctrl)
1174 return E_OUTOFMEMORY;
1176 ctrl->hwnd = control_hwnd;
1177 ctrl->wrapper_hwnd = ns_hwnd;
1178 ctrl->id = id;
1179 ctrl->dlgid = This->cctrl_next_dlgid;
1180 ctrl->cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
1181 list_init(&ctrl->sub_cctrls);
1182 list_init(&ctrl->sub_items);
1184 if(This->cctrl_active_vg)
1185 list_add_tail(&This->cctrl_active_vg->sub_cctrls, &ctrl->sub_cctrls_entry);
1186 else
1187 list_add_tail(&This->cctrls, &ctrl->entry);
1189 SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)ctrl);
1191 if(ppctrl) *ppctrl = ctrl;
1193 This->cctrl_next_dlgid++;
1194 return S_OK;
1197 /**************************************************************************
1198 * Container functions.
1200 static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
1202 UINT container_height;
1203 UINT column_width;
1204 UINT nr_of_cols;
1205 UINT max_control_height, total_height = 0;
1206 UINT cur_col_pos, cur_row_pos;
1207 customctrl *ctrl;
1208 BOOL fits_height;
1209 UINT cspacing = MulDiv(90, This->dpi_x, USER_DEFAULT_SCREEN_DPI); /* Columns are spaced with 90px */
1210 UINT rspacing = MulDiv(4, This->dpi_y, USER_DEFAULT_SCREEN_DPI); /* Rows are spaced with 4 px. */
1212 /* Given the new width of the container, this function determines the
1213 * needed height of the container and places the controls according to
1214 * the new layout. Returns the new height.
1217 TRACE("%p\n", This);
1219 column_width = This->cctrl_width + cspacing;
1220 nr_of_cols = (container_width - This->cctrl_indent + cspacing) / column_width;
1222 /* We don't need to do anything unless the number of visible columns has changed. */
1223 if(nr_of_cols == This->cctrls_cols)
1225 RECT rc;
1226 GetWindowRect(This->cctrls_hwnd, &rc);
1227 return rc.bottom - rc.top;
1230 This->cctrls_cols = nr_of_cols;
1232 /* Get the size of the tallest control, and the total size of
1233 * all the controls to figure out the number of slots we need.
1235 max_control_height = 0;
1236 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1238 if(ctrl->cdcstate & CDCS_VISIBLE)
1240 UINT control_height = ctrl_get_height(ctrl);
1241 max_control_height = max(max_control_height, control_height);
1243 total_height += control_height + rspacing;
1247 if(!total_height)
1248 return 0;
1250 container_height = max(total_height / nr_of_cols, max_control_height + rspacing);
1251 TRACE("Guess: container_height: %d\n",container_height);
1253 /* Incrementally increase container_height until all the controls
1254 * fit.
1256 do {
1257 UINT columns_needed = 1;
1258 cur_row_pos = 0;
1260 fits_height = TRUE;
1261 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1263 if(ctrl->cdcstate & CDCS_VISIBLE)
1265 UINT control_height = ctrl_get_height(ctrl);
1267 if(cur_row_pos + control_height > container_height)
1269 if(++columns_needed > nr_of_cols)
1271 container_height += 1;
1272 fits_height = FALSE;
1273 break;
1275 cur_row_pos = 0;
1278 cur_row_pos += control_height + rspacing;
1281 } while(!fits_height);
1283 TRACE("Final container height: %d\n", container_height);
1285 /* Move the controls to their final destination
1287 cur_col_pos = 0; cur_row_pos = 0;
1288 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1290 if(ctrl->cdcstate & CDCS_VISIBLE)
1292 RECT rc;
1293 UINT control_height, control_indent;
1294 GetWindowRect(ctrl->wrapper_hwnd, &rc);
1295 control_height = rc.bottom - rc.top;
1297 if(cur_row_pos + control_height > container_height)
1299 cur_row_pos = 0;
1300 cur_col_pos += This->cctrl_width + cspacing;
1304 if(ctrl->type == IDLG_CCTRL_VISUALGROUP)
1305 control_indent = 0;
1306 else
1307 control_indent = This->cctrl_indent;
1309 SetWindowPos(ctrl->wrapper_hwnd, NULL, cur_col_pos + control_indent, cur_row_pos, 0, 0,
1310 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
1312 cur_row_pos += control_height + rspacing;
1316 /* Sanity check */
1317 if(cur_row_pos + This->cctrl_width > container_width)
1318 ERR("-- Failed to place controls properly.\n");
1320 return container_height;
1323 static void ctrl_set_font(customctrl *ctrl, HFONT font)
1325 customctrl *sub_ctrl;
1326 cctrl_item* item;
1328 SendMessageW(ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1330 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
1332 ctrl_set_font(sub_ctrl, font);
1335 if (ctrl->type == IDLG_CCTRL_RADIOBUTTONLIST)
1337 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
1339 SendMessageW(item->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1344 static void ctrl_container_reparent(FileDialogImpl *This, HWND parent)
1346 LONG wndstyle;
1348 if(parent)
1350 customctrl *ctrl;
1351 HFONT font;
1353 wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1354 wndstyle &= ~(WS_POPUP);
1355 wndstyle |= WS_CHILD;
1356 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1358 SetParent(This->cctrls_hwnd, parent);
1359 ShowWindow(This->cctrls_hwnd, TRUE);
1361 /* Set the fonts to match the dialog font. */
1362 font = (HFONT)SendMessageW(parent, WM_GETFONT, 0, 0);
1363 if(!font)
1364 ERR("Failed to get font handle from dialog.\n");
1366 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1368 if(font) ctrl_set_font(ctrl, font);
1369 customctrl_resize(This, ctrl);
1372 else
1374 ShowWindow(This->cctrls_hwnd, FALSE);
1376 wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1377 wndstyle &= ~(WS_CHILD);
1378 wndstyle |= WS_POPUP;
1379 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1381 SetParent(This->cctrls_hwnd, NULL);
1385 static LRESULT ctrl_container_on_create(HWND hwnd, CREATESTRUCTW *crs)
1387 FileDialogImpl *This = crs->lpCreateParams;
1388 TRACE("%p\n", This);
1390 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1391 return TRUE;
1394 static LRESULT ctrl_container_on_wm_destroy(FileDialogImpl *This)
1396 customctrl *cur1, *cur2;
1397 TRACE("%p\n", This);
1399 LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->cctrls, customctrl, entry)
1401 list_remove(&cur1->entry);
1402 ctrl_free(cur1);
1405 return TRUE;
1408 static LRESULT CALLBACK ctrl_container_wndproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1410 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1412 switch(umessage)
1414 case WM_NCCREATE: return ctrl_container_on_create(hwnd, (CREATESTRUCTW*)lparam);
1415 case WM_DESTROY: return ctrl_container_on_wm_destroy(This);
1416 default: return DefWindowProcW(hwnd, umessage, wparam, lparam);
1419 return FALSE;
1422 static void radiobuttonlist_set_selected_item(FileDialogImpl *This, customctrl *ctrl, cctrl_item *item)
1424 cctrl_item *cursor;
1426 LIST_FOR_EACH_ENTRY(cursor, &ctrl->sub_items, cctrl_item, entry)
1428 SendMessageW(cursor->hwnd, BM_SETCHECK, (cursor == item) ? BST_CHECKED : BST_UNCHECKED, 0);
1432 static LRESULT radiobuttonlist_on_bn_clicked(FileDialogImpl *This, HWND hwnd, HWND child)
1434 DWORD ctrl_id = (DWORD)GetWindowLongPtrW(hwnd, GWLP_ID);
1435 customctrl *ctrl;
1436 cctrl_item *item;
1437 BOOL found_item=FALSE;
1439 ctrl = get_cctrl_from_dlgid(This, ctrl_id);
1441 if (!ctrl)
1443 ERR("Can't find this control\n");
1444 return 0;
1447 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
1449 if (item->hwnd == child)
1451 found_item = TRUE;
1452 break;
1456 if (!found_item)
1458 ERR("Can't find control item\n");
1459 return 0;
1462 radiobuttonlist_set_selected_item(This, ctrl, item);
1464 cctrl_event_OnItemSelected(This, ctrl->id, item->id);
1466 return 0;
1469 static LRESULT radiobuttonlist_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
1471 switch(HIWORD(wparam))
1473 case BN_CLICKED: return radiobuttonlist_on_bn_clicked(This, hwnd, (HWND)lparam);
1476 return FALSE;
1479 static LRESULT CALLBACK radiobuttonlist_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
1481 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1483 switch(message)
1485 case WM_COMMAND: return radiobuttonlist_on_wm_command(This, hwnd, wparam, lparam);
1488 return DefWindowProcW(hwnd, message, wparam, lparam);
1491 static HRESULT init_custom_controls(FileDialogImpl *This)
1493 WNDCLASSW wc;
1494 HDC hdc;
1495 static const WCHAR ctrl_container_classname[] =
1496 {'i','d','l','g','_','c','o','n','t','a','i','n','e','r','_','p','a','n','e',0};
1498 InitCommonControlsEx(NULL);
1500 if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
1502 wc.style = CS_HREDRAW | CS_VREDRAW;
1503 wc.lpfnWndProc = ctrl_container_wndproc;
1504 wc.cbClsExtra = 0;
1505 wc.cbWndExtra = 0;
1506 wc.hInstance = COMDLG32_hInstance;
1507 wc.hIcon = 0;
1508 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1509 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1510 wc.lpszMenuName = NULL;
1511 wc.lpszClassName = ctrl_container_classname;
1513 if(!RegisterClassW(&wc)) return E_FAIL;
1516 This->cctrls_hwnd = CreateWindowExW(0, ctrl_container_classname, NULL,
1517 WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1518 0, 0, 0, 0, NULL, 0,
1519 COMDLG32_hInstance, This);
1520 if(!This->cctrls_hwnd)
1521 return E_FAIL;
1523 hdc = GetDC(This->cctrls_hwnd);
1524 This->dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
1525 This->dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
1526 ReleaseDC(This->cctrls_hwnd, hdc);
1528 This->cctrl_width = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI); /* Controls have a fixed width */
1529 This->cctrl_indent = MulDiv(100, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1530 This->cctrl_def_height = MulDiv(23, This->dpi_y, USER_DEFAULT_SCREEN_DPI);
1531 This->cctrls_cols = 0;
1533 This->cctrl_next_dlgid = 0x2000;
1534 list_init(&This->cctrls);
1535 This->cctrl_active_vg = NULL;
1537 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, WS_TABSTOP);
1539 /* Register class for */
1540 if( !GetClassInfoW(COMDLG32_hInstance, floatnotifysinkW, &wc) ||
1541 wc.hInstance != COMDLG32_hInstance)
1543 wc.style = CS_HREDRAW | CS_VREDRAW;
1544 wc.lpfnWndProc = notifysink_proc;
1545 wc.cbClsExtra = 0;
1546 wc.cbWndExtra = 0;
1547 wc.hInstance = COMDLG32_hInstance;
1548 wc.hIcon = 0;
1549 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1550 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1551 wc.lpszMenuName = NULL;
1552 wc.lpszClassName = floatnotifysinkW;
1554 if (!RegisterClassW(&wc))
1555 ERR("Failed to register FloatNotifySink window class.\n");
1558 if( !GetClassInfoW(COMDLG32_hInstance, radiobuttonlistW, &wc) ||
1559 wc.hInstance != COMDLG32_hInstance)
1561 wc.style = CS_HREDRAW | CS_VREDRAW;
1562 wc.lpfnWndProc = radiobuttonlist_proc;
1563 wc.cbClsExtra = 0;
1564 wc.cbWndExtra = 0;
1565 wc.hInstance = COMDLG32_hInstance;
1566 wc.hIcon = 0;
1567 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1568 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1569 wc.lpszMenuName = NULL;
1570 wc.lpszClassName = radiobuttonlistW;
1572 if (!RegisterClassW(&wc))
1573 ERR("Failed to register RadioButtonList window class.\n");
1576 return S_OK;
1579 /**************************************************************************
1580 * Window related functions.
1582 static BOOL update_open_dropdown(FileDialogImpl *This)
1584 /* Show or hide the open dropdown button as appropriate */
1585 BOOL show=FALSE, showing;
1586 HWND open_hwnd, dropdown_hwnd;
1588 if (This->hmenu_opendropdown)
1590 INT num_visible_items=0;
1591 cctrl_item* item;
1593 LIST_FOR_EACH_ENTRY(item, &This->cctrl_opendropdown.sub_items, cctrl_item, entry)
1595 if (item->cdcstate & CDCS_VISIBLE)
1597 num_visible_items++;
1598 if (num_visible_items >= 2)
1600 show = TRUE;
1601 break;
1607 open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1608 dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
1610 showing = (GetWindowLongPtrW(dropdown_hwnd, GWL_STYLE) & WS_VISIBLE) != 0;
1612 if (showing != show)
1614 RECT open_rc, dropdown_rc;
1616 GetWindowRect(open_hwnd, &open_rc);
1617 GetWindowRect(dropdown_hwnd, &dropdown_rc);
1619 if (show)
1621 ShowWindow(dropdown_hwnd, SW_SHOW);
1623 SetWindowPos(open_hwnd, NULL, 0, 0,
1624 (open_rc.right - open_rc.left) - (dropdown_rc.right - dropdown_rc.left),
1625 open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1627 else
1629 ShowWindow(dropdown_hwnd, SW_HIDE);
1631 SetWindowPos(open_hwnd, NULL, 0, 0,
1632 (open_rc.right - open_rc.left) + (dropdown_rc.right - dropdown_rc.left),
1633 open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1637 return show;
1640 static void update_layout(FileDialogImpl *This)
1642 HDWP hdwp;
1643 HWND hwnd;
1644 RECT dialog_rc;
1645 RECT cancel_rc, dropdown_rc, open_rc;
1646 RECT filetype_rc, filename_rc, filenamelabel_rc;
1647 RECT toolbar_rc, ebrowser_rc, customctrls_rc;
1648 static const UINT vspacing = 4, hspacing = 4;
1649 static const UINT min_width = 320, min_height = 200;
1650 BOOL show_dropdown;
1652 if (!GetClientRect(This->dlg_hwnd, &dialog_rc))
1654 TRACE("Invalid dialog window, not updating layout\n");
1655 return;
1658 if(dialog_rc.right < min_width || dialog_rc.bottom < min_height)
1660 TRACE("Dialog size (%d, %d) too small, not updating layout\n", dialog_rc.right, dialog_rc.bottom);
1661 return;
1664 /****
1665 * Calculate the size of the dialog and all the parts.
1668 /* Cancel button */
1669 hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL);
1670 if(hwnd)
1672 int cancel_width, cancel_height;
1673 GetWindowRect(hwnd, &cancel_rc);
1674 cancel_width = cancel_rc.right - cancel_rc.left;
1675 cancel_height = cancel_rc.bottom - cancel_rc.top;
1677 cancel_rc.left = dialog_rc.right - cancel_width - hspacing;
1678 cancel_rc.top = dialog_rc.bottom - cancel_height - vspacing;
1679 cancel_rc.right = cancel_rc.left + cancel_width;
1680 cancel_rc.bottom = cancel_rc.top + cancel_height;
1683 /* Open/Save dropdown */
1684 show_dropdown = update_open_dropdown(This);
1686 if(show_dropdown)
1688 int dropdown_width, dropdown_height;
1689 hwnd = GetDlgItem(This->dlg_hwnd, psh1);
1691 GetWindowRect(hwnd, &dropdown_rc);
1692 dropdown_width = dropdown_rc.right - dropdown_rc.left;
1693 dropdown_height = dropdown_rc.bottom - dropdown_rc.top;
1695 dropdown_rc.left = cancel_rc.left - dropdown_width - hspacing;
1696 dropdown_rc.top = cancel_rc.top;
1697 dropdown_rc.right = dropdown_rc.left + dropdown_width;
1698 dropdown_rc.bottom = dropdown_rc.top + dropdown_height;
1700 else
1702 dropdown_rc.left = dropdown_rc.right = cancel_rc.left - hspacing;
1703 dropdown_rc.top = cancel_rc.top;
1704 dropdown_rc.bottom = cancel_rc.bottom;
1707 /* Open/Save button */
1708 hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1709 if(hwnd)
1711 int open_width, open_height;
1712 GetWindowRect(hwnd, &open_rc);
1713 open_width = open_rc.right - open_rc.left;
1714 open_height = open_rc.bottom - open_rc.top;
1716 open_rc.left = dropdown_rc.left - open_width;
1717 open_rc.top = dropdown_rc.top;
1718 open_rc.right = open_rc.left + open_width;
1719 open_rc.bottom = open_rc.top + open_height;
1722 /* The filetype combobox. */
1723 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1724 if(hwnd)
1726 int filetype_width, filetype_height;
1727 GetWindowRect(hwnd, &filetype_rc);
1729 filetype_width = filetype_rc.right - filetype_rc.left;
1730 filetype_height = filetype_rc.bottom - filetype_rc.top;
1732 filetype_rc.right = cancel_rc.right;
1734 filetype_rc.left = filetype_rc.right - filetype_width;
1735 filetype_rc.top = cancel_rc.top - filetype_height - vspacing;
1736 filetype_rc.bottom = filetype_rc.top + filetype_height;
1738 if(!This->filterspec_count)
1739 filetype_rc.left = filetype_rc.right;
1742 /* Filename label. */
1743 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC);
1744 if(hwnd)
1746 int filetypelabel_width, filetypelabel_height;
1747 GetWindowRect(hwnd, &filenamelabel_rc);
1749 filetypelabel_width = filenamelabel_rc.right - filenamelabel_rc.left;
1750 filetypelabel_height = filenamelabel_rc.bottom - filenamelabel_rc.top;
1752 filenamelabel_rc.left = 160; /* FIXME */
1753 filenamelabel_rc.top = filetype_rc.top;
1754 filenamelabel_rc.right = filenamelabel_rc.left + filetypelabel_width;
1755 filenamelabel_rc.bottom = filenamelabel_rc.top + filetypelabel_height;
1758 /* Filename edit box. */
1759 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
1760 if(hwnd)
1762 int filename_width, filename_height;
1763 GetWindowRect(hwnd, &filename_rc);
1765 filename_width = filetype_rc.left - filenamelabel_rc.right - hspacing*2;
1766 filename_height = filename_rc.bottom - filename_rc.top;
1768 filename_rc.left = filenamelabel_rc.right + hspacing;
1769 filename_rc.top = filetype_rc.top;
1770 filename_rc.right = filename_rc.left + filename_width;
1771 filename_rc.bottom = filename_rc.top + filename_height;
1774 hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR);
1775 if(hwnd)
1777 GetWindowRect(hwnd, &toolbar_rc);
1778 MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2);
1781 /* The custom controls */
1782 customctrls_rc.left = dialog_rc.left + hspacing;
1783 customctrls_rc.right = dialog_rc.right - hspacing;
1784 customctrls_rc.bottom = filename_rc.top - vspacing;
1785 customctrls_rc.top = customctrls_rc.bottom -
1786 ctrl_container_resize(This, customctrls_rc.right - customctrls_rc.left);
1788 /* The ExplorerBrowser control. */
1789 ebrowser_rc.left = dialog_rc.left + hspacing;
1790 ebrowser_rc.top = toolbar_rc.bottom + vspacing;
1791 ebrowser_rc.right = dialog_rc.right - hspacing;
1792 ebrowser_rc.bottom = customctrls_rc.top - vspacing;
1794 /****
1795 * Move everything to the right place.
1798 /* FIXME: The Save Dialog uses a slightly different layout. */
1799 hdwp = BeginDeferWindowPos(7);
1801 if(hdwp && This->peb)
1802 IExplorerBrowser_SetRect(This->peb, &hdwp, ebrowser_rc);
1804 if(hdwp && This->cctrls_hwnd)
1805 DeferWindowPos(hdwp, This->cctrls_hwnd, NULL,
1806 customctrls_rc.left, customctrls_rc.top,
1807 customctrls_rc.right - customctrls_rc.left, customctrls_rc.bottom - customctrls_rc.top,
1808 SWP_NOZORDER | SWP_NOACTIVATE);
1810 /* The default controls */
1811 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE)) )
1812 DeferWindowPos(hdwp, hwnd, NULL, filetype_rc.left, filetype_rc.top, 0, 0,
1813 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1815 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
1816 DeferWindowPos(hdwp, hwnd, NULL, filename_rc.left, filename_rc.top,
1817 filename_rc.right - filename_rc.left, filename_rc.bottom - filename_rc.top,
1818 SWP_NOZORDER | SWP_NOACTIVATE);
1820 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)) )
1821 DeferWindowPos(hdwp, hwnd, NULL, filenamelabel_rc.left, filenamelabel_rc.top, 0, 0,
1822 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1824 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDOK)) )
1825 DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
1826 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1828 if(hdwp && This->hmenu_opendropdown && (hwnd = GetDlgItem(This->dlg_hwnd, psh1)))
1829 DeferWindowPos(hdwp, hwnd, NULL, dropdown_rc.left, dropdown_rc.top, 0, 0,
1830 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1832 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
1833 DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
1834 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1836 if(hdwp)
1837 EndDeferWindowPos(hdwp);
1838 else
1839 ERR("Failed to position dialog controls.\n");
1841 return;
1844 static HRESULT init_explorerbrowser(FileDialogImpl *This)
1846 IShellItem *psi_folder;
1847 IObjectWithSite *client;
1848 FOLDERSETTINGS fos;
1849 RECT rc = {0};
1850 HRESULT hr;
1852 /* Create ExplorerBrowser instance */
1853 OleInitialize(NULL);
1855 hr = CoCreateInstance(&CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER,
1856 &IID_IExplorerBrowser, (void**)&This->peb);
1857 if(FAILED(hr))
1859 ERR("Failed to instantiate ExplorerBrowser control.\n");
1860 return hr;
1863 IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES | EBO_NOBORDER);
1865 hr = IExplorerBrowser_Initialize(This->peb, This->dlg_hwnd, &rc, NULL);
1866 if(FAILED(hr))
1868 ERR("Failed to initialize the ExplorerBrowser control.\n");
1869 IExplorerBrowser_Release(This->peb);
1870 This->peb = NULL;
1871 return hr;
1873 hr = IExplorerBrowser_Advise(This->peb, &This->IExplorerBrowserEvents_iface, &This->ebevents_cookie);
1874 if(FAILED(hr))
1875 ERR("Advise (ExplorerBrowser) failed.\n");
1877 /* Get previous options? */
1878 fos.ViewMode = fos.fFlags = 0;
1879 if(!(This->options & FOS_ALLOWMULTISELECT))
1880 fos.fFlags |= FWF_SINGLESEL;
1882 IExplorerBrowser_SetFolderSettings(This->peb, &fos);
1884 hr = IExplorerBrowser_QueryInterface(This->peb, &IID_IObjectWithSite, (void**)&client);
1885 if (hr == S_OK)
1887 hr = IObjectWithSite_SetSite(client, (IUnknown*)&This->IFileDialog2_iface);
1888 IObjectWithSite_Release(client);
1889 if(FAILED(hr))
1890 ERR("SetSite failed, 0x%08x\n", hr);
1893 /* Browse somewhere */
1894 psi_folder = This->psi_setfolder ? This->psi_setfolder : This->psi_defaultfolder;
1895 IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psi_folder, SBSP_DEFBROWSER);
1897 return S_OK;
1900 static void init_toolbar(FileDialogImpl *This, HWND hwnd)
1902 HWND htoolbar;
1903 TBADDBITMAP tbab;
1904 TBBUTTON button[2];
1906 htoolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE,
1907 0, 0, 0, 0,
1908 hwnd, (HMENU)IDC_NAV_TOOLBAR, NULL, NULL);
1910 tbab.hInst = HINST_COMMCTRL;
1911 tbab.nID = IDB_HIST_LARGE_COLOR;
1912 SendMessageW(htoolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
1914 button[0].iBitmap = HIST_BACK;
1915 button[0].idCommand = IDC_NAVBACK;
1916 button[0].fsState = TBSTATE_ENABLED;
1917 button[0].fsStyle = BTNS_BUTTON;
1918 button[0].dwData = 0;
1919 button[0].iString = 0;
1921 button[1].iBitmap = HIST_FORWARD;
1922 button[1].idCommand = IDC_NAVFORWARD;
1923 button[1].fsState = TBSTATE_ENABLED;
1924 button[1].fsStyle = BTNS_BUTTON;
1925 button[1].dwData = 0;
1926 button[1].iString = 0;
1928 SendMessageW(htoolbar, TB_ADDBUTTONSW, 2, (LPARAM)button);
1929 SendMessageW(htoolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(24,24));
1930 SendMessageW(htoolbar, TB_AUTOSIZE, 0, 0);
1933 static void update_control_text(FileDialogImpl *This)
1935 HWND hitem;
1936 LPCWSTR custom_okbutton;
1937 cctrl_item* item;
1938 UINT min_width = MulDiv(50, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1939 UINT max_width = MulDiv(250, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
1941 if(This->custom_title)
1942 SetWindowTextW(This->dlg_hwnd, This->custom_title);
1944 if(This->hmenu_opendropdown && (item = get_first_item(&This->cctrl_opendropdown)))
1945 custom_okbutton = item->label;
1946 else
1947 custom_okbutton = This->custom_okbutton;
1949 if(custom_okbutton &&
1950 (hitem = GetDlgItem(This->dlg_hwnd, IDOK)))
1952 SetWindowTextW(hitem, custom_okbutton);
1953 ctrl_resize(hitem, min_width, max_width, FALSE);
1956 if(This->custom_cancelbutton &&
1957 (hitem = GetDlgItem(This->dlg_hwnd, IDCANCEL)))
1959 SetWindowTextW(hitem, This->custom_cancelbutton);
1960 ctrl_resize(hitem, min_width, max_width, FALSE);
1963 if(This->custom_filenamelabel &&
1964 (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)))
1966 SetWindowTextW(hitem, This->custom_filenamelabel);
1967 ctrl_resize(hitem, min_width, max_width, FALSE);
1971 static LRESULT CALLBACK dropdown_subclass_proc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1973 static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
1974 static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
1976 if (umessage == WM_LBUTTONDOWN)
1978 FileDialogImpl *This = GetPropW(hwnd, prop_this);
1980 SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
1981 show_opendropdown(This);
1982 SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
1984 return 0;
1987 return CallWindowProcW((WNDPROC)GetPropW(hwnd, prop_oldwndproc), hwnd, umessage, wparam, lparam);
1990 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
1992 FileDialogImpl *This = (FileDialogImpl*)lParam;
1993 HWND hitem;
1995 TRACE("(%p, %p)\n", This, hwnd);
1997 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1998 This->dlg_hwnd = hwnd;
2000 hitem = GetDlgItem(This->dlg_hwnd, pshHelp);
2001 if(hitem) ShowWindow(hitem, SW_HIDE);
2003 hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPESTATIC);
2004 if(hitem) ShowWindow(hitem, SW_HIDE);
2006 /* Fill filetypes combobox, or hide it. */
2007 hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
2008 if(This->filterspec_count)
2010 HDC hdc;
2011 HFONT font;
2012 SIZE size;
2013 UINT i, maxwidth = 0;
2015 hdc = GetDC(hitem);
2016 font = (HFONT)SendMessageW(hitem, WM_GETFONT, 0, 0);
2017 SelectObject(hdc, font);
2019 for(i = 0; i < This->filterspec_count; i++)
2021 SendMessageW(hitem, CB_ADDSTRING, 0, (LPARAM)This->filterspecs[i].pszName);
2023 if(GetTextExtentPoint32W(hdc, This->filterspecs[i].pszName, lstrlenW(This->filterspecs[i].pszName), &size))
2024 maxwidth = max(maxwidth, size.cx);
2026 ReleaseDC(hitem, hdc);
2028 if(maxwidth > 0)
2030 maxwidth += GetSystemMetrics(SM_CXVSCROLL) + 4;
2031 SendMessageW(hitem, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0);
2033 else
2034 ERR("Failed to calculate width of filetype dropdown\n");
2036 SendMessageW(hitem, CB_SETCURSEL, This->filetypeindex, 0);
2038 else
2039 ShowWindow(hitem, SW_HIDE);
2041 if(This->set_filename &&
2042 (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
2043 SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
2045 if(This->hmenu_opendropdown)
2047 HWND dropdown_hwnd;
2048 LOGFONTW lfw, lfw_marlett;
2049 HFONT dialog_font;
2050 static const WCHAR marlett[] = {'M','a','r','l','e','t','t',0};
2051 static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
2052 static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
2054 dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
2056 /* Change dropdown button font to Marlett */
2057 dialog_font = (HFONT)SendMessageW(dropdown_hwnd, WM_GETFONT, 0, 0);
2059 GetObjectW(dialog_font, sizeof(lfw), &lfw);
2061 memset(&lfw_marlett, 0, sizeof(lfw_marlett));
2062 lstrcpyW(lfw_marlett.lfFaceName, marlett);
2063 lfw_marlett.lfHeight = lfw.lfHeight;
2064 lfw_marlett.lfCharSet = SYMBOL_CHARSET;
2066 This->hfont_opendropdown = CreateFontIndirectW(&lfw_marlett);
2068 SendMessageW(dropdown_hwnd, WM_SETFONT, (LPARAM)This->hfont_opendropdown, 0);
2070 /* Subclass button so we can handle LBUTTONDOWN */
2071 SetPropW(dropdown_hwnd, prop_this, This);
2072 SetPropW(dropdown_hwnd, prop_oldwndproc,
2073 (HANDLE)SetWindowLongPtrW(dropdown_hwnd, GWLP_WNDPROC, (LONG_PTR)dropdown_subclass_proc));
2076 ctrl_container_reparent(This, This->dlg_hwnd);
2077 init_explorerbrowser(This);
2078 init_toolbar(This, hwnd);
2079 update_control_text(This);
2080 update_layout(This);
2082 if(This->filterspec_count)
2083 events_OnTypeChange(This);
2085 if ((hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)))
2086 SetFocus(hitem);
2088 return FALSE;
2091 static LRESULT on_wm_size(FileDialogImpl *This)
2093 update_layout(This);
2094 return FALSE;
2097 static LRESULT on_wm_getminmaxinfo(FileDialogImpl *This, LPARAM lparam)
2099 MINMAXINFO *mmi = (MINMAXINFO*)lparam;
2100 TRACE("%p (%p)\n", This, mmi);
2102 /* FIXME */
2103 mmi->ptMinTrackSize.x = 640;
2104 mmi->ptMinTrackSize.y = 480;
2106 return FALSE;
2109 static LRESULT on_wm_destroy(FileDialogImpl *This)
2111 TRACE("%p\n", This);
2113 if(This->peb)
2115 IExplorerBrowser_Destroy(This->peb);
2116 IExplorerBrowser_Release(This->peb);
2117 This->peb = NULL;
2120 ctrl_container_reparent(This, NULL);
2121 This->dlg_hwnd = NULL;
2123 DeleteObject(This->hfont_opendropdown);
2124 This->hfont_opendropdown = NULL;
2126 return TRUE;
2129 static LRESULT on_idok(FileDialogImpl *This)
2131 TRACE("%p\n", This);
2133 if(SUCCEEDED(on_default_action(This)))
2134 EndDialog(This->dlg_hwnd, S_OK);
2136 return FALSE;
2139 static LRESULT on_idcancel(FileDialogImpl *This)
2141 TRACE("%p\n", This);
2143 EndDialog(This->dlg_hwnd, HRESULT_FROM_WIN32(ERROR_CANCELLED));
2145 return FALSE;
2148 static LRESULT on_command_opendropdown(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2150 if(HIWORD(wparam) == BN_CLICKED)
2152 HWND hwnd = (HWND)lparam;
2153 SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
2154 show_opendropdown(This);
2155 SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
2158 return FALSE;
2161 static LRESULT on_browse_back(FileDialogImpl *This)
2163 TRACE("%p\n", This);
2164 IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEBACK);
2165 return FALSE;
2168 static LRESULT on_browse_forward(FileDialogImpl *This)
2170 TRACE("%p\n", This);
2171 IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEFORWARD);
2172 return FALSE;
2175 static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2177 if(HIWORD(wparam) == CBN_SELCHANGE)
2179 IShellView *psv;
2180 HRESULT hr;
2181 LPWSTR filename;
2182 UINT prev_index = This->filetypeindex;
2184 This->filetypeindex = SendMessageW((HWND)lparam, CB_GETCURSEL, 0, 0);
2185 TRACE("File type selection changed to %d.\n", This->filetypeindex);
2187 if(prev_index == This->filetypeindex)
2188 return FALSE;
2190 hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&psv);
2191 if(SUCCEEDED(hr))
2193 IShellView_Refresh(psv);
2194 IShellView_Release(psv);
2197 if(This->dlg_type == ITEMDLG_TYPE_SAVE && get_file_name(This, &filename))
2199 WCHAR buf[MAX_PATH], extbuf[MAX_PATH], *ext;
2201 ext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
2202 if(ext)
2204 lstrcpyW(buf, filename);
2206 if(PathMatchSpecW(buf, This->filterspecs[prev_index].pszSpec))
2207 PathRemoveExtensionW(buf);
2209 lstrcatW(buf, ext);
2210 set_file_name(This, buf);
2212 CoTaskMemFree(filename);
2215 /* The documentation claims that OnTypeChange is called only
2216 * when the dialog is opened, but this is obviously not the
2217 * case. */
2218 events_OnTypeChange(This);
2221 return FALSE;
2224 static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
2226 switch(LOWORD(wparam))
2228 case IDOK: return on_idok(This);
2229 case IDCANCEL: return on_idcancel(This);
2230 case psh1: return on_command_opendropdown(This, wparam, lparam);
2231 case IDC_NAVBACK: return on_browse_back(This);
2232 case IDC_NAVFORWARD: return on_browse_forward(This);
2233 case IDC_FILETYPE: return on_command_filetype(This, wparam, lparam);
2234 default: TRACE("Unknown command.\n");
2236 return FALSE;
2239 static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
2241 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
2243 switch(umessage)
2245 case WM_INITDIALOG: return on_wm_initdialog(hwnd, lparam);
2246 case WM_COMMAND: return on_wm_command(This, wparam, lparam);
2247 case WM_SIZE: return on_wm_size(This);
2248 case WM_GETMINMAXINFO: return on_wm_getminmaxinfo(This, lparam);
2249 case WM_DESTROY: return on_wm_destroy(This);
2252 return FALSE;
2255 static HRESULT create_dialog(FileDialogImpl *This, HWND parent)
2257 INT_PTR res;
2259 SetLastError(0);
2260 res = DialogBoxParamW(COMDLG32_hInstance,
2261 MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
2262 parent, itemdlg_dlgproc, (LPARAM)This);
2263 This->dlg_hwnd = NULL;
2264 if(res == -1)
2266 ERR("Failed to show dialog (LastError: %d)\n", GetLastError());
2267 return E_FAIL;
2270 TRACE("Returning 0x%08x\n", (HRESULT)res);
2271 return (HRESULT)res;
2274 /**************************************************************************
2275 * IFileDialog implementation
2277 static inline FileDialogImpl *impl_from_IFileDialog2(IFileDialog2 *iface)
2279 return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialog2_iface);
2282 static HRESULT WINAPI IFileDialog2_fnQueryInterface(IFileDialog2 *iface,
2283 REFIID riid,
2284 void **ppvObject)
2286 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2287 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
2289 *ppvObject = NULL;
2290 if(IsEqualGUID(riid, &IID_IUnknown) ||
2291 IsEqualGUID(riid, &IID_IFileDialog) ||
2292 IsEqualGUID(riid, &IID_IFileDialog2))
2294 *ppvObject = iface;
2296 else if(IsEqualGUID(riid, &IID_IFileOpenDialog) && This->dlg_type == ITEMDLG_TYPE_OPEN)
2298 *ppvObject = &This->u.IFileOpenDialog_iface;
2300 else if(IsEqualGUID(riid, &IID_IFileSaveDialog) && This->dlg_type == ITEMDLG_TYPE_SAVE)
2302 *ppvObject = &This->u.IFileSaveDialog_iface;
2304 else if(IsEqualGUID(riid, &IID_IExplorerBrowserEvents))
2306 *ppvObject = &This->IExplorerBrowserEvents_iface;
2308 else if(IsEqualGUID(riid, &IID_IServiceProvider))
2310 *ppvObject = &This->IServiceProvider_iface;
2312 else if(IsEqualGUID(&IID_ICommDlgBrowser3, riid) ||
2313 IsEqualGUID(&IID_ICommDlgBrowser2, riid) ||
2314 IsEqualGUID(&IID_ICommDlgBrowser, riid))
2316 *ppvObject = &This->ICommDlgBrowser3_iface;
2318 else if(IsEqualGUID(&IID_IOleWindow, riid))
2320 *ppvObject = &This->IOleWindow_iface;
2322 else if(IsEqualGUID(riid, &IID_IFileDialogCustomize) ||
2323 IsEqualGUID(riid, &IID_IFileDialogCustomizeAlt))
2325 *ppvObject = &This->IFileDialogCustomize_iface;
2327 else
2328 FIXME("Unknown interface requested: %s.\n", debugstr_guid(riid));
2330 if(*ppvObject)
2332 IUnknown_AddRef((IUnknown*)*ppvObject);
2333 return S_OK;
2336 return E_NOINTERFACE;
2339 static ULONG WINAPI IFileDialog2_fnAddRef(IFileDialog2 *iface)
2341 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2342 LONG ref = InterlockedIncrement(&This->ref);
2343 TRACE("%p - ref %d\n", This, ref);
2345 return ref;
2348 static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface)
2350 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2351 LONG ref = InterlockedDecrement(&This->ref);
2352 TRACE("%p - ref %d\n", This, ref);
2354 if(!ref)
2356 UINT i;
2357 for(i = 0; i < This->filterspec_count; i++)
2359 LocalFree((void*)This->filterspecs[i].pszName);
2360 LocalFree((void*)This->filterspecs[i].pszSpec);
2362 HeapFree(GetProcessHeap(), 0, This->filterspecs);
2364 DestroyWindow(This->cctrls_hwnd);
2366 if(This->psi_defaultfolder) IShellItem_Release(This->psi_defaultfolder);
2367 if(This->psi_setfolder) IShellItem_Release(This->psi_setfolder);
2368 if(This->psi_folder) IShellItem_Release(This->psi_folder);
2369 if(This->psia_selection) IShellItemArray_Release(This->psia_selection);
2370 if(This->psia_results) IShellItemArray_Release(This->psia_results);
2372 LocalFree(This->set_filename);
2373 LocalFree(This->default_ext);
2374 LocalFree(This->custom_title);
2375 LocalFree(This->custom_okbutton);
2376 LocalFree(This->custom_cancelbutton);
2377 LocalFree(This->custom_filenamelabel);
2379 DestroyMenu(This->hmenu_opendropdown);
2380 DeleteObject(This->hfont_opendropdown);
2382 HeapFree(GetProcessHeap(), 0, This);
2385 return ref;
2388 static HRESULT WINAPI IFileDialog2_fnShow(IFileDialog2 *iface, HWND hwndOwner)
2390 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2391 TRACE("%p (%p)\n", iface, hwndOwner);
2393 This->opendropdown_has_selection = FALSE;
2395 return create_dialog(This, hwndOwner);
2398 static HRESULT WINAPI IFileDialog2_fnSetFileTypes(IFileDialog2 *iface, UINT cFileTypes,
2399 const COMDLG_FILTERSPEC *rgFilterSpec)
2401 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2402 UINT i;
2403 TRACE("%p (%d, %p)\n", This, cFileTypes, rgFilterSpec);
2405 if(This->filterspecs)
2406 return E_UNEXPECTED;
2408 if(!rgFilterSpec)
2409 return E_INVALIDARG;
2411 if(!cFileTypes)
2412 return S_OK;
2414 This->filterspecs = HeapAlloc(GetProcessHeap(), 0, sizeof(COMDLG_FILTERSPEC)*cFileTypes);
2415 for(i = 0; i < cFileTypes; i++)
2417 This->filterspecs[i].pszName = StrDupW(rgFilterSpec[i].pszName);
2418 This->filterspecs[i].pszSpec = StrDupW(rgFilterSpec[i].pszSpec);
2420 This->filterspec_count = cFileTypes;
2422 return S_OK;
2425 static HRESULT WINAPI IFileDialog2_fnSetFileTypeIndex(IFileDialog2 *iface, UINT iFileType)
2427 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2428 TRACE("%p (%d)\n", This, iFileType);
2430 if(!This->filterspecs)
2431 return E_FAIL;
2433 iFileType = max(iFileType, 1);
2434 iFileType = min(iFileType, This->filterspec_count);
2435 This->filetypeindex = iFileType-1;
2437 return S_OK;
2440 static HRESULT WINAPI IFileDialog2_fnGetFileTypeIndex(IFileDialog2 *iface, UINT *piFileType)
2442 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2443 TRACE("%p (%p)\n", This, piFileType);
2445 if(!piFileType)
2446 return E_INVALIDARG;
2448 if(This->filterspec_count == 0)
2449 *piFileType = 0;
2450 else
2451 *piFileType = This->filetypeindex + 1;
2453 return S_OK;
2456 static HRESULT WINAPI IFileDialog2_fnAdvise(IFileDialog2 *iface, IFileDialogEvents *pfde, DWORD *pdwCookie)
2458 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2459 events_client *client;
2460 TRACE("%p (%p, %p)\n", This, pfde, pdwCookie);
2462 if(!pfde || !pdwCookie)
2463 return E_INVALIDARG;
2465 client = HeapAlloc(GetProcessHeap(), 0, sizeof(events_client));
2466 client->pfde = pfde;
2467 client->cookie = ++This->events_next_cookie;
2469 IFileDialogEvents_AddRef(pfde);
2470 *pdwCookie = client->cookie;
2472 list_add_tail(&This->events_clients, &client->entry);
2474 return S_OK;
2477 static HRESULT WINAPI IFileDialog2_fnUnadvise(IFileDialog2 *iface, DWORD dwCookie)
2479 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2480 events_client *client, *found = NULL;
2481 TRACE("%p (%d)\n", This, dwCookie);
2483 LIST_FOR_EACH_ENTRY(client, &This->events_clients, events_client, entry)
2485 if(client->cookie == dwCookie)
2487 found = client;
2488 break;
2492 if(found)
2494 list_remove(&found->entry);
2495 IFileDialogEvents_Release(found->pfde);
2496 HeapFree(GetProcessHeap(), 0, found);
2497 return S_OK;
2500 return E_INVALIDARG;
2503 static HRESULT WINAPI IFileDialog2_fnSetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS fos)
2505 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2506 TRACE("%p (0x%x)\n", This, fos);
2508 if (fos & ~(FOS_OVERWRITEPROMPT | FOS_STRICTFILETYPES | FOS_NOCHANGEDIR | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM
2509 | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_ALLOWMULTISELECT | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST
2510 | FOS_CREATEPROMPT | FOS_SHAREAWARE | FOS_NOREADONLYRETURN | FOS_NOTESTFILECREATE | FOS_HIDEMRUPLACES
2511 | FOS_HIDEPINNEDPLACES | FOS_NODEREFERENCELINKS | FOS_DONTADDTORECENT | FOS_FORCESHOWHIDDEN
2512 | FOS_DEFAULTNOMINIMODE | FOS_FORCEPREVIEWPANEON | FOS_SUPPORTSTREAMABLEITEMS))
2514 WARN("Invalid option %#x\n", fos);
2515 return E_INVALIDARG;
2518 if( !(This->options & FOS_PICKFOLDERS) && (fos & FOS_PICKFOLDERS) )
2520 WCHAR buf[30];
2521 LoadStringW(COMDLG32_hInstance, IDS_SELECT_FOLDER, buf, ARRAY_SIZE(buf));
2522 IFileDialog2_SetTitle(iface, buf);
2525 This->options = fos;
2527 return S_OK;
2530 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
2532 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2533 TRACE("%p (%p)\n", This, pfos);
2535 if(!pfos)
2536 return E_INVALIDARG;
2538 *pfos = This->options;
2540 return S_OK;
2543 static HRESULT WINAPI IFileDialog2_fnSetDefaultFolder(IFileDialog2 *iface, IShellItem *psi)
2545 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2546 TRACE("%p (%p)\n", This, psi);
2547 if(This->psi_defaultfolder)
2548 IShellItem_Release(This->psi_defaultfolder);
2550 This->psi_defaultfolder = psi;
2552 if(This->psi_defaultfolder)
2553 IShellItem_AddRef(This->psi_defaultfolder);
2555 return S_OK;
2558 static HRESULT WINAPI IFileDialog2_fnSetFolder(IFileDialog2 *iface, IShellItem *psi)
2560 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2561 TRACE("%p (%p)\n", This, psi);
2562 if(This->psi_setfolder)
2563 IShellItem_Release(This->psi_setfolder);
2565 This->psi_setfolder = psi;
2567 if(This->psi_setfolder)
2568 IShellItem_AddRef(This->psi_setfolder);
2570 return S_OK;
2573 static HRESULT WINAPI IFileDialog2_fnGetFolder(IFileDialog2 *iface, IShellItem **ppsi)
2575 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2576 TRACE("%p (%p)\n", This, ppsi);
2577 if(!ppsi)
2578 return E_INVALIDARG;
2580 /* FIXME:
2581 If the dialog is shown, return the current(ly selected) folder. */
2583 *ppsi = NULL;
2584 if(This->psi_folder)
2585 *ppsi = This->psi_folder;
2586 else if(This->psi_setfolder)
2587 *ppsi = This->psi_setfolder;
2588 else if(This->psi_defaultfolder)
2589 *ppsi = This->psi_defaultfolder;
2591 if(*ppsi)
2593 IShellItem_AddRef(*ppsi);
2594 return S_OK;
2597 return E_FAIL;
2600 static HRESULT WINAPI IFileDialog2_fnGetCurrentSelection(IFileDialog2 *iface, IShellItem **ppsi)
2602 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2603 HRESULT hr;
2604 TRACE("%p (%p)\n", This, ppsi);
2606 if(!ppsi)
2607 return E_INVALIDARG;
2609 if(This->psia_selection)
2611 /* FIXME: Check filename edit box */
2612 hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
2613 return hr;
2616 return E_FAIL;
2619 static HRESULT WINAPI IFileDialog2_fnSetFileName(IFileDialog2 *iface, LPCWSTR pszName)
2621 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2622 TRACE("%p (%s)\n", iface, debugstr_w(pszName));
2624 set_file_name(This, pszName);
2626 return S_OK;
2629 static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *pszName)
2631 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2632 TRACE("%p (%p)\n", iface, pszName);
2634 if(!pszName)
2635 return E_INVALIDARG;
2637 *pszName = NULL;
2638 get_file_name(This, pszName);
2639 return *pszName ? S_OK : E_FAIL;
2642 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
2644 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2645 TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
2647 LocalFree(This->custom_title);
2648 This->custom_title = StrDupW(pszTitle);
2649 update_control_text(This);
2651 return S_OK;
2654 static HRESULT WINAPI IFileDialog2_fnSetOkButtonLabel(IFileDialog2 *iface, LPCWSTR pszText)
2656 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2657 TRACE("%p (%s)\n", This, debugstr_w(pszText));
2659 LocalFree(This->custom_okbutton);
2660 This->custom_okbutton = StrDupW(pszText);
2661 update_control_text(This);
2662 update_layout(This);
2664 return S_OK;
2667 static HRESULT WINAPI IFileDialog2_fnSetFileNameLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2669 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2670 TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2672 LocalFree(This->custom_filenamelabel);
2673 This->custom_filenamelabel = StrDupW(pszLabel);
2674 update_control_text(This);
2675 update_layout(This);
2677 return S_OK;
2680 static HRESULT WINAPI IFileDialog2_fnGetResult(IFileDialog2 *iface, IShellItem **ppsi)
2682 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2683 HRESULT hr;
2684 TRACE("%p (%p)\n", This, ppsi);
2686 if(!ppsi)
2687 return E_INVALIDARG;
2689 if(This->psia_results)
2691 UINT item_count;
2692 hr = IShellItemArray_GetCount(This->psia_results, &item_count);
2693 if(SUCCEEDED(hr))
2695 if(item_count != 1)
2696 return E_FAIL;
2698 /* Adds a reference. */
2699 hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
2702 return hr;
2705 return E_UNEXPECTED;
2708 static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *psi, FDAP fdap)
2710 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2711 FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
2712 return S_OK;
2715 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
2717 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2718 TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
2720 LocalFree(This->default_ext);
2721 This->default_ext = StrDupW(pszDefaultExtension);
2723 return S_OK;
2726 static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
2728 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2729 TRACE("%p (0x%08x)\n", This, hr);
2731 if(This->dlg_hwnd)
2732 EndDialog(This->dlg_hwnd, hr);
2734 return S_OK;
2737 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
2739 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2740 TRACE("%p (%s)\n", This, debugstr_guid(guid));
2741 This->client_guid = *guid;
2742 return S_OK;
2745 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
2747 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2748 FIXME("stub - %p\n", This);
2749 return E_NOTIMPL;
2752 static HRESULT WINAPI IFileDialog2_fnSetFilter(IFileDialog2 *iface, IShellItemFilter *pFilter)
2754 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2755 FIXME("stub - %p (%p)\n", This, pFilter);
2756 return E_NOTIMPL;
2759 static HRESULT WINAPI IFileDialog2_fnSetCancelButtonLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2761 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2762 TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2764 LocalFree(This->custom_cancelbutton);
2765 This->custom_cancelbutton = StrDupW(pszLabel);
2766 update_control_text(This);
2767 update_layout(This);
2769 return S_OK;
2772 static HRESULT WINAPI IFileDialog2_fnSetNavigationRoot(IFileDialog2 *iface, IShellItem *psi)
2774 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2775 FIXME("stub - %p (%p)\n", This, psi);
2776 return E_NOTIMPL;
2779 static const IFileDialog2Vtbl vt_IFileDialog2 = {
2780 IFileDialog2_fnQueryInterface,
2781 IFileDialog2_fnAddRef,
2782 IFileDialog2_fnRelease,
2783 IFileDialog2_fnShow,
2784 IFileDialog2_fnSetFileTypes,
2785 IFileDialog2_fnSetFileTypeIndex,
2786 IFileDialog2_fnGetFileTypeIndex,
2787 IFileDialog2_fnAdvise,
2788 IFileDialog2_fnUnadvise,
2789 IFileDialog2_fnSetOptions,
2790 IFileDialog2_fnGetOptions,
2791 IFileDialog2_fnSetDefaultFolder,
2792 IFileDialog2_fnSetFolder,
2793 IFileDialog2_fnGetFolder,
2794 IFileDialog2_fnGetCurrentSelection,
2795 IFileDialog2_fnSetFileName,
2796 IFileDialog2_fnGetFileName,
2797 IFileDialog2_fnSetTitle,
2798 IFileDialog2_fnSetOkButtonLabel,
2799 IFileDialog2_fnSetFileNameLabel,
2800 IFileDialog2_fnGetResult,
2801 IFileDialog2_fnAddPlace,
2802 IFileDialog2_fnSetDefaultExtension,
2803 IFileDialog2_fnClose,
2804 IFileDialog2_fnSetClientGuid,
2805 IFileDialog2_fnClearClientData,
2806 IFileDialog2_fnSetFilter,
2807 IFileDialog2_fnSetCancelButtonLabel,
2808 IFileDialog2_fnSetNavigationRoot
2811 /**************************************************************************
2812 * IFileOpenDialog
2814 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
2816 return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
2819 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
2820 REFIID riid, void **ppvObject)
2822 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2823 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2826 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
2828 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2829 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2832 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
2834 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2835 return IFileDialog2_Release(&This->IFileDialog2_iface);
2838 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
2840 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2841 return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2844 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
2845 const COMDLG_FILTERSPEC *rgFilterSpec)
2847 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2848 return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2851 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
2853 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2854 return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2857 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
2859 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2860 return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2863 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
2864 DWORD *pdwCookie)
2866 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2867 return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2870 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
2872 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2873 return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2876 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
2878 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2879 return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2882 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2884 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2885 return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2888 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
2890 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2891 return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2894 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
2896 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2897 return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2900 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
2902 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2903 return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2906 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
2908 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2909 return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2912 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
2914 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2915 return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2918 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
2920 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2921 return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2924 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
2926 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2927 return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2930 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
2932 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2933 return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2936 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
2938 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2939 return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2942 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
2944 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2945 return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2948 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
2950 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2951 return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2954 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultExtension(IFileOpenDialog *iface,
2955 LPCWSTR pszDefaultExtension)
2957 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2958 return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2961 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
2963 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2964 return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2967 static HRESULT WINAPI IFileOpenDialog_fnSetClientGuid(IFileOpenDialog *iface, REFGUID guid)
2969 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2970 return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2973 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
2975 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2976 return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2979 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
2981 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2982 return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2985 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
2987 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2988 TRACE("%p (%p)\n", This, ppenum);
2990 *ppenum = This->psia_results;
2992 if(*ppenum)
2994 IShellItemArray_AddRef(*ppenum);
2995 return S_OK;
2998 return E_FAIL;
3001 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
3003 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
3004 TRACE("%p (%p)\n", This, ppsai);
3006 if(This->psia_selection)
3008 *ppsai = This->psia_selection;
3009 IShellItemArray_AddRef(*ppsai);
3010 return S_OK;
3013 return E_FAIL;
3016 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
3017 IFileOpenDialog_fnQueryInterface,
3018 IFileOpenDialog_fnAddRef,
3019 IFileOpenDialog_fnRelease,
3020 IFileOpenDialog_fnShow,
3021 IFileOpenDialog_fnSetFileTypes,
3022 IFileOpenDialog_fnSetFileTypeIndex,
3023 IFileOpenDialog_fnGetFileTypeIndex,
3024 IFileOpenDialog_fnAdvise,
3025 IFileOpenDialog_fnUnadvise,
3026 IFileOpenDialog_fnSetOptions,
3027 IFileOpenDialog_fnGetOptions,
3028 IFileOpenDialog_fnSetDefaultFolder,
3029 IFileOpenDialog_fnSetFolder,
3030 IFileOpenDialog_fnGetFolder,
3031 IFileOpenDialog_fnGetCurrentSelection,
3032 IFileOpenDialog_fnSetFileName,
3033 IFileOpenDialog_fnGetFileName,
3034 IFileOpenDialog_fnSetTitle,
3035 IFileOpenDialog_fnSetOkButtonLabel,
3036 IFileOpenDialog_fnSetFileNameLabel,
3037 IFileOpenDialog_fnGetResult,
3038 IFileOpenDialog_fnAddPlace,
3039 IFileOpenDialog_fnSetDefaultExtension,
3040 IFileOpenDialog_fnClose,
3041 IFileOpenDialog_fnSetClientGuid,
3042 IFileOpenDialog_fnClearClientData,
3043 IFileOpenDialog_fnSetFilter,
3044 IFileOpenDialog_fnGetResults,
3045 IFileOpenDialog_fnGetSelectedItems
3048 /**************************************************************************
3049 * IFileSaveDialog
3051 static inline FileDialogImpl *impl_from_IFileSaveDialog(IFileSaveDialog *iface)
3053 return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
3056 static HRESULT WINAPI IFileSaveDialog_fnQueryInterface(IFileSaveDialog *iface,
3057 REFIID riid,
3058 void **ppvObject)
3060 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3061 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3064 static ULONG WINAPI IFileSaveDialog_fnAddRef(IFileSaveDialog *iface)
3066 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3067 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3070 static ULONG WINAPI IFileSaveDialog_fnRelease(IFileSaveDialog *iface)
3072 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3073 return IFileDialog2_Release(&This->IFileDialog2_iface);
3076 static HRESULT WINAPI IFileSaveDialog_fnShow(IFileSaveDialog *iface, HWND hwndOwner)
3078 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3079 return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
3082 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypes(IFileSaveDialog *iface, UINT cFileTypes,
3083 const COMDLG_FILTERSPEC *rgFilterSpec)
3085 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3086 return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
3089 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypeIndex(IFileSaveDialog *iface, UINT iFileType)
3091 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3092 return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
3095 static HRESULT WINAPI IFileSaveDialog_fnGetFileTypeIndex(IFileSaveDialog *iface, UINT *piFileType)
3097 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3098 return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
3101 static HRESULT WINAPI IFileSaveDialog_fnAdvise(IFileSaveDialog *iface, IFileDialogEvents *pfde,
3102 DWORD *pdwCookie)
3104 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3105 return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
3108 static HRESULT WINAPI IFileSaveDialog_fnUnadvise(IFileSaveDialog *iface, DWORD dwCookie)
3110 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3111 return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
3114 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
3116 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3117 return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
3120 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
3122 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3123 return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
3126 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultFolder(IFileSaveDialog *iface, IShellItem *psi)
3128 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3129 return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
3132 static HRESULT WINAPI IFileSaveDialog_fnSetFolder(IFileSaveDialog *iface, IShellItem *psi)
3134 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3135 return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
3138 static HRESULT WINAPI IFileSaveDialog_fnGetFolder(IFileSaveDialog *iface, IShellItem **ppsi)
3140 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3141 return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
3144 static HRESULT WINAPI IFileSaveDialog_fnGetCurrentSelection(IFileSaveDialog *iface, IShellItem **ppsi)
3146 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3147 return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
3150 static HRESULT WINAPI IFileSaveDialog_fnSetFileName(IFileSaveDialog *iface, LPCWSTR pszName)
3152 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3153 return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
3156 static HRESULT WINAPI IFileSaveDialog_fnGetFileName(IFileSaveDialog *iface, LPWSTR *pszName)
3158 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3159 return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
3162 static HRESULT WINAPI IFileSaveDialog_fnSetTitle(IFileSaveDialog *iface, LPCWSTR pszTitle)
3164 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3165 return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
3168 static HRESULT WINAPI IFileSaveDialog_fnSetOkButtonLabel(IFileSaveDialog *iface, LPCWSTR pszText)
3170 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3171 return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
3174 static HRESULT WINAPI IFileSaveDialog_fnSetFileNameLabel(IFileSaveDialog *iface, LPCWSTR pszLabel)
3176 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3177 return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
3180 static HRESULT WINAPI IFileSaveDialog_fnGetResult(IFileSaveDialog *iface, IShellItem **ppsi)
3182 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3183 return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
3186 static HRESULT WINAPI IFileSaveDialog_fnAddPlace(IFileSaveDialog *iface, IShellItem *psi, FDAP fdap)
3188 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3189 return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
3192 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultExtension(IFileSaveDialog *iface,
3193 LPCWSTR pszDefaultExtension)
3195 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3196 return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
3199 static HRESULT WINAPI IFileSaveDialog_fnClose(IFileSaveDialog *iface, HRESULT hr)
3201 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3202 return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
3205 static HRESULT WINAPI IFileSaveDialog_fnSetClientGuid(IFileSaveDialog *iface, REFGUID guid)
3207 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3208 return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
3211 static HRESULT WINAPI IFileSaveDialog_fnClearClientData(IFileSaveDialog *iface)
3213 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3214 return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
3217 static HRESULT WINAPI IFileSaveDialog_fnSetFilter(IFileSaveDialog *iface, IShellItemFilter *pFilter)
3219 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3220 return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
3223 static HRESULT WINAPI IFileSaveDialog_fnSetSaveAsItem(IFileSaveDialog* iface, IShellItem *psi)
3225 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3226 FIXME("stub - %p (%p)\n", This, psi);
3227 return E_NOTIMPL;
3230 static HRESULT WINAPI IFileSaveDialog_fnSetProperties(IFileSaveDialog* iface, IPropertyStore *pStore)
3232 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3233 FIXME("stub - %p (%p)\n", This, pStore);
3234 return E_NOTIMPL;
3237 static HRESULT WINAPI IFileSaveDialog_fnSetCollectedProperties(IFileSaveDialog* iface,
3238 IPropertyDescriptionList *pList,
3239 BOOL fAppendDefault)
3241 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3242 FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
3243 return E_NOTIMPL;
3246 static HRESULT WINAPI IFileSaveDialog_fnGetProperties(IFileSaveDialog* iface, IPropertyStore **ppStore)
3248 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3249 FIXME("stub - %p (%p)\n", This, ppStore);
3250 return E_NOTIMPL;
3253 static HRESULT WINAPI IFileSaveDialog_fnApplyProperties(IFileSaveDialog* iface,
3254 IShellItem *psi,
3255 IPropertyStore *pStore,
3256 HWND hwnd,
3257 IFileOperationProgressSink *pSink)
3259 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
3260 FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
3261 return E_NOTIMPL;
3264 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
3265 IFileSaveDialog_fnQueryInterface,
3266 IFileSaveDialog_fnAddRef,
3267 IFileSaveDialog_fnRelease,
3268 IFileSaveDialog_fnShow,
3269 IFileSaveDialog_fnSetFileTypes,
3270 IFileSaveDialog_fnSetFileTypeIndex,
3271 IFileSaveDialog_fnGetFileTypeIndex,
3272 IFileSaveDialog_fnAdvise,
3273 IFileSaveDialog_fnUnadvise,
3274 IFileSaveDialog_fnSetOptions,
3275 IFileSaveDialog_fnGetOptions,
3276 IFileSaveDialog_fnSetDefaultFolder,
3277 IFileSaveDialog_fnSetFolder,
3278 IFileSaveDialog_fnGetFolder,
3279 IFileSaveDialog_fnGetCurrentSelection,
3280 IFileSaveDialog_fnSetFileName,
3281 IFileSaveDialog_fnGetFileName,
3282 IFileSaveDialog_fnSetTitle,
3283 IFileSaveDialog_fnSetOkButtonLabel,
3284 IFileSaveDialog_fnSetFileNameLabel,
3285 IFileSaveDialog_fnGetResult,
3286 IFileSaveDialog_fnAddPlace,
3287 IFileSaveDialog_fnSetDefaultExtension,
3288 IFileSaveDialog_fnClose,
3289 IFileSaveDialog_fnSetClientGuid,
3290 IFileSaveDialog_fnClearClientData,
3291 IFileSaveDialog_fnSetFilter,
3292 IFileSaveDialog_fnSetSaveAsItem,
3293 IFileSaveDialog_fnSetProperties,
3294 IFileSaveDialog_fnSetCollectedProperties,
3295 IFileSaveDialog_fnGetProperties,
3296 IFileSaveDialog_fnApplyProperties
3299 /**************************************************************************
3300 * IExplorerBrowserEvents implementation
3302 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
3304 return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
3307 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
3308 REFIID riid, void **ppvObject)
3310 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3311 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
3313 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3316 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
3318 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3319 TRACE("%p\n", This);
3320 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3323 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
3325 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3326 TRACE("%p\n", This);
3327 return IFileDialog2_Release(&This->IFileDialog2_iface);
3330 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
3331 PCIDLIST_ABSOLUTE pidlFolder)
3333 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3334 IShellItem *psi;
3335 HRESULT hr;
3336 TRACE("%p (%p)\n", This, pidlFolder);
3338 hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
3339 if(SUCCEEDED(hr))
3341 hr = events_OnFolderChanging(This, psi);
3342 IShellItem_Release(psi);
3344 /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
3345 if(hr == S_FALSE)
3346 hr = E_FAIL;
3348 return hr;
3350 else
3351 ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
3353 return S_OK;
3356 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
3357 IShellView *psv)
3359 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3360 TRACE("%p (%p)\n", This, psv);
3361 return S_OK;
3364 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
3365 PCIDLIST_ABSOLUTE pidlFolder)
3367 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3368 HRESULT hr;
3369 TRACE("%p (%p)\n", This, pidlFolder);
3371 if(This->psi_folder)
3372 IShellItem_Release(This->psi_folder);
3374 hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
3375 if(FAILED(hr))
3377 ERR("Failed to get the current folder.\n");
3378 This->psi_folder = NULL;
3381 events_OnFolderChange(This);
3383 return S_OK;
3386 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
3387 PCIDLIST_ABSOLUTE pidlFolder)
3389 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
3390 TRACE("%p (%p)\n", This, pidlFolder);
3391 return S_OK;
3394 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
3395 IExplorerBrowserEvents_fnQueryInterface,
3396 IExplorerBrowserEvents_fnAddRef,
3397 IExplorerBrowserEvents_fnRelease,
3398 IExplorerBrowserEvents_fnOnNavigationPending,
3399 IExplorerBrowserEvents_fnOnViewCreated,
3400 IExplorerBrowserEvents_fnOnNavigationComplete,
3401 IExplorerBrowserEvents_fnOnNavigationFailed
3404 /**************************************************************************
3405 * IServiceProvider implementation
3407 static inline FileDialogImpl *impl_from_IServiceProvider(IServiceProvider *iface)
3409 return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
3412 static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface,
3413 REFIID riid, void **ppvObject)
3415 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3416 TRACE("%p\n", This);
3417 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3420 static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface)
3422 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3423 TRACE("%p\n", This);
3424 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3427 static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface)
3429 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3430 TRACE("%p\n", This);
3431 return IFileDialog2_Release(&This->IFileDialog2_iface);
3434 static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
3435 REFGUID guidService,
3436 REFIID riid, void **ppv)
3438 FileDialogImpl *This = impl_from_IServiceProvider(iface);
3439 HRESULT hr = E_NOTIMPL;
3440 TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
3442 *ppv = NULL;
3443 if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
3444 hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
3445 else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
3446 hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
3447 else
3448 FIXME("Interface %s requested from unknown service %s\n",
3449 debugstr_guid(riid), debugstr_guid(guidService));
3451 return hr;
3454 static const IServiceProviderVtbl vt_IServiceProvider = {
3455 IServiceProvider_fnQueryInterface,
3456 IServiceProvider_fnAddRef,
3457 IServiceProvider_fnRelease,
3458 IServiceProvider_fnQueryService
3461 /**************************************************************************
3462 * ICommDlgBrowser3 implementation
3464 static inline FileDialogImpl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface)
3466 return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
3469 static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface,
3470 REFIID riid, void **ppvObject)
3472 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3473 TRACE("%p\n", This);
3474 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3477 static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface)
3479 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3480 TRACE("%p\n", This);
3481 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3484 static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface)
3486 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3487 TRACE("%p\n", This);
3488 return IFileDialog2_Release(&This->IFileDialog2_iface);
3491 static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3 *iface,
3492 IShellView *shv)
3494 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3495 HRESULT hr;
3496 TRACE("%p (%p)\n", This, shv);
3498 hr = on_default_action(This);
3500 if(SUCCEEDED(hr))
3501 EndDialog(This->dlg_hwnd, S_OK);
3503 return S_OK;
3506 static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange(ICommDlgBrowser3 *iface,
3507 IShellView *shv, ULONG uChange )
3509 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3510 IDataObject *new_selection;
3511 HRESULT hr;
3512 TRACE("%p (%p, %x)\n", This, shv, uChange);
3514 switch(uChange)
3516 case CDBOSC_SELCHANGE:
3517 if(This->psia_selection)
3519 IShellItemArray_Release(This->psia_selection);
3520 This->psia_selection = NULL;
3523 hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
3524 if(SUCCEEDED(hr))
3526 hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
3527 (void**)&This->psia_selection);
3528 if(SUCCEEDED(hr))
3530 fill_filename_from_selection(This);
3531 events_OnSelectionChange(This);
3534 IDataObject_Release(new_selection);
3536 break;
3537 default:
3538 TRACE("Unhandled state change\n");
3540 return S_OK;
3543 static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
3544 IShellView *shv, LPCITEMIDLIST pidl)
3546 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3547 IShellItem *psi;
3548 LPWSTR filename;
3549 LPITEMIDLIST parent_pidl;
3550 HRESULT hr;
3551 ULONG attr;
3552 TRACE("%p (%p, %p)\n", This, shv, pidl);
3554 if(!This->filterspec_count && !(This->options & FOS_PICKFOLDERS))
3555 return S_OK;
3557 hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
3558 if(SUCCEEDED(hr))
3560 LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
3561 hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
3562 ILFree(parent_pidl);
3563 ILFree(full_pidl);
3565 if(FAILED(hr))
3567 ERR("Failed to get shellitem (%08x).\n", hr);
3568 return S_OK;
3571 hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
3572 if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
3574 IShellItem_Release(psi);
3575 return S_OK;
3578 if((This->options & FOS_PICKFOLDERS) && !(attr & (SFGAO_FOLDER | SFGAO_LINK)))
3580 IShellItem_Release(psi);
3581 return S_FALSE;
3584 hr = S_OK;
3585 if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
3587 if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
3588 hr = S_FALSE;
3589 CoTaskMemFree(filename);
3592 IShellItem_Release(psi);
3593 return hr;
3596 static HRESULT WINAPI ICommDlgBrowser3_fnNotify(ICommDlgBrowser3 *iface,
3597 IShellView *ppshv, DWORD dwNotifyType)
3599 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3600 FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
3601 return E_NOTIMPL;
3604 static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText(ICommDlgBrowser3 *iface,
3605 IShellView *pshv,
3606 LPWSTR pszText, int cchMax)
3608 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3609 FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
3610 return E_NOTIMPL;
3613 static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags(ICommDlgBrowser3 *iface, DWORD *pdwFlags)
3615 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3616 FIXME("Stub: %p (%p)\n", This, pdwFlags);
3617 return E_NOTIMPL;
3620 static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked(ICommDlgBrowser3 *iface,
3621 IShellView *pshv, int iColumn)
3623 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3624 FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
3625 return E_NOTIMPL;
3628 static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter(ICommDlgBrowser3 *iface,
3629 LPWSTR pszFileSpec, int cchFileSpec)
3631 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3632 FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
3633 return E_NOTIMPL;
3636 static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated(ICommDlgBrowser3 *iface,
3637 IShellView *pshv)
3639 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3640 FIXME("Stub: %p (%p)\n", This, pshv);
3641 return E_NOTIMPL;
3644 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
3645 ICommDlgBrowser3_fnQueryInterface,
3646 ICommDlgBrowser3_fnAddRef,
3647 ICommDlgBrowser3_fnRelease,
3648 ICommDlgBrowser3_fnOnDefaultCommand,
3649 ICommDlgBrowser3_fnOnStateChange,
3650 ICommDlgBrowser3_fnIncludeObject,
3651 ICommDlgBrowser3_fnNotify,
3652 ICommDlgBrowser3_fnGetDefaultMenuText,
3653 ICommDlgBrowser3_fnGetViewFlags,
3654 ICommDlgBrowser3_fnOnColumnClicked,
3655 ICommDlgBrowser3_fnGetCurrentFilter,
3656 ICommDlgBrowser3_fnOnPreviewCreated
3659 /**************************************************************************
3660 * IOleWindow implementation
3662 static inline FileDialogImpl *impl_from_IOleWindow(IOleWindow *iface)
3664 return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
3667 static HRESULT WINAPI IOleWindow_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
3669 FileDialogImpl *This = impl_from_IOleWindow(iface);
3670 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3673 static ULONG WINAPI IOleWindow_fnAddRef(IOleWindow *iface)
3675 FileDialogImpl *This = impl_from_IOleWindow(iface);
3676 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3679 static ULONG WINAPI IOleWindow_fnRelease(IOleWindow *iface)
3681 FileDialogImpl *This = impl_from_IOleWindow(iface);
3682 return IFileDialog2_Release(&This->IFileDialog2_iface);
3685 static HRESULT WINAPI IOleWindow_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMOde)
3687 FileDialogImpl *This = impl_from_IOleWindow(iface);
3688 FIXME("Stub: %p (%d)\n", This, fEnterMOde);
3689 return E_NOTIMPL;
3692 static HRESULT WINAPI IOleWindow_fnGetWindow(IOleWindow *iface, HWND *phwnd)
3694 FileDialogImpl *This = impl_from_IOleWindow(iface);
3695 TRACE("%p (%p)\n", This, phwnd);
3696 *phwnd = This->dlg_hwnd;
3697 return S_OK;
3700 static const IOleWindowVtbl vt_IOleWindow = {
3701 IOleWindow_fnQueryInterface,
3702 IOleWindow_fnAddRef,
3703 IOleWindow_fnRelease,
3704 IOleWindow_fnGetWindow,
3705 IOleWindow_fnContextSensitiveHelp
3708 /**************************************************************************
3709 * IFileDialogCustomize implementation
3711 static inline FileDialogImpl *impl_from_IFileDialogCustomize(IFileDialogCustomize *iface)
3713 return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialogCustomize_iface);
3716 static HRESULT WINAPI IFileDialogCustomize_fnQueryInterface(IFileDialogCustomize *iface,
3717 REFIID riid, void **ppvObject)
3719 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3720 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3723 static ULONG WINAPI IFileDialogCustomize_fnAddRef(IFileDialogCustomize *iface)
3725 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3726 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3729 static ULONG WINAPI IFileDialogCustomize_fnRelease(IFileDialogCustomize *iface)
3731 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3732 return IFileDialog2_Release(&This->IFileDialog2_iface);
3735 static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCustomize *iface,
3736 DWORD dwIDCtl)
3738 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3739 MENUINFO mi;
3740 TRACE("%p (%d)\n", This, dwIDCtl);
3742 if (This->hmenu_opendropdown || get_cctrl(This, dwIDCtl))
3743 return E_UNEXPECTED;
3745 This->hmenu_opendropdown = CreatePopupMenu();
3747 if (!This->hmenu_opendropdown)
3748 return E_OUTOFMEMORY;
3750 mi.cbSize = sizeof(mi);
3751 mi.fMask = MIM_STYLE;
3752 mi.dwStyle = MNS_NOTIFYBYPOS;
3753 SetMenuInfo(This->hmenu_opendropdown, &mi);
3755 This->cctrl_opendropdown.hwnd = NULL;
3756 This->cctrl_opendropdown.wrapper_hwnd = NULL;
3757 This->cctrl_opendropdown.id = dwIDCtl;
3758 This->cctrl_opendropdown.dlgid = 0;
3759 This->cctrl_opendropdown.type = IDLG_CCTRL_OPENDROPDOWN;
3760 This->cctrl_opendropdown.cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
3761 list_init(&This->cctrl_opendropdown.sub_cctrls);
3762 list_init(&This->cctrl_opendropdown.sub_items);
3764 return S_OK;
3767 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
3768 DWORD dwIDCtl,
3769 LPCWSTR pszLabel)
3771 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3772 customctrl *ctrl;
3773 TBBUTTON tbb;
3774 HRESULT hr;
3775 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3777 hr = cctrl_create_new(This, dwIDCtl, NULL, TOOLBARCLASSNAMEW,
3778 TBSTYLE_FLAT | CCS_NODIVIDER, 0,
3779 This->cctrl_def_height, &ctrl);
3780 if(SUCCEEDED(hr))
3782 SendMessageW(ctrl->hwnd, TB_BUTTONSTRUCTSIZE, sizeof(tbb), 0);
3783 ctrl->type = IDLG_CCTRL_MENU;
3785 /* Add the actual button with a popup menu. */
3786 tbb.iBitmap = I_IMAGENONE;
3787 tbb.dwData = (DWORD_PTR)CreatePopupMenu();
3788 tbb.iString = (DWORD_PTR)pszLabel;
3789 tbb.fsState = TBSTATE_ENABLED;
3790 tbb.fsStyle = BTNS_WHOLEDROPDOWN;
3791 tbb.idCommand = 1;
3793 SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
3796 return hr;
3799 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
3800 DWORD dwIDCtl,
3801 LPCWSTR pszLabel)
3803 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3804 customctrl *ctrl;
3805 HRESULT hr;
3806 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3808 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
3809 This->cctrl_def_height, &ctrl);
3810 if(SUCCEEDED(hr))
3811 ctrl->type = IDLG_CCTRL_PUSHBUTTON;
3813 return hr;
3816 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
3817 DWORD dwIDCtl)
3819 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3820 customctrl *ctrl;
3821 HRESULT hr;
3822 TRACE("%p (%d)\n", This, dwIDCtl);
3824 hr = cctrl_create_new(This, dwIDCtl, NULL, WC_COMBOBOXW, CBS_DROPDOWNLIST, 0,
3825 This->cctrl_def_height, &ctrl);
3826 if(SUCCEEDED(hr))
3827 ctrl->type = IDLG_CCTRL_COMBOBOX;
3829 return hr;
3832 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
3833 DWORD dwIDCtl)
3835 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3836 customctrl *ctrl;
3837 HRESULT hr;
3838 TRACE("%p (%d)\n", This, dwIDCtl);
3840 hr = cctrl_create_new(This, dwIDCtl, NULL, radiobuttonlistW, 0, 0, 0, &ctrl);
3841 if(SUCCEEDED(hr))
3843 ctrl->type = IDLG_CCTRL_RADIOBUTTONLIST;
3844 SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)This);
3847 return hr;
3850 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
3851 DWORD dwIDCtl,
3852 LPCWSTR pszLabel,
3853 BOOL bChecked)
3855 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3856 customctrl *ctrl;
3857 HRESULT hr;
3858 TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
3860 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX|BS_MULTILINE, 0,
3861 This->cctrl_def_height, &ctrl);
3862 if(SUCCEEDED(hr))
3864 ctrl->type = IDLG_CCTRL_CHECKBUTTON;
3865 SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
3868 return hr;
3871 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
3872 DWORD dwIDCtl,
3873 LPCWSTR pszText)
3875 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3876 customctrl *ctrl;
3877 HRESULT hr;
3878 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3880 hr = cctrl_create_new(This, dwIDCtl, pszText, WC_EDITW, ES_AUTOHSCROLL, WS_EX_CLIENTEDGE,
3881 This->cctrl_def_height, &ctrl);
3882 if(SUCCEEDED(hr))
3883 ctrl->type = IDLG_CCTRL_EDITBOX;
3885 return hr;
3888 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
3889 DWORD dwIDCtl)
3891 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3892 customctrl *ctrl;
3893 HRESULT hr;
3894 TRACE("%p (%d)\n", This, dwIDCtl);
3896 hr = cctrl_create_new(This, dwIDCtl, NULL, WC_STATICW, SS_ETCHEDHORZ, 0,
3897 GetSystemMetrics(SM_CYEDGE), &ctrl);
3898 if(SUCCEEDED(hr))
3899 ctrl->type = IDLG_CCTRL_SEPARATOR;
3901 return hr;
3904 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
3905 DWORD dwIDCtl,
3906 LPCWSTR pszText)
3908 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3909 customctrl *ctrl;
3910 HRESULT hr;
3911 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3913 hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
3914 This->cctrl_def_height, &ctrl);
3915 if(SUCCEEDED(hr))
3916 ctrl->type = IDLG_CCTRL_TEXT;
3918 return hr;
3921 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
3922 DWORD dwIDCtl,
3923 LPCWSTR pszLabel)
3925 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3926 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3927 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3929 if(!ctrl) return E_INVALIDARG;
3931 switch(ctrl->type)
3933 case IDLG_CCTRL_MENU:
3934 case IDLG_CCTRL_PUSHBUTTON:
3935 case IDLG_CCTRL_CHECKBUTTON:
3936 case IDLG_CCTRL_TEXT:
3937 case IDLG_CCTRL_VISUALGROUP:
3938 SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
3939 break;
3940 case IDLG_CCTRL_OPENDROPDOWN:
3941 return E_NOTIMPL;
3942 default:
3943 break;
3946 return S_OK;
3949 static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomize *iface,
3950 DWORD dwIDCtl,
3951 CDCONTROLSTATEF *pdwState)
3953 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3954 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3955 TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
3957 if(!ctrl || ctrl->type == IDLG_CCTRL_OPENDROPDOWN) return E_NOTIMPL;
3959 *pdwState = ctrl->cdcstate;
3960 return S_OK;
3963 static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomize *iface,
3964 DWORD dwIDCtl,
3965 CDCONTROLSTATEF dwState)
3967 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3968 customctrl *ctrl = get_cctrl(This,dwIDCtl);
3969 TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
3971 if(ctrl && ctrl->hwnd)
3973 LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
3975 if(dwState & CDCS_ENABLED)
3976 wndstyle &= ~(WS_DISABLED);
3977 else
3978 wndstyle |= WS_DISABLED;
3980 if(dwState & CDCS_VISIBLE)
3981 wndstyle |= WS_VISIBLE;
3982 else
3983 wndstyle &= ~(WS_VISIBLE);
3985 SetWindowLongW(ctrl->hwnd, GWL_STYLE, wndstyle);
3987 /* We save the state separately since at least one application
3988 * relies on being able to hide a control. */
3989 ctrl->cdcstate = dwState;
3992 return S_OK;
3995 static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize *iface,
3996 DWORD dwIDCtl,
3997 WCHAR **ppszText)
3999 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4000 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4001 WCHAR len, *text;
4002 TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
4004 if(!ctrl || !ctrl->hwnd || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
4005 return E_FAIL;
4007 text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
4008 if(!text) return E_FAIL;
4010 SendMessageW(ctrl->hwnd, WM_GETTEXT, len+1, (LPARAM)text);
4011 *ppszText = text;
4012 return S_OK;
4015 static HRESULT WINAPI IFileDialogCustomize_fnSetEditBoxText(IFileDialogCustomize *iface,
4016 DWORD dwIDCtl,
4017 LPCWSTR pszText)
4019 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4020 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4021 TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
4023 if(!ctrl || ctrl->type != IDLG_CCTRL_EDITBOX)
4024 return E_FAIL;
4026 SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
4027 return S_OK;
4030 static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCustomize *iface,
4031 DWORD dwIDCtl,
4032 BOOL *pbChecked)
4034 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4035 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4036 TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
4038 if(ctrl && ctrl->hwnd)
4039 *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
4041 return S_OK;
4044 static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCustomize *iface,
4045 DWORD dwIDCtl,
4046 BOOL bChecked)
4048 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4049 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4050 TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
4052 if(ctrl && ctrl->hwnd)
4053 SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
4055 return S_OK;
4058 static UINT get_combobox_index_from_id(HWND cb_hwnd, DWORD dwIDItem)
4060 UINT count = SendMessageW(cb_hwnd, CB_GETCOUNT, 0, 0);
4061 UINT i;
4062 if(!count || (count == CB_ERR))
4063 return -1;
4065 for(i = 0; i < count; i++)
4066 if(SendMessageW(cb_hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
4067 return i;
4069 TRACE("Item with id %d not found in combobox %p (item count: %d)\n", dwIDItem, cb_hwnd, count);
4070 return -1;
4073 static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize *iface,
4074 DWORD dwIDCtl,
4075 DWORD dwIDItem,
4076 LPCWSTR pszLabel)
4078 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4079 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4080 HRESULT hr;
4081 TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4083 if(!ctrl) return E_FAIL;
4085 switch(ctrl->type)
4087 case IDLG_CCTRL_COMBOBOX:
4089 UINT index;
4090 cctrl_item* item;
4092 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4094 if (FAILED(hr)) return hr;
4096 index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
4097 SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
4099 return S_OK;
4101 case IDLG_CCTRL_MENU:
4102 case IDLG_CCTRL_OPENDROPDOWN:
4104 cctrl_item* item;
4105 HMENU hmenu;
4107 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4109 if (FAILED(hr)) return hr;
4111 if (ctrl->type == IDLG_CCTRL_MENU)
4113 TBBUTTON tbb;
4114 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4115 hmenu = (HMENU)tbb.dwData;
4117 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4118 hmenu = This->hmenu_opendropdown;
4120 AppendMenuW(hmenu, MF_STRING, dwIDItem, pszLabel);
4121 return S_OK;
4123 case IDLG_CCTRL_RADIOBUTTONLIST:
4125 cctrl_item* item;
4127 hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4129 if (SUCCEEDED(hr))
4131 item->hwnd = CreateWindowExW(0, WC_BUTTONW, pszLabel,
4132 WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|BS_RADIOBUTTON|BS_MULTILINE,
4133 0, 0, 0, 0, ctrl->hwnd, ULongToHandle(dwIDItem), COMDLG32_hInstance, 0);
4135 if (!item->hwnd)
4137 ERR("Failed to create radio button\n");
4138 list_remove(&item->entry);
4139 item_free(item);
4140 return E_FAIL;
4144 return hr;
4146 default:
4147 break;
4150 return E_NOINTERFACE; /* win7 */
4153 static HRESULT WINAPI IFileDialogCustomize_fnRemoveControlItem(IFileDialogCustomize *iface,
4154 DWORD dwIDCtl,
4155 DWORD dwIDItem)
4157 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4158 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4159 TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4161 if(!ctrl) return E_FAIL;
4163 switch(ctrl->type)
4165 case IDLG_CCTRL_COMBOBOX:
4167 cctrl_item* item;
4168 DWORD position;
4170 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4172 if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
4174 if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0) == CB_ERR)
4175 return E_FAIL;
4178 list_remove(&item->entry);
4179 item_free(item);
4181 return S_OK;
4183 case IDLG_CCTRL_MENU:
4184 case IDLG_CCTRL_OPENDROPDOWN:
4186 HMENU hmenu;
4187 cctrl_item* item;
4189 item = get_item(ctrl, dwIDItem, 0, NULL);
4191 if (!item)
4192 return E_UNEXPECTED;
4194 if (item->cdcstate & CDCS_VISIBLE)
4196 if (ctrl->type == IDLG_CCTRL_MENU)
4198 TBBUTTON tbb;
4199 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4200 hmenu = (HMENU)tbb.dwData;
4202 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4203 hmenu = This->hmenu_opendropdown;
4205 if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
4206 return E_UNEXPECTED;
4209 list_remove(&item->entry);
4210 item_free(item);
4212 return S_OK;
4214 case IDLG_CCTRL_RADIOBUTTONLIST:
4216 cctrl_item* item;
4218 item = get_item(ctrl, dwIDItem, 0, NULL);
4220 if (!item)
4221 return E_UNEXPECTED;
4223 list_remove(&item->entry);
4224 item_free(item);
4226 return S_OK;
4228 default:
4229 break;
4232 return E_FAIL;
4235 static HRESULT WINAPI IFileDialogCustomize_fnRemoveAllControlItems(IFileDialogCustomize *iface,
4236 DWORD dwIDCtl)
4238 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4239 TRACE("%p (%d)\n", This, dwIDCtl);
4241 /* Not implemented by native */
4242 return E_NOTIMPL;
4245 static HRESULT WINAPI IFileDialogCustomize_fnGetControlItemState(IFileDialogCustomize *iface,
4246 DWORD dwIDCtl,
4247 DWORD dwIDItem,
4248 CDCONTROLSTATEF *pdwState)
4250 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4251 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4252 TRACE("%p (%d, %d, %p)\n", This, dwIDCtl, dwIDItem, pdwState);
4254 if(!ctrl) return E_FAIL;
4256 switch(ctrl->type)
4258 case IDLG_CCTRL_COMBOBOX:
4259 case IDLG_CCTRL_MENU:
4260 case IDLG_CCTRL_OPENDROPDOWN:
4261 case IDLG_CCTRL_RADIOBUTTONLIST:
4263 cctrl_item* item;
4265 item = get_item(ctrl, dwIDItem, 0, NULL);
4267 if (!item)
4268 return E_UNEXPECTED;
4270 *pdwState = item->cdcstate;
4272 return S_OK;
4274 default:
4275 break;
4278 return E_FAIL;
4281 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCustomize *iface,
4282 DWORD dwIDCtl,
4283 DWORD dwIDItem,
4284 CDCONTROLSTATEF dwState)
4286 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4287 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4288 TRACE("%p (%d, %d, %x)\n", This, dwIDCtl, dwIDItem, dwState);
4290 if(!ctrl) return E_FAIL;
4292 switch(ctrl->type)
4294 case IDLG_CCTRL_COMBOBOX:
4296 cctrl_item* item;
4297 BOOL visible, was_visible;
4298 DWORD position;
4300 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
4302 if (!item)
4303 return E_UNEXPECTED;
4305 visible = ((dwState & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4306 was_visible = ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
4308 if (visible && !was_visible)
4310 SendMessageW(ctrl->hwnd, CB_INSERTSTRING, position, (LPARAM)item->label);
4311 SendMessageW(ctrl->hwnd, CB_SETITEMDATA, position, dwIDItem);
4313 else if (!visible && was_visible)
4315 SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0);
4318 item->cdcstate = dwState;
4320 return S_OK;
4322 case IDLG_CCTRL_MENU:
4323 case IDLG_CCTRL_OPENDROPDOWN:
4325 HMENU hmenu;
4326 cctrl_item* item;
4327 CDCONTROLSTATEF prev_state;
4328 DWORD position;
4330 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, &position);
4332 if (!item)
4333 return E_UNEXPECTED;
4335 prev_state = item->cdcstate;
4337 if (ctrl->type == IDLG_CCTRL_MENU)
4339 TBBUTTON tbb;
4340 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4341 hmenu = (HMENU)tbb.dwData;
4343 else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4344 hmenu = This->hmenu_opendropdown;
4346 if (dwState & CDCS_VISIBLE)
4348 if (prev_state & CDCS_VISIBLE)
4350 /* change state */
4351 EnableMenuItem(hmenu, dwIDItem,
4352 MF_BYCOMMAND|((dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED));
4354 else
4356 /* show item */
4357 MENUITEMINFOW mii;
4359 mii.cbSize = sizeof(mii);
4360 mii.fMask = MIIM_ID|MIIM_STATE|MIIM_STRING;
4361 mii.fState = (dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED;
4362 mii.wID = dwIDItem;
4363 mii.dwTypeData = item->label;
4365 InsertMenuItemW(hmenu, position, TRUE, &mii);
4368 else if (prev_state & CDCS_VISIBLE)
4370 /* hide item */
4371 DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND);
4374 item->cdcstate = dwState;
4376 if (ctrl->type == IDLG_CCTRL_OPENDROPDOWN)
4378 update_control_text(This);
4379 update_layout(This);
4382 return S_OK;
4384 case IDLG_CCTRL_RADIOBUTTONLIST:
4386 cctrl_item* item;
4388 item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, NULL);
4390 if (!item)
4391 return E_UNEXPECTED;
4393 /* Oddly, native allows setting this but doesn't seem to do anything with it. */
4394 item->cdcstate = dwState;
4396 return S_OK;
4398 default:
4399 break;
4402 return E_FAIL;
4405 static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogCustomize *iface,
4406 DWORD dwIDCtl,
4407 DWORD *pdwIDItem)
4409 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4410 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4411 TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwIDItem);
4413 if(!ctrl) return E_FAIL;
4415 switch(ctrl->type)
4417 case IDLG_CCTRL_COMBOBOX:
4419 UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
4420 if(index == CB_ERR)
4421 return E_FAIL;
4423 *pdwIDItem = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
4424 return S_OK;
4426 case IDLG_CCTRL_OPENDROPDOWN:
4427 if (This->opendropdown_has_selection)
4429 *pdwIDItem = This->opendropdown_selection;
4430 return S_OK;
4432 else
4434 /* Return first enabled item. */
4435 cctrl_item* item = get_first_item(ctrl);
4437 if (item)
4439 *pdwIDItem = item->id;
4440 return S_OK;
4443 WARN("no enabled items in open dropdown\n");
4444 return E_FAIL;
4446 case IDLG_CCTRL_RADIOBUTTONLIST:
4448 cctrl_item* item;
4450 LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
4452 if (SendMessageW(item->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED)
4454 *pdwIDItem = item->id;
4455 return S_OK;
4459 WARN("no checked items in radio button list\n");
4460 return E_FAIL;
4462 default:
4463 FIXME("Unsupported control type %d\n", ctrl->type);
4466 return E_NOTIMPL;
4469 static HRESULT WINAPI IFileDialogCustomize_fnSetSelectedControlItem(IFileDialogCustomize *iface,
4470 DWORD dwIDCtl,
4471 DWORD dwIDItem)
4473 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4474 customctrl *ctrl = get_cctrl(This, dwIDCtl);
4475 TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
4477 if(!ctrl) return E_INVALIDARG;
4479 switch(ctrl->type)
4481 case IDLG_CCTRL_COMBOBOX:
4483 UINT index = get_combobox_index_from_id(ctrl->hwnd, dwIDItem);
4485 if(index == -1)
4486 return E_INVALIDARG;
4488 if(SendMessageW(ctrl->hwnd, CB_SETCURSEL, index, 0) == CB_ERR)
4489 return E_FAIL;
4491 return S_OK;
4493 case IDLG_CCTRL_RADIOBUTTONLIST:
4495 cctrl_item* item;
4497 item = get_item(ctrl, dwIDItem, 0, NULL);
4499 if (item)
4501 radiobuttonlist_set_selected_item(This, ctrl, item);
4502 return S_OK;
4505 return E_INVALIDARG;
4507 default:
4508 FIXME("Unsupported control type %d\n", ctrl->type);
4511 return E_INVALIDARG;
4514 static HRESULT WINAPI IFileDialogCustomize_fnStartVisualGroup(IFileDialogCustomize *iface,
4515 DWORD dwIDCtl,
4516 LPCWSTR pszLabel)
4518 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4519 customctrl *vg;
4520 HRESULT hr;
4521 TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
4523 if(This->cctrl_active_vg)
4524 return E_UNEXPECTED;
4526 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_STATICW, 0, 0,
4527 This->cctrl_def_height, &vg);
4528 if(SUCCEEDED(hr))
4530 vg->type = IDLG_CCTRL_VISUALGROUP;
4531 This->cctrl_active_vg = vg;
4534 return hr;
4537 static HRESULT WINAPI IFileDialogCustomize_fnEndVisualGroup(IFileDialogCustomize *iface)
4539 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4540 TRACE("%p\n", This);
4542 This->cctrl_active_vg = NULL;
4544 return S_OK;
4547 static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize *iface,
4548 DWORD dwIDCtl)
4550 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4551 FIXME("stub - %p (%d)\n", This, dwIDCtl);
4552 return S_OK;
4555 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCustomize *iface,
4556 DWORD dwIDCtl,
4557 DWORD dwIDItem,
4558 LPCWSTR pszLabel)
4560 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
4561 FIXME("stub - %p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4562 return E_NOTIMPL;
4565 static const IFileDialogCustomizeVtbl vt_IFileDialogCustomize = {
4566 IFileDialogCustomize_fnQueryInterface,
4567 IFileDialogCustomize_fnAddRef,
4568 IFileDialogCustomize_fnRelease,
4569 IFileDialogCustomize_fnEnableOpenDropDown,
4570 IFileDialogCustomize_fnAddMenu,
4571 IFileDialogCustomize_fnAddPushButton,
4572 IFileDialogCustomize_fnAddComboBox,
4573 IFileDialogCustomize_fnAddRadioButtonList,
4574 IFileDialogCustomize_fnAddCheckButton,
4575 IFileDialogCustomize_fnAddEditBox,
4576 IFileDialogCustomize_fnAddSeparator,
4577 IFileDialogCustomize_fnAddText,
4578 IFileDialogCustomize_fnSetControlLabel,
4579 IFileDialogCustomize_fnGetControlState,
4580 IFileDialogCustomize_fnSetControlState,
4581 IFileDialogCustomize_fnGetEditBoxText,
4582 IFileDialogCustomize_fnSetEditBoxText,
4583 IFileDialogCustomize_fnGetCheckButtonState,
4584 IFileDialogCustomize_fnSetCheckButtonState,
4585 IFileDialogCustomize_fnAddControlItem,
4586 IFileDialogCustomize_fnRemoveControlItem,
4587 IFileDialogCustomize_fnRemoveAllControlItems,
4588 IFileDialogCustomize_fnGetControlItemState,
4589 IFileDialogCustomize_fnSetControlItemState,
4590 IFileDialogCustomize_fnGetSelectedControlItem,
4591 IFileDialogCustomize_fnSetSelectedControlItem,
4592 IFileDialogCustomize_fnStartVisualGroup,
4593 IFileDialogCustomize_fnEndVisualGroup,
4594 IFileDialogCustomize_fnMakeProminent,
4595 IFileDialogCustomize_fnSetControlItemText
4598 static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv, enum ITEMDLG_TYPE type)
4600 FileDialogImpl *fdimpl;
4601 HRESULT hr;
4602 IShellFolder *psf;
4603 TRACE("%p, %s, %p\n", pUnkOuter, debugstr_guid(riid), ppv);
4605 if(!ppv)
4606 return E_POINTER;
4607 if(pUnkOuter)
4608 return CLASS_E_NOAGGREGATION;
4610 fdimpl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FileDialogImpl));
4611 if(!fdimpl)
4612 return E_OUTOFMEMORY;
4614 fdimpl->ref = 1;
4615 fdimpl->IFileDialog2_iface.lpVtbl = &vt_IFileDialog2;
4616 fdimpl->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
4617 fdimpl->IServiceProvider_iface.lpVtbl = &vt_IServiceProvider;
4618 fdimpl->ICommDlgBrowser3_iface.lpVtbl = &vt_ICommDlgBrowser3;
4619 fdimpl->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
4620 fdimpl->IFileDialogCustomize_iface.lpVtbl = &vt_IFileDialogCustomize;
4622 if(type == ITEMDLG_TYPE_OPEN)
4624 fdimpl->dlg_type = ITEMDLG_TYPE_OPEN;
4625 fdimpl->u.IFileOpenDialog_iface.lpVtbl = &vt_IFileOpenDialog;
4626 fdimpl->options = FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
4627 fdimpl->custom_title = fdimpl->custom_okbutton = NULL;
4629 else
4631 WCHAR buf[16];
4632 fdimpl->dlg_type = ITEMDLG_TYPE_SAVE;
4633 fdimpl->u.IFileSaveDialog_iface.lpVtbl = &vt_IFileSaveDialog;
4634 fdimpl->options = FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
4636 LoadStringW(COMDLG32_hInstance, IDS_SAVE, buf, ARRAY_SIZE(buf));
4637 fdimpl->custom_title = StrDupW(buf);
4638 fdimpl->custom_okbutton = StrDupW(buf);
4641 list_init(&fdimpl->events_clients);
4643 /* FIXME: The default folder setting should be restored for the
4644 * application if it was previously set. */
4645 SHGetDesktopFolder(&psf);
4646 SHGetItemFromObject((IUnknown*)psf, &IID_IShellItem, (void**)&fdimpl->psi_defaultfolder);
4647 IShellFolder_Release(psf);
4649 hr = init_custom_controls(fdimpl);
4650 if(FAILED(hr))
4652 ERR("Failed to initialize custom controls (0x%08x).\n", hr);
4653 IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4654 return E_FAIL;
4657 hr = IFileDialog2_QueryInterface(&fdimpl->IFileDialog2_iface, riid, ppv);
4658 IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
4659 return hr;
4662 HRESULT FileOpenDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4664 return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_OPEN);
4667 HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
4669 return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);