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