Sync with trunk r64509.
[reactos.git] / dll / win32 / comdlg32 / itemdlg.c
1 /*
2 * Common Item Dialog
3 *
4 * Copyright 2010,2011 David Hedberg
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #ifndef __REACTOS__ /* Win 7 */
22
23 #include <stdarg.h>
24
25 #define COBJMACROS
26 #define NONAMELESSUNION
27 #define NONAMELESSSTRUCT
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "wingdi.h"
33 #include "winreg.h"
34 #include "shlwapi.h"
35
36 #include "commdlg.h"
37 #include "cdlg.h"
38 #include "filedlgbrowser.h"
39
40 #include "wine/debug.h"
41 #include "wine/list.h"
42
43 #define IDC_NAV_TOOLBAR 200
44 #define IDC_NAVBACK 201
45 #define IDC_NAVFORWARD 202
46
47 #include <initguid.h>
48 /* This seems to be another version of IID_IFileDialogCustomize. If
49 * there is any difference I have yet to find it. */
50 DEFINE_GUID(IID_IFileDialogCustomizeAlt, 0x8016B7B3, 0x3D49, 0x4504, 0xA0,0xAA, 0x2A,0x37,0x49,0x4E,0x60,0x6F);
51
52 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
53
54 static const WCHAR notifysink_childW[] = {'n','f','s','_','c','h','i','l','d',0};
55 static const WCHAR floatnotifysinkW[] = {'F','l','o','a','t','N','o','t','i','f','y','S','i','n','k',0};
56
57 enum ITEMDLG_TYPE {
58 ITEMDLG_TYPE_OPEN,
59 ITEMDLG_TYPE_SAVE
60 };
61
62 enum ITEMDLG_CCTRL_TYPE {
63 IDLG_CCTRL_MENU,
64 IDLG_CCTRL_PUSHBUTTON,
65 IDLG_CCTRL_COMBOBOX,
66 IDLG_CCTRL_RADIOBUTTONLIST,
67 IDLG_CCTRL_CHECKBUTTON,
68 IDLG_CCTRL_EDITBOX,
69 IDLG_CCTRL_SEPARATOR,
70 IDLG_CCTRL_TEXT,
71 IDLG_CCTRL_VISUALGROUP
72 };
73
74 typedef struct {
75 HWND hwnd, wrapper_hwnd;
76 UINT id, dlgid;
77 enum ITEMDLG_CCTRL_TYPE type;
78 CDCONTROLSTATEF cdcstate;
79 struct list entry;
80
81 struct list sub_cctrls;
82 struct list sub_cctrls_entry;
83 } customctrl;
84
85 typedef struct {
86 struct list entry;
87 IFileDialogEvents *pfde;
88 DWORD cookie;
89 } events_client;
90
91 typedef struct FileDialogImpl {
92 IFileDialog2 IFileDialog2_iface;
93 union {
94 IFileOpenDialog IFileOpenDialog_iface;
95 IFileSaveDialog IFileSaveDialog_iface;
96 } u;
97 enum ITEMDLG_TYPE dlg_type;
98 IExplorerBrowserEvents IExplorerBrowserEvents_iface;
99 IServiceProvider IServiceProvider_iface;
100 ICommDlgBrowser3 ICommDlgBrowser3_iface;
101 IOleWindow IOleWindow_iface;
102 IFileDialogCustomize IFileDialogCustomize_iface;
103 LONG ref;
104
105 FILEOPENDIALOGOPTIONS options;
106 COMDLG_FILTERSPEC *filterspecs;
107 UINT filterspec_count;
108 UINT filetypeindex;
109
110 struct list events_clients;
111 DWORD events_next_cookie;
112
113 IShellItemArray *psia_selection;
114 IShellItemArray *psia_results;
115 IShellItem *psi_defaultfolder;
116 IShellItem *psi_setfolder;
117 IShellItem *psi_folder;
118
119 HWND dlg_hwnd;
120 IExplorerBrowser *peb;
121 DWORD ebevents_cookie;
122
123 LPWSTR set_filename;
124 LPWSTR default_ext;
125 LPWSTR custom_title;
126 LPWSTR custom_okbutton;
127 LPWSTR custom_cancelbutton;
128 LPWSTR custom_filenamelabel;
129
130 UINT cctrl_width, cctrl_def_height, cctrls_cols;
131 UINT cctrl_indent;
132 HWND cctrls_hwnd;
133 struct list cctrls;
134 UINT_PTR cctrl_next_dlgid;
135 customctrl *cctrl_active_vg;
136
137 GUID client_guid;
138 } FileDialogImpl;
139
140 /**************************************************************************
141 * Event wrappers.
142 */
143 static HRESULT events_OnFileOk(FileDialogImpl *This)
144 {
145 events_client *cursor;
146 HRESULT hr = S_OK;
147 TRACE("%p\n", This);
148
149 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
150 {
151 TRACE("Notifying %p\n", cursor);
152 hr = IFileDialogEvents_OnFileOk(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
153 if(FAILED(hr) && hr != E_NOTIMPL)
154 break;
155 }
156
157 if(hr == E_NOTIMPL)
158 hr = S_OK;
159
160 return hr;
161 }
162
163 static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder)
164 {
165 events_client *cursor;
166 HRESULT hr = S_OK;
167 TRACE("%p (%p)\n", This, folder);
168
169 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
170 {
171 TRACE("Notifying %p\n", cursor);
172 hr = IFileDialogEvents_OnFolderChanging(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, folder);
173 if(FAILED(hr) && hr != E_NOTIMPL)
174 break;
175 }
176
177 if(hr == E_NOTIMPL)
178 hr = S_OK;
179
180 return hr;
181 }
182
183 static void events_OnFolderChange(FileDialogImpl *This)
184 {
185 events_client *cursor;
186 TRACE("%p\n", This);
187
188 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
189 {
190 TRACE("Notifying %p\n", cursor);
191 IFileDialogEvents_OnFolderChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
192 }
193 }
194
195 static void events_OnSelectionChange(FileDialogImpl *This)
196 {
197 events_client *cursor;
198 TRACE("%p\n", This);
199
200 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
201 {
202 TRACE("Notifying %p\n", cursor);
203 IFileDialogEvents_OnSelectionChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
204 }
205 }
206
207 static void events_OnTypeChange(FileDialogImpl *This)
208 {
209 events_client *cursor;
210 TRACE("%p\n", This);
211
212 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
213 {
214 TRACE("Notifying %p\n", cursor);
215 IFileDialogEvents_OnTypeChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
216 }
217 }
218
219 static inline HRESULT get_cctrl_event(IFileDialogEvents *pfde, IFileDialogControlEvents **pfdce)
220 {
221 return IFileDialogEvents_QueryInterface(pfde, &IID_IFileDialogControlEvents, (void**)pfdce);
222 }
223
224 static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id)
225 {
226 events_client *cursor;
227 TRACE("%p\n", This);
228
229 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
230 {
231 IFileDialogControlEvents *pfdce;
232 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
233 {
234 TRACE("Notifying %p\n", cursor);
235 IFileDialogControlEvents_OnButtonClicked(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
236 IFileDialogControlEvents_Release(pfdce);
237 }
238 }
239
240 return S_OK;
241 }
242
243 static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DWORD item_id)
244 {
245 events_client *cursor;
246 TRACE("%p\n", This);
247
248 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
249 {
250 IFileDialogControlEvents *pfdce;
251 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
252 {
253 TRACE("Notifying %p\n", cursor);
254 IFileDialogControlEvents_OnItemSelected(pfdce, &This->IFileDialogCustomize_iface, ctl_id, item_id);
255 IFileDialogControlEvents_Release(pfdce);
256 }
257 }
258
259 return S_OK;
260 }
261
262 static HRESULT cctrl_event_OnCheckButtonToggled(FileDialogImpl *This, DWORD ctl_id, BOOL checked)
263 {
264 events_client *cursor;
265 TRACE("%p\n", This);
266
267 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
268 {
269 IFileDialogControlEvents *pfdce;
270 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
271 {
272 TRACE("Notifying %p\n", cursor);
273 IFileDialogControlEvents_OnCheckButtonToggled(pfdce, &This->IFileDialogCustomize_iface, ctl_id, checked);
274 IFileDialogControlEvents_Release(pfdce);
275 }
276 }
277
278 return S_OK;
279 }
280
281 static HRESULT cctrl_event_OnControlActivating(FileDialogImpl *This,
282 DWORD ctl_id)
283 {
284 events_client *cursor;
285 TRACE("%p\n", This);
286
287 LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
288 {
289 IFileDialogControlEvents *pfdce;
290 if(SUCCEEDED(get_cctrl_event(cursor->pfde, &pfdce)))
291 {
292 TRACE("Notifying %p\n", cursor);
293 IFileDialogControlEvents_OnControlActivating(pfdce, &This->IFileDialogCustomize_iface, ctl_id);
294 IFileDialogControlEvents_Release(pfdce);
295 }
296 }
297
298 return S_OK;
299 }
300
301 /**************************************************************************
302 * Helper functions.
303 */
304 static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
305 {
306 HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
307 UINT len;
308
309 if(!hwnd_edit)
310 {
311 if(This->set_filename)
312 {
313 len = lstrlenW(This->set_filename);
314 *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
315 lstrcpyW(*str, This->set_filename);
316 return len;
317 }
318 return FALSE;
319 }
320
321 len = SendMessageW(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);
322 *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
323 if(!*str)
324 return FALSE;
325
326 SendMessageW(hwnd_edit, WM_GETTEXT, len+1, (LPARAM)*str);
327 return len;
328 }
329
330 static BOOL set_file_name(FileDialogImpl *This, LPCWSTR str)
331 {
332 HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
333
334 if(This->set_filename)
335 LocalFree(This->set_filename);
336
337 This->set_filename = StrDupW(str);
338
339 return SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str);
340 }
341
342 static void fill_filename_from_selection(FileDialogImpl *This)
343 {
344 IShellItem *psi;
345 LPWSTR *names;
346 HRESULT hr;
347 UINT item_count, valid_count;
348 UINT len_total, i;
349
350 if(!This->psia_selection)
351 return;
352
353 hr = IShellItemArray_GetCount(This->psia_selection, &item_count);
354 if(FAILED(hr) || !item_count)
355 return;
356
357 names = HeapAlloc(GetProcessHeap(), 0, item_count*sizeof(LPWSTR));
358
359 /* Get names of the selected items */
360 valid_count = 0; len_total = 0;
361 for(i = 0; i < item_count; i++)
362 {
363 hr = IShellItemArray_GetItemAt(This->psia_selection, i, &psi);
364 if(SUCCEEDED(hr))
365 {
366 UINT attr;
367
368 hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &attr);
369 if(SUCCEEDED(hr) &&
370 (( (This->options & FOS_PICKFOLDERS) && !(attr & SFGAO_FOLDER)) ||
371 (!(This->options & FOS_PICKFOLDERS) && (attr & SFGAO_FOLDER))))
372 continue;
373
374 hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &names[valid_count]);
375 if(SUCCEEDED(hr))
376 {
377 len_total += lstrlenW(names[valid_count]) + 3;
378 valid_count++;
379 }
380 IShellItem_Release(psi);
381 }
382 }
383
384 if(valid_count == 1)
385 {
386 set_file_name(This, names[0]);
387 CoTaskMemFree(names[0]);
388 }
389 else if(valid_count > 1)
390 {
391 LPWSTR string = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len_total);
392 LPWSTR cur_point = string;
393
394 for(i = 0; i < valid_count; i++)
395 {
396 LPWSTR file = names[i];
397 *cur_point++ = '\"';
398 lstrcpyW(cur_point, file);
399 cur_point += lstrlenW(file);
400 *cur_point++ = '\"';
401 *cur_point++ = ' ';
402 CoTaskMemFree(file);
403 }
404 *(cur_point-1) = '\0';
405
406 set_file_name(This, string);
407 HeapFree(GetProcessHeap(), 0, string);
408 }
409
410 HeapFree(GetProcessHeap(), 0, names);
411 return;
412 }
413
414 static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec)
415 {
416 WCHAR *endpos, *ext;
417
418 lstrcpyW(buf, spec);
419 if( (endpos = StrChrW(buf, ';')) )
420 *endpos = '\0';
421
422 ext = PathFindExtensionW(buf);
423 if(StrChrW(ext, '*'))
424 return NULL;
425
426 return ext;
427 }
428
429 static HRESULT on_default_action(FileDialogImpl *This)
430 {
431 IShellFolder *psf_parent, *psf_desktop;
432 LPITEMIDLIST *pidla;
433 LPITEMIDLIST current_folder;
434 LPWSTR fn_iter, files = NULL, tmp_files;
435 UINT file_count = 0, len, i;
436 int open_action;
437 HRESULT hr, ret = E_FAIL;
438
439 len = get_file_name(This, &tmp_files);
440 if(len)
441 {
442 UINT size_used;
443 file_count = COMDLG32_SplitFileNames(tmp_files, len, &files, &size_used);
444 CoTaskMemFree(tmp_files);
445 }
446 if(!file_count) return E_FAIL;
447
448 hr = SHGetIDListFromObject((IUnknown*)This->psi_folder, &current_folder);
449 if(FAILED(hr))
450 {
451 ERR("Failed to get pidl for current directory.\n");
452 HeapFree(GetProcessHeap(), 0, files);
453 return hr;
454 }
455
456 TRACE("Acting on %d file(s).\n", file_count);
457
458 pidla = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST) * file_count);
459 open_action = ONOPEN_OPEN;
460 fn_iter = files;
461
462 for(i = 0; i < file_count && open_action == ONOPEN_OPEN; i++)
463 {
464 WCHAR canon_filename[MAX_PATH];
465 psf_parent = NULL;
466
467 COMDLG32_GetCanonicalPath(current_folder, fn_iter, canon_filename);
468
469 if( (This->options & FOS_NOVALIDATE) &&
470 !(This->options & FOS_FILEMUSTEXIST) )
471 open_action = ONOPEN_OPEN;
472 else
473 open_action = ONOPEN_BROWSE;
474
475 open_action = FILEDLG95_ValidatePathAction(canon_filename, &psf_parent, This->dlg_hwnd,
476 This->options & ~FOS_FILEMUSTEXIST,
477 (This->dlg_type == ITEMDLG_TYPE_SAVE),
478 open_action);
479
480 /* Add the proper extension */
481 if(open_action == ONOPEN_OPEN)
482 {
483 static const WCHAR dotW[] = {'.',0};
484
485 if(This->dlg_type == ITEMDLG_TYPE_SAVE)
486 {
487 WCHAR extbuf[MAX_PATH], *newext = NULL;
488
489 if(This->filterspec_count)
490 {
491 newext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
492 }
493 else if(This->default_ext)
494 {
495 lstrcpyW(extbuf, dotW);
496 lstrcatW(extbuf, This->default_ext);
497 newext = extbuf;
498 }
499
500 if(newext)
501 {
502 WCHAR *ext = PathFindExtensionW(canon_filename);
503 if(lstrcmpW(ext, newext))
504 lstrcatW(canon_filename, newext);
505 }
506 }
507 else
508 {
509 if( !(This->options & FOS_NOVALIDATE) && (This->options & FOS_FILEMUSTEXIST) &&
510 !PathFileExistsW(canon_filename))
511 {
512 if(This->default_ext)
513 {
514 lstrcatW(canon_filename, dotW);
515 lstrcatW(canon_filename, This->default_ext);
516
517 if(!PathFileExistsW(canon_filename))
518 {
519 FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
520 open_action = ONOPEN_BROWSE;
521 }
522 }
523 else
524 {
525 FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING);
526 open_action = ONOPEN_BROWSE;
527 }
528 }
529 }
530 }
531
532 pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename);
533
534 if(psf_parent && !(open_action == ONOPEN_BROWSE))
535 IShellFolder_Release(psf_parent);
536
537 fn_iter += (WCHAR)lstrlenW(fn_iter) + 1;
538 }
539
540 HeapFree(GetProcessHeap(), 0, files);
541 ILFree(current_folder);
542
543 if((This->options & FOS_PICKFOLDERS) && open_action == ONOPEN_BROWSE)
544 open_action = ONOPEN_OPEN; /* FIXME: Multiple folders? */
545
546 switch(open_action)
547 {
548 case ONOPEN_SEARCH:
549 FIXME("Filtering not implemented.\n");
550 break;
551
552 case ONOPEN_BROWSE:
553 hr = IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psf_parent, SBSP_DEFBROWSER);
554 if(FAILED(hr))
555 ERR("Failed to browse to directory: %08x\n", hr);
556
557 IShellFolder_Release(psf_parent);
558 break;
559
560 case ONOPEN_OPEN:
561 hr = SHGetDesktopFolder(&psf_desktop);
562 if(SUCCEEDED(hr))
563 {
564 if(This->psia_results)
565 {
566 IShellItemArray_Release(This->psia_results);
567 This->psia_results = NULL;
568 }
569
570 hr = SHCreateShellItemArray(NULL, psf_desktop, file_count, (PCUITEMID_CHILD_ARRAY)pidla,
571 &This->psia_results);
572
573 IShellFolder_Release(psf_desktop);
574
575 if(FAILED(hr))
576 break;
577
578 if(This->options & FOS_PICKFOLDERS)
579 {
580 SFGAOF attributes;
581 hr = IShellItemArray_GetAttributes(This->psia_results, SIATTRIBFLAGS_AND, SFGAO_FOLDER, &attributes);
582 if(hr != S_OK)
583 {
584 WCHAR buf[64];
585 LoadStringW(COMDLG32_hInstance, IDS_INVALID_FOLDERNAME, buf, sizeof(buf)/sizeof(WCHAR));
586
587 MessageBoxW(This->dlg_hwnd, buf, This->custom_title, MB_OK | MB_ICONEXCLAMATION);
588
589 IShellItemArray_Release(This->psia_results);
590 This->psia_results = NULL;
591 break;
592 }
593 }
594
595 if(events_OnFileOk(This) == S_OK)
596 ret = S_OK;
597 }
598 break;
599
600 default:
601 ERR("Failed.\n");
602 break;
603 }
604
605 /* Clean up */
606 for(i = 0; i < file_count; i++)
607 ILFree(pidla[i]);
608 HeapFree(GetProcessHeap(), 0, pidla);
609
610 /* Success closes the dialog */
611 return ret;
612 }
613
614 /**************************************************************************
615 * Control functions.
616 */
617 static inline customctrl *get_cctrl_from_dlgid(FileDialogImpl *This, DWORD dlgid)
618 {
619 customctrl *ctrl, *sub_ctrl;
620
621 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
622 {
623 if(ctrl->dlgid == dlgid)
624 return ctrl;
625
626 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
627 if(sub_ctrl->dlgid == dlgid)
628 return sub_ctrl;
629 }
630
631 ERR("Failed to find control with dialog id %d\n", dlgid);
632 return NULL;
633 }
634
635 static inline customctrl *get_cctrl(FileDialogImpl *This, DWORD ctlid)
636 {
637 customctrl *ctrl, *sub_ctrl;
638
639 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
640 {
641 if(ctrl->id == ctlid)
642 return ctrl;
643
644 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
645 if(sub_ctrl->id == ctlid)
646 return sub_ctrl;
647 }
648
649
650 TRACE("No existing control with control id %d\n", ctlid);
651 return NULL;
652 }
653
654 static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multiline)
655 {
656 LPWSTR text;
657 UINT len, final_width;
658 UINT lines, final_height;
659 SIZE size;
660 RECT rc;
661 HDC hdc;
662 WCHAR *c;
663
664 TRACE("\n");
665
666 len = SendMessageW(hctrl, WM_GETTEXTLENGTH, 0, 0);
667 text = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
668 if(!text) return;
669 SendMessageW(hctrl, WM_GETTEXT, len+1, (LPARAM)text);
670
671 hdc = GetDC(hctrl);
672 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &size);
673 ReleaseDC(hctrl, hdc);
674
675 if(len && multiline)
676 {
677 /* FIXME: line-wrap */
678 for(lines = 1, c = text; *c != '\0'; c++)
679 if(*c == '\n') lines++;
680
681 final_height = size.cy*lines + 2*4;
682 }
683 else
684 {
685 GetWindowRect(hctrl, &rc);
686 final_height = rc.bottom - rc.top;
687 }
688
689 final_width = min(max(size.cx, min_width) + 4, max_width);
690 SetWindowPos(hctrl, NULL, 0, 0, final_width, final_height,
691 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
692
693 HeapFree(GetProcessHeap(), 0, text);
694 }
695
696 static UINT ctrl_get_height(customctrl *ctrl) {
697 RECT rc;
698 GetWindowRect(ctrl->wrapper_hwnd, &rc);
699 return rc.bottom - rc.top;
700 }
701
702 static void ctrl_free(customctrl *ctrl)
703 {
704 customctrl *sub_cur1, *sub_cur2;
705
706 TRACE("Freeing control %p\n", ctrl);
707 if(ctrl->type == IDLG_CCTRL_MENU)
708 {
709 TBBUTTON tbb;
710 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
711 DestroyMenu((HMENU)tbb.dwData);
712 }
713
714 LIST_FOR_EACH_ENTRY_SAFE(sub_cur1, sub_cur2, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
715 {
716 list_remove(&sub_cur1->sub_cctrls_entry);
717 ctrl_free(sub_cur1);
718 }
719
720 DestroyWindow(ctrl->hwnd);
721 HeapFree(GetProcessHeap(), 0, ctrl);
722 }
723
724 static void customctrl_resize(FileDialogImpl *This, customctrl *ctrl)
725 {
726 RECT rc;
727 UINT total_height;
728 customctrl *sub_ctrl;
729
730 switch(ctrl->type)
731 {
732 case IDLG_CCTRL_PUSHBUTTON:
733 case IDLG_CCTRL_COMBOBOX:
734 case IDLG_CCTRL_CHECKBUTTON:
735 case IDLG_CCTRL_TEXT:
736 ctrl_resize(ctrl->hwnd, 160, 160, TRUE);
737 GetWindowRect(ctrl->hwnd, &rc);
738 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
739 SWP_NOZORDER|SWP_NOMOVE);
740 break;
741 case IDLG_CCTRL_VISUALGROUP:
742 total_height = 0;
743 ctrl_resize(ctrl->hwnd, 0, This->cctrl_indent, TRUE);
744
745 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
746 {
747 customctrl_resize(This, sub_ctrl);
748 SetWindowPos(sub_ctrl->wrapper_hwnd, NULL, This->cctrl_indent, total_height, 0, 0,
749 SWP_NOZORDER|SWP_NOSIZE);
750
751 total_height += ctrl_get_height(sub_ctrl);
752 }
753
754 /* The label should be right adjusted */
755 {
756 UINT width, height;
757
758 GetWindowRect(ctrl->hwnd, &rc);
759 width = rc.right - rc.left;
760 height = rc.bottom - rc.top;
761
762 SetWindowPos(ctrl->hwnd, NULL, This->cctrl_indent - width, 0, width, height, SWP_NOZORDER);
763 }
764
765 /* Resize the wrapper window to fit all the sub controls */
766 SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, This->cctrl_width + This->cctrl_indent, total_height,
767 SWP_NOZORDER|SWP_NOMOVE);
768 break;
769 case IDLG_CCTRL_RADIOBUTTONLIST:
770 case IDLG_CCTRL_EDITBOX:
771 case IDLG_CCTRL_SEPARATOR:
772 case IDLG_CCTRL_MENU:
773 /* Nothing */
774 break;
775 }
776 }
777
778 static LRESULT notifysink_on_create(HWND hwnd, CREATESTRUCTW *crs)
779 {
780 FileDialogImpl *This = crs->lpCreateParams;
781 TRACE("%p\n", This);
782
783 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
784 return TRUE;
785 }
786
787 static LRESULT notifysink_on_bn_clicked(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
788 {
789 customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
790
791 TRACE("%p, %lx\n", This, wparam);
792
793 if(ctrl)
794 {
795 if(ctrl->type == IDLG_CCTRL_CHECKBUTTON)
796 {
797 BOOL checked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
798 cctrl_event_OnCheckButtonToggled(This, ctrl->id, checked);
799 }
800 else
801 cctrl_event_OnButtonClicked(This, ctrl->id);
802 }
803
804 return TRUE;
805 }
806
807 static LRESULT notifysink_on_cbn_selchange(FileDialogImpl *This, HWND hwnd, WPARAM wparam)
808 {
809 customctrl *ctrl = get_cctrl_from_dlgid(This, LOWORD(wparam));
810 TRACE("%p, %p (%lx)\n", This, ctrl, wparam);
811
812 if(ctrl)
813 {
814 UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
815 UINT selid = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
816
817 cctrl_event_OnItemSelected(This, ctrl->id, selid);
818 }
819 return TRUE;
820 }
821
822 static LRESULT notifysink_on_tvn_dropdown(FileDialogImpl *This, LPARAM lparam)
823 {
824 NMTOOLBARW *nmtb = (NMTOOLBARW*)lparam;
825 customctrl *ctrl = get_cctrl_from_dlgid(This, GetDlgCtrlID(nmtb->hdr.hwndFrom));
826 POINT pt = { 0, nmtb->rcButton.bottom };
827 TBBUTTON tbb;
828 UINT idcmd;
829
830 TRACE("%p, %p (%lx)\n", This, ctrl, lparam);
831
832 if(ctrl)
833 {
834 cctrl_event_OnControlActivating(This,ctrl->id);
835
836 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
837 ClientToScreen(ctrl->hwnd, &pt);
838 idcmd = TrackPopupMenu((HMENU)tbb.dwData, TPM_RETURNCMD, pt.x, pt.y, 0, This->dlg_hwnd, NULL);
839 if(idcmd)
840 cctrl_event_OnItemSelected(This, ctrl->id, idcmd);
841 }
842
843 return TBDDRET_DEFAULT;
844 }
845
846 static LRESULT notifysink_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
847 {
848 switch(HIWORD(wparam))
849 {
850 case BN_CLICKED: return notifysink_on_bn_clicked(This, hwnd, wparam);
851 case CBN_SELCHANGE: return notifysink_on_cbn_selchange(This, hwnd, wparam);
852 }
853
854 return FALSE;
855 }
856
857 static LRESULT notifysink_on_wm_notify(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
858 {
859 NMHDR *nmhdr = (NMHDR*)lparam;
860
861 switch(nmhdr->code)
862 {
863 case TBN_DROPDOWN: return notifysink_on_tvn_dropdown(This, lparam);
864 }
865
866 return FALSE;
867 }
868
869 static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
870 {
871 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
872 customctrl *ctrl;
873 HWND hwnd_child;
874 RECT rc;
875
876 switch(message)
877 {
878 case WM_NCCREATE: return notifysink_on_create(hwnd, (CREATESTRUCTW*)lparam);
879 case WM_COMMAND: return notifysink_on_wm_command(This, hwnd, wparam, lparam);
880 case WM_NOTIFY: return notifysink_on_wm_notify(This, hwnd, wparam, lparam);
881 case WM_SIZE:
882 hwnd_child = GetPropW(hwnd, notifysink_childW);
883 ctrl = (customctrl*)GetWindowLongPtrW(hwnd_child, GWLP_USERDATA);
884 if(ctrl && ctrl->type != IDLG_CCTRL_VISUALGROUP)
885 {
886 GetClientRect(hwnd, &rc);
887 SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
888 }
889 return TRUE;
890 }
891
892 return DefWindowProcW(hwnd, message, wparam, lparam);
893 }
894
895 static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
896 LPCWSTR text, LPCWSTR wndclass, DWORD ctrl_wsflags,
897 DWORD ctrl_exflags, UINT height, customctrl **ppctrl)
898 {
899 HWND ns_hwnd, control_hwnd, parent_hwnd;
900 DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
901 customctrl *ctrl;
902
903 if(get_cctrl(This, id))
904 return E_UNEXPECTED; /* Duplicate id */
905
906 if(This->cctrl_active_vg)
907 parent_hwnd = This->cctrl_active_vg->wrapper_hwnd;
908 else
909 parent_hwnd = This->cctrls_hwnd;
910
911 ns_hwnd = CreateWindowExW(0, floatnotifysinkW, NULL, wsflags,
912 0, 0, This->cctrl_width, height, parent_hwnd,
913 (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
914 control_hwnd = CreateWindowExW(ctrl_exflags, wndclass, text, wsflags | ctrl_wsflags,
915 0, 0, This->cctrl_width, height, ns_hwnd,
916 (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, 0);
917
918 if(!ns_hwnd || !control_hwnd)
919 {
920 ERR("Failed to create wrapper (%p) or control (%p)\n", ns_hwnd, control_hwnd);
921 DestroyWindow(ns_hwnd);
922 DestroyWindow(control_hwnd);
923
924 return E_FAIL;
925 }
926
927 SetPropW(ns_hwnd, notifysink_childW, control_hwnd);
928
929 ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(customctrl));
930 if(!ctrl)
931 return E_OUTOFMEMORY;
932
933 ctrl->hwnd = control_hwnd;
934 ctrl->wrapper_hwnd = ns_hwnd;
935 ctrl->id = id;
936 ctrl->dlgid = This->cctrl_next_dlgid;
937 ctrl->cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
938 list_init(&ctrl->sub_cctrls);
939
940 if(This->cctrl_active_vg)
941 list_add_tail(&This->cctrl_active_vg->sub_cctrls, &ctrl->sub_cctrls_entry);
942 else
943 list_add_tail(&This->cctrls, &ctrl->entry);
944
945 SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)ctrl);
946
947 if(ppctrl) *ppctrl = ctrl;
948
949 This->cctrl_next_dlgid++;
950 return S_OK;
951 }
952
953 /**************************************************************************
954 * Container functions.
955 */
956 static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
957 {
958 UINT container_height;
959 UINT column_width;
960 UINT nr_of_cols;
961 UINT max_control_height, total_height = 0;
962 UINT cur_col_pos, cur_row_pos;
963 customctrl *ctrl;
964 BOOL fits_height;
965 static const UINT cspacing = 90; /* Columns are spaced with 90px */
966 static const UINT rspacing = 4; /* Rows are spaced with 4 px. */
967
968 /* Given the new width of the container, this function determines the
969 * needed height of the container and places the controls according to
970 * the new layout. Returns the new height.
971 */
972
973 TRACE("%p\n", This);
974
975 column_width = This->cctrl_width + cspacing;
976 nr_of_cols = (container_width - This->cctrl_indent + cspacing) / column_width;
977
978 /* We don't need to do anything unless the number of visible columns has changed. */
979 if(nr_of_cols == This->cctrls_cols)
980 {
981 RECT rc;
982 GetWindowRect(This->cctrls_hwnd, &rc);
983 return rc.bottom - rc.top;
984 }
985
986 This->cctrls_cols = nr_of_cols;
987
988 /* Get the size of the tallest control, and the total size of
989 * all the controls to figure out the number of slots we need.
990 */
991 max_control_height = 0;
992 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
993 {
994 if(ctrl->cdcstate & CDCS_VISIBLE)
995 {
996 UINT control_height = ctrl_get_height(ctrl);
997 max_control_height = max(max_control_height, control_height);
998
999 total_height += control_height + rspacing;
1000 }
1001 }
1002
1003 if(!total_height)
1004 return 0;
1005
1006 container_height = max(total_height / nr_of_cols, max_control_height + rspacing);
1007 TRACE("Guess: container_height: %d\n",container_height);
1008
1009 /* Incrementally increase container_height until all the controls
1010 * fit.
1011 */
1012 do {
1013 UINT columns_needed = 1;
1014 cur_row_pos = 0;
1015
1016 fits_height = TRUE;
1017 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1018 {
1019 if(ctrl->cdcstate & CDCS_VISIBLE)
1020 {
1021 UINT control_height = ctrl_get_height(ctrl);
1022
1023 if(cur_row_pos + control_height > container_height)
1024 {
1025 if(++columns_needed > nr_of_cols)
1026 {
1027 container_height += 1;
1028 fits_height = FALSE;
1029 break;
1030 }
1031 cur_row_pos = 0;
1032 }
1033
1034 cur_row_pos += control_height + rspacing;
1035 }
1036 }
1037 } while(!fits_height);
1038
1039 TRACE("Final container height: %d\n", container_height);
1040
1041 /* Move the controls to their final destination
1042 */
1043 cur_col_pos = 0, cur_row_pos = 0;
1044 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1045 {
1046 if(ctrl->cdcstate & CDCS_VISIBLE)
1047 {
1048 RECT rc;
1049 UINT control_height, control_indent;
1050 GetWindowRect(ctrl->wrapper_hwnd, &rc);
1051 control_height = rc.bottom - rc.top;
1052
1053 if(cur_row_pos + control_height > container_height)
1054 {
1055 cur_row_pos = 0;
1056 cur_col_pos += This->cctrl_width + cspacing;
1057 }
1058
1059
1060 if(ctrl->type == IDLG_CCTRL_VISUALGROUP)
1061 control_indent = 0;
1062 else
1063 control_indent = This->cctrl_indent;
1064
1065 SetWindowPos(ctrl->wrapper_hwnd, NULL, cur_col_pos + control_indent, cur_row_pos, 0, 0,
1066 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
1067
1068 cur_row_pos += control_height + rspacing;
1069 }
1070 }
1071
1072 /* Sanity check */
1073 if(cur_row_pos + This->cctrl_width > container_width)
1074 ERR("-- Failed to place controls properly.\n");
1075
1076 return container_height;
1077 }
1078
1079 static void ctrl_container_reparent(FileDialogImpl *This, HWND parent)
1080 {
1081 LONG wndstyle;
1082
1083 if(parent)
1084 {
1085 customctrl *ctrl, *sub_ctrl;
1086 HFONT font;
1087
1088 wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1089 wndstyle &= ~(WS_POPUP);
1090 wndstyle |= WS_CHILD;
1091 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1092
1093 SetParent(This->cctrls_hwnd, parent);
1094 ShowWindow(This->cctrls_hwnd, TRUE);
1095
1096 /* Set the fonts to match the dialog font. */
1097 font = (HFONT)SendMessageW(parent, WM_GETFONT, 0, 0);
1098 if(!font)
1099 ERR("Failed to get font handle from dialog.\n");
1100
1101 LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
1102 {
1103 if(font) SendMessageW(ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1104
1105 /* If this is a VisualGroup */
1106 LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
1107 {
1108 if(font) SendMessageW(sub_ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
1109 }
1110
1111 customctrl_resize(This, ctrl);
1112 }
1113 }
1114 else
1115 {
1116 ShowWindow(This->cctrls_hwnd, FALSE);
1117
1118 wndstyle = GetWindowLongW(This->cctrls_hwnd, GWL_STYLE);
1119 wndstyle &= ~(WS_CHILD);
1120 wndstyle |= WS_POPUP;
1121 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, wndstyle);
1122
1123 SetParent(This->cctrls_hwnd, NULL);
1124 }
1125 }
1126
1127 static LRESULT ctrl_container_on_create(HWND hwnd, CREATESTRUCTW *crs)
1128 {
1129 FileDialogImpl *This = crs->lpCreateParams;
1130 TRACE("%p\n", This);
1131
1132 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1133 return TRUE;
1134 }
1135
1136 static LRESULT ctrl_container_on_wm_destroy(FileDialogImpl *This)
1137 {
1138 customctrl *cur1, *cur2;
1139 TRACE("%p\n", This);
1140
1141 LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->cctrls, customctrl, entry)
1142 {
1143 list_remove(&cur1->entry);
1144 ctrl_free(cur1);
1145 }
1146
1147 return TRUE;
1148 }
1149
1150 static LRESULT CALLBACK ctrl_container_wndproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1151 {
1152 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1153
1154 switch(umessage)
1155 {
1156 case WM_NCCREATE: return ctrl_container_on_create(hwnd, (CREATESTRUCTW*)lparam);
1157 case WM_DESTROY: return ctrl_container_on_wm_destroy(This);
1158 default: return DefWindowProcW(hwnd, umessage, wparam, lparam);
1159 }
1160
1161 return FALSE;
1162 }
1163
1164 static HRESULT init_custom_controls(FileDialogImpl *This)
1165 {
1166 WNDCLASSW wc;
1167 static const WCHAR ctrl_container_classname[] =
1168 {'i','d','l','g','_','c','o','n','t','a','i','n','e','r','_','p','a','n','e',0};
1169
1170 InitCommonControlsEx(NULL);
1171
1172 This->cctrl_width = 160; /* Controls have a fixed width */
1173 This->cctrl_indent = 100;
1174 This->cctrl_def_height = 23;
1175 This->cctrls_cols = 0;
1176
1177 This->cctrl_next_dlgid = 0x2000;
1178 list_init(&This->cctrls);
1179 This->cctrl_active_vg = NULL;
1180
1181 if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
1182 {
1183 wc.style = CS_HREDRAW | CS_VREDRAW;
1184 wc.lpfnWndProc = ctrl_container_wndproc;
1185 wc.cbClsExtra = 0;
1186 wc.cbWndExtra = 0;
1187 wc.hInstance = COMDLG32_hInstance;
1188 wc.hIcon = 0;
1189 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1190 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1191 wc.lpszMenuName = NULL;
1192 wc.lpszClassName = ctrl_container_classname;
1193
1194 if(!RegisterClassW(&wc)) return E_FAIL;
1195 }
1196
1197 This->cctrls_hwnd = CreateWindowExW(0, ctrl_container_classname, NULL,
1198 WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1199 0, 0, 0, 0, NULL, 0,
1200 COMDLG32_hInstance, (void*)This);
1201 if(!This->cctrls_hwnd)
1202 return E_FAIL;
1203
1204 SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, WS_TABSTOP);
1205
1206 /* Register class for */
1207 if( !GetClassInfoW(COMDLG32_hInstance, floatnotifysinkW, &wc) ||
1208 wc.hInstance != COMDLG32_hInstance)
1209 {
1210 wc.style = CS_HREDRAW | CS_VREDRAW;
1211 wc.lpfnWndProc = notifysink_proc;
1212 wc.cbClsExtra = 0;
1213 wc.cbWndExtra = 0;
1214 wc.hInstance = COMDLG32_hInstance;
1215 wc.hIcon = 0;
1216 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1217 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1218 wc.lpszMenuName = NULL;
1219 wc.lpszClassName = floatnotifysinkW;
1220
1221 if (!RegisterClassW(&wc))
1222 ERR("Failed to register FloatNotifySink window class.\n");
1223 }
1224
1225 return S_OK;
1226 }
1227
1228 /**************************************************************************
1229 * Window related functions.
1230 */
1231 static SIZE update_layout(FileDialogImpl *This)
1232 {
1233 HDWP hdwp;
1234 HWND hwnd;
1235 RECT dialog_rc;
1236 RECT cancel_rc, open_rc;
1237 RECT filetype_rc, filename_rc, filenamelabel_rc;
1238 RECT toolbar_rc, ebrowser_rc, customctrls_rc;
1239 int missing_width, missing_height;
1240 static const UINT vspacing = 4, hspacing = 4;
1241 SIZE ret;
1242
1243 GetClientRect(This->dlg_hwnd, &dialog_rc);
1244
1245 missing_width = max(0, 320 - dialog_rc.right);
1246 missing_height = max(0, 200 - dialog_rc.bottom);
1247
1248 if(missing_width || missing_height)
1249 {
1250 TRACE("Missing (%d, %d)\n", missing_width, missing_height);
1251 ret.cx = missing_width;
1252 ret.cy = missing_height;
1253 return ret;
1254 }
1255
1256 /****
1257 * Calculate the size of the dialog and all the parts.
1258 */
1259
1260 /* Cancel button */
1261 hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL);
1262 if(hwnd)
1263 {
1264 int cancel_width, cancel_height;
1265 GetWindowRect(hwnd, &cancel_rc);
1266 cancel_width = cancel_rc.right - cancel_rc.left;
1267 cancel_height = cancel_rc.bottom - cancel_rc.top;
1268
1269 cancel_rc.left = dialog_rc.right - cancel_width - hspacing;
1270 cancel_rc.top = dialog_rc.bottom - cancel_height - vspacing;
1271 cancel_rc.right = cancel_rc.left + cancel_width;
1272 cancel_rc.bottom = cancel_rc.top + cancel_height;
1273 }
1274
1275 /* Open/Save button */
1276 hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
1277 if(hwnd)
1278 {
1279 int open_width, open_height;
1280 GetWindowRect(hwnd, &open_rc);
1281 open_width = open_rc.right - open_rc.left;
1282 open_height = open_rc.bottom - open_rc.top;
1283
1284 open_rc.left = cancel_rc.left - open_width - hspacing;
1285 open_rc.top = cancel_rc.top;
1286 open_rc.right = open_rc.left + open_width;
1287 open_rc.bottom = open_rc.top + open_height;
1288 }
1289
1290 /* The filetype combobox. */
1291 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1292 if(hwnd)
1293 {
1294 int filetype_width, filetype_height;
1295 GetWindowRect(hwnd, &filetype_rc);
1296
1297 filetype_width = filetype_rc.right - filetype_rc.left;
1298 filetype_height = filetype_rc.bottom - filetype_rc.top;
1299
1300 filetype_rc.right = cancel_rc.right;
1301
1302 filetype_rc.left = filetype_rc.right - filetype_width;
1303 filetype_rc.top = cancel_rc.top - filetype_height - vspacing;
1304 filetype_rc.bottom = filetype_rc.top + filetype_height;
1305
1306 if(!This->filterspec_count)
1307 filetype_rc.left = filetype_rc.right;
1308 }
1309
1310 /* Filename label. */
1311 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC);
1312 if(hwnd)
1313 {
1314 int filetypelabel_width, filetypelabel_height;
1315 GetWindowRect(hwnd, &filenamelabel_rc);
1316
1317 filetypelabel_width = filenamelabel_rc.right - filenamelabel_rc.left;
1318 filetypelabel_height = filenamelabel_rc.bottom - filenamelabel_rc.top;
1319
1320 filenamelabel_rc.left = 160; /* FIXME */
1321 filenamelabel_rc.top = filetype_rc.top;
1322 filenamelabel_rc.right = filenamelabel_rc.left + filetypelabel_width;
1323 filenamelabel_rc.bottom = filenamelabel_rc.top + filetypelabel_height;
1324 }
1325
1326 /* Filename edit box. */
1327 hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
1328 if(hwnd)
1329 {
1330 int filename_width, filename_height;
1331 GetWindowRect(hwnd, &filename_rc);
1332
1333 filename_width = filetype_rc.left - filenamelabel_rc.right - hspacing*2;
1334 filename_height = filename_rc.bottom - filename_rc.top;
1335
1336 filename_rc.left = filenamelabel_rc.right + hspacing;
1337 filename_rc.top = filetype_rc.top;
1338 filename_rc.right = filename_rc.left + filename_width;
1339 filename_rc.bottom = filename_rc.top + filename_height;
1340 }
1341
1342 hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR);
1343 if(hwnd)
1344 {
1345 GetWindowRect(hwnd, &toolbar_rc);
1346 MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2);
1347 }
1348
1349 /* The custom controls */
1350 customctrls_rc.left = dialog_rc.left + hspacing;
1351 customctrls_rc.right = dialog_rc.right - hspacing;
1352 customctrls_rc.bottom = filename_rc.top - vspacing;
1353 customctrls_rc.top = customctrls_rc.bottom -
1354 ctrl_container_resize(This, customctrls_rc.right - customctrls_rc.left);
1355
1356 /* The ExplorerBrowser control. */
1357 ebrowser_rc.left = dialog_rc.left + hspacing;
1358 ebrowser_rc.top = toolbar_rc.bottom + vspacing;
1359 ebrowser_rc.right = dialog_rc.right - hspacing;
1360 ebrowser_rc.bottom = customctrls_rc.top - vspacing;
1361
1362 /****
1363 * Move everything to the right place.
1364 */
1365
1366 /* FIXME: The Save Dialog uses a slightly different layout. */
1367 hdwp = BeginDeferWindowPos(7);
1368
1369 if(hdwp && This->peb)
1370 IExplorerBrowser_SetRect(This->peb, &hdwp, ebrowser_rc);
1371
1372 if(hdwp && This->cctrls_hwnd)
1373 DeferWindowPos(hdwp, This->cctrls_hwnd, NULL,
1374 customctrls_rc.left, customctrls_rc.top,
1375 customctrls_rc.right - customctrls_rc.left, customctrls_rc.bottom - customctrls_rc.top,
1376 SWP_NOZORDER | SWP_NOACTIVATE);
1377
1378 /* The default controls */
1379 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE)) )
1380 DeferWindowPos(hdwp, hwnd, NULL, filetype_rc.left, filetype_rc.top, 0, 0,
1381 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1382
1383 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
1384 DeferWindowPos(hdwp, hwnd, NULL, filename_rc.left, filename_rc.top,
1385 filename_rc.right - filename_rc.left, filename_rc.bottom - filename_rc.top,
1386 SWP_NOZORDER | SWP_NOACTIVATE);
1387
1388 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)) )
1389 DeferWindowPos(hdwp, hwnd, NULL, filenamelabel_rc.left, filenamelabel_rc.top, 0, 0,
1390 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1391
1392 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDOK)) )
1393 DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
1394 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1395
1396 if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
1397 DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
1398 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1399
1400 if(hdwp)
1401 EndDeferWindowPos(hdwp);
1402 else
1403 ERR("Failed to position dialog controls.\n");
1404
1405 ret.cx = 0; ret.cy = 0;
1406 return ret;
1407 }
1408
1409 static HRESULT init_explorerbrowser(FileDialogImpl *This)
1410 {
1411 IShellItem *psi_folder;
1412 FOLDERSETTINGS fos;
1413 RECT rc = {0};
1414 HRESULT hr;
1415
1416 /* Create ExplorerBrowser instance */
1417 OleInitialize(NULL);
1418
1419 hr = CoCreateInstance(&CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER,
1420 &IID_IExplorerBrowser, (void**)&This->peb);
1421 if(FAILED(hr))
1422 {
1423 ERR("Failed to instantiate ExplorerBrowser control.\n");
1424 return hr;
1425 }
1426
1427 IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES);
1428
1429 hr = IExplorerBrowser_Initialize(This->peb, This->dlg_hwnd, &rc, NULL);
1430 if(FAILED(hr))
1431 {
1432 ERR("Failed to initialize the ExplorerBrowser control.\n");
1433 IExplorerBrowser_Release(This->peb);
1434 This->peb = NULL;
1435 return hr;
1436 }
1437 hr = IExplorerBrowser_Advise(This->peb, &This->IExplorerBrowserEvents_iface, &This->ebevents_cookie);
1438 if(FAILED(hr))
1439 ERR("Advise (ExplorerBrowser) failed.\n");
1440
1441 /* Get previous options? */
1442 fos.ViewMode = fos.fFlags = 0;
1443 if(!(This->options & FOS_ALLOWMULTISELECT))
1444 fos.fFlags |= FWF_SINGLESEL;
1445
1446 IExplorerBrowser_SetFolderSettings(This->peb, &fos);
1447
1448 hr = IUnknown_SetSite((IUnknown*)This->peb, (IUnknown*)This);
1449 if(FAILED(hr))
1450 ERR("SetSite (ExplorerBrowser) failed.\n");
1451
1452 /* Browse somewhere */
1453 psi_folder = This->psi_setfolder ? This->psi_setfolder : This->psi_defaultfolder;
1454 IExplorerBrowser_BrowseToObject(This->peb, (IUnknown*)psi_folder, SBSP_DEFBROWSER);
1455
1456 return S_OK;
1457 }
1458
1459 static void init_toolbar(FileDialogImpl *This, HWND hwnd)
1460 {
1461 HWND htoolbar;
1462 TBADDBITMAP tbab;
1463 TBBUTTON button[2];
1464
1465 htoolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE,
1466 0, 0, 0, 0,
1467 hwnd, (HMENU)IDC_NAV_TOOLBAR, NULL, NULL);
1468
1469 tbab.hInst = HINST_COMMCTRL;
1470 tbab.nID = IDB_HIST_LARGE_COLOR;
1471 SendMessageW(htoolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab);
1472
1473 button[0].iBitmap = HIST_BACK;
1474 button[0].idCommand = IDC_NAVBACK;
1475 button[0].fsState = TBSTATE_ENABLED;
1476 button[0].fsStyle = BTNS_BUTTON;
1477 button[0].dwData = 0;
1478 button[0].iString = 0;
1479
1480 button[1].iBitmap = HIST_FORWARD;
1481 button[1].idCommand = IDC_NAVFORWARD;
1482 button[1].fsState = TBSTATE_ENABLED;
1483 button[1].fsStyle = BTNS_BUTTON;
1484 button[1].dwData = 0;
1485 button[1].iString = 0;
1486
1487 SendMessageW(htoolbar, TB_ADDBUTTONSW, 2, (LPARAM)button);
1488 SendMessageW(htoolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(24,24));
1489 SendMessageW(htoolbar, TB_AUTOSIZE, 0, 0);
1490 }
1491
1492 static void update_control_text(FileDialogImpl *This)
1493 {
1494 HWND hitem;
1495 if(This->custom_title)
1496 SetWindowTextW(This->dlg_hwnd, This->custom_title);
1497
1498 if(This->custom_okbutton &&
1499 (hitem = GetDlgItem(This->dlg_hwnd, IDOK)))
1500 {
1501 SetWindowTextW(hitem, This->custom_okbutton);
1502 ctrl_resize(hitem, 50, 250, FALSE);
1503 }
1504
1505 if(This->custom_cancelbutton &&
1506 (hitem = GetDlgItem(This->dlg_hwnd, IDCANCEL)))
1507 {
1508 SetWindowTextW(hitem, This->custom_cancelbutton);
1509 ctrl_resize(hitem, 50, 250, FALSE);
1510 }
1511
1512 if(This->custom_filenamelabel &&
1513 (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)))
1514 {
1515 SetWindowTextW(hitem, This->custom_filenamelabel);
1516 ctrl_resize(hitem, 50, 250, FALSE);
1517 }
1518 }
1519
1520 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
1521 {
1522 FileDialogImpl *This = (FileDialogImpl*)lParam;
1523 HWND hitem;
1524
1525 TRACE("(%p, %p)\n", This, hwnd);
1526
1527 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
1528 This->dlg_hwnd = hwnd;
1529
1530 hitem = GetDlgItem(This->dlg_hwnd, pshHelp);
1531 if(hitem) ShowWindow(hitem, SW_HIDE);
1532
1533 hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPESTATIC);
1534 if(hitem) ShowWindow(hitem, SW_HIDE);
1535
1536 /* Fill filetypes combobox, or hide it. */
1537 hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
1538 if(This->filterspec_count)
1539 {
1540 HDC hdc;
1541 HFONT font;
1542 SIZE size;
1543 UINT i, maxwidth = 0;
1544
1545 hdc = GetDC(hitem);
1546 font = (HFONT)SendMessageW(hitem, WM_GETFONT, 0, 0);
1547 SelectObject(hdc, font);
1548
1549 for(i = 0; i < This->filterspec_count; i++)
1550 {
1551 SendMessageW(hitem, CB_ADDSTRING, 0, (LPARAM)This->filterspecs[i].pszName);
1552
1553 if(GetTextExtentPoint32W(hdc, This->filterspecs[i].pszName, lstrlenW(This->filterspecs[i].pszName), &size))
1554 maxwidth = max(maxwidth, size.cx);
1555 }
1556 ReleaseDC(hitem, hdc);
1557
1558 if(maxwidth > 0)
1559 {
1560 maxwidth += GetSystemMetrics(SM_CXVSCROLL) + 4;
1561 SendMessageW(hitem, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0);
1562 }
1563 else
1564 ERR("Failed to calculate width of filetype dropdown\n");
1565
1566 SendMessageW(hitem, CB_SETCURSEL, This->filetypeindex, 0);
1567 }
1568 else
1569 ShowWindow(hitem, SW_HIDE);
1570
1571 if(This->set_filename &&
1572 (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
1573 SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
1574
1575 ctrl_container_reparent(This, This->dlg_hwnd);
1576 init_explorerbrowser(This);
1577 init_toolbar(This, hwnd);
1578 update_control_text(This);
1579 update_layout(This);
1580
1581 if(This->filterspec_count)
1582 events_OnTypeChange(This);
1583
1584 return TRUE;
1585 }
1586
1587 static LRESULT on_wm_size(FileDialogImpl *This)
1588 {
1589 update_layout(This);
1590 return FALSE;
1591 }
1592
1593 static LRESULT on_wm_getminmaxinfo(FileDialogImpl *This, LPARAM lparam)
1594 {
1595 MINMAXINFO *mmi = (MINMAXINFO*)lparam;
1596 TRACE("%p (%p)\n", This, mmi);
1597
1598 /* FIXME */
1599 mmi->ptMinTrackSize.x = 640;
1600 mmi->ptMinTrackSize.y = 480;
1601
1602 return FALSE;
1603 }
1604
1605 static LRESULT on_wm_destroy(FileDialogImpl *This)
1606 {
1607 TRACE("%p\n", This);
1608
1609 if(This->peb)
1610 {
1611 IExplorerBrowser_Destroy(This->peb);
1612 IExplorerBrowser_Release(This->peb);
1613 This->peb = NULL;
1614 }
1615
1616 ctrl_container_reparent(This, NULL);
1617 This->dlg_hwnd = NULL;
1618
1619 return TRUE;
1620 }
1621
1622 static LRESULT on_idok(FileDialogImpl *This)
1623 {
1624 TRACE("%p\n", This);
1625
1626 if(SUCCEEDED(on_default_action(This)))
1627 EndDialog(This->dlg_hwnd, S_OK);
1628
1629 return FALSE;
1630 }
1631
1632 static LRESULT on_idcancel(FileDialogImpl *This)
1633 {
1634 TRACE("%p\n", This);
1635
1636 EndDialog(This->dlg_hwnd, HRESULT_FROM_WIN32(ERROR_CANCELLED));
1637
1638 return FALSE;
1639 }
1640
1641 static LRESULT on_browse_back(FileDialogImpl *This)
1642 {
1643 TRACE("%p\n", This);
1644 IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEBACK);
1645 return FALSE;
1646 }
1647
1648 static LRESULT on_browse_forward(FileDialogImpl *This)
1649 {
1650 TRACE("%p\n", This);
1651 IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEFORWARD);
1652 return FALSE;
1653 }
1654
1655 static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
1656 {
1657 if(HIWORD(wparam) == CBN_SELCHANGE)
1658 {
1659 IShellView *psv;
1660 HRESULT hr;
1661 LPWSTR filename;
1662 UINT prev_index = This->filetypeindex;
1663
1664 This->filetypeindex = SendMessageW((HWND)lparam, CB_GETCURSEL, 0, 0);
1665 TRACE("File type selection changed to %d.\n", This->filetypeindex);
1666
1667 if(prev_index == This->filetypeindex)
1668 return FALSE;
1669
1670 hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&psv);
1671 if(SUCCEEDED(hr))
1672 {
1673 IShellView_Refresh(psv);
1674 IShellView_Release(psv);
1675 }
1676
1677 if(This->dlg_type == ITEMDLG_TYPE_SAVE && get_file_name(This, &filename))
1678 {
1679 WCHAR buf[MAX_PATH], extbuf[MAX_PATH], *ext;
1680
1681 ext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec);
1682 if(ext)
1683 {
1684 lstrcpyW(buf, filename);
1685
1686 if(PathMatchSpecW(buf, This->filterspecs[prev_index].pszSpec))
1687 PathRemoveExtensionW(buf);
1688
1689 lstrcatW(buf, ext);
1690 set_file_name(This, buf);
1691 }
1692 CoTaskMemFree(filename);
1693 }
1694
1695 /* The documentation claims that OnTypeChange is called only
1696 * when the dialog is opened, but this is obviously not the
1697 * case. */
1698 events_OnTypeChange(This);
1699 }
1700
1701 return FALSE;
1702 }
1703
1704 static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
1705 {
1706 switch(LOWORD(wparam))
1707 {
1708 case IDOK: return on_idok(This);
1709 case IDCANCEL: return on_idcancel(This);
1710 case IDC_NAVBACK: return on_browse_back(This);
1711 case IDC_NAVFORWARD: return on_browse_forward(This);
1712 case IDC_FILETYPE: return on_command_filetype(This, wparam, lparam);
1713 default: TRACE("Unknown command.\n");
1714 }
1715 return FALSE;
1716 }
1717
1718 static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
1719 {
1720 FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1721
1722 switch(umessage)
1723 {
1724 case WM_INITDIALOG: return on_wm_initdialog(hwnd, lparam);
1725 case WM_COMMAND: return on_wm_command(This, wparam, lparam);
1726 case WM_SIZE: return on_wm_size(This);
1727 case WM_GETMINMAXINFO: return on_wm_getminmaxinfo(This, lparam);
1728 case WM_DESTROY: return on_wm_destroy(This);
1729 }
1730
1731 return FALSE;
1732 }
1733
1734 static HRESULT create_dialog(FileDialogImpl *This, HWND parent)
1735 {
1736 INT_PTR res;
1737
1738 SetLastError(0);
1739 res = DialogBoxParamW(COMDLG32_hInstance,
1740 MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
1741 parent, itemdlg_dlgproc, (LPARAM)This);
1742 This->dlg_hwnd = NULL;
1743 if(res == -1)
1744 {
1745 ERR("Failed to show dialog (LastError: %d)\n", GetLastError());
1746 return E_FAIL;
1747 }
1748
1749 TRACE("Returning 0x%08x\n", (HRESULT)res);
1750 return (HRESULT)res;
1751 }
1752
1753 /**************************************************************************
1754 * IFileDialog implementation
1755 */
1756 static inline FileDialogImpl *impl_from_IFileDialog2(IFileDialog2 *iface)
1757 {
1758 return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialog2_iface);
1759 }
1760
1761 static HRESULT WINAPI IFileDialog2_fnQueryInterface(IFileDialog2 *iface,
1762 REFIID riid,
1763 void **ppvObject)
1764 {
1765 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1766 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
1767
1768 *ppvObject = NULL;
1769 if(IsEqualGUID(riid, &IID_IUnknown) ||
1770 IsEqualGUID(riid, &IID_IFileDialog) ||
1771 IsEqualGUID(riid, &IID_IFileDialog2))
1772 {
1773 *ppvObject = iface;
1774 }
1775 else if(IsEqualGUID(riid, &IID_IFileOpenDialog) && This->dlg_type == ITEMDLG_TYPE_OPEN)
1776 {
1777 *ppvObject = &This->u.IFileOpenDialog_iface;
1778 }
1779 else if(IsEqualGUID(riid, &IID_IFileSaveDialog) && This->dlg_type == ITEMDLG_TYPE_SAVE)
1780 {
1781 *ppvObject = &This->u.IFileSaveDialog_iface;
1782 }
1783 else if(IsEqualGUID(riid, &IID_IExplorerBrowserEvents))
1784 {
1785 *ppvObject = &This->IExplorerBrowserEvents_iface;
1786 }
1787 else if(IsEqualGUID(riid, &IID_IServiceProvider))
1788 {
1789 *ppvObject = &This->IServiceProvider_iface;
1790 }
1791 else if(IsEqualGUID(&IID_ICommDlgBrowser3, riid) ||
1792 IsEqualGUID(&IID_ICommDlgBrowser2, riid) ||
1793 IsEqualGUID(&IID_ICommDlgBrowser, riid))
1794 {
1795 *ppvObject = &This->ICommDlgBrowser3_iface;
1796 }
1797 else if(IsEqualGUID(&IID_IOleWindow, riid))
1798 {
1799 *ppvObject = &This->IOleWindow_iface;
1800 }
1801 else if(IsEqualGUID(riid, &IID_IFileDialogCustomize) ||
1802 IsEqualGUID(riid, &IID_IFileDialogCustomizeAlt))
1803 {
1804 *ppvObject = &This->IFileDialogCustomize_iface;
1805 }
1806 else
1807 FIXME("Unknown interface requested: %s.\n", debugstr_guid(riid));
1808
1809 if(*ppvObject)
1810 {
1811 IUnknown_AddRef((IUnknown*)*ppvObject);
1812 return S_OK;
1813 }
1814
1815 return E_NOINTERFACE;
1816 }
1817
1818 static ULONG WINAPI IFileDialog2_fnAddRef(IFileDialog2 *iface)
1819 {
1820 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1821 LONG ref = InterlockedIncrement(&This->ref);
1822 TRACE("%p - ref %d\n", This, ref);
1823
1824 return ref;
1825 }
1826
1827 static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface)
1828 {
1829 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1830 LONG ref = InterlockedDecrement(&This->ref);
1831 TRACE("%p - ref %d\n", This, ref);
1832
1833 if(!ref)
1834 {
1835 UINT i;
1836 for(i = 0; i < This->filterspec_count; i++)
1837 {
1838 LocalFree((void*)This->filterspecs[i].pszName);
1839 LocalFree((void*)This->filterspecs[i].pszSpec);
1840 }
1841 HeapFree(GetProcessHeap(), 0, This->filterspecs);
1842
1843 DestroyWindow(This->cctrls_hwnd);
1844
1845 if(This->psi_defaultfolder) IShellItem_Release(This->psi_defaultfolder);
1846 if(This->psi_setfolder) IShellItem_Release(This->psi_setfolder);
1847 if(This->psi_folder) IShellItem_Release(This->psi_folder);
1848 if(This->psia_selection) IShellItemArray_Release(This->psia_selection);
1849 if(This->psia_results) IShellItemArray_Release(This->psia_results);
1850
1851 LocalFree(This->set_filename);
1852 LocalFree(This->default_ext);
1853 LocalFree(This->custom_title);
1854 LocalFree(This->custom_okbutton);
1855 LocalFree(This->custom_cancelbutton);
1856 LocalFree(This->custom_filenamelabel);
1857
1858 HeapFree(GetProcessHeap(), 0, This);
1859 }
1860
1861 return ref;
1862 }
1863
1864 static HRESULT WINAPI IFileDialog2_fnShow(IFileDialog2 *iface, HWND hwndOwner)
1865 {
1866 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1867 TRACE("%p (%p)\n", iface, hwndOwner);
1868
1869 return create_dialog(This, hwndOwner);
1870 }
1871
1872 static HRESULT WINAPI IFileDialog2_fnSetFileTypes(IFileDialog2 *iface, UINT cFileTypes,
1873 const COMDLG_FILTERSPEC *rgFilterSpec)
1874 {
1875 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1876 UINT i;
1877 TRACE("%p (%d, %p)\n", This, cFileTypes, rgFilterSpec);
1878
1879 if(This->filterspecs)
1880 return E_UNEXPECTED;
1881
1882 if(!rgFilterSpec)
1883 return E_INVALIDARG;
1884
1885 if(!cFileTypes)
1886 return S_OK;
1887
1888 This->filterspecs = HeapAlloc(GetProcessHeap(), 0, sizeof(COMDLG_FILTERSPEC)*cFileTypes);
1889 for(i = 0; i < cFileTypes; i++)
1890 {
1891 This->filterspecs[i].pszName = StrDupW(rgFilterSpec[i].pszName);
1892 This->filterspecs[i].pszSpec = StrDupW(rgFilterSpec[i].pszSpec);
1893 }
1894 This->filterspec_count = cFileTypes;
1895
1896 return S_OK;
1897 }
1898
1899 static HRESULT WINAPI IFileDialog2_fnSetFileTypeIndex(IFileDialog2 *iface, UINT iFileType)
1900 {
1901 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1902 TRACE("%p (%d)\n", This, iFileType);
1903
1904 if(!This->filterspecs)
1905 return E_FAIL;
1906
1907 iFileType = max(iFileType, 1);
1908 iFileType = min(iFileType, This->filterspec_count);
1909 This->filetypeindex = iFileType-1;
1910
1911 return S_OK;
1912 }
1913
1914 static HRESULT WINAPI IFileDialog2_fnGetFileTypeIndex(IFileDialog2 *iface, UINT *piFileType)
1915 {
1916 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1917 TRACE("%p (%p)\n", This, piFileType);
1918
1919 if(!piFileType)
1920 return E_INVALIDARG;
1921
1922 if(This->filterspec_count == 0)
1923 *piFileType = 0;
1924 else
1925 *piFileType = This->filetypeindex + 1;
1926
1927 return S_OK;
1928 }
1929
1930 static HRESULT WINAPI IFileDialog2_fnAdvise(IFileDialog2 *iface, IFileDialogEvents *pfde, DWORD *pdwCookie)
1931 {
1932 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1933 events_client *client;
1934 TRACE("%p (%p, %p)\n", This, pfde, pdwCookie);
1935
1936 if(!pfde || !pdwCookie)
1937 return E_INVALIDARG;
1938
1939 client = HeapAlloc(GetProcessHeap(), 0, sizeof(events_client));
1940 client->pfde = pfde;
1941 client->cookie = ++This->events_next_cookie;
1942
1943 IFileDialogEvents_AddRef(pfde);
1944 *pdwCookie = client->cookie;
1945
1946 list_add_tail(&This->events_clients, &client->entry);
1947
1948 return S_OK;
1949 }
1950
1951 static HRESULT WINAPI IFileDialog2_fnUnadvise(IFileDialog2 *iface, DWORD dwCookie)
1952 {
1953 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1954 events_client *client, *found = NULL;
1955 TRACE("%p (%d)\n", This, dwCookie);
1956
1957 LIST_FOR_EACH_ENTRY(client, &This->events_clients, events_client, entry)
1958 {
1959 if(client->cookie == dwCookie)
1960 {
1961 found = client;
1962 break;
1963 }
1964 }
1965
1966 if(found)
1967 {
1968 list_remove(&found->entry);
1969 IFileDialogEvents_Release(found->pfde);
1970 HeapFree(GetProcessHeap(), 0, found);
1971 return S_OK;
1972 }
1973
1974 return E_INVALIDARG;
1975 }
1976
1977 static HRESULT WINAPI IFileDialog2_fnSetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS fos)
1978 {
1979 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1980 TRACE("%p (0x%x)\n", This, fos);
1981
1982 if( !(This->options & FOS_PICKFOLDERS) && (fos & FOS_PICKFOLDERS) )
1983 {
1984 WCHAR buf[30];
1985 LoadStringW(COMDLG32_hInstance, IDS_SELECT_FOLDER, buf, sizeof(buf)/sizeof(WCHAR));
1986 IFileDialog2_SetTitle(iface, buf);
1987 }
1988
1989 This->options = fos;
1990
1991 return S_OK;
1992 }
1993
1994 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
1995 {
1996 FileDialogImpl *This = impl_from_IFileDialog2(iface);
1997 TRACE("%p (%p)\n", This, pfos);
1998
1999 if(!pfos)
2000 return E_INVALIDARG;
2001
2002 *pfos = This->options;
2003
2004 return S_OK;
2005 }
2006
2007 static HRESULT WINAPI IFileDialog2_fnSetDefaultFolder(IFileDialog2 *iface, IShellItem *psi)
2008 {
2009 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2010 TRACE("%p (%p)\n", This, psi);
2011 if(This->psi_defaultfolder)
2012 IShellItem_Release(This->psi_defaultfolder);
2013
2014 This->psi_defaultfolder = psi;
2015
2016 if(This->psi_defaultfolder)
2017 IShellItem_AddRef(This->psi_defaultfolder);
2018
2019 return S_OK;
2020 }
2021
2022 static HRESULT WINAPI IFileDialog2_fnSetFolder(IFileDialog2 *iface, IShellItem *psi)
2023 {
2024 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2025 TRACE("%p (%p)\n", This, psi);
2026 if(This->psi_setfolder)
2027 IShellItem_Release(This->psi_setfolder);
2028
2029 This->psi_setfolder = psi;
2030
2031 if(This->psi_setfolder)
2032 IShellItem_AddRef(This->psi_setfolder);
2033
2034 return S_OK;
2035 }
2036
2037 static HRESULT WINAPI IFileDialog2_fnGetFolder(IFileDialog2 *iface, IShellItem **ppsi)
2038 {
2039 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2040 TRACE("%p (%p)\n", This, ppsi);
2041 if(!ppsi)
2042 return E_INVALIDARG;
2043
2044 /* FIXME:
2045 If the dialog is shown, return the current(ly selected) folder. */
2046
2047 *ppsi = NULL;
2048 if(This->psi_folder)
2049 *ppsi = This->psi_folder;
2050 else if(This->psi_setfolder)
2051 *ppsi = This->psi_setfolder;
2052 else if(This->psi_defaultfolder)
2053 *ppsi = This->psi_defaultfolder;
2054
2055 if(*ppsi)
2056 {
2057 IShellItem_AddRef(*ppsi);
2058 return S_OK;
2059 }
2060
2061 return E_FAIL;
2062 }
2063
2064 static HRESULT WINAPI IFileDialog2_fnGetCurrentSelection(IFileDialog2 *iface, IShellItem **ppsi)
2065 {
2066 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2067 HRESULT hr;
2068 TRACE("%p (%p)\n", This, ppsi);
2069
2070 if(!ppsi)
2071 return E_INVALIDARG;
2072
2073 if(This->psia_selection)
2074 {
2075 /* FIXME: Check filename edit box */
2076 hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
2077 return hr;
2078 }
2079
2080 return E_FAIL;
2081 }
2082
2083 static HRESULT WINAPI IFileDialog2_fnSetFileName(IFileDialog2 *iface, LPCWSTR pszName)
2084 {
2085 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2086 TRACE("%p (%s)\n", iface, debugstr_w(pszName));
2087
2088 set_file_name(This, pszName);
2089
2090 return S_OK;
2091 }
2092
2093 static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *pszName)
2094 {
2095 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2096 TRACE("%p (%p)\n", iface, pszName);
2097
2098 if(!pszName)
2099 return E_INVALIDARG;
2100
2101 *pszName = NULL;
2102 if(get_file_name(This, pszName))
2103 return S_OK;
2104 else
2105 return E_FAIL;
2106 }
2107
2108 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
2109 {
2110 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2111 TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
2112
2113 LocalFree(This->custom_title);
2114 This->custom_title = StrDupW(pszTitle);
2115 update_control_text(This);
2116
2117 return S_OK;
2118 }
2119
2120 static HRESULT WINAPI IFileDialog2_fnSetOkButtonLabel(IFileDialog2 *iface, LPCWSTR pszText)
2121 {
2122 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2123 TRACE("%p (%s)\n", This, debugstr_w(pszText));
2124
2125 LocalFree(This->custom_okbutton);
2126 This->custom_okbutton = StrDupW(pszText);
2127 update_control_text(This);
2128 update_layout(This);
2129
2130 return S_OK;
2131 }
2132
2133 static HRESULT WINAPI IFileDialog2_fnSetFileNameLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2134 {
2135 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2136 TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2137
2138 LocalFree(This->custom_filenamelabel);
2139 This->custom_filenamelabel = StrDupW(pszLabel);
2140 update_control_text(This);
2141 update_layout(This);
2142
2143 return S_OK;
2144 }
2145
2146 static HRESULT WINAPI IFileDialog2_fnGetResult(IFileDialog2 *iface, IShellItem **ppsi)
2147 {
2148 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2149 HRESULT hr;
2150 TRACE("%p (%p)\n", This, ppsi);
2151
2152 if(!ppsi)
2153 return E_INVALIDARG;
2154
2155 if(This->psia_results)
2156 {
2157 UINT item_count;
2158 hr = IShellItemArray_GetCount(This->psia_results, &item_count);
2159 if(SUCCEEDED(hr))
2160 {
2161 if(item_count != 1)
2162 return E_FAIL;
2163
2164 /* Adds a reference. */
2165 hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
2166 }
2167
2168 return hr;
2169 }
2170
2171 return E_UNEXPECTED;
2172 }
2173
2174 static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *psi, FDAP fdap)
2175 {
2176 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2177 FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
2178 return E_NOTIMPL;
2179 }
2180
2181 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
2182 {
2183 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2184 TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
2185
2186 LocalFree(This->default_ext);
2187 This->default_ext = StrDupW(pszDefaultExtension);
2188
2189 return S_OK;
2190 }
2191
2192 static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
2193 {
2194 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2195 TRACE("%p (0x%08x)\n", This, hr);
2196
2197 if(This->dlg_hwnd)
2198 EndDialog(This->dlg_hwnd, hr);
2199
2200 return S_OK;
2201 }
2202
2203 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
2204 {
2205 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2206 TRACE("%p (%s)\n", This, debugstr_guid(guid));
2207 This->client_guid = *guid;
2208 return S_OK;
2209 }
2210
2211 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
2212 {
2213 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2214 FIXME("stub - %p\n", This);
2215 return E_NOTIMPL;
2216 }
2217
2218 static HRESULT WINAPI IFileDialog2_fnSetFilter(IFileDialog2 *iface, IShellItemFilter *pFilter)
2219 {
2220 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2221 FIXME("stub - %p (%p)\n", This, pFilter);
2222 return E_NOTIMPL;
2223 }
2224
2225 static HRESULT WINAPI IFileDialog2_fnSetCancelButtonLabel(IFileDialog2 *iface, LPCWSTR pszLabel)
2226 {
2227 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2228 TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2229
2230 LocalFree(This->custom_cancelbutton);
2231 This->custom_cancelbutton = StrDupW(pszLabel);
2232 update_control_text(This);
2233 update_layout(This);
2234
2235 return S_OK;
2236 }
2237
2238 static HRESULT WINAPI IFileDialog2_fnSetNavigationRoot(IFileDialog2 *iface, IShellItem *psi)
2239 {
2240 FileDialogImpl *This = impl_from_IFileDialog2(iface);
2241 FIXME("stub - %p (%p)\n", This, psi);
2242 return E_NOTIMPL;
2243 }
2244
2245 static const IFileDialog2Vtbl vt_IFileDialog2 = {
2246 IFileDialog2_fnQueryInterface,
2247 IFileDialog2_fnAddRef,
2248 IFileDialog2_fnRelease,
2249 IFileDialog2_fnShow,
2250 IFileDialog2_fnSetFileTypes,
2251 IFileDialog2_fnSetFileTypeIndex,
2252 IFileDialog2_fnGetFileTypeIndex,
2253 IFileDialog2_fnAdvise,
2254 IFileDialog2_fnUnadvise,
2255 IFileDialog2_fnSetOptions,
2256 IFileDialog2_fnGetOptions,
2257 IFileDialog2_fnSetDefaultFolder,
2258 IFileDialog2_fnSetFolder,
2259 IFileDialog2_fnGetFolder,
2260 IFileDialog2_fnGetCurrentSelection,
2261 IFileDialog2_fnSetFileName,
2262 IFileDialog2_fnGetFileName,
2263 IFileDialog2_fnSetTitle,
2264 IFileDialog2_fnSetOkButtonLabel,
2265 IFileDialog2_fnSetFileNameLabel,
2266 IFileDialog2_fnGetResult,
2267 IFileDialog2_fnAddPlace,
2268 IFileDialog2_fnSetDefaultExtension,
2269 IFileDialog2_fnClose,
2270 IFileDialog2_fnSetClientGuid,
2271 IFileDialog2_fnClearClientData,
2272 IFileDialog2_fnSetFilter,
2273 IFileDialog2_fnSetCancelButtonLabel,
2274 IFileDialog2_fnSetNavigationRoot
2275 };
2276
2277 /**************************************************************************
2278 * IFileOpenDialog
2279 */
2280 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
2281 {
2282 return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
2283 }
2284
2285 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
2286 REFIID riid, void **ppvObject)
2287 {
2288 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2289 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2290 }
2291
2292 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
2293 {
2294 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2295 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2296 }
2297
2298 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
2299 {
2300 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2301 return IFileDialog2_Release(&This->IFileDialog2_iface);
2302 }
2303
2304 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
2305 {
2306 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2307 return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2308 }
2309
2310 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
2311 const COMDLG_FILTERSPEC *rgFilterSpec)
2312 {
2313 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2314 return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2315 }
2316
2317 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
2318 {
2319 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2320 return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2321 }
2322
2323 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
2324 {
2325 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2326 return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2327 }
2328
2329 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
2330 DWORD *pdwCookie)
2331 {
2332 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2333 return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2334 }
2335
2336 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
2337 {
2338 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2339 return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2340 }
2341
2342 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
2343 {
2344 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2345 return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2346 }
2347
2348 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2349 {
2350 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2351 return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2352 }
2353
2354 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
2355 {
2356 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2357 return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2358 }
2359
2360 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
2361 {
2362 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2363 return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2364 }
2365
2366 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
2367 {
2368 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2369 return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2370 }
2371
2372 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
2373 {
2374 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2375 return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2376 }
2377
2378 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
2379 {
2380 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2381 return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2382 }
2383
2384 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
2385 {
2386 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2387 return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2388 }
2389
2390 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
2391 {
2392 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2393 return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2394 }
2395
2396 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
2397 {
2398 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2399 return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2400 }
2401
2402 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
2403 {
2404 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2405 return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2406 }
2407
2408 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
2409 {
2410 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2411 return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2412 }
2413
2414 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
2415 {
2416 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2417 return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2418 }
2419
2420 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultExtension(IFileOpenDialog *iface,
2421 LPCWSTR pszDefaultExtension)
2422 {
2423 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2424 return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2425 }
2426
2427 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
2428 {
2429 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2430 return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2431 }
2432
2433 static HRESULT WINAPI IFileOpenDialog_fnSetClientGuid(IFileOpenDialog *iface, REFGUID guid)
2434 {
2435 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2436 return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2437 }
2438
2439 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
2440 {
2441 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2442 return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2443 }
2444
2445 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
2446 {
2447 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2448 return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2449 }
2450
2451 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
2452 {
2453 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2454 TRACE("%p (%p)\n", This, ppenum);
2455
2456 *ppenum = This->psia_results;
2457
2458 if(*ppenum)
2459 {
2460 IShellItemArray_AddRef(*ppenum);
2461 return S_OK;
2462 }
2463
2464 return E_FAIL;
2465 }
2466
2467 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
2468 {
2469 FileDialogImpl *This = impl_from_IFileOpenDialog(iface);
2470 TRACE("%p (%p)\n", This, ppsai);
2471
2472 if(This->psia_selection)
2473 {
2474 *ppsai = This->psia_selection;
2475 IShellItemArray_AddRef(*ppsai);
2476 return S_OK;
2477 }
2478
2479 return E_FAIL;
2480 }
2481
2482 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
2483 IFileOpenDialog_fnQueryInterface,
2484 IFileOpenDialog_fnAddRef,
2485 IFileOpenDialog_fnRelease,
2486 IFileOpenDialog_fnShow,
2487 IFileOpenDialog_fnSetFileTypes,
2488 IFileOpenDialog_fnSetFileTypeIndex,
2489 IFileOpenDialog_fnGetFileTypeIndex,
2490 IFileOpenDialog_fnAdvise,
2491 IFileOpenDialog_fnUnadvise,
2492 IFileOpenDialog_fnSetOptions,
2493 IFileOpenDialog_fnGetOptions,
2494 IFileOpenDialog_fnSetDefaultFolder,
2495 IFileOpenDialog_fnSetFolder,
2496 IFileOpenDialog_fnGetFolder,
2497 IFileOpenDialog_fnGetCurrentSelection,
2498 IFileOpenDialog_fnSetFileName,
2499 IFileOpenDialog_fnGetFileName,
2500 IFileOpenDialog_fnSetTitle,
2501 IFileOpenDialog_fnSetOkButtonLabel,
2502 IFileOpenDialog_fnSetFileNameLabel,
2503 IFileOpenDialog_fnGetResult,
2504 IFileOpenDialog_fnAddPlace,
2505 IFileOpenDialog_fnSetDefaultExtension,
2506 IFileOpenDialog_fnClose,
2507 IFileOpenDialog_fnSetClientGuid,
2508 IFileOpenDialog_fnClearClientData,
2509 IFileOpenDialog_fnSetFilter,
2510 IFileOpenDialog_fnGetResults,
2511 IFileOpenDialog_fnGetSelectedItems
2512 };
2513
2514 /**************************************************************************
2515 * IFileSaveDialog
2516 */
2517 static inline FileDialogImpl *impl_from_IFileSaveDialog(IFileSaveDialog *iface)
2518 {
2519 return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
2520 }
2521
2522 static HRESULT WINAPI IFileSaveDialog_fnQueryInterface(IFileSaveDialog *iface,
2523 REFIID riid,
2524 void **ppvObject)
2525 {
2526 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2527 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2528 }
2529
2530 static ULONG WINAPI IFileSaveDialog_fnAddRef(IFileSaveDialog *iface)
2531 {
2532 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2533 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2534 }
2535
2536 static ULONG WINAPI IFileSaveDialog_fnRelease(IFileSaveDialog *iface)
2537 {
2538 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2539 return IFileDialog2_Release(&This->IFileDialog2_iface);
2540 }
2541
2542 static HRESULT WINAPI IFileSaveDialog_fnShow(IFileSaveDialog *iface, HWND hwndOwner)
2543 {
2544 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2545 return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2546 }
2547
2548 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypes(IFileSaveDialog *iface, UINT cFileTypes,
2549 const COMDLG_FILTERSPEC *rgFilterSpec)
2550 {
2551 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2552 return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2553 }
2554
2555 static HRESULT WINAPI IFileSaveDialog_fnSetFileTypeIndex(IFileSaveDialog *iface, UINT iFileType)
2556 {
2557 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2558 return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2559 }
2560
2561 static HRESULT WINAPI IFileSaveDialog_fnGetFileTypeIndex(IFileSaveDialog *iface, UINT *piFileType)
2562 {
2563 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2564 return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2565 }
2566
2567 static HRESULT WINAPI IFileSaveDialog_fnAdvise(IFileSaveDialog *iface, IFileDialogEvents *pfde,
2568 DWORD *pdwCookie)
2569 {
2570 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2571 return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2572 }
2573
2574 static HRESULT WINAPI IFileSaveDialog_fnUnadvise(IFileSaveDialog *iface, DWORD dwCookie)
2575 {
2576 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2577 return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2578 }
2579
2580 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
2581 {
2582 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2583 return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2584 }
2585
2586 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2587 {
2588 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2589 return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2590 }
2591
2592 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultFolder(IFileSaveDialog *iface, IShellItem *psi)
2593 {
2594 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2595 return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2596 }
2597
2598 static HRESULT WINAPI IFileSaveDialog_fnSetFolder(IFileSaveDialog *iface, IShellItem *psi)
2599 {
2600 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2601 return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2602 }
2603
2604 static HRESULT WINAPI IFileSaveDialog_fnGetFolder(IFileSaveDialog *iface, IShellItem **ppsi)
2605 {
2606 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2607 return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2608 }
2609
2610 static HRESULT WINAPI IFileSaveDialog_fnGetCurrentSelection(IFileSaveDialog *iface, IShellItem **ppsi)
2611 {
2612 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2613 return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2614 }
2615
2616 static HRESULT WINAPI IFileSaveDialog_fnSetFileName(IFileSaveDialog *iface, LPCWSTR pszName)
2617 {
2618 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2619 return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2620 }
2621
2622 static HRESULT WINAPI IFileSaveDialog_fnGetFileName(IFileSaveDialog *iface, LPWSTR *pszName)
2623 {
2624 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2625 return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2626 }
2627
2628 static HRESULT WINAPI IFileSaveDialog_fnSetTitle(IFileSaveDialog *iface, LPCWSTR pszTitle)
2629 {
2630 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2631 return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2632 }
2633
2634 static HRESULT WINAPI IFileSaveDialog_fnSetOkButtonLabel(IFileSaveDialog *iface, LPCWSTR pszText)
2635 {
2636 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2637 return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2638 }
2639
2640 static HRESULT WINAPI IFileSaveDialog_fnSetFileNameLabel(IFileSaveDialog *iface, LPCWSTR pszLabel)
2641 {
2642 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2643 return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2644 }
2645
2646 static HRESULT WINAPI IFileSaveDialog_fnGetResult(IFileSaveDialog *iface, IShellItem **ppsi)
2647 {
2648 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2649 return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2650 }
2651
2652 static HRESULT WINAPI IFileSaveDialog_fnAddPlace(IFileSaveDialog *iface, IShellItem *psi, FDAP fdap)
2653 {
2654 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2655 return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2656 }
2657
2658 static HRESULT WINAPI IFileSaveDialog_fnSetDefaultExtension(IFileSaveDialog *iface,
2659 LPCWSTR pszDefaultExtension)
2660 {
2661 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2662 return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2663 }
2664
2665 static HRESULT WINAPI IFileSaveDialog_fnClose(IFileSaveDialog *iface, HRESULT hr)
2666 {
2667 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2668 return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2669 }
2670
2671 static HRESULT WINAPI IFileSaveDialog_fnSetClientGuid(IFileSaveDialog *iface, REFGUID guid)
2672 {
2673 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2674 return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2675 }
2676
2677 static HRESULT WINAPI IFileSaveDialog_fnClearClientData(IFileSaveDialog *iface)
2678 {
2679 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2680 return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2681 }
2682
2683 static HRESULT WINAPI IFileSaveDialog_fnSetFilter(IFileSaveDialog *iface, IShellItemFilter *pFilter)
2684 {
2685 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2686 return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2687 }
2688
2689 static HRESULT WINAPI IFileSaveDialog_fnSetSaveAsItem(IFileSaveDialog* iface, IShellItem *psi)
2690 {
2691 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2692 FIXME("stub - %p (%p)\n", This, psi);
2693 return E_NOTIMPL;
2694 }
2695
2696 static HRESULT WINAPI IFileSaveDialog_fnSetProperties(IFileSaveDialog* iface, IPropertyStore *pStore)
2697 {
2698 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2699 FIXME("stub - %p (%p)\n", This, pStore);
2700 return E_NOTIMPL;
2701 }
2702
2703 static HRESULT WINAPI IFileSaveDialog_fnSetCollectedProperties(IFileSaveDialog* iface,
2704 IPropertyDescriptionList *pList,
2705 BOOL fAppendDefault)
2706 {
2707 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2708 FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
2709 return E_NOTIMPL;
2710 }
2711
2712 static HRESULT WINAPI IFileSaveDialog_fnGetProperties(IFileSaveDialog* iface, IPropertyStore **ppStore)
2713 {
2714 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2715 FIXME("stub - %p (%p)\n", This, ppStore);
2716 return E_NOTIMPL;
2717 }
2718
2719 static HRESULT WINAPI IFileSaveDialog_fnApplyProperties(IFileSaveDialog* iface,
2720 IShellItem *psi,
2721 IPropertyStore *pStore,
2722 HWND hwnd,
2723 IFileOperationProgressSink *pSink)
2724 {
2725 FileDialogImpl *This = impl_from_IFileSaveDialog(iface);
2726 FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
2727 return E_NOTIMPL;
2728 }
2729
2730 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
2731 IFileSaveDialog_fnQueryInterface,
2732 IFileSaveDialog_fnAddRef,
2733 IFileSaveDialog_fnRelease,
2734 IFileSaveDialog_fnShow,
2735 IFileSaveDialog_fnSetFileTypes,
2736 IFileSaveDialog_fnSetFileTypeIndex,
2737 IFileSaveDialog_fnGetFileTypeIndex,
2738 IFileSaveDialog_fnAdvise,
2739 IFileSaveDialog_fnUnadvise,
2740 IFileSaveDialog_fnSetOptions,
2741 IFileSaveDialog_fnGetOptions,
2742 IFileSaveDialog_fnSetDefaultFolder,
2743 IFileSaveDialog_fnSetFolder,
2744 IFileSaveDialog_fnGetFolder,
2745 IFileSaveDialog_fnGetCurrentSelection,
2746 IFileSaveDialog_fnSetFileName,
2747 IFileSaveDialog_fnGetFileName,
2748 IFileSaveDialog_fnSetTitle,
2749 IFileSaveDialog_fnSetOkButtonLabel,
2750 IFileSaveDialog_fnSetFileNameLabel,
2751 IFileSaveDialog_fnGetResult,
2752 IFileSaveDialog_fnAddPlace,
2753 IFileSaveDialog_fnSetDefaultExtension,
2754 IFileSaveDialog_fnClose,
2755 IFileSaveDialog_fnSetClientGuid,
2756 IFileSaveDialog_fnClearClientData,
2757 IFileSaveDialog_fnSetFilter,
2758 IFileSaveDialog_fnSetSaveAsItem,
2759 IFileSaveDialog_fnSetProperties,
2760 IFileSaveDialog_fnSetCollectedProperties,
2761 IFileSaveDialog_fnGetProperties,
2762 IFileSaveDialog_fnApplyProperties
2763 };
2764
2765 /**************************************************************************
2766 * IExplorerBrowserEvents implementation
2767 */
2768 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
2769 {
2770 return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
2771 }
2772
2773 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
2774 REFIID riid, void **ppvObject)
2775 {
2776 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2777 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
2778
2779 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2780 }
2781
2782 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
2783 {
2784 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2785 TRACE("%p\n", This);
2786 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2787 }
2788
2789 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
2790 {
2791 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2792 TRACE("%p\n", This);
2793 return IFileDialog2_Release(&This->IFileDialog2_iface);
2794 }
2795
2796 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
2797 PCIDLIST_ABSOLUTE pidlFolder)
2798 {
2799 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2800 IShellItem *psi;
2801 HRESULT hr;
2802 TRACE("%p (%p)\n", This, pidlFolder);
2803
2804 hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
2805 if(SUCCEEDED(hr))
2806 {
2807 hr = events_OnFolderChanging(This, psi);
2808 IShellItem_Release(psi);
2809
2810 /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
2811 if(hr == S_FALSE)
2812 hr = E_FAIL;
2813
2814 return hr;
2815 }
2816 else
2817 ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
2818
2819 return S_OK;
2820 }
2821
2822 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
2823 IShellView *psv)
2824 {
2825 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2826 TRACE("%p (%p)\n", This, psv);
2827 return S_OK;
2828 }
2829
2830 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
2831 PCIDLIST_ABSOLUTE pidlFolder)
2832 {
2833 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2834 HRESULT hr;
2835 TRACE("%p (%p)\n", This, pidlFolder);
2836
2837 if(This->psi_folder)
2838 IShellItem_Release(This->psi_folder);
2839
2840 hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
2841 if(FAILED(hr))
2842 {
2843 ERR("Failed to get the current folder.\n");
2844 This->psi_folder = NULL;
2845 }
2846
2847 events_OnFolderChange(This);
2848
2849 return S_OK;
2850 }
2851
2852 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
2853 PCIDLIST_ABSOLUTE pidlFolder)
2854 {
2855 FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface);
2856 TRACE("%p (%p)\n", This, pidlFolder);
2857 return S_OK;
2858 }
2859
2860 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
2861 IExplorerBrowserEvents_fnQueryInterface,
2862 IExplorerBrowserEvents_fnAddRef,
2863 IExplorerBrowserEvents_fnRelease,
2864 IExplorerBrowserEvents_fnOnNavigationPending,
2865 IExplorerBrowserEvents_fnOnViewCreated,
2866 IExplorerBrowserEvents_fnOnNavigationComplete,
2867 IExplorerBrowserEvents_fnOnNavigationFailed
2868 };
2869
2870 /**************************************************************************
2871 * IServiceProvider implementation
2872 */
2873 static inline FileDialogImpl *impl_from_IServiceProvider(IServiceProvider *iface)
2874 {
2875 return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
2876 }
2877
2878 static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface,
2879 REFIID riid, void **ppvObject)
2880 {
2881 FileDialogImpl *This = impl_from_IServiceProvider(iface);
2882 TRACE("%p\n", This);
2883 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2884 }
2885
2886 static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface)
2887 {
2888 FileDialogImpl *This = impl_from_IServiceProvider(iface);
2889 TRACE("%p\n", This);
2890 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2891 }
2892
2893 static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface)
2894 {
2895 FileDialogImpl *This = impl_from_IServiceProvider(iface);
2896 TRACE("%p\n", This);
2897 return IFileDialog2_Release(&This->IFileDialog2_iface);
2898 }
2899
2900 static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
2901 REFGUID guidService,
2902 REFIID riid, void **ppv)
2903 {
2904 FileDialogImpl *This = impl_from_IServiceProvider(iface);
2905 HRESULT hr = E_NOTIMPL;
2906 TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
2907
2908 *ppv = NULL;
2909 if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
2910 hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
2911 else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
2912 hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
2913 else
2914 FIXME("Interface %s requested from unknown service %s\n",
2915 debugstr_guid(riid), debugstr_guid(guidService));
2916
2917 return hr;
2918 }
2919
2920 static const IServiceProviderVtbl vt_IServiceProvider = {
2921 IServiceProvider_fnQueryInterface,
2922 IServiceProvider_fnAddRef,
2923 IServiceProvider_fnRelease,
2924 IServiceProvider_fnQueryService
2925 };
2926
2927 /**************************************************************************
2928 * ICommDlgBrowser3 implementation
2929 */
2930 static inline FileDialogImpl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface)
2931 {
2932 return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
2933 }
2934
2935 static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface,
2936 REFIID riid, void **ppvObject)
2937 {
2938 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2939 TRACE("%p\n", This);
2940 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2941 }
2942
2943 static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface)
2944 {
2945 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2946 TRACE("%p\n", This);
2947 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2948 }
2949
2950 static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface)
2951 {
2952 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2953 TRACE("%p\n", This);
2954 return IFileDialog2_Release(&This->IFileDialog2_iface);
2955 }
2956
2957 static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3 *iface,
2958 IShellView *shv)
2959 {
2960 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2961 HRESULT hr;
2962 TRACE("%p (%p)\n", This, shv);
2963
2964 hr = on_default_action(This);
2965
2966 if(SUCCEEDED(hr))
2967 EndDialog(This->dlg_hwnd, S_OK);
2968
2969 return S_OK;
2970 }
2971
2972 static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange(ICommDlgBrowser3 *iface,
2973 IShellView *shv, ULONG uChange )
2974 {
2975 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
2976 IDataObject *new_selection;
2977 HRESULT hr;
2978 TRACE("%p (%p, %x)\n", This, shv, uChange);
2979
2980 switch(uChange)
2981 {
2982 case CDBOSC_SELCHANGE:
2983 if(This->psia_selection)
2984 {
2985 IShellItemArray_Release(This->psia_selection);
2986 This->psia_selection = NULL;
2987 }
2988
2989 hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
2990 if(SUCCEEDED(hr))
2991 {
2992 hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
2993 (void**)&This->psia_selection);
2994 if(SUCCEEDED(hr))
2995 {
2996 fill_filename_from_selection(This);
2997 events_OnSelectionChange(This);
2998 }
2999
3000 IDataObject_Release(new_selection);
3001 }
3002 break;
3003 default:
3004 TRACE("Unhandled state change\n");
3005 }
3006 return S_OK;
3007 }
3008
3009 static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
3010 IShellView *shv, LPCITEMIDLIST pidl)
3011 {
3012 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3013 IShellItem *psi;
3014 LPWSTR filename;
3015 LPITEMIDLIST parent_pidl;
3016 HRESULT hr;
3017 ULONG attr;
3018 TRACE("%p (%p, %p)\n", This, shv, pidl);
3019
3020 if(!This->filterspec_count && !(This->options & FOS_PICKFOLDERS))
3021 return S_OK;
3022
3023 hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
3024 if(SUCCEEDED(hr))
3025 {
3026 LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
3027 hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
3028 ILFree(parent_pidl);
3029 ILFree(full_pidl);
3030 }
3031 if(FAILED(hr))
3032 {
3033 ERR("Failed to get shellitem (%08x).\n", hr);
3034 return S_OK;
3035 }
3036
3037 hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
3038 if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
3039 {
3040 IShellItem_Release(psi);
3041 return S_OK;
3042 }
3043
3044 if((This->options & FOS_PICKFOLDERS) && !(attr & (SFGAO_FOLDER | SFGAO_LINK)))
3045 {
3046 IShellItem_Release(psi);
3047 return S_FALSE;
3048 }
3049
3050 hr = S_OK;
3051 if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
3052 {
3053 if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
3054 hr = S_FALSE;
3055 CoTaskMemFree(filename);
3056 }
3057
3058 IShellItem_Release(psi);
3059 return hr;
3060 }
3061
3062 static HRESULT WINAPI ICommDlgBrowser3_fnNotify(ICommDlgBrowser3 *iface,
3063 IShellView *ppshv, DWORD dwNotifyType)
3064 {
3065 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3066 FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
3067 return E_NOTIMPL;
3068 }
3069
3070 static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText(ICommDlgBrowser3 *iface,
3071 IShellView *pshv,
3072 LPWSTR pszText, int cchMax)
3073 {
3074 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3075 FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
3076 return E_NOTIMPL;
3077 }
3078
3079 static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags(ICommDlgBrowser3 *iface, DWORD *pdwFlags)
3080 {
3081 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3082 FIXME("Stub: %p (%p)\n", This, pdwFlags);
3083 return E_NOTIMPL;
3084 }
3085
3086 static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked(ICommDlgBrowser3 *iface,
3087 IShellView *pshv, int iColumn)
3088 {
3089 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3090 FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
3091 return E_NOTIMPL;
3092 }
3093
3094 static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter(ICommDlgBrowser3 *iface,
3095 LPWSTR pszFileSpec, int cchFileSpec)
3096 {
3097 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3098 FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
3099 return E_NOTIMPL;
3100 }
3101
3102 static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated(ICommDlgBrowser3 *iface,
3103 IShellView *pshv)
3104 {
3105 FileDialogImpl *This = impl_from_ICommDlgBrowser3(iface);
3106 FIXME("Stub: %p (%p)\n", This, pshv);
3107 return E_NOTIMPL;
3108 }
3109
3110 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
3111 ICommDlgBrowser3_fnQueryInterface,
3112 ICommDlgBrowser3_fnAddRef,
3113 ICommDlgBrowser3_fnRelease,
3114 ICommDlgBrowser3_fnOnDefaultCommand,
3115 ICommDlgBrowser3_fnOnStateChange,
3116 ICommDlgBrowser3_fnIncludeObject,
3117 ICommDlgBrowser3_fnNotify,
3118 ICommDlgBrowser3_fnGetDefaultMenuText,
3119 ICommDlgBrowser3_fnGetViewFlags,
3120 ICommDlgBrowser3_fnOnColumnClicked,
3121 ICommDlgBrowser3_fnGetCurrentFilter,
3122 ICommDlgBrowser3_fnOnPreviewCreated
3123 };
3124
3125 /**************************************************************************
3126 * IOleWindow implementation
3127 */
3128 static inline FileDialogImpl *impl_from_IOleWindow(IOleWindow *iface)
3129 {
3130 return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
3131 }
3132
3133 static HRESULT WINAPI IOleWindow_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
3134 {
3135 FileDialogImpl *This = impl_from_IOleWindow(iface);
3136 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3137 }
3138
3139 static ULONG WINAPI IOleWindow_fnAddRef(IOleWindow *iface)
3140 {
3141 FileDialogImpl *This = impl_from_IOleWindow(iface);
3142 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3143 }
3144
3145 static ULONG WINAPI IOleWindow_fnRelease(IOleWindow *iface)
3146 {
3147 FileDialogImpl *This = impl_from_IOleWindow(iface);
3148 return IFileDialog2_Release(&This->IFileDialog2_iface);
3149 }
3150
3151 static HRESULT WINAPI IOleWindow_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMOde)
3152 {
3153 FileDialogImpl *This = impl_from_IOleWindow(iface);
3154 FIXME("Stub: %p (%d)\n", This, fEnterMOde);
3155 return E_NOTIMPL;
3156 }
3157
3158 static HRESULT WINAPI IOleWindow_fnGetWindow(IOleWindow *iface, HWND *phwnd)
3159 {
3160 FileDialogImpl *This = impl_from_IOleWindow(iface);
3161 TRACE("%p (%p)\n", This, phwnd);
3162 *phwnd = This->dlg_hwnd;
3163 return S_OK;
3164 }
3165
3166 static const IOleWindowVtbl vt_IOleWindow = {
3167 IOleWindow_fnQueryInterface,
3168 IOleWindow_fnAddRef,
3169 IOleWindow_fnRelease,
3170 IOleWindow_fnGetWindow,
3171 IOleWindow_fnContextSensitiveHelp
3172 };
3173
3174 /**************************************************************************
3175 * IFileDialogCustomize implementation
3176 */
3177 static inline FileDialogImpl *impl_from_IFileDialogCustomize(IFileDialogCustomize *iface)
3178 {
3179 return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialogCustomize_iface);
3180 }
3181
3182 static HRESULT WINAPI IFileDialogCustomize_fnQueryInterface(IFileDialogCustomize *iface,
3183 REFIID riid, void **ppvObject)
3184 {
3185 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3186 return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3187 }
3188
3189 static ULONG WINAPI IFileDialogCustomize_fnAddRef(IFileDialogCustomize *iface)
3190 {
3191 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3192 return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3193 }
3194
3195 static ULONG WINAPI IFileDialogCustomize_fnRelease(IFileDialogCustomize *iface)
3196 {
3197 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3198 return IFileDialog2_Release(&This->IFileDialog2_iface);
3199 }
3200
3201 static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCustomize *iface,
3202 DWORD dwIDCtl)
3203 {
3204 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3205 FIXME("stub - %p (%d)\n", This, dwIDCtl);
3206 return E_NOTIMPL;
3207 }
3208
3209 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
3210 DWORD dwIDCtl,
3211 LPCWSTR pszLabel)
3212 {
3213 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3214 customctrl *ctrl;
3215 TBBUTTON tbb;
3216 HRESULT hr;
3217 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3218
3219 hr = cctrl_create_new(This, dwIDCtl, NULL, TOOLBARCLASSNAMEW,
3220 TBSTYLE_FLAT | CCS_NODIVIDER, 0,
3221 This->cctrl_def_height, &ctrl);
3222 if(SUCCEEDED(hr))
3223 {
3224 SendMessageW(ctrl->hwnd, TB_BUTTONSTRUCTSIZE, sizeof(tbb), 0);
3225 ctrl->type = IDLG_CCTRL_MENU;
3226
3227 /* Add the actual button with a popup menu. */
3228 tbb.iBitmap = I_IMAGENONE;
3229 tbb.dwData = (DWORD_PTR)CreatePopupMenu();
3230 tbb.iString = (DWORD_PTR)pszLabel;
3231 tbb.fsState = TBSTATE_ENABLED;
3232 tbb.fsStyle = BTNS_WHOLEDROPDOWN;
3233 tbb.idCommand = 1;
3234
3235 SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
3236 }
3237
3238 return hr;
3239 }
3240
3241 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
3242 DWORD dwIDCtl,
3243 LPCWSTR pszLabel)
3244 {
3245 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3246 customctrl *ctrl;
3247 HRESULT hr;
3248 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3249
3250 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
3251 This->cctrl_def_height, &ctrl);
3252 if(SUCCEEDED(hr))
3253 ctrl->type = IDLG_CCTRL_PUSHBUTTON;
3254
3255 return hr;
3256 }
3257
3258 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
3259 DWORD dwIDCtl)
3260 {
3261 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3262 customctrl *ctrl;
3263 HRESULT hr;
3264 TRACE("%p (%d)\n", This, dwIDCtl);
3265
3266 hr = cctrl_create_new(This, dwIDCtl, NULL, WC_COMBOBOXW, CBS_DROPDOWNLIST, 0,
3267 This->cctrl_def_height, &ctrl);
3268 if(SUCCEEDED(hr))
3269 ctrl->type = IDLG_CCTRL_COMBOBOX;
3270
3271 return hr;
3272 }
3273
3274 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
3275 DWORD dwIDCtl)
3276 {
3277 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3278 FIXME("stub - %p (%d)\n", This, dwIDCtl);
3279 return E_NOTIMPL;
3280 }
3281
3282 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
3283 DWORD dwIDCtl,
3284 LPCWSTR pszLabel,
3285 BOOL bChecked)
3286 {
3287 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3288 customctrl *ctrl;
3289 HRESULT hr;
3290 TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
3291
3292 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX|BS_MULTILINE, 0,
3293 This->cctrl_def_height, &ctrl);
3294 if(SUCCEEDED(hr))
3295 {
3296 ctrl->type = IDLG_CCTRL_CHECKBUTTON;
3297 SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
3298 }
3299
3300 return hr;
3301 }
3302
3303 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
3304 DWORD dwIDCtl,
3305 LPCWSTR pszText)
3306 {
3307 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3308 customctrl *ctrl;
3309 HRESULT hr;
3310 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3311
3312 hr = cctrl_create_new(This, dwIDCtl, pszText, WC_EDITW, ES_AUTOHSCROLL, WS_EX_CLIENTEDGE,
3313 This->cctrl_def_height, &ctrl);
3314 if(SUCCEEDED(hr))
3315 ctrl->type = IDLG_CCTRL_EDITBOX;
3316
3317 return hr;
3318 }
3319
3320 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
3321 DWORD dwIDCtl)
3322 {
3323 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3324 customctrl *ctrl;
3325 HRESULT hr;
3326 TRACE("%p (%d)\n", This, dwIDCtl);
3327
3328 hr = cctrl_create_new(This, dwIDCtl, NULL, WC_STATICW, SS_ETCHEDHORZ, 0,
3329 GetSystemMetrics(SM_CYEDGE), &ctrl);
3330 if(SUCCEEDED(hr))
3331 ctrl->type = IDLG_CCTRL_SEPARATOR;
3332
3333 return hr;
3334 }
3335
3336 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
3337 DWORD dwIDCtl,
3338 LPCWSTR pszText)
3339 {
3340 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3341 customctrl *ctrl;
3342 HRESULT hr;
3343 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3344
3345 hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
3346 This->cctrl_def_height, &ctrl);
3347 if(SUCCEEDED(hr))
3348 ctrl->type = IDLG_CCTRL_TEXT;
3349
3350 return hr;
3351 }
3352
3353 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
3354 DWORD dwIDCtl,
3355 LPCWSTR pszLabel)
3356 {
3357 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3358 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3359 TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3360
3361 if(!ctrl) return E_INVALIDARG;
3362
3363 switch(ctrl->type)
3364 {
3365 case IDLG_CCTRL_MENU:
3366 case IDLG_CCTRL_PUSHBUTTON:
3367 case IDLG_CCTRL_CHECKBUTTON:
3368 case IDLG_CCTRL_TEXT:
3369 case IDLG_CCTRL_VISUALGROUP:
3370 SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
3371 break;
3372 default:
3373 break;
3374 }
3375
3376 return S_OK;
3377 }
3378
3379 static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomize *iface,
3380 DWORD dwIDCtl,
3381 CDCONTROLSTATEF *pdwState)
3382 {
3383 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3384 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3385 TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
3386
3387 if(!ctrl) return E_NOTIMPL;
3388
3389 *pdwState = ctrl->cdcstate;
3390 return S_OK;
3391 }
3392
3393 static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomize *iface,
3394 DWORD dwIDCtl,
3395 CDCONTROLSTATEF dwState)
3396 {
3397 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3398 customctrl *ctrl = get_cctrl(This,dwIDCtl);
3399 TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
3400
3401 if(ctrl)
3402 {
3403 LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
3404
3405 if(dwState & CDCS_ENABLED)
3406 wndstyle &= ~(WS_DISABLED);
3407 else
3408 wndstyle |= WS_DISABLED;
3409
3410 if(dwState & CDCS_VISIBLE)
3411 wndstyle |= WS_VISIBLE;
3412 else
3413 wndstyle &= ~(WS_VISIBLE);
3414
3415 SetWindowLongW(ctrl->hwnd, GWL_STYLE, wndstyle);
3416
3417 /* We save the state separately since at least one application
3418 * relies on being able to hide a control. */
3419 ctrl->cdcstate = dwState;
3420 }
3421
3422 return S_OK;
3423 }
3424
3425 static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize *iface,
3426 DWORD dwIDCtl,
3427 WCHAR **ppszText)
3428 {
3429 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3430 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3431 WCHAR len, *text;
3432 TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
3433
3434 if(!ctrl || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
3435 return E_FAIL;
3436
3437 text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
3438 if(!text) return E_FAIL;
3439
3440 SendMessageW(ctrl->hwnd, WM_GETTEXT, len+1, (LPARAM)text);
3441 *ppszText = text;
3442 return S_OK;
3443 }
3444
3445 static HRESULT WINAPI IFileDialogCustomize_fnSetEditBoxText(IFileDialogCustomize *iface,
3446 DWORD dwIDCtl,
3447 LPCWSTR pszText)
3448 {
3449 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3450 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3451 TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
3452
3453 if(!ctrl || ctrl->type != IDLG_CCTRL_EDITBOX)
3454 return E_FAIL;
3455
3456 SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
3457 return S_OK;
3458 }
3459
3460 static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCustomize *iface,
3461 DWORD dwIDCtl,
3462 BOOL *pbChecked)
3463 {
3464 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3465 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3466 TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
3467
3468 if(ctrl)
3469 *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
3470
3471 return S_OK;
3472 }
3473
3474 static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCustomize *iface,
3475 DWORD dwIDCtl,
3476 BOOL bChecked)
3477 {
3478 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3479 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3480 TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
3481
3482 if(ctrl)
3483 SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
3484
3485 return S_OK;
3486 }
3487
3488 static UINT get_combobox_index_from_id(HWND cb_hwnd, DWORD dwIDItem)
3489 {
3490 UINT count = SendMessageW(cb_hwnd, CB_GETCOUNT, 0, 0);
3491 UINT i;
3492 if(!count || (count == CB_ERR))
3493 return -1;
3494
3495 for(i = 0; i < count; i++)
3496 if(SendMessageW(cb_hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
3497 return i;
3498
3499 TRACE("Item with id %d not found in combobox %p (item count: %d)\n", dwIDItem, cb_hwnd, count);
3500 return -1;
3501 }
3502
3503 static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize *iface,
3504 DWORD dwIDCtl,
3505 DWORD dwIDItem,
3506 LPCWSTR pszLabel)
3507 {
3508 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3509 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3510 TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
3511
3512 if(!ctrl) return E_FAIL;
3513
3514 switch(ctrl->type)
3515 {
3516 case IDLG_CCTRL_COMBOBOX:
3517 {
3518 UINT index;
3519
3520 if(get_combobox_index_from_id(ctrl->hwnd, dwIDItem) != -1)
3521 return E_INVALIDARG;
3522
3523 index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
3524 SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
3525
3526 return S_OK;
3527 }
3528 case IDLG_CCTRL_MENU:
3529 {
3530 TBBUTTON tbb;
3531 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
3532
3533 if(GetMenuState((HMENU)tbb.dwData, dwIDItem, MF_BYCOMMAND) != -1)
3534 return E_INVALIDARG;
3535
3536 AppendMenuW((HMENU)tbb.dwData, MF_STRING, dwIDItem, pszLabel);
3537 return S_OK;
3538 }
3539 default:
3540 break;
3541 }
3542
3543 return E_NOINTERFACE; /* win7 */
3544 }
3545
3546 static HRESULT WINAPI IFileDialogCustomize_fnRemoveControlItem(IFileDialogCustomize *iface,
3547 DWORD dwIDCtl,
3548 DWORD dwIDItem)
3549 {
3550 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3551 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3552 TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
3553
3554 if(!ctrl) return E_FAIL;
3555
3556 switch(ctrl->type)
3557 {
3558 case IDLG_CCTRL_COMBOBOX:
3559 {
3560 UINT i, count = SendMessageW(ctrl->hwnd, CB_GETCOUNT, 0, 0);
3561 if(!count || (count == CB_ERR))
3562 return E_FAIL;
3563
3564 for(i = 0; i < count; i++)
3565 if(SendMessageW(ctrl->hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
3566 {
3567 if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, i, 0) == CB_ERR)
3568 return E_FAIL;
3569 return S_OK;
3570 }
3571
3572 return E_UNEXPECTED;
3573 }
3574 case IDLG_CCTRL_MENU:
3575 {
3576 TBBUTTON tbb;
3577 HMENU hmenu;
3578 SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
3579 hmenu = (HMENU)tbb.dwData;
3580
3581 if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
3582 return E_UNEXPECTED;
3583
3584 return S_OK;
3585 }
3586 default:
3587 break;
3588 }
3589
3590 return E_FAIL;
3591 }
3592
3593 static HRESULT WINAPI IFileDialogCustomize_fnRemoveAllControlItems(IFileDialogCustomize *iface,
3594 DWORD dwIDCtl)
3595 {
3596 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3597 TRACE("%p (%d)\n", This, dwIDCtl);
3598
3599 /* Not implemented by native */
3600 return E_NOTIMPL;
3601 }
3602
3603 static HRESULT WINAPI IFileDialogCustomize_fnGetControlItemState(IFileDialogCustomize *iface,
3604 DWORD dwIDCtl,
3605 DWORD dwIDItem,
3606 CDCONTROLSTATEF *pdwState)
3607 {
3608 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3609 FIXME("stub - %p\n", This);
3610 return E_NOTIMPL;
3611 }
3612
3613 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCustomize *iface,
3614 DWORD dwIDCtl,
3615 DWORD dwIDItem,
3616 CDCONTROLSTATEF dwState)
3617 {
3618 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3619 FIXME("stub - %p\n", This);
3620 return E_NOTIMPL;
3621 }
3622
3623 static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogCustomize *iface,
3624 DWORD dwIDCtl,
3625 DWORD *pdwIDItem)
3626 {
3627 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3628 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3629 TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwIDItem);
3630
3631 if(!ctrl) return E_FAIL;
3632
3633 switch(ctrl->type)
3634 {
3635 case IDLG_CCTRL_COMBOBOX:
3636 {
3637 UINT index = SendMessageW(ctrl->hwnd, CB_GETCURSEL, 0, 0);
3638 if(index == CB_ERR)
3639 return E_FAIL;
3640
3641 *pdwIDItem = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
3642 return S_OK;
3643 }
3644 default:
3645 FIXME("Unsupported control type %d\n", ctrl->type);
3646 }
3647
3648 return E_NOTIMPL;
3649 }
3650
3651 static HRESULT WINAPI IFileDialogCustomize_fnSetSelectedControlItem(IFileDialogCustomize *iface,
3652 DWORD dwIDCtl,
3653 DWORD dwIDItem)
3654 {
3655 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3656 customctrl *ctrl = get_cctrl(This, dwIDCtl);
3657 TRACE("%p (%d, %d)\n", This, dwIDCtl, dwIDItem);
3658
3659 if(!ctrl) return E_INVALIDARG;
3660
3661 switch(ctrl->type)
3662 {
3663 case IDLG_CCTRL_COMBOBOX:
3664 {
3665 UINT index = get_combobox_index_from_id(ctrl->hwnd, dwIDItem);
3666
3667 if(index == -1)
3668 return E_INVALIDARG;
3669
3670 if(SendMessageW(ctrl->hwnd, CB_SETCURSEL, index, 0) == CB_ERR)
3671 return E_FAIL;
3672
3673 return S_OK;
3674 }
3675 default:
3676 FIXME("Unsupported control type %d\n", ctrl->type);
3677 }
3678
3679 return E_INVALIDARG;
3680 }
3681
3682 static HRESULT WINAPI IFileDialogCustomize_fnStartVisualGroup(IFileDialogCustomize *iface,
3683 DWORD dwIDCtl,
3684 LPCWSTR pszLabel)
3685 {
3686 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3687 customctrl *vg;
3688 HRESULT hr;
3689 TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
3690
3691 if(This->cctrl_active_vg)
3692 return E_UNEXPECTED;
3693
3694 hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_STATICW, 0, 0,
3695 This->cctrl_def_height, &vg);
3696 if(SUCCEEDED(hr))
3697 {
3698 vg->type = IDLG_CCTRL_VISUALGROUP;
3699 This->cctrl_active_vg = vg;
3700 }
3701
3702 return hr;
3703 }
3704
3705 static HRESULT WINAPI IFileDialogCustomize_fnEndVisualGroup(IFileDialogCustomize *iface)
3706 {
3707 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3708 TRACE("%p\n", This);
3709
3710 This->cctrl_active_vg = NULL;
3711
3712 return S_OK;
3713 }
3714
3715 static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize *iface,
3716 DWORD dwIDCtl)
3717 {
3718 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3719 FIXME("stub - %p (%d)\n", This, dwIDCtl);
3720 return E_NOTIMPL;
3721 }
3722
3723 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCustomize *iface,
3724 DWORD dwIDCtl,
3725 DWORD dwIDItem,
3726 LPCWSTR pszLabel)
3727 {
3728 FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
3729 FIXME("stub - %p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
3730 return E_NOTIMPL;
3731 }
3732
3733 static const IFileDialogCustomizeVtbl vt_IFileDialogCustomize = {
3734 IFileDialogCustomize_fnQueryInterface,
3735 IFileDialogCustomize_fnAddRef,
3736 IFileDialogCustomize_fnRelease,
3737 IFileDialogCustomize_fnEnableOpenDropDown,
3738 IFileDialogCustomize_fnAddMenu,
3739 IFileDialogCustomize_fnAddPushButton,
3740 IFileDialogCustomize_fnAddComboBox,
3741 IFileDialogCustomize_fnAddRadioButtonList,
3742 IFileDialogCustomize_fnAddCheckButton,
3743 IFileDialogCustomize_fnAddEditBox,
3744 IFileDialogCustomize_fnAddSeparator,
3745 IFileDialogCustomize_fnAddText,
3746 IFileDialogCustomize_fnSetControlLabel,
3747 IFileDialogCustomize_fnGetControlState,
3748 IFileDialogCustomize_fnSetControlState,
3749 IFileDialogCustomize_fnGetEditBoxText,
3750 IFileDialogCustomize_fnSetEditBoxText,
3751 IFileDialogCustomize_fnGetCheckButtonState,
3752 IFileDialogCustomize_fnSetCheckButtonState,
3753 IFileDialogCustomize_fnAddControlItem,
3754 IFileDialogCustomize_fnRemoveControlItem,
3755 IFileDialogCustomize_fnRemoveAllControlItems,
3756 IFileDialogCustomize_fnGetControlItemState,
3757 IFileDialogCustomize_fnSetControlItemState,
3758 IFileDialogCustomize_fnGetSelectedControlItem,
3759 IFileDialogCustomize_fnSetSelectedControlItem,
3760 IFileDialogCustomize_fnStartVisualGroup,
3761 IFileDialogCustomize_fnEndVisualGroup,
3762 IFileDialogCustomize_fnMakeProminent,
3763 IFileDialogCustomize_fnSetControlItemText
3764 };
3765
3766 static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv, enum ITEMDLG_TYPE type)
3767 {
3768 FileDialogImpl *fdimpl;
3769 HRESULT hr;
3770 IShellFolder *psf;
3771 TRACE("%p, %s, %p\n", pUnkOuter, debugstr_guid(riid), ppv);
3772
3773 if(!ppv)
3774 return E_POINTER;
3775 if(pUnkOuter)
3776 return CLASS_E_NOAGGREGATION;
3777
3778 fdimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(FileDialogImpl));
3779 if(!fdimpl)
3780 return E_OUTOFMEMORY;
3781
3782 fdimpl->ref = 1;
3783 fdimpl->IFileDialog2_iface.lpVtbl = &vt_IFileDialog2;
3784 fdimpl->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
3785 fdimpl->IServiceProvider_iface.lpVtbl = &vt_IServiceProvider;
3786 fdimpl->ICommDlgBrowser3_iface.lpVtbl = &vt_ICommDlgBrowser3;
3787 fdimpl->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
3788 fdimpl->IFileDialogCustomize_iface.lpVtbl = &vt_IFileDialogCustomize;
3789
3790 if(type == ITEMDLG_TYPE_OPEN)
3791 {
3792 fdimpl->dlg_type = ITEMDLG_TYPE_OPEN;
3793 fdimpl->u.IFileOpenDialog_iface.lpVtbl = &vt_IFileOpenDialog;
3794 fdimpl->options = FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
3795 fdimpl->custom_title = fdimpl->custom_okbutton = NULL;
3796 }
3797 else
3798 {
3799 WCHAR buf[16];
3800 fdimpl->dlg_type = ITEMDLG_TYPE_SAVE;
3801 fdimpl->u.IFileSaveDialog_iface.lpVtbl = &vt_IFileSaveDialog;
3802 fdimpl->options = FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
3803
3804 LoadStringW(COMDLG32_hInstance, IDS_SAVE, buf, sizeof(buf)/sizeof(WCHAR));
3805 fdimpl->custom_title = StrDupW(buf);
3806 fdimpl->custom_okbutton = StrDupW(buf);
3807 }
3808
3809 fdimpl->filterspecs = NULL;
3810 fdimpl->filterspec_count = 0;
3811 fdimpl->filetypeindex = 0;
3812
3813 fdimpl->psia_selection = fdimpl->psia_results = NULL;
3814 fdimpl->psi_setfolder = fdimpl->psi_folder = NULL;
3815
3816 list_init(&fdimpl->events_clients);
3817 fdimpl->events_next_cookie = 0;
3818
3819 fdimpl->dlg_hwnd = NULL;
3820 fdimpl->peb = NULL;
3821
3822 fdimpl->set_filename = NULL;
3823 fdimpl->default_ext = NULL;
3824 fdimpl->custom_cancelbutton = fdimpl->custom_filenamelabel = NULL;
3825
3826 fdimpl->client_guid = GUID_NULL;
3827
3828 /* FIXME: The default folder setting should be restored for the
3829 * application if it was previously set. */
3830 SHGetDesktopFolder(&psf);
3831 SHGetItemFromObject((IUnknown*)psf, &IID_IShellItem, (void**)&fdimpl->psi_defaultfolder);
3832 IShellFolder_Release(psf);
3833
3834 hr = init_custom_controls(fdimpl);
3835 if(FAILED(hr))
3836 {
3837 ERR("Failed to initialize custom controls (0x%08x).\n", hr);
3838 IUnknown_Release((IUnknown*)fdimpl);
3839 return E_FAIL;
3840 }
3841
3842 hr = IUnknown_QueryInterface((IUnknown*)fdimpl, riid, ppv);
3843 IUnknown_Release((IUnknown*)fdimpl);
3844 return hr;
3845 }
3846
3847 HRESULT FileOpenDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
3848 {
3849 return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_OPEN);
3850 }
3851
3852 HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
3853 {
3854 return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);
3855 }
3856
3857 #endif /* Win 7 */