ReactOS  0.4.13-dev-455-g28ed234
itemdlg.c
Go to the documentation of this file.
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 
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 
60 };
61 
73 };
74 
75 typedef struct cctrl_item {
78  CDCONTROLSTATEF cdcstate;
80  struct list entry;
81 } cctrl_item;
82 
83 typedef struct {
85  UINT id, dlgid;
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;
100 
101 typedef struct FileDialogImpl {
103  union {
104  IFileOpenDialog IFileOpenDialog_iface;
106  } u;
108  IExplorerBrowserEvents IExplorerBrowserEvents_iface;
112  IFileDialogCustomize IFileDialogCustomize_iface;
114 
115  FILEOPENDIALOGOPTIONS options;
119 
122 
123  IShellItemArray *psia_selection;
124  IShellItemArray *psia_results;
128 
132 
139 
143  struct list cctrls;
146 
152 
155 
156 /**************************************************************************
157  * Event wrappers.
158  */
160 {
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 
180 {
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 
200 {
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 
212 {
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 
224 {
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 
236 {
238  HRESULT hr = S_OK;
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 
262  answer = MessageBoxW(This->dlg_hwnd, buf, This->custom_title,
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 
282 {
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 
301 {
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 
320 {
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 
339  DWORD ctl_id)
340 {
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  */
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 
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 
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, (This->options & FOS_PICKFOLDERS) ? SIGDN_FILESYSPATH : 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++ = ' ';
458  }
459  *(cur_point-1) = '\0';
460 
461  set_file_name(This, string);
462  HeapFree(GetProcessHeap(), 0, string);
463  }
464 
466  return;
467 }
468 
470 {
471  WCHAR *endpos, *ext;
472 
473  lstrcpyW(buf, spec);
474  if( (endpos = StrChrW(buf, ';')) )
475  *endpos = '\0';
476 
478  if(StrChrW(ext, '*'))
479  return NULL;
480 
481  return ext;
482 }
483 
485 {
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. */
496  }
497  else
498  {
499  SFGAOF attributes;
500  result = SUCCEEDED(IShellItem_GetAttributes(shellitem, SFGAO_VALIDATE, &attributes));
501  }
502 
503  return result;
504 }
505 
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  {
597  open_action = ONOPEN_BROWSE;
598  }
599  }
600  else
601  {
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];
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 
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 
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);
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 
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 
773 {
774  cctrl_item* item;
775 
777  {
778  if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
779  return item;
780  }
781 
782  return NULL;
783 }
784 
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  {
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  */
820 {
821  customctrl *ctrl, *sub_ctrl;
822 
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 
838 {
839  customctrl *ctrl, *sub_ctrl;
840 
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);
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,
900 
902 }
903 
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);
937 }
938 
940 {
941  RECT rc;
942  UINT total_height;
943  UINT max_width, size;
944  customctrl *sub_ctrl;
945 
946  switch(ctrl->type)
947  {
949  case IDLG_CCTRL_COMBOBOX:
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,
957  break;
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,
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,
985  break;
987  {
988  cctrl_item* item;
989 
990  total_height = 0;
991  max_width = 0;
992 
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,
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,
1008 
1009  SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, max_width, total_height,
1011 
1012  break;
1013  }
1014  case IDLG_CCTRL_EDITBOX:
1015  case IDLG_CCTRL_SEPARATOR:
1016  case IDLG_CCTRL_MENU:
1018  /* Nothing */
1019  break;
1020  }
1021 }
1022 
1024 {
1025  FileDialogImpl *This = crs->lpCreateParams;
1026  TRACE("%p\n", This);
1027 
1029  return TRUE;
1030 }
1031 
1033 {
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);
1044  }
1045  else
1047  }
1048 
1049  return TRUE;
1050 }
1051 
1053 {
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 
1068 {
1069  NMTOOLBARW *nmtb = (NMTOOLBARW*)lparam;
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  {
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 
1092 {
1093  switch(HIWORD(wparam))
1094  {
1097  }
1098 
1099  return FALSE;
1100 }
1101 
1103 {
1104  NMHDR *nmhdr = (NMHDR*)lparam;
1105 
1106  switch(nmhdr->code)
1107  {
1109  }
1110 
1111  return FALSE;
1112 }
1113 
1115 {
1117  customctrl *ctrl;
1118  HWND hwnd_child;
1119  RECT rc;
1120 
1121  switch(message)
1122  {
1126  case WM_SIZE:
1127  hwnd_child = GetPropW(hwnd, notifysink_childW);
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 
1138 }
1139 
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 
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 
1192 
1193  if(ppctrl) *ppctrl = ctrl;
1194 
1195  This->cctrl_next_dlgid++;
1196  return S_OK;
1197 }
1198 
1199 /**************************************************************************
1200  * Container functions.
1201  */
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;
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;
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;
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,
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 
1326 {
1327  customctrl *sub_ctrl;
1328  cctrl_item* item;
1329 
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  {
1342  }
1343  }
1344 }
1345 
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. */
1365  if(!font)
1366  ERR("Failed to get font handle from dialog.\n");
1367 
1369  {
1370  if(font) ctrl_set_font(ctrl, font);
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 
1388 {
1389  FileDialogImpl *This = crs->lpCreateParams;
1390  TRACE("%p\n", This);
1391 
1393  return TRUE;
1394 }
1395 
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 
1411 {
1413 
1414  switch(umessage)
1415  {
1418  default: return DefWindowProcW(hwnd, umessage, wparam, lparam);
1419  }
1420 
1421  return FALSE;
1422 }
1423 
1425 {
1426  cctrl_item *cursor;
1427 
1429  {
1431  }
1432 }
1433 
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 
1465 
1467 
1468  return 0;
1469 }
1470 
1472 {
1473  switch(HIWORD(wparam))
1474  {
1476  }
1477 
1478  return FALSE;
1479 }
1480 
1482 {
1484 
1485  switch(message)
1486  {
1488  }
1489 
1491 }
1492 
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 
1501 
1502  if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
1503  {
1504  wc.style = CS_HREDRAW | CS_VREDRAW;
1506  wc.cbClsExtra = 0;
1507  wc.cbWndExtra = 0;
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,
1520  0, 0, 0, 0, NULL, 0,
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 */
1544  {
1545  wc.style = CS_HREDRAW | CS_VREDRAW;
1547  wc.cbClsExtra = 0;
1548  wc.cbWndExtra = 0;
1550  wc.hIcon = 0;
1551  wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1552  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1553  wc.lpszMenuName = NULL;
1555 
1556  if (!RegisterClassW(&wc))
1557  ERR("Failed to register FloatNotifySink window class.\n");
1558  }
1559 
1562  {
1563  wc.style = CS_HREDRAW | CS_VREDRAW;
1565  wc.cbClsExtra = 0;
1566  wc.cbWndExtra = 0;
1568  wc.hIcon = 0;
1569  wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1570  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1571  wc.lpszMenuName = NULL;
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  */
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 
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,
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,
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,
1821 
1822  if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)) )
1823  DeferWindowPos(hdwp, hwnd, NULL, filenamelabel_rc.left, filenamelabel_rc.top, 0, 0,
1825 
1826  if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDOK)) )
1827  DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
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,
1833 
1834  if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
1835  DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
1837 
1838  if(hdwp)
1839  EndDeferWindowPos(hdwp);
1840  else
1841  ERR("Failed to position dialog controls.\n");
1842 
1843  return;
1844 }
1845 
1847 {
1848  IShellItem *psi_folder;
1850  FOLDERSETTINGS fos;
1851  RECT rc = {0};
1852  HRESULT hr;
1853 
1854  /* Create ExplorerBrowser instance */
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 
1903 {
1904  HWND htoolbar;
1905  TBADDBITMAP tbab;
1906  TBBUTTON button[2];
1907 
1909  0, 0, 0, 0,
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 
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 
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 
1985 
1986  return 0;
1987  }
1988 
1989  return CallWindowProcW((WNDPROC)GetPropW(hwnd, prop_oldwndproc), hwnd, umessage, wparam, lparam);
1990 }
1991 
1993 {
1995  HWND hitem;
1996 
1997  TRACE("(%p, %p)\n", This, hwnd);
1998 
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,
2076  }
2077 
2078  ctrl_container_reparent(This, This->dlg_hwnd);
2083 
2084  if(This->filterspec_count)
2086 
2087  if ((hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)))
2088  SetFocus(hitem);
2089 
2090  return FALSE;
2091 }
2092 
2094 {
2096  return FALSE;
2097 }
2098 
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 
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 
2123  This->dlg_hwnd = NULL;
2124 
2125  DeleteObject(This->hfont_opendropdown);
2126  This->hfont_opendropdown = NULL;
2127 
2128  return TRUE;
2129 }
2130 
2132 {
2133  TRACE("%p\n", This);
2134 
2136  EndDialog(This->dlg_hwnd, S_OK);
2137 
2138  return FALSE;
2139 }
2140 
2142 {
2143  TRACE("%p\n", This);
2144 
2146 
2147  return FALSE;
2148 }
2149 
2151 {
2152  if(HIWORD(wparam) == BN_CLICKED)
2153  {
2154  HWND hwnd = (HWND)lparam;
2158  }
2159 
2160  return FALSE;
2161 }
2162 
2164 {
2165  TRACE("%p\n", This);
2166  IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEBACK);
2167  return FALSE;
2168 }
2169 
2171 {
2172  TRACE("%p\n", This);
2173  IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_NAVIGATEFORWARD);
2174  return FALSE;
2175 }
2176 
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))
2210 
2211  lstrcatW(buf, ext);
2213  }
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. */
2221  }
2222 
2223  return FALSE;
2224 }
2225 
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);
2236  default: TRACE("Unknown command.\n");
2237  }
2238  return FALSE;
2239 }
2240 
2242 {
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);
2251  case WM_DESTROY: return on_wm_destroy(This);
2252  }
2253 
2254  return FALSE;
2255 }
2256 
2258 {
2259  INT_PTR res;
2260 
2261  SetLastError(0);
2263  MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
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  */
2280 {
2281  return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialog2_iface);
2282 }
2283 
2285  REFIID riid,
2286  void **ppvObject)
2287 {
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 
2342 {
2344  LONG ref = InterlockedIncrement(&This->ref);
2345  TRACE("%p - ref %d\n", This, ref);
2346 
2347  return ref;
2348 }
2349 
2351 {
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 
2391 {
2393  TRACE("%p (%p)\n", iface, hwndOwner);
2394 
2395  This->opendropdown_has_selection = FALSE;
2396 
2397  return create_dialog(This, hwndOwner);
2398 }
2399 
2401  const COMDLG_FILTERSPEC *rgFilterSpec)
2402 {
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 
2428 {
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 
2443 {
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 
2459 {
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 
2480 {
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 {
2508  TRACE("%p (0x%x)\n", This, fos);
2509 
2510  if (fos & ~(FOS_OVERWRITEPROMPT | FOS_STRICTFILETYPES | FOS_NOCHANGEDIR | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM
2511  | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_ALLOWMULTISELECT | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST
2512  | FOS_CREATEPROMPT | FOS_SHAREAWARE | FOS_NOREADONLYRETURN | FOS_NOTESTFILECREATE | FOS_HIDEMRUPLACES
2513  | FOS_HIDEPINNEDPLACES | FOS_NODEREFERENCELINKS | FOS_DONTADDTORECENT | FOS_FORCESHOWHIDDEN
2514  | FOS_DEFAULTNOMINIMODE | FOS_FORCEPREVIEWPANEON | FOS_SUPPORTSTREAMABLEITEMS))
2515  {
2516  WARN("Invalid option %#x\n", fos);
2517  return E_INVALIDARG;
2518  }
2519 
2520  if( !(This->options & FOS_PICKFOLDERS) && (fos & FOS_PICKFOLDERS) )
2521  {
2522  WCHAR buf[30];
2524  IFileDialog2_SetTitle(iface, buf);
2525  }
2526 
2527  This->options = fos;
2528 
2529  return S_OK;
2530 }
2531 
2532 static HRESULT WINAPI IFileDialog2_fnGetOptions(IFileDialog2 *iface, FILEOPENDIALOGOPTIONS *pfos)
2533 {
2535  TRACE("%p (%p)\n", This, pfos);
2536 
2537  if(!pfos)
2538  return E_INVALIDARG;
2539 
2540  *pfos = This->options;
2541 
2542  return S_OK;
2543 }
2544 
2546 {
2548  TRACE("%p (%p)\n", This, psi);
2549  if(This->psi_defaultfolder)
2550  IShellItem_Release(This->psi_defaultfolder);
2551 
2552  This->psi_defaultfolder = psi;
2553 
2554  if(This->psi_defaultfolder)
2555  IShellItem_AddRef(This->psi_defaultfolder);
2556 
2557  return S_OK;
2558 }
2559 
2561 {
2563  TRACE("%p (%p)\n", This, psi);
2564  if(This->psi_setfolder)
2565  IShellItem_Release(This->psi_setfolder);
2566 
2567  This->psi_setfolder = psi;
2568 
2569  if(This->psi_setfolder)
2570  IShellItem_AddRef(This->psi_setfolder);
2571 
2572  return S_OK;
2573 }
2574 
2576 {
2578  TRACE("%p (%p)\n", This, ppsi);
2579  if(!ppsi)
2580  return E_INVALIDARG;
2581 
2582  /* FIXME:
2583  If the dialog is shown, return the current(ly selected) folder. */
2584 
2585  *ppsi = NULL;
2586  if(This->psi_folder)
2587  *ppsi = This->psi_folder;
2588  else if(This->psi_setfolder)
2589  *ppsi = This->psi_setfolder;
2590  else if(This->psi_defaultfolder)
2591  *ppsi = This->psi_defaultfolder;
2592 
2593  if(*ppsi)
2594  {
2595  IShellItem_AddRef(*ppsi);
2596  return S_OK;
2597  }
2598 
2599  return E_FAIL;
2600 }
2601 
2603 {
2605  HRESULT hr;
2606  TRACE("%p (%p)\n", This, ppsi);
2607 
2608  if(!ppsi)
2609  return E_INVALIDARG;
2610 
2611  if(This->psia_selection)
2612  {
2613  /* FIXME: Check filename edit box */
2614  hr = IShellItemArray_GetItemAt(This->psia_selection, 0, ppsi);
2615  return hr;
2616  }
2617 
2618  return E_FAIL;
2619 }
2620 
2622 {
2624  TRACE("%p (%s)\n", iface, debugstr_w(pszName));
2625 
2626  set_file_name(This, pszName);
2627 
2628  return S_OK;
2629 }
2630 
2632 {
2634  TRACE("%p (%p)\n", iface, pszName);
2635 
2636  if(!pszName)
2637  return E_INVALIDARG;
2638 
2639  *pszName = NULL;
2640  get_file_name(This, pszName);
2641  return *pszName ? S_OK : E_FAIL;
2642 }
2643 
2645 {
2647  TRACE("%p (%s)\n", This, debugstr_w(pszTitle));
2648 
2649  LocalFree(This->custom_title);
2650  This->custom_title = StrDupW(pszTitle);
2652 
2653  return S_OK;
2654 }
2655 
2657 {
2659  TRACE("%p (%s)\n", This, debugstr_w(pszText));
2660 
2661  LocalFree(This->custom_okbutton);
2662  This->custom_okbutton = StrDupW(pszText);
2665 
2666  return S_OK;
2667 }
2668 
2670 {
2672  TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2673 
2674  LocalFree(This->custom_filenamelabel);
2675  This->custom_filenamelabel = StrDupW(pszLabel);
2678 
2679  return S_OK;
2680 }
2681 
2683 {
2685  HRESULT hr;
2686  TRACE("%p (%p)\n", This, ppsi);
2687 
2688  if(!ppsi)
2689  return E_INVALIDARG;
2690 
2691  if(This->psia_results)
2692  {
2693  UINT item_count;
2694  hr = IShellItemArray_GetCount(This->psia_results, &item_count);
2695  if(SUCCEEDED(hr))
2696  {
2697  if(item_count != 1)
2698  return E_FAIL;
2699 
2700  /* Adds a reference. */
2701  hr = IShellItemArray_GetItemAt(This->psia_results, 0, ppsi);
2702  }
2703 
2704  return hr;
2705  }
2706 
2707  return E_UNEXPECTED;
2708 }
2709 
2711 {
2713  FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
2714  return S_OK;
2715 }
2716 
2718 {
2720  TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension));
2721 
2722  LocalFree(This->default_ext);
2723  This->default_ext = StrDupW(pszDefaultExtension);
2724 
2725  return S_OK;
2726 }
2727 
2729 {
2731  TRACE("%p (0x%08x)\n", This, hr);
2732 
2733  if(This->dlg_hwnd)
2734  EndDialog(This->dlg_hwnd, hr);
2735 
2736  return S_OK;
2737 }
2738 
2740 {
2742  TRACE("%p (%s)\n", This, debugstr_guid(guid));
2743  This->client_guid = *guid;
2744  return S_OK;
2745 }
2746 
2748 {
2750  FIXME("stub - %p\n", This);
2751  return E_NOTIMPL;
2752 }
2753 
2755 {
2757  FIXME("stub - %p (%p)\n", This, pFilter);
2758  return E_NOTIMPL;
2759 }
2760 
2762 {
2764  TRACE("%p (%s)\n", This, debugstr_w(pszLabel));
2765 
2766  LocalFree(This->custom_cancelbutton);
2767  This->custom_cancelbutton = StrDupW(pszLabel);
2770 
2771  return S_OK;
2772 }
2773 
2775 {
2777  FIXME("stub - %p (%p)\n", This, psi);
2778  return E_NOTIMPL;
2779 }
2780 
2781 static const IFileDialog2Vtbl vt_IFileDialog2 = {
2811 };
2812 
2813 /**************************************************************************
2814  * IFileOpenDialog
2815  */
2816 static inline FileDialogImpl *impl_from_IFileOpenDialog(IFileOpenDialog *iface)
2817 {
2818  return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileOpenDialog_iface);
2819 }
2820 
2821 static HRESULT WINAPI IFileOpenDialog_fnQueryInterface(IFileOpenDialog *iface,
2822  REFIID riid, void **ppvObject)
2823 {
2825  return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
2826 }
2827 
2828 static ULONG WINAPI IFileOpenDialog_fnAddRef(IFileOpenDialog *iface)
2829 {
2831  return IFileDialog2_AddRef(&This->IFileDialog2_iface);
2832 }
2833 
2834 static ULONG WINAPI IFileOpenDialog_fnRelease(IFileOpenDialog *iface)
2835 {
2837  return IFileDialog2_Release(&This->IFileDialog2_iface);
2838 }
2839 
2840 static HRESULT WINAPI IFileOpenDialog_fnShow(IFileOpenDialog *iface, HWND hwndOwner)
2841 {
2843  return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
2844 }
2845 
2846 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypes(IFileOpenDialog *iface, UINT cFileTypes,
2847  const COMDLG_FILTERSPEC *rgFilterSpec)
2848 {
2850  return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
2851 }
2852 
2853 static HRESULT WINAPI IFileOpenDialog_fnSetFileTypeIndex(IFileOpenDialog *iface, UINT iFileType)
2854 {
2856  return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
2857 }
2858 
2859 static HRESULT WINAPI IFileOpenDialog_fnGetFileTypeIndex(IFileOpenDialog *iface, UINT *piFileType)
2860 {
2862  return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
2863 }
2864 
2865 static HRESULT WINAPI IFileOpenDialog_fnAdvise(IFileOpenDialog *iface, IFileDialogEvents *pfde,
2866  DWORD *pdwCookie)
2867 {
2869  return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
2870 }
2871 
2872 static HRESULT WINAPI IFileOpenDialog_fnUnadvise(IFileOpenDialog *iface, DWORD dwCookie)
2873 {
2875  return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
2876 }
2877 
2878 static HRESULT WINAPI IFileOpenDialog_fnSetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS fos)
2879 {
2881  return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
2882 }
2883 
2884 static HRESULT WINAPI IFileOpenDialog_fnGetOptions(IFileOpenDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
2885 {
2887  return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
2888 }
2889 
2890 static HRESULT WINAPI IFileOpenDialog_fnSetDefaultFolder(IFileOpenDialog *iface, IShellItem *psi)
2891 {
2893  return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
2894 }
2895 
2896 static HRESULT WINAPI IFileOpenDialog_fnSetFolder(IFileOpenDialog *iface, IShellItem *psi)
2897 {
2899  return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
2900 }
2901 
2902 static HRESULT WINAPI IFileOpenDialog_fnGetFolder(IFileOpenDialog *iface, IShellItem **ppsi)
2903 {
2905  return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
2906 }
2907 
2908 static HRESULT WINAPI IFileOpenDialog_fnGetCurrentSelection(IFileOpenDialog *iface, IShellItem **ppsi)
2909 {
2911  return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
2912 }
2913 
2914 static HRESULT WINAPI IFileOpenDialog_fnSetFileName(IFileOpenDialog *iface, LPCWSTR pszName)
2915 {
2917  return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
2918 }
2919 
2920 static HRESULT WINAPI IFileOpenDialog_fnGetFileName(IFileOpenDialog *iface, LPWSTR *pszName)
2921 {
2923  return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
2924 }
2925 
2926 static HRESULT WINAPI IFileOpenDialog_fnSetTitle(IFileOpenDialog *iface, LPCWSTR pszTitle)
2927 {
2929  return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
2930 }
2931 
2932 static HRESULT WINAPI IFileOpenDialog_fnSetOkButtonLabel(IFileOpenDialog *iface, LPCWSTR pszText)
2933 {
2935  return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
2936 }
2937 
2938 static HRESULT WINAPI IFileOpenDialog_fnSetFileNameLabel(IFileOpenDialog *iface, LPCWSTR pszLabel)
2939 {
2941  return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
2942 }
2943 
2944 static HRESULT WINAPI IFileOpenDialog_fnGetResult(IFileOpenDialog *iface, IShellItem **ppsi)
2945 {
2947  return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
2948 }
2949 
2950 static HRESULT WINAPI IFileOpenDialog_fnAddPlace(IFileOpenDialog *iface, IShellItem *psi, FDAP fdap)
2951 {
2953  return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
2954 }
2955 
2957  LPCWSTR pszDefaultExtension)
2958 {
2960  return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
2961 }
2962 
2963 static HRESULT WINAPI IFileOpenDialog_fnClose(IFileOpenDialog *iface, HRESULT hr)
2964 {
2966  return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
2967 }
2968 
2970 {
2972  return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
2973 }
2974 
2975 static HRESULT WINAPI IFileOpenDialog_fnClearClientData(IFileOpenDialog *iface)
2976 {
2978  return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
2979 }
2980 
2981 static HRESULT WINAPI IFileOpenDialog_fnSetFilter(IFileOpenDialog *iface, IShellItemFilter *pFilter)
2982 {
2984  return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
2985 }
2986 
2987 static HRESULT WINAPI IFileOpenDialog_fnGetResults(IFileOpenDialog *iface, IShellItemArray **ppenum)
2988 {
2990  TRACE("%p (%p)\n", This, ppenum);
2991 
2992  *ppenum = This->psia_results;
2993 
2994  if(*ppenum)
2995  {
2996  IShellItemArray_AddRef(*ppenum);
2997  return S_OK;
2998  }
2999 
3000  return E_FAIL;
3001 }
3002 
3003 static HRESULT WINAPI IFileOpenDialog_fnGetSelectedItems(IFileOpenDialog *iface, IShellItemArray **ppsai)
3004 {
3006  TRACE("%p (%p)\n", This, ppsai);
3007 
3008  if(This->psia_selection)
3009  {
3010  *ppsai = This->psia_selection;
3011  IShellItemArray_AddRef(*ppsai);
3012  return S_OK;
3013  }
3014 
3015  return E_FAIL;
3016 }
3017 
3018 static const IFileOpenDialogVtbl vt_IFileOpenDialog = {
3048 };
3049 
3050 /**************************************************************************
3051  * IFileSaveDialog
3052  */
3054 {
3055  return CONTAINING_RECORD(iface, FileDialogImpl, u.IFileSaveDialog_iface);
3056 }
3057 
3059  REFIID riid,
3060  void **ppvObject)
3061 {
3063  return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3064 }
3065 
3067 {
3069  return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3070 }
3071 
3073 {
3075  return IFileDialog2_Release(&This->IFileDialog2_iface);
3076 }
3077 
3079 {
3081  return IFileDialog2_Show(&This->IFileDialog2_iface, hwndOwner);
3082 }
3083 
3085  const COMDLG_FILTERSPEC *rgFilterSpec)
3086 {
3088  return IFileDialog2_SetFileTypes(&This->IFileDialog2_iface, cFileTypes, rgFilterSpec);
3089 }
3090 
3092 {
3094  return IFileDialog2_SetFileTypeIndex(&This->IFileDialog2_iface, iFileType);
3095 }
3096 
3098 {
3100  return IFileDialog2_GetFileTypeIndex(&This->IFileDialog2_iface, piFileType);
3101 }
3102 
3104  DWORD *pdwCookie)
3105 {
3107  return IFileDialog2_Advise(&This->IFileDialog2_iface, pfde, pdwCookie);
3108 }
3109 
3111 {
3113  return IFileDialog2_Unadvise(&This->IFileDialog2_iface, dwCookie);
3114 }
3115 
3116 static HRESULT WINAPI IFileSaveDialog_fnSetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS fos)
3117 {
3119  return IFileDialog2_SetOptions(&This->IFileDialog2_iface, fos);
3120 }
3121 
3122 static HRESULT WINAPI IFileSaveDialog_fnGetOptions(IFileSaveDialog *iface, FILEOPENDIALOGOPTIONS *pfos)
3123 {
3125  return IFileDialog2_GetOptions(&This->IFileDialog2_iface, pfos);
3126 }
3127 
3129 {
3131  return IFileDialog2_SetDefaultFolder(&This->IFileDialog2_iface, psi);
3132 }
3133 
3135 {
3137  return IFileDialog2_SetFolder(&This->IFileDialog2_iface, psi);
3138 }
3139 
3141 {
3143  return IFileDialog2_GetFolder(&This->IFileDialog2_iface, ppsi);
3144 }
3145 
3147 {
3149  return IFileDialog2_GetCurrentSelection(&This->IFileDialog2_iface, ppsi);
3150 }
3151 
3153 {
3155  return IFileDialog2_SetFileName(&This->IFileDialog2_iface, pszName);
3156 }
3157 
3159 {
3161  return IFileDialog2_GetFileName(&This->IFileDialog2_iface, pszName);
3162 }
3163 
3165 {
3167  return IFileDialog2_SetTitle(&This->IFileDialog2_iface, pszTitle);
3168 }
3169 
3171 {
3173  return IFileDialog2_SetOkButtonLabel(&This->IFileDialog2_iface, pszText);
3174 }
3175 
3177 {
3179  return IFileDialog2_SetFileNameLabel(&This->IFileDialog2_iface, pszLabel);
3180 }
3181 
3183 {
3185  return IFileDialog2_GetResult(&This->IFileDialog2_iface, ppsi);
3186 }
3187 
3189 {
3191  return IFileDialog2_AddPlace(&This->IFileDialog2_iface, psi, fdap);
3192 }
3193 
3195  LPCWSTR pszDefaultExtension)
3196 {
3198  return IFileDialog2_SetDefaultExtension(&This->IFileDialog2_iface, pszDefaultExtension);
3199 }
3200 
3202 {
3204  return IFileDialog2_Close(&This->IFileDialog2_iface, hr);
3205 }
3206 
3208 {
3210  return IFileDialog2_SetClientGuid(&This->IFileDialog2_iface, guid);
3211 }
3212 
3214 {
3216  return IFileDialog2_ClearClientData(&This->IFileDialog2_iface);
3217 }
3218 
3220 {
3222  return IFileDialog2_SetFilter(&This->IFileDialog2_iface, pFilter);
3223 }
3224 
3226 {
3228  FIXME("stub - %p (%p)\n", This, psi);
3229  return E_NOTIMPL;
3230 }
3231 
3233 {
3235  FIXME("stub - %p (%p)\n", This, pStore);
3236  return E_NOTIMPL;
3237 }
3238 
3240  IPropertyDescriptionList *pList,
3241  BOOL fAppendDefault)
3242 {
3244  FIXME("stub - %p (%p, %d)\n", This, pList, fAppendDefault);
3245  return E_NOTIMPL;
3246 }
3247 
3249 {
3251  FIXME("stub - %p (%p)\n", This, ppStore);
3252  return E_NOTIMPL;
3253 }
3254 
3256  IShellItem *psi,
3257  IPropertyStore *pStore,
3258  HWND hwnd,
3260 {
3262  FIXME("%p (%p, %p, %p, %p)\n", This, psi, pStore, hwnd, pSink);
3263  return E_NOTIMPL;
3264 }
3265 
3266 static const IFileSaveDialogVtbl vt_IFileSaveDialog = {
3299 };
3300 
3301 /**************************************************************************
3302  * IExplorerBrowserEvents implementation
3303  */
3304 static inline FileDialogImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
3305 {
3306  return CONTAINING_RECORD(iface, FileDialogImpl, IExplorerBrowserEvents_iface);
3307 }
3308 
3309 static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface,
3310  REFIID riid, void **ppvObject)
3311 {
3313  TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
3314 
3315  return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3316 }
3317 
3318 static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface)
3319 {
3321  TRACE("%p\n", This);
3322  return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3323 }
3324 
3325 static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface)
3326 {
3328  TRACE("%p\n", This);
3329  return IFileDialog2_Release(&This->IFileDialog2_iface);
3330 }
3331 
3332 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface,
3333  PCIDLIST_ABSOLUTE pidlFolder)
3334 {
3336  IShellItem *psi;
3337  HRESULT hr;
3338  TRACE("%p (%p)\n", This, pidlFolder);
3339 
3340  hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&psi);
3341  if(SUCCEEDED(hr))
3342  {
3344  IShellItem_Release(psi);
3345 
3346  /* The ExplorerBrowser treats S_FALSE as S_OK, we don't. */
3347  if(hr == S_FALSE)
3348  hr = E_FAIL;
3349 
3350  return hr;
3351  }
3352  else
3353  ERR("Failed to convert pidl (%p) to a shellitem.\n", pidlFolder);
3354 
3355  return S_OK;
3356 }
3357 
3358 static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface,
3359  IShellView *psv)
3360 {
3362  TRACE("%p (%p)\n", This, psv);
3363  return S_OK;
3364 }
3365 
3366 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface,
3367  PCIDLIST_ABSOLUTE pidlFolder)
3368 {
3370  HRESULT hr;
3371  TRACE("%p (%p)\n", This, pidlFolder);
3372 
3373  if(This->psi_folder)
3374  IShellItem_Release(This->psi_folder);
3375 
3376  hr = SHCreateItemFromIDList(pidlFolder, &IID_IShellItem, (void**)&This->psi_folder);
3377  if(FAILED(hr))
3378  {
3379  ERR("Failed to get the current folder.\n");
3380  This->psi_folder = NULL;
3381  }
3382 
3384 
3385  return S_OK;
3386 }
3387 
3388 static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface,
3389  PCIDLIST_ABSOLUTE pidlFolder)
3390 {
3392  TRACE("%p (%p)\n", This, pidlFolder);
3393  return S_OK;
3394 }
3395 
3396 static const IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents = {
3404 };
3405 
3406 /**************************************************************************
3407  * IServiceProvider implementation
3408  */
3410 {
3411  return CONTAINING_RECORD(iface, FileDialogImpl, IServiceProvider_iface);
3412 }
3413 
3415  REFIID riid, void **ppvObject)
3416 {
3418  TRACE("%p\n", This);
3419  return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3420 }
3421 
3423 {
3425  TRACE("%p\n", This);
3426  return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3427 }
3428 
3430 {
3432  TRACE("%p\n", This);
3433  return IFileDialog2_Release(&This->IFileDialog2_iface);
3434 }
3435 
3437  REFGUID guidService,
3438  REFIID riid, void **ppv)
3439 {
3441  HRESULT hr = E_NOTIMPL;
3442  TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
3443 
3444  *ppv = NULL;
3445  if(IsEqualGUID(guidService, &SID_STopLevelBrowser) && This->peb)
3446  hr = IExplorerBrowser_QueryInterface(This->peb, riid, ppv);
3447  else if(IsEqualGUID(guidService, &SID_SExplorerBrowserFrame))
3448  hr = IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppv);
3449  else
3450  FIXME("Interface %s requested from unknown service %s\n",
3451  debugstr_guid(riid), debugstr_guid(guidService));
3452 
3453  return hr;
3454 }
3455 
3456 static const IServiceProviderVtbl vt_IServiceProvider = {
3461 };
3462 
3463 /**************************************************************************
3464  * ICommDlgBrowser3 implementation
3465  */
3467 {
3468  return CONTAINING_RECORD(iface, FileDialogImpl, ICommDlgBrowser3_iface);
3469 }
3470 
3472  REFIID riid, void **ppvObject)
3473 {
3475  TRACE("%p\n", This);
3476  return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3477 }
3478 
3480 {
3482  TRACE("%p\n", This);
3483  return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3484 }
3485 
3487 {
3489  TRACE("%p\n", This);
3490  return IFileDialog2_Release(&This->IFileDialog2_iface);
3491 }
3492 
3494  IShellView *shv)
3495 {
3497  HRESULT hr;
3498  TRACE("%p (%p)\n", This, shv);
3499 
3501 
3502  if(SUCCEEDED(hr))
3503  EndDialog(This->dlg_hwnd, S_OK);
3504 
3505  return S_OK;
3506 }
3507 
3509  IShellView *shv, ULONG uChange )
3510 {
3512  IDataObject *new_selection;
3513  HRESULT hr;
3514  TRACE("%p (%p, %x)\n", This, shv, uChange);
3515 
3516  switch(uChange)
3517  {
3518  case CDBOSC_SELCHANGE:
3519  if(This->psia_selection)
3520  {
3521  IShellItemArray_Release(This->psia_selection);
3522  This->psia_selection = NULL;
3523  }
3524 
3525  hr = IShellView_GetItemObject(shv, SVGIO_SELECTION, &IID_IDataObject, (void**)&new_selection);
3526  if(SUCCEEDED(hr))
3527  {
3528  hr = SHCreateShellItemArrayFromDataObject(new_selection, &IID_IShellItemArray,
3529  (void**)&This->psia_selection);
3530  if(SUCCEEDED(hr))
3531  {
3534  }
3535 
3536  IDataObject_Release(new_selection);
3537  }
3538  break;
3539  default:
3540  TRACE("Unhandled state change\n");
3541  }
3542  return S_OK;
3543 }
3544 
3546  IShellView *shv, LPCITEMIDLIST pidl)
3547 {
3549  IShellItem *psi;
3550  LPWSTR filename;
3551  LPITEMIDLIST parent_pidl;
3552  HRESULT hr;
3553  ULONG attr;
3554  TRACE("%p (%p, %p)\n", This, shv, pidl);
3555 
3556  if(!This->filterspec_count && !(This->options & FOS_PICKFOLDERS))
3557  return S_OK;
3558 
3559  hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
3560  if(SUCCEEDED(hr))
3561  {
3562  LPITEMIDLIST full_pidl = ILCombine(parent_pidl, pidl);
3563  hr = SHCreateItemFromIDList(full_pidl, &IID_IShellItem, (void**)&psi);
3564  ILFree(parent_pidl);
3565  ILFree(full_pidl);
3566  }
3567  if(FAILED(hr))
3568  {
3569  ERR("Failed to get shellitem (%08x).\n", hr);
3570  return S_OK;
3571  }
3572 
3573  hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER|SFGAO_LINK, &attr);
3574  if(FAILED(hr) || (attr & (SFGAO_FOLDER | SFGAO_LINK)))
3575  {
3576  IShellItem_Release(psi);
3577  return S_OK;
3578  }
3579 
3580  if((This->options & FOS_PICKFOLDERS) && !(attr & (SFGAO_FOLDER | SFGAO_LINK)))
3581  {
3582  IShellItem_Release(psi);
3583  return S_FALSE;
3584  }
3585 
3586  hr = S_OK;
3587  if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
3588  {
3589  if(!PathMatchSpecW(filename, This->filterspecs[This->filetypeindex].pszSpec))
3590  hr = S_FALSE;
3592  }
3593 
3594  IShellItem_Release(psi);
3595  return hr;
3596 }
3597 
3599  IShellView *ppshv, DWORD dwNotifyType)
3600 {
3602  FIXME("Stub: %p (%p, 0x%x)\n", This, ppshv, dwNotifyType);
3603  return E_NOTIMPL;
3604 }
3605 
3607  IShellView *pshv,
3608  LPWSTR pszText, int cchMax)
3609 {
3611  FIXME("Stub: %p (%p, %p, %d)\n", This, pshv, pszText, cchMax);
3612  return E_NOTIMPL;
3613 }
3614 
3616 {
3618  FIXME("Stub: %p (%p)\n", This, pdwFlags);
3619  return E_NOTIMPL;
3620 }
3621 
3623  IShellView *pshv, int iColumn)
3624 {
3626  FIXME("Stub: %p (%p, %d)\n", This, pshv, iColumn);
3627  return E_NOTIMPL;
3628 }
3629 
3631  LPWSTR pszFileSpec, int cchFileSpec)
3632 {
3634  FIXME("Stub: %p (%p, %d)\n", This, pszFileSpec, cchFileSpec);
3635  return E_NOTIMPL;
3636 }
3637 
3639  IShellView *pshv)
3640 {
3642  FIXME("Stub: %p (%p)\n", This, pshv);
3643  return E_NOTIMPL;
3644 }
3645 
3646 static const ICommDlgBrowser3Vtbl vt_ICommDlgBrowser3 = {
3659 };
3660 
3661 /**************************************************************************
3662  * IOleWindow implementation
3663  */
3665 {
3666  return CONTAINING_RECORD(iface, FileDialogImpl, IOleWindow_iface);
3667 }
3668 
3670 {
3672  return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3673 }
3674 
3676 {
3678  return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3679 }
3680 
3682 {
3684  return IFileDialog2_Release(&This->IFileDialog2_iface);
3685 }
3686 
3688 {
3690  FIXME("Stub: %p (%d)\n", This, fEnterMOde);
3691  return E_NOTIMPL;
3692 }
3693 
3695 {
3697  TRACE("%p (%p)\n", This, phwnd);
3698  *phwnd = This->dlg_hwnd;
3699  return S_OK;
3700 }
3701 
3702 static const IOleWindowVtbl vt_IOleWindow = {
3708 };
3709 
3710 /**************************************************************************
3711  * IFileDialogCustomize implementation
3712  */
3713 static inline FileDialogImpl *impl_from_IFileDialogCustomize(IFileDialogCustomize *iface)
3714 {
3715  return CONTAINING_RECORD(iface, FileDialogImpl, IFileDialogCustomize_iface);
3716 }
3717 
3718 static HRESULT WINAPI IFileDialogCustomize_fnQueryInterface(IFileDialogCustomize *iface,
3719  REFIID riid, void **ppvObject)
3720 {
3722  return IFileDialog2_QueryInterface(&This->IFileDialog2_iface, riid, ppvObject);
3723 }
3724 
3725 static ULONG WINAPI IFileDialogCustomize_fnAddRef(IFileDialogCustomize *iface)
3726 {
3728  return IFileDialog2_AddRef(&This->IFileDialog2_iface);
3729 }
3730 
3731 static ULONG WINAPI IFileDialogCustomize_fnRelease(IFileDialogCustomize *iface)
3732 {
3734  return IFileDialog2_Release(&This->IFileDialog2_iface);
3735 }
3736 
3737 static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCustomize *iface,
3738  DWORD dwIDCtl)
3739 {
3741  MENUINFO mi;
3742  TRACE("%p (%d)\n", This, dwIDCtl);
3743 
3744  if (This->hmenu_opendropdown || get_cctrl(This, dwIDCtl))
3745  return E_UNEXPECTED;
3746 
3747  This->hmenu_opendropdown = CreatePopupMenu();
3748 
3749  if (!This->hmenu_opendropdown)
3750  return E_OUTOFMEMORY;
3751 
3752  mi.cbSize = sizeof(mi);
3753  mi.fMask = MIM_STYLE;
3754  mi.dwStyle = MNS_NOTIFYBYPOS;
3755  SetMenuInfo(This->hmenu_opendropdown, &mi);
3756 
3757  This->cctrl_opendropdown.hwnd = NULL;
3758  This->cctrl_opendropdown.wrapper_hwnd = NULL;
3759  This->cctrl_opendropdown.id = dwIDCtl;
3760  This->cctrl_opendropdown.dlgid = 0;
3761  This->cctrl_opendropdown.type = IDLG_CCTRL_OPENDROPDOWN;
3762  This->cctrl_opendropdown.cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
3763  list_init(&This->cctrl_opendropdown.sub_cctrls);
3764  list_init(&This->cctrl_opendropdown.sub_items);
3765 
3766  return S_OK;
3767 }
3768 
3769 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
3770  DWORD dwIDCtl,
3771  LPCWSTR pszLabel)
3772 {
3774  customctrl *ctrl;
3775  TBBUTTON tbb;
3776  HRESULT hr;
3777  TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3778 
3781  This->cctrl_def_height, &ctrl);
3782  if(SUCCEEDED(hr))
3783  {
3784  SendMessageW(ctrl->hwnd, TB_BUTTONSTRUCTSIZE, sizeof(tbb), 0);
3785  ctrl->type = IDLG_CCTRL_MENU;
3786 
3787  /* Add the actual button with a popup menu. */
3788  tbb.iBitmap = I_IMAGENONE;
3789  tbb.dwData = (DWORD_PTR)CreatePopupMenu();
3790  tbb.iString = (DWORD_PTR)pszLabel;
3791  tbb.fsState = TBSTATE_ENABLED;
3793  tbb.idCommand = 1;
3794 
3795  SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
3796  }
3797 
3798  return hr;
3799 }
3800 
3801 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
3802  DWORD dwIDCtl,
3803  LPCWSTR pszLabel)
3804 {
3806  customctrl *ctrl;
3807  HRESULT hr;
3808  TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3809 
3810  hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
3811  This->cctrl_def_height, &ctrl);
3812  if(SUCCEEDED(hr))
3813  ctrl->type = IDLG_CCTRL_PUSHBUTTON;
3814 
3815  return hr;
3816 }
3817 
3818 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
3819  DWORD dwIDCtl)
3820 {
3822  customctrl *ctrl;
3823  HRESULT hr;
3824  TRACE("%p (%d)\n", This, dwIDCtl);
3825 
3827  This->cctrl_def_height, &ctrl);
3828  if(SUCCEEDED(hr))
3829  ctrl->type = IDLG_CCTRL_COMBOBOX;
3830 
3831  return hr;
3832 }
3833 
3834 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
3835  DWORD dwIDCtl)
3836 {
3838  customctrl *ctrl;
3839  HRESULT hr;
3840  TRACE("%p (%d)\n", This, dwIDCtl);
3841 
3842  hr = cctrl_create_new(This, dwIDCtl, NULL, radiobuttonlistW, 0, 0, 0, &ctrl);
3843  if(SUCCEEDED(hr))
3844  {
3847  }
3848 
3849  return hr;
3850 }
3851 
3852 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
3853  DWORD dwIDCtl,
3854  LPCWSTR pszLabel,
3855  BOOL bChecked)
3856 {
3858  customctrl *ctrl;
3859  HRESULT hr;
3860  TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
3861 
3862  hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX|BS_MULTILINE, 0,
3863  This->cctrl_def_height, &ctrl);
3864  if(SUCCEEDED(hr))
3865  {
3866  ctrl->type = IDLG_CCTRL_CHECKBUTTON;
3867  SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
3868  }
3869 
3870  return hr;
3871 }
3872 
3873 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
3874  DWORD dwIDCtl,
3875  LPCWSTR pszText)
3876 {
3878  customctrl *ctrl;
3879  HRESULT hr;
3880  TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3881 
3883  This->cctrl_def_height, &ctrl);
3884  if(SUCCEEDED(hr))
3885  ctrl->type = IDLG_CCTRL_EDITBOX;
3886 
3887  return hr;
3888 }
3889 
3890 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
3891  DWORD dwIDCtl)
3892 {
3894  customctrl *ctrl;
3895  HRESULT hr;
3896  TRACE("%p (%d)\n", This, dwIDCtl);
3897 
3900  if(SUCCEEDED(hr))
3901  ctrl->type = IDLG_CCTRL_SEPARATOR;
3902 
3903  return hr;
3904 }
3905 
3906 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
3907  DWORD dwIDCtl,
3908  LPCWSTR pszText)
3909 {
3911  customctrl *ctrl;
3912  HRESULT hr;
3913  TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
3914 
3915  hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
3916  This->cctrl_def_height, &ctrl);
3917  if(SUCCEEDED(hr))
3918  ctrl->type = IDLG_CCTRL_TEXT;
3919 
3920  return hr;
3921 }
3922 
3923 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
3924  DWORD dwIDCtl,
3925  LPCWSTR pszLabel)
3926 {
3928  customctrl *ctrl = get_cctrl(This, dwIDCtl);
3929  TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
3930 
3931  if(!ctrl) return E_INVALIDARG;
3932 
3933  switch(ctrl->type)
3934  {
3935  case IDLG_CCTRL_MENU:
3936  case IDLG_CCTRL_PUSHBUTTON:
3938  case IDLG_CCTRL_TEXT:
3940  SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
3941  break;
3943  return E_NOTIMPL;
3944  default:
3945  break;
3946  }
3947 
3948  return S_OK;
3949 }
3950 
3951 static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomize *iface,
3952  DWORD dwIDCtl,
3953  CDCONTROLSTATEF *pdwState)
3954 {
3956  customctrl *ctrl = get_cctrl(This, dwIDCtl);
3957  TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
3958 
3959  if(!ctrl || ctrl->type == IDLG_CCTRL_OPENDROPDOWN) return E_NOTIMPL;
3960 
3961  *pdwState = ctrl->cdcstate;
3962  return S_OK;
3963 }
3964 
3965 static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomize *iface,
3966  DWORD dwIDCtl,
3967  CDCONTROLSTATEF dwState)
3968 {
3970  customctrl *ctrl = get_cctrl(This,dwIDCtl);
3971  TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
3972 
3973  if(ctrl && ctrl->hwnd)
3974  {
3975  LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
3976 
3977  if(dwState & CDCS_ENABLED)
3978  wndstyle &= ~(WS_DISABLED);
3979  else
3980  wndstyle |= WS_DISABLED;
3981 
3982  if(dwState & CDCS_VISIBLE)
3983  wndstyle |= WS_VISIBLE;
3984  else
3985  wndstyle &= ~(WS_VISIBLE);
3986 
3987  SetWindowLongW(ctrl->hwnd, GWL_STYLE, wndstyle);
3988 
3989  /* We save the state separately since at least one application
3990  * relies on being able to hide a control. */
3991  ctrl->cdcstate = dwState;
3992  }
3993 
3994  return S_OK;
3995 }
3996 
3997 static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize *iface,
3998  DWORD dwIDCtl,
3999  WCHAR **ppszText)
4000 {
4002  customctrl *ctrl = get_cctrl(This, dwIDCtl);
4003  WCHAR len, *text;
4004  TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
4005 
4006  if(!ctrl || !ctrl->hwnd || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
4007  return E_FAIL;
4008 
4009  text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
4010  if(!text) return E_FAIL;
4011 
4012  SendMessageW(ctrl->hwnd, WM_GETTEXT, len+1, (LPARAM)text);
4013  *ppszText = text;
4014  return S_OK;
4015 }
4016 
4017 static HRESULT WINAPI IFileDialogCustomize_fnSetEditBoxText(IFileDialogCustomize *iface,
4018  DWORD dwIDCtl,
4019  LPCWSTR pszText)
4020 {
4022  customctrl *ctrl = get_cctrl(This, dwIDCtl);
4023  TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
4024 
4025  if(!ctrl || ctrl->type != IDLG_CCTRL_EDITBOX)
4026  return E_FAIL;
4027 
4028  SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
4029  return S_OK;
4030 }
4031 
4032 static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCustomize *iface,
4033  DWORD dwIDCtl,
4034  BOOL *pbChecked)
4035 {
4037  customctrl *ctrl = get_cctrl(This, dwIDCtl);
4038  TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
4039 
4040  if(ctrl && ctrl->hwnd)
4041  *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
4042 
4043  return S_OK;
4044 }
4045 
4046 static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCustomize *iface,
4047  DWORD dwIDCtl,
4048  BOOL bChecked)
4049 {
4051  customctrl *ctrl = get_cctrl(This, dwIDCtl);
4052  TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
4053 
4054  if(ctrl && ctrl->hwnd)
4055  SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
4056 
4057  return S_OK;
4058 }
4059 
4060 static UINT get_combobox_index_from_id(HWND cb_hwnd, DWORD dwIDItem)
4061 {
4062  UINT count = SendMessageW(cb_hwnd, CB_GETCOUNT, 0, 0);
4063  UINT i;
4064  if(!count || (count == CB_ERR))
4065  return -1;
4066 
4067  for(i = 0; i < count; i++)
4068  if(SendMessageW(cb_hwnd, CB_GETITEMDATA, i, 0) == dwIDItem)
4069  return i;
4070 
4071  TRACE("Item with id %d not found in combobox %p (item count: %d)\n", dwIDItem, cb_hwnd, count);
4072  return -1;
4073 }
4074 
4075 static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize *iface,
4076  DWORD dwIDCtl,
4077  DWORD dwIDItem,
4078  LPCWSTR pszLabel)
4079 {
4081  customctrl *ctrl = get_cctrl(This, dwIDCtl);
4082  HRESULT hr;
4083  TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
4084 
4085  if(!ctrl) return E_FAIL;
4086 
4087  switch(ctrl->type)
4088  {
4089  case IDLG_CCTRL_COMBOBOX:
4090  {
4091  UINT index;
4092  cctrl_item* item;
4093 
4094  hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4095 
4096  if (FAILED(hr)) return hr;
4097 
4098  index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
4099  SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
4100 
4101  return S_OK;
4102  }
4103  case IDLG_CCTRL_MENU:
4105  {
4106  cctrl_item* item;
4107  HMENU hmenu;
4108 
4109  hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4110 
4111  if (FAILED(hr)) return hr;
4112 
4113  if (ctrl->type == IDLG_CCTRL_MENU)
4114  {
4115  TBBUTTON tbb;
4116  SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
4117  hmenu = (HMENU)tbb.dwData;
4118  }
4119  else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
4120  hmenu = This->hmenu_opendropdown;
4121 
4122  AppendMenuW(hmenu, MF_STRING, dwIDItem, pszLabel);
4123  return S_OK;
4124  }
4126  {
4127  cctrl_item* item;
4128 
4129  hr = add_item(ctrl, dwIDItem, pszLabel, &item);
4130 
4131  if (SUCCEEDED(hr))
4132  {
4133  item->hwnd = CreateWindowExW(0, WC_BUTTONW, pszLabel,
4135  0, 0, 0, 0, ctrl->hwnd, ULongToHandle(dwIDItem), COMDLG32_hInstance, 0);
4136 
4137  if (!item->hwnd)
4138  {
4139  ERR(