ReactOS  0.4.14-dev-604-gcfdd483
filedlg.c
Go to the documentation of this file.
1 /*
2  * COMMDLG - File Open Dialogs Win95 look and feel
3  *
4  * Copyright 1999 Francois Boisvert
5  * Copyright 1999, 2000 Juergen Schmied
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * FIXME: The whole concept of handling unicode is badly broken.
22  * many hook-messages expect a pointer to a
23  * OPENFILENAMEA or W structure. With the current architecture
24  * we would have to convert the beast at every call to a hook.
25  * we have to find a better solution but it would likely cause
26  * a complete rewrite after which we should handle the
27  * OPENFILENAME structure without any converting (jsch).
28  *
29  * FIXME: any hook gets a OPENFILENAMEA structure
30  *
31  * FIXME: CDN_FILEOK is wrong implemented, other CDN_ messages likely too
32  *
33  * FIXME: old style hook messages are not implemented (except FILEOKSTRING)
34  *
35  * FIXME: algorithm for selecting the initial directory is too simple
36  *
37  * FIXME: add to recent docs
38  *
39  * FIXME: flags not implemented: OFN_DONTADDTORECENT,
40  * OFN_NODEREFERENCELINKS, OFN_NOREADONLYRETURN,
41  * OFN_NOTESTFILECREATE, OFN_USEMONIKERS
42  *
43  * FIXME: lCustData for lpfnHook (WM_INITDIALOG)
44  *
45  *
46  */
47 
48 #include <ctype.h>
49 #include <stdlib.h>
50 #include <stdarg.h>
51 #include <stdio.h>
52 #include <string.h>
53 
54 #define COBJMACROS
55 #define NONAMELESSUNION
56 
57 #include "windef.h"
58 #include "winbase.h"
59 #include "winternl.h"
60 #include "winnls.h"
61 #include "wingdi.h"
62 #ifdef __REACTOS__
63 /* RegGetValueW is supported by Win2k3 SP1 but headers need Win Vista */
64 #undef _WIN32_WINNT
65 #define _WIN32_WINNT 0x0600
66 #endif
67 #include "winreg.h"
68 #include "winuser.h"
69 #include "commdlg.h"
70 #include "dlgs.h"
71 #include "cdlg.h"
72 #include "cderr.h"
73 #include "shellapi.h"
74 #include "shlobj.h"
75 #include "filedlgbrowser.h"
76 #include "shlwapi.h"
77 
78 #include "wine/debug.h"
79 #include "wine/heap.h"
80 #ifdef __REACTOS__
81 #include "wine/unicode.h"
82 #endif
83 
85 
86 #define UNIMPLEMENTED_FLAGS \
87 (OFN_DONTADDTORECENT |\
88 OFN_NODEREFERENCELINKS | OFN_NOREADONLYRETURN |\
89 OFN_NOTESTFILECREATE /*| OFN_USEMONIKERS*/)
90 
91 /***********************************************************************
92  * Data structure and global variables
93  */
94 typedef struct SFolder
95 {
96  int m_iImageIndex; /* Index of picture in image list */
98  int m_iIndent; /* Indentation index */
99  LPITEMIDLIST pidlItem; /* absolute pidl of the item */
100 
101 } SFOLDER,*LPSFOLDER;
102 
103 typedef struct tagLookInInfo
104 {
107 } LookInInfos;
108 
109 
110 /***********************************************************************
111  * Defines and global variables
112  */
113 
114 /* Draw item constant */
115 #define XTEXTOFFSET 3
116 
117 /* AddItem flags*/
118 #define LISTEND -1
119 
120 /* SearchItem methods */
121 #define SEARCH_PIDL 1
122 #define SEARCH_EXP 2
123 #define ITEM_NOTFOUND -1
124 
125 /* Undefined windows message sent by CreateViewObject*/
126 #define WM_GETISHELLBROWSER WM_USER+7
127 
128 #define TBPLACES_CMDID_PLACE0 0xa064
129 #define TBPLACES_CMDID_PLACE1 0xa065
130 #define TBPLACES_CMDID_PLACE2 0xa066
131 #define TBPLACES_CMDID_PLACE3 0xa067
132 #define TBPLACES_CMDID_PLACE4 0xa068
133 
134 /* NOTE
135  * Those macros exist in windowsx.h. However, you can't really use them since
136  * they rely on the UNICODE defines and can't be used inside Wine itself.
137  */
138 
139 /* Combo box macros */
140 #define CBGetItemDataPtr(hwnd,iItemId) \
141  SendMessageW(hwnd, CB_GETITEMDATA, (WPARAM)(iItemId), 0)
142 
143 static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
144 static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
145 
146 static const WCHAR LastVisitedMRUW[] =
147  {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
148  'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
149  'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
150  'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
151 static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
152 
153 static const WCHAR filedlg_info_propnameW[] = {'F','i','l','e','O','p','e','n','D','l','g','I','n','f','o','s',0};
154 
156 {
158 }
159 
161 {
162  return (info->ofnInfos->Flags & OFN_ENABLEHOOK) && info->ofnInfos->lpfnHook;
163 }
164 
166 {
167  return (info->ofnInfos->Flags & OFN_HIDEREADONLY) || (info->DlgInfos.dwDlgProp & FODPROP_SAVEDLG);
168 }
169 
170 /***********************************************************************
171  * Prototypes
172  */
173 
174 /* Internal functions used by the dialog */
181 static void FILEDLG95_Clean(HWND hwnd);
182 
183 /* Functions used by the shell navigation */
187 static void FILEDLG95_SHELL_Clean(HWND hwnd);
188 
189 /* Functions used by the EDIT box */
190 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed);
191 
192 /* Functions used by the filetype combo box */
194 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode);
195 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt);
196 static void FILEDLG95_FILETYPE_Clean(HWND hwnd);
197 
198 /* Functions used by the Look In combo box */
199 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo);
201 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode);
202 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId);
203 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod);
207 static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
208 
209 /* Functions for dealing with the most-recently-used registry keys */
210 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
211 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
213 #ifdef __REACTOS__
214 static void FILEDLG95_MRU_load_ext(LPWSTR stored_path, size_t cchMax, LPCWSTR defext);
215 static void FILEDLG95_MRU_save_ext(LPCWSTR filename);
216 #endif
217 
218 /* Miscellaneous tool functions */
219 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
222 static LPITEMIDLIST GetPidlFromName(IShellFolder *psf,LPWSTR lpcstrFileName);
223 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl);
224 static UINT GetNumSelected( IDataObject *doSelected );
225 static void COMCTL32_ReleaseStgMedium(STGMEDIUM medium);
226 
229 static BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed);
231 
233 {
234  DWORD type, data, size;
235 
236  size = sizeof(data);
237  if (hkey && !RegQueryValueExW(hkey, name, 0, &type, (BYTE *)&data, &size))
238  {
239  *value = data;
240  return TRUE;
241  }
242 
243  return FALSE;
244 }
245 
247 {
248  DWORD type, data, size;
249 
250  size = sizeof(data);
251  if (hkey && !RegQueryValueExW(hkey, name, 0, &type, (BYTE *)&data, &size) && type == REG_DWORD)
252  {
253  *value = data;
254  return TRUE;
255  }
256 
257  return FALSE;
258 }
259 
261 {
262  DWORD type, size;
263  WCHAR *str;
264 
265  if (hkey && !RegQueryValueExW(hkey, name, 0, &type, NULL, &size))
266  {
267  if (type != REG_SZ && type != REG_EXPAND_SZ)
268  return FALSE;
269  }
270 
271  str = heap_alloc(size);
272  if (RegQueryValueExW(hkey, name, 0, &type, (BYTE *)str, &size))
273  {
274  heap_free(str);
275  return FALSE;
276  }
277 
278  *value = str;
279  return TRUE;
280 }
281 
283 {
284  static const WCHAR noplacesbarW[] = {'N','o','P','l','a','c','e','s','B','a','r',0};
285  DWORD value;
286  HKEY hkey;
287 
288  if (fodInfos->ofnInfos->lStructSize != sizeof(*fodInfos->ofnInfos) ||
289  (fodInfos->ofnInfos->FlagsEx & OFN_EX_NOPLACESBAR) ||
290  !(fodInfos->ofnInfos->Flags & OFN_EXPLORER))
291  {
292  return FALSE;
293  }
294 
295  if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32", &hkey))
296  return TRUE;
297 
298  value = 0;
299  get_config_key_as_dword(hkey, noplacesbarW, &value);
300  RegCloseKey(hkey);
301  return value == 0;
302 }
303 
305 {
306  static const int default_places[] =
307  {
310  CSIDL_DRIVES,
311  };
312  unsigned int i;
313  HKEY hkey;
314 
315  if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32\\Placesbar",
316  &hkey))
317  {
318  for (i = 0; i < ARRAY_SIZE(fodInfos->places); i++)
319  {
320  static const WCHAR placeW[] = {'P','l','a','c','e','%','d',0};
321  WCHAR nameW[8];
322  DWORD value;
323  HRESULT hr;
324  WCHAR *str;
325 
326  swprintf(nameW, placeW, i);
327  if (get_config_key_dword(hkey, nameW, &value))
328  {
329  hr = SHGetSpecialFolderLocation(NULL, value, &fodInfos->places[i]);
330  if (FAILED(hr))
331  WARN("Unrecognized special folder %u.\n", value);
332  }
333  else if (get_config_key_string(hkey, nameW, &str))
334  {
335  hr = SHParseDisplayName(str, NULL, &fodInfos->places[i], 0, NULL);
336  if (FAILED(hr))
337  WARN("Failed to parse custom places location, %s.\n", debugstr_w(str));
338  heap_free(str);
339  }
340  }
341 
342  /* FIXME: eliminate duplicates. */
343 
344  RegCloseKey(hkey);
345  return;
346  }
347 
348  for (i = 0; i < ARRAY_SIZE(default_places); i++)
349  SHGetSpecialFolderLocation(NULL, default_places[i], &fodInfos->places[i]);
350 }
351 
352 /***********************************************************************
353  * GetFileName95
354  *
355  * Creates an Open common dialog box that lets the user select
356  * the drive, directory, and the name of a file or set of files to open.
357  *
358  * IN : The FileOpenDlgInfos structure associated with the dialog
359  * OUT : TRUE on success
360  * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
361  */
363 {
364  LRESULT lRes;
365  void *template;
366  HRSRC hRes;
367  HANDLE hDlgTmpl = 0;
368  WORD templateid;
369 
370  /* test for missing functionality */
371  if (fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS)
372  {
373  FIXME("Flags 0x%08x not yet implemented\n",
374  fodInfos->ofnInfos->Flags & UNIMPLEMENTED_FLAGS);
375  }
376 
377  /* Create the dialog from a template */
378 
379  if (is_places_bar_enabled(fodInfos))
380  templateid = NEWFILEOPENV2ORD;
381  else
382  templateid = NEWFILEOPENORD;
383 
384  if (!(hRes = FindResourceW(COMDLG32_hInstance, MAKEINTRESOURCEW(templateid), (LPCWSTR)RT_DIALOG)))
385  {
387  return FALSE;
388  }
389  if (!(hDlgTmpl = LoadResource(COMDLG32_hInstance, hRes )) ||
390  !(template = LockResource( hDlgTmpl )))
391  {
393  return FALSE;
394  }
395 
396  /* msdn: explorer style dialogs permit sizing by default.
397  * The OFN_ENABLESIZING flag is only needed when a hook or
398  * custom template is provided */
399  if( (fodInfos->ofnInfos->Flags & OFN_EXPLORER) &&
401  fodInfos->ofnInfos->Flags |= OFN_ENABLESIZING;
402 
403  if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
404  {
405  fodInfos->sizedlg.cx = fodInfos->sizedlg.cy = 0;
406  fodInfos->initial_size.x = fodInfos->initial_size.y = 0;
407  }
408 
409  /* old style hook messages */
410  if (is_dialog_hooked(fodInfos))
411  {
412  fodInfos->HookMsg.fileokstring = RegisterWindowMessageW(FILEOKSTRINGW);
413  fodInfos->HookMsg.lbselchstring = RegisterWindowMessageW(LBSELCHSTRINGW);
414  fodInfos->HookMsg.helpmsgstring = RegisterWindowMessageW(HELPMSGSTRINGW);
415  fodInfos->HookMsg.sharevistring = RegisterWindowMessageW(SHAREVISTRINGW);
416  }
417 
418  if (fodInfos->unicode)
420  template,
421  fodInfos->ofnInfos->hwndOwner,
423  (LPARAM) fodInfos);
424  else
426  template,
427  fodInfos->ofnInfos->hwndOwner,
429  (LPARAM) fodInfos);
430  if (fodInfos->ole_initialized)
431  OleUninitialize();
432 
433  /* Unable to create the dialog */
434  if( lRes == -1)
435  return FALSE;
436 
437  return lRes;
438 }
439 
440 static WCHAR *heap_strdupAtoW(const char *str)
441 {
442  WCHAR *ret;
443  INT len;
444 
445  if (!str)
446  return NULL;
447 
448  len = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
449  ret = heap_alloc(len * sizeof(WCHAR));
450  MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
451 
452  return ret;
453 }
454 
456 {
458 
459  /* Initialize ComboBoxEx32 */
460  icc.dwSize = sizeof(icc);
462  InitCommonControlsEx(&icc);
463 
464  /* Initialize CommDlgExtendedError() */
466 
467  memset(info, 0, sizeof(*info));
468 
469  /* Pass in the original ofn */
470  info->ofnInfos = ofn;
471 
472  info->title = ofn->lpstrTitle;
473  info->defext = ofn->lpstrDefExt;
474  info->filter = ofn->lpstrFilter;
475  info->customfilter = ofn->lpstrCustomFilter;
476 
477  if (ofn->lpstrFile)
478  {
479  info->filename = heap_alloc(ofn->nMaxFile * sizeof(WCHAR));
480  lstrcpynW(info->filename, ofn->lpstrFile, ofn->nMaxFile);
481  }
482 
483  if (ofn->lpstrInitialDir)
484  {
486  if (len)
487  {
488  info->initdir = heap_alloc(len * sizeof(WCHAR));
490  }
491  }
492 
493  info->unicode = TRUE;
494 }
495 
497 {
498  OPENFILENAMEW ofnW;
499  int len;
500 
501  ofnW = *(OPENFILENAMEW *)ofn;
502 
506 
507  if (ofn->lpstrFile)
508  {
510  ofnW.lpstrFile = heap_alloc(len * sizeof(WCHAR));
512  ofnW.nMaxFile = len;
513  }
514 
515  if (ofn->lpstrFilter)
516  {
517  LPCSTR s;
518  int n;
519 
520  /* filter is a list... title\0ext\0......\0\0 */
521  s = ofn->lpstrFilter;
522  while (*s) s = s+strlen(s)+1;
523  s++;
524  n = s - ofn->lpstrFilter;
526  ofnW.lpstrFilter = heap_alloc(len * sizeof(WCHAR));
528  }
529 
530  /* convert lpstrCustomFilter */
531  if (ofn->lpstrCustomFilter)
532  {
533  int n, len;
534  LPCSTR s;
535 
536  /* customfilter contains a pair of strings... title\0ext\0 */
538  if (*s) s = s+strlen(s)+1;
539  if (*s) s = s+strlen(s)+1;
540  n = s - ofn->lpstrCustomFilter;
542  ofnW.lpstrCustomFilter = heap_alloc(len * sizeof(WCHAR));
544  }
545 
546  init_filedlg_infoW(&ofnW, info);
547 
548  /* fixup A-specific fields */
549  info->ofnInfos = (OPENFILENAMEW *)ofn;
550  info->unicode = FALSE;
551 
552  /* free what was duplicated */
553  heap_free((void *)ofnW.lpstrInitialDir);
554  heap_free(ofnW.lpstrFile);
555 }
556 
557 /***********************************************************************
558  * GetFileDialog95
559  *
560  * Call GetFileName95 with this structure and clean the memory.
561  */
563 {
564  WCHAR *current_dir = NULL;
565  unsigned int i;
566  BOOL ret;
567 
568  /* save current directory */
569  if (info->ofnInfos->Flags & OFN_NOCHANGEDIR)
570  {
571  current_dir = heap_alloc(MAX_PATH * sizeof(WCHAR));
572  GetCurrentDirectoryW(MAX_PATH, current_dir);
573  }
574 
575  switch (dlg_type)
576  {
577  case OPEN_DIALOG:
579  break;
580  case SAVE_DIALOG:
581  info->DlgInfos.dwDlgProp |= FODPROP_SAVEDLG;
583  break;
584  default:
585  ret = FALSE;
586  }
587 
588  /* set the lpstrFileTitle */
589  if (ret && info->ofnInfos->lpstrFile && info->ofnInfos->lpstrFileTitle)
590  {
591  if (info->unicode)
592  {
593  LPOPENFILENAMEW ofn = info->ofnInfos;
594  WCHAR *file_title = PathFindFileNameW(ofn->lpstrFile);
595  lstrcpynW(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
596  }
597  else
598  {
600  char *file_title = PathFindFileNameA(ofn->lpstrFile);
601  lstrcpynA(ofn->lpstrFileTitle, file_title, ofn->nMaxFileTitle);
602  }
603  }
604 
605  if (current_dir)
606  {
607  SetCurrentDirectoryW(current_dir);
608  heap_free(current_dir);
609  }
610 
611  if (!info->unicode)
612  {
613  heap_free((void *)info->defext);
614  heap_free((void *)info->title);
615  heap_free((void *)info->filter);
616  heap_free((void *)info->customfilter);
617  }
618 
619  heap_free(info->filename);
620  heap_free(info->initdir);
621 
622  for (i = 0; i < ARRAY_SIZE(info->places); i++)
623  ILFree(info->places[i]);
624 
625  return ret;
626 }
627 
628 /******************************************************************************
629  * COMDLG32_GetDisplayNameOf [internal]
630  *
631  * Helper function to get the display name for a pidl.
632  */
634  LPSHELLFOLDER psfDesktop;
635  STRRET strret;
636 
637  if (FAILED(SHGetDesktopFolder(&psfDesktop)))
638  return FALSE;
639 
640  if (FAILED(IShellFolder_GetDisplayNameOf(psfDesktop, pidl, SHGDN_FORPARSING, &strret))) {
641  IShellFolder_Release(psfDesktop);
642  return FALSE;
643  }
644 
645  IShellFolder_Release(psfDesktop);
646  return SUCCEEDED(StrRetToBufW(&strret, pidl, pwszPath, MAX_PATH));
647 }
648 
649 /******************************************************************************
650  * COMDLG32_GetCanonicalPath [internal]
651  *
652  * Helper function to get the canonical path.
653  */
655  LPWSTR lpstrFile, LPWSTR lpstrPathAndFile)
656 {
657  WCHAR lpstrTemp[MAX_PATH];
658 
659  /* Get the current directory name */
660  if (!COMDLG32_GetDisplayNameOf(pidlAbsCurrent, lpstrPathAndFile))
661  {
662  /* last fallback */
663  GetCurrentDirectoryW(MAX_PATH, lpstrPathAndFile);
664  }
665  PathAddBackslashW(lpstrPathAndFile);
666 
667  TRACE("current directory=%s, file=%s\n", debugstr_w(lpstrPathAndFile), debugstr_w(lpstrFile));
668 
669  /* if the user specified a fully qualified path use it */
670  if(PathIsRelativeW(lpstrFile))
671  {
672  lstrcatW(lpstrPathAndFile, lpstrFile);
673  }
674  else
675  {
676  /* does the path have a drive letter? */
677  if (PathGetDriveNumberW(lpstrFile) == -1)
678  lstrcpyW(lpstrPathAndFile+2, lpstrFile);
679  else
680  lstrcpyW(lpstrPathAndFile, lpstrFile);
681  }
682 
683  /* resolve "." and ".." */
684  PathCanonicalizeW(lpstrTemp, lpstrPathAndFile );
685  lstrcpyW(lpstrPathAndFile, lpstrTemp);
686  TRACE("canon=%s\n", debugstr_w(lpstrPathAndFile));
687 }
688 
689 /***********************************************************************
690  * COMDLG32_SplitFileNames [internal]
691  *
692  * Creates a delimited list of filenames.
693  */
694 int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed)
695 {
696  UINT nStrCharCount = 0; /* index in src buffer */
697  UINT nFileIndex = 0; /* index in dest buffer */
698  UINT nFileCount = 0; /* number of files */
699 
700  /* we might get single filename without any '"',
701  * so we need nStrLen + terminating \0 + end-of-list \0 */
702  *lpstrFileList = heap_alloc((nStrLen + 2) * sizeof(WCHAR));
703  *sizeUsed = 0;
704 
705  /* build delimited file list from filenames */
706  while ( nStrCharCount <= nStrLen )
707  {
708  if ( lpstrEdit[nStrCharCount]=='"' )
709  {
710  nStrCharCount++;
711  while ((nStrCharCount <= nStrLen) && (lpstrEdit[nStrCharCount]!='"'))
712  {
713  (*lpstrFileList)[nFileIndex++] = lpstrEdit[nStrCharCount];
714  nStrCharCount++;
715  }
716  (*lpstrFileList)[nFileIndex++] = 0;
717  nFileCount++;
718  }
719  nStrCharCount++;
720  }
721 
722  /* single, unquoted string */
723  if ((nStrLen > 0) && (nFileIndex == 0) )
724  {
725  lstrcpyW(*lpstrFileList, lpstrEdit);
726  nFileIndex = lstrlenW(lpstrEdit) + 1;
727  nFileCount = 1;
728  }
729 
730  /* trailing \0 */
731  (*lpstrFileList)[nFileIndex++] = '\0';
732 
733  *sizeUsed = nFileIndex;
734  return nFileCount;
735 }
736 
737 /***********************************************************************
738  * ArrangeCtrlPositions [internal]
739  *
740  * NOTE: Make sure to add testcases for any changes made here.
741  */
742 static void ArrangeCtrlPositions(HWND hwndChildDlg, HWND hwndParentDlg, BOOL hide_help)
743 {
744  HWND hwndChild, hwndStc32;
745  RECT rectParent, rectChild, rectStc32;
746  INT help_fixup = 0;
747  int chgx, chgy;
748 
749  /* Take into account if open as read only checkbox and help button
750  * are hidden
751  */
752  if (hide_help)
753  {
754  RECT rectHelp, rectCancel;
755  GetWindowRect(GetDlgItem(hwndParentDlg, pshHelp), &rectHelp);
756  GetWindowRect(GetDlgItem(hwndParentDlg, IDCANCEL), &rectCancel);
757  /* subtract the height of the help button plus the space between
758  * the help button and the cancel button to the height of the dialog
759  */
760  help_fixup = rectHelp.bottom - rectCancel.bottom;
761  }
762 
763  /*
764  There are two possibilities to add components to the default file dialog box.
765 
766  By default, all the new components are added below the standard dialog box (the else case).
767 
768  However, if there is a static text component with the stc32 id, a special case happens.
769  The x and y coordinates of stc32 indicate the top left corner where to place the standard file dialog box
770  in the window and the cx and cy indicate how to size the window.
771  Moreover, if the new component's coordinates are on the left of the stc32 , it is placed on the left
772  of the standard file dialog box. If they are above the stc32 component, it is placed above and so on....
773 
774  */
775 
776  GetClientRect(hwndParentDlg, &rectParent);
777 
778  /* when arranging controls we have to use fixed parent size */
779  rectParent.bottom -= help_fixup;
780 
781  hwndStc32 = GetDlgItem(hwndChildDlg, stc32);
782  if (hwndStc32)
783  {
784  GetWindowRect(hwndStc32, &rectStc32);
785  MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectStc32, 2);
786 
787  /* set the size of the stc32 control according to the size of
788  * client area of the parent dialog
789  */
790  SetWindowPos(hwndStc32, 0,
791  0, 0,
792  rectParent.right, rectParent.bottom,
794  }
795  else
796  SetRectEmpty(&rectStc32);
797 
798  /* this part moves controls of the child dialog */
799  hwndChild = GetWindow(hwndChildDlg, GW_CHILD);
800  while (hwndChild)
801  {
802  if (hwndChild != hwndStc32)
803  {
804  GetWindowRect(hwndChild, &rectChild);
805  MapWindowPoints(0, hwndChildDlg, (LPPOINT)&rectChild, 2);
806 
807  /* move only if stc32 exist */
808  if (hwndStc32 && rectChild.left > rectStc32.right)
809  {
810  /* move to the right of visible controls of the parent dialog */
811  rectChild.left += rectParent.right;
812  rectChild.left -= rectStc32.right;
813  }
814  /* move even if stc32 doesn't exist */
815  if (rectChild.top >= rectStc32.bottom)
816  {
817  /* move below visible controls of the parent dialog */
818  rectChild.top += rectParent.bottom;
819  rectChild.top -= rectStc32.bottom - rectStc32.top;
820  }
821 
822  SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
823  0, 0, SWP_NOSIZE | SWP_NOZORDER);
824  }
825  hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
826  }
827 
828  /* this part moves controls of the parent dialog */
829  hwndChild = GetWindow(hwndParentDlg, GW_CHILD);
830  while (hwndChild)
831  {
832  if (hwndChild != hwndChildDlg)
833  {
834  GetWindowRect(hwndChild, &rectChild);
835  MapWindowPoints(0, hwndParentDlg, (LPPOINT)&rectChild, 2);
836 
837  /* left,top of stc32 marks the position of controls
838  * from the parent dialog
839  */
840  rectChild.left += rectStc32.left;
841  rectChild.top += rectStc32.top;
842 
843  SetWindowPos(hwndChild, 0, rectChild.left, rectChild.top,
844  0, 0, SWP_NOSIZE | SWP_NOZORDER);
845  }
846  hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
847  }
848 
849  /* calculate the size of the resulting dialog */
850 
851  /* here we have to use original parent size */
852  GetClientRect(hwndParentDlg, &rectParent);
853  GetClientRect(hwndChildDlg, &rectChild);
854  TRACE( "parent %s child %s stc32 %s\n", wine_dbgstr_rect( &rectParent),
855  wine_dbgstr_rect( &rectChild), wine_dbgstr_rect( &rectStc32));
856 
857  if (hwndStc32)
858  {
859  /* width */
860  if (rectParent.right > rectStc32.right - rectStc32.left)
861  chgx = rectChild.right - ( rectStc32.right - rectStc32.left);
862  else
863  chgx = rectChild.right - rectParent.right;
864  /* height */
865  if (rectParent.bottom > rectStc32.bottom - rectStc32.top)
866  chgy = rectChild.bottom - ( rectStc32.bottom - rectStc32.top) - help_fixup;
867  else
868  /* Unconditionally set new dialog
869  * height to that of the child
870  */
871  chgy = rectChild.bottom - rectParent.bottom;
872  }
873  else
874  {
875  chgx = 0;
876  chgy = rectChild.bottom - help_fixup;
877  }
878  /* set the size of the parent dialog */
879  GetWindowRect(hwndParentDlg, &rectParent);
880  SetWindowPos(hwndParentDlg, 0,
881  0, 0,
882  rectParent.right - rectParent.left + chgx,
883  rectParent.bottom - rectParent.top + chgy,
885 }
886 
888 {
889  switch(uMsg) {
890  case WM_INITDIALOG:
891  return TRUE;
892  }
893  return FALSE;
894 }
895 
897 {
898  LPCVOID template;
899  HRSRC hRes;
900  HANDLE hDlgTmpl = 0;
901  HWND hChildDlg = 0;
902 
903  TRACE("%p, %p\n", fodInfos, hwnd);
904 
905  /*
906  * If OFN_ENABLETEMPLATEHANDLE is specified, the OPENFILENAME
907  * structure's hInstance parameter is not a HINSTANCE, but
908  * instead a pointer to a template resource to use.
909  */
911  {
913  if (fodInfos->ofnInfos->Flags & OFN_ENABLETEMPLATEHANDLE)
914  {
916  if( !(template = LockResource( fodInfos->ofnInfos->hInstance)))
917  {
919  return NULL;
920  }
921  }
922  else
923  {
924  hinst = fodInfos->ofnInfos->hInstance;
925  if(fodInfos->unicode)
926  {
927  LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
929  }
930  else
931  {
934  }
935  if (!hRes)
936  {
938  return NULL;
939  }
940  if (!(hDlgTmpl = LoadResource( hinst, hRes )) ||
941  !(template = LockResource( hDlgTmpl )))
942  {
944  return NULL;
945  }
946  }
947  if (fodInfos->unicode)
948  hChildDlg = CreateDialogIndirectParamW(hinst, template, hwnd,
950  (LPARAM)fodInfos->ofnInfos);
951  else
952  hChildDlg = CreateDialogIndirectParamA(hinst, template, hwnd,
954  (LPARAM)fodInfos->ofnInfos);
955  return hChildDlg;
956  }
957  else if (is_dialog_hooked(fodInfos))
958  {
959  RECT rectHwnd;
960  struct {
961  DLGTEMPLATE tmplate;
962  WORD menu,class,title;
963  } temp;
964  GetClientRect(hwnd,&rectHwnd);
966  temp.tmplate.dwExtendedStyle = 0;
967  temp.tmplate.cdit = 0;
968  temp.tmplate.x = 0;
969  temp.tmplate.y = 0;
970  temp.tmplate.cx = 0;
971  temp.tmplate.cy = 0;
972  temp.menu = temp.class = temp.title = 0;
973 
974  hChildDlg = CreateDialogIndirectParamA(COMDLG32_hInstance, &temp.tmplate,
975  hwnd, (DLGPROC)fodInfos->ofnInfos->lpfnHook, (LPARAM)fodInfos->ofnInfos);
976 
977  return hChildDlg;
978  }
979  return NULL;
980 }
981 
982 /***********************************************************************
983 * SendCustomDlgNotificationMessage
984 *
985 * Send CustomDialogNotification (CDN_FIRST -- CDN_LAST) message to the custom template dialog
986 */
987 
989 {
990  FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwndParentDlg);
991  LRESULT hook_result;
992  OFNOTIFYW ofnNotify;
993 
994  TRACE("%p %d\n", hwndParentDlg, uCode);
995 
996  if (!fodInfos || !fodInfos->DlgInfos.hwndCustomDlg)
997  return 0;
998 
999  TRACE("CALL NOTIFY for %d\n", uCode);
1000 
1001  ofnNotify.hdr.hwndFrom = hwndParentDlg;
1002  ofnNotify.hdr.idFrom = 0;
1003  ofnNotify.hdr.code = uCode;
1004  ofnNotify.lpOFN = fodInfos->ofnInfos;
1005  ofnNotify.pszFile = NULL;
1006 
1007  if (fodInfos->unicode)
1008  hook_result = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
1009  else
1010  hook_result = SendMessageA(fodInfos->DlgInfos.hwndCustomDlg, WM_NOTIFY, 0, (LPARAM)&ofnNotify);
1011 
1012  TRACE("RET NOTIFY retval %#lx\n", hook_result);
1013 
1014  return hook_result;
1015 }
1016 
1018 {
1019  UINT len, total;
1020  WCHAR *p, *buffer;
1022 
1023  TRACE("CDM_GETFILEPATH:\n");
1024 
1025  if ( ! (fodInfos->ofnInfos->Flags & OFN_EXPLORER ) )
1026  return -1;
1027 
1028  /* get path and filenames */
1029  len = SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0 );
1030  buffer = heap_alloc( (len + 2 + MAX_PATH) * sizeof(WCHAR) );
1031  COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, buffer );
1032  if (len)
1033  {
1034  p = buffer + lstrlenW(buffer);
1035  *p++ = '\\';
1036  SendMessageW( fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, len + 1, (LPARAM)p );
1037  }
1038  if (fodInfos->unicode)
1039  {
1040  total = lstrlenW( buffer) + 1;
1041  if (result) lstrcpynW( result, buffer, size );
1042  TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_w(result));
1043  }
1044  else
1045  {
1046  total = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
1047  if (total <= size) WideCharToMultiByte( CP_ACP, 0, buffer, -1, result, size, NULL, NULL );
1048  TRACE( "CDM_GETFILEPATH: returning %u %s\n", total, debugstr_a(result));
1049  }
1050  heap_free( buffer );
1051  return total;
1052 }
1053 
1054 /***********************************************************************
1055 * FILEDLG95_HandleCustomDialogMessages
1056 *
1057 * Handle Custom Dialog Messages (CDM_FIRST -- CDM_LAST) messages
1058 */
1060 {
1062  WCHAR lpstrPath[MAX_PATH];
1063  INT_PTR retval;
1064 
1065  if(!fodInfos) return FALSE;
1066 
1067  switch(uMsg)
1068  {
1069  case CDM_GETFILEPATH:
1071  break;
1072 
1073  case CDM_GETFOLDERPATH:
1074  TRACE("CDM_GETFOLDERPATH:\n");
1075  COMDLG32_GetDisplayNameOf(fodInfos->ShellInfos.pidlAbsCurrent, lpstrPath);
1076  if (lParam)
1077  {
1078  if (fodInfos->unicode)
1079  lstrcpynW((LPWSTR)lParam, lpstrPath, (int)wParam);
1080  else
1081  WideCharToMultiByte(CP_ACP, 0, lpstrPath, -1,
1082  (LPSTR)lParam, (int)wParam, NULL, NULL);
1083  }
1084  retval = lstrlenW(lpstrPath) + 1;
1085  break;
1086 
1087  case CDM_GETFOLDERIDLIST:
1088  retval = ILGetSize(fodInfos->ShellInfos.pidlAbsCurrent);
1089  if (retval <= wParam)
1090  memcpy((void*)lParam, fodInfos->ShellInfos.pidlAbsCurrent, retval);
1091  break;
1092 
1093  case CDM_GETSPEC:
1094  TRACE("CDM_GETSPEC:\n");
1095  retval = SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXTLENGTH, 0, 0) + 1;
1096  if (lParam)
1097  {
1098  if (fodInfos->unicode)
1099  SendMessageW(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1100  else
1101  SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_GETTEXT, wParam, lParam);
1102  }
1103  break;
1104 
1105  case CDM_SETCONTROLTEXT:
1106  TRACE("CDM_SETCONTROLTEXT:\n");
1107  if ( lParam )
1108  {
1109  if( fodInfos->unicode )
1111  else
1113  }
1114  retval = TRUE;
1115  break;
1116 
1117  case CDM_HIDECONTROL:
1118  /* MSDN states that it should fail for not OFN_EXPLORER case */
1119  if (fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1120  {
1121  HWND control = GetDlgItem( hwnd, wParam );
1122  if (control) ShowWindow( control, SW_HIDE );
1123  retval = TRUE;
1124  }
1125  else retval = FALSE;
1126  break;
1127 
1128  default:
1129  if (uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1130  FIXME("message CDM_FIRST+%04x not implemented\n", uMsg - CDM_FIRST);
1131  return FALSE;
1132  }
1134  return TRUE;
1135 }
1136 
1137 /***********************************************************************
1138  * FILEDLG95_OnWMGetMMI
1139  *
1140  * WM_GETMINMAXINFO message handler for resizable dialogs
1141  */
1143 {
1145  if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1146  if( fodInfos->initial_size.x || fodInfos->initial_size.y)
1147  {
1148  mmiptr->ptMinTrackSize = fodInfos->initial_size;
1149  }
1150  return TRUE;
1151 }
1152 
1153 /***********************************************************************
1154  * FILEDLG95_OnWMSize
1155  *
1156  * WM_SIZE message handler, resize the dialog. Re-arrange controls.
1157  *
1158  * FIXME: this could be made more elaborate. Now use a simple scheme
1159  * where the file view is enlarged and the controls are either moved
1160  * vertically or horizontally to get out of the way. Only the "grip"
1161  * is moved in both directions to stay in the corner.
1162  */
1164 {
1165  RECT rc, rcview;
1166  int chgx, chgy;
1167  HWND ctrl;
1168  HDWP hdwp;
1169  FileOpenDlgInfos *fodInfos;
1170 
1171  if( wParam != SIZE_RESTORED) return FALSE;
1172  fodInfos = get_filedlg_infoptr(hwnd);
1173  if( !(fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)) return FALSE;
1174  /* get the new dialog rectangle */
1175  GetWindowRect( hwnd, &rc);
1176  TRACE("%p, size from %d,%d to %d,%d\n", hwnd, fodInfos->sizedlg.cx, fodInfos->sizedlg.cy,
1177  rc.right -rc.left, rc.bottom -rc.top);
1178  /* not initialized yet */
1179  if( (fodInfos->sizedlg.cx == 0 && fodInfos->sizedlg.cy == 0) ||
1180  ((fodInfos->sizedlg.cx == rc.right -rc.left) && /* no change */
1181  (fodInfos->sizedlg.cy == rc.bottom -rc.top)))
1182  return FALSE;
1183  chgx = rc.right - rc.left - fodInfos->sizedlg.cx;
1184  chgy = rc.bottom - rc.top - fodInfos->sizedlg.cy;
1185  fodInfos->sizedlg.cx = rc.right - rc.left;
1186  fodInfos->sizedlg.cy = rc.bottom - rc.top;
1187  /* change the size of the view window */
1188  GetWindowRect( fodInfos->ShellInfos.hwndView, &rcview);
1189  MapWindowPoints( NULL, hwnd, (LPPOINT) &rcview, 2);
1190  hdwp = BeginDeferWindowPos( 10);
1191  DeferWindowPos( hdwp, fodInfos->ShellInfos.hwndView, NULL, 0, 0,
1192  rcview.right - rcview.left + chgx,
1193  rcview.bottom - rcview.top + chgy,
1195  /* change position and sizes of the controls */
1197  {
1198  int ctrlid = GetDlgCtrlID( ctrl);
1199  GetWindowRect( ctrl, &rc);
1200  MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1201  if( ctrl == fodInfos->DlgInfos.hwndGrip)
1202  {
1203  DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1204  0, 0,
1206  }
1207  else if( rc.top > rcview.bottom)
1208  {
1209  /* if it was below the shell view
1210  * move to bottom */
1211  switch( ctrlid)
1212  {
1213  /* file name (edit or comboboxex) and file types combo change also width */
1214  case edt1:
1215  case cmb13:
1216  case cmb1:
1217  DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1218  rc.right - rc.left + chgx, rc.bottom - rc.top,
1220  break;
1221  /* then these buttons must move out of the way */
1222  case IDOK:
1223  case IDCANCEL:
1224  case pshHelp:
1225  DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top + chgy,
1226  0, 0,
1228  break;
1229  default:
1230  DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1231  0, 0,
1233  }
1234  }
1235  else if( rc.left > rcview.right)
1236  {
1237  /* if it was to the right of the shell view
1238  * move to right */
1239  DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1240  0, 0,
1242  }
1243  else
1244  /* special cases */
1245  {
1246  switch( ctrlid)
1247  {
1248 #if 0 /* this is Win2k, Win XP. Vista and Higher don't move/size these controls */
1249  case IDC_LOOKIN:
1250  DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1251  rc.right - rc.left + chgx, rc.bottom - rc.top,
1253  break;
1254  case IDC_TOOLBARSTATIC:
1255  case IDC_TOOLBAR:
1256  DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1257  0, 0,
1259  break;
1260 #endif
1261  /* not resized in windows. Since wine uses this invisible control
1262  * to size the browser view it needs to be resized */
1263  case IDC_SHELLSTATIC:
1264  DeferWindowPos( hdwp, ctrl, NULL, 0, 0,
1265  rc.right - rc.left + chgx,
1266  rc.bottom - rc.top + chgy,
1268  break;
1269  case IDC_TOOLBARPLACES:
1270  DeferWindowPos( hdwp, ctrl, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top + chgy,
1272  break;
1273  }
1274  }
1275  }
1276  if(fodInfos->DlgInfos.hwndCustomDlg &&
1278  {
1279  for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD);
1281  {
1282  GetWindowRect( ctrl, &rc);
1283  MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2);
1284  if( rc.top > rcview.bottom)
1285  {
1286  /* if it was below the shell view
1287  * move to bottom */
1288  DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy,
1289  rc.right - rc.left, rc.bottom - rc.top,
1291  }
1292  else if( rc.left > rcview.right)
1293  {
1294  /* if it was to the right of the shell view
1295  * move to right */
1296  DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top,
1297  rc.right - rc.left, rc.bottom - rc.top,
1299  }
1300  }
1301  /* size the custom dialog at the end: some applications do some
1302  * control re-arranging at this point */
1303  GetClientRect(hwnd, &rc);
1304  DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL,
1305  0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
1306  }
1307  EndDeferWindowPos( hdwp);
1308  /* should not be needed */
1310  return TRUE;
1311 }
1312 
1313 /***********************************************************************
1314  * FileOpenDlgProc95
1315  *
1316  * File open dialog procedure
1317  */
1319 {
1320 #if 0
1321  TRACE("%p 0x%04x\n", hwnd, uMsg);
1322 #endif
1323 
1324  switch(uMsg)
1325  {
1326  case WM_INITDIALOG:
1327  {
1328  FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam;
1329  RECT rc, rcstc;
1330  int gripx = GetSystemMetrics( SM_CYHSCROLL);
1331  int gripy = GetSystemMetrics( SM_CYVSCROLL);
1332 
1333  /* Some shell namespace extensions depend on COM being initialized. */
1335  fodInfos->ole_initialized = TRUE;
1336 
1337  SetPropW(hwnd, filedlg_info_propnameW, fodInfos);
1338 
1340 
1341  if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1342  {
1344  DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
1345  RECT client, client_adjusted;
1346 
1347  if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1348  {
1349  style |= WS_SIZEBOX;
1350  ex_style |= WS_EX_WINDOWEDGE;
1351  }
1352  else
1353  style &= ~WS_SIZEBOX;
1355  SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style);
1356 
1357  GetClientRect( hwnd, &client );
1358  GetClientRect( hwnd, &client_adjusted );
1359  AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style );
1360 
1361  GetWindowRect( hwnd, &rc );
1362  rc.right += client_adjusted.right - client.right;
1363  rc.bottom += client_adjusted.bottom - client.bottom;
1364  SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE |
1366 
1367  GetWindowRect( hwnd, &rc );
1368  fodInfos->DlgInfos.hwndGrip =
1369  CreateWindowExA( 0, "SCROLLBAR", NULL,
1372  rc.right - gripx, rc.bottom - gripy,
1373  gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL);
1374  }
1375 
1376  fodInfos->DlgInfos.hwndCustomDlg =
1378 
1381 
1382  if( fodInfos->DlgInfos.hwndCustomDlg)
1383  ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW);
1384 
1385  if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) {
1388  }
1389 
1390  /* if the app has changed the position of the invisible listbox,
1391  * change that of the listview (browser) as well */
1392  GetWindowRect( fodInfos->ShellInfos.hwndView, &rc);
1394  if( !EqualRect( &rc, &rcstc))
1395  {
1396  MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2);
1397  SetWindowPos( fodInfos->ShellInfos.hwndView, NULL,
1398  rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top,
1400  }
1401 
1402  if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1403  {
1404  GetWindowRect( hwnd, &rc);
1405  fodInfos->sizedlg.cx = rc.right - rc.left;
1406  fodInfos->sizedlg.cy = rc.bottom - rc.top;
1407  fodInfos->initial_size.x = fodInfos->sizedlg.cx;
1408  fodInfos->initial_size.y = fodInfos->sizedlg.cy;
1409  GetClientRect( hwnd, &rc);
1410  SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL,
1411  rc.right - gripx, rc.bottom - gripy,
1413  /* resize the dialog to the previous invocation */
1418  }
1419 
1420  if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
1422 
1423  return 0;
1424  }
1425  case WM_SIZE:
1426  return FILEDLG95_OnWMSize(hwnd, wParam);
1427  case WM_GETMINMAXINFO:
1429  case WM_COMMAND:
1431  case WM_DRAWITEM:
1432  {
1433  switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
1434  {
1435  case IDC_LOOKIN:
1437  return TRUE;
1438  }
1439  }
1440  return FALSE;
1441 
1442  case WM_GETISHELLBROWSER:
1444 
1445  case WM_DESTROY:
1446  {
1448  HWND places_bar = GetDlgItem(hwnd, IDC_TOOLBARPLACES);
1449  HIMAGELIST himl;
1450 
1451  if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING)
1452  MemDialogSize = fodInfos->sizedlg;
1453 
1454  if (places_bar)
1455  {
1459  }
1460  return FALSE;
1461  }
1462 
1463  case WM_NCDESTROY:
1465  return 0;
1466 
1467  case WM_NOTIFY:
1468  {
1469  LPNMHDR lpnmh = (LPNMHDR)lParam;
1470  UINT stringId = -1;
1471 
1472  /* set up the button tooltips strings */
1473  if(TTN_GETDISPINFOA == lpnmh->code )
1474  {
1476  switch(lpnmh->idFrom )
1477  {
1478  /* Up folder button */
1479  case FCIDM_TB_UPFOLDER:
1480  stringId = IDS_UPFOLDER;
1481  break;
1482  /* New folder button */
1483  case FCIDM_TB_NEWFOLDER:
1484  stringId = IDS_NEWFOLDER;
1485  break;
1486  /* List option button */
1487  case FCIDM_TB_SMALLICON:
1488  stringId = IDS_LISTVIEW;
1489  break;
1490  /* Details option button */
1491  case FCIDM_TB_REPORTVIEW:
1492  stringId = IDS_REPORTVIEW;
1493  break;
1494  /* Desktop button */
1495  case FCIDM_TB_DESKTOP:
1496  stringId = IDS_TODESKTOP;
1497  break;
1498  default:
1499  stringId = 0;
1500  }
1501  lpdi->hinst = COMDLG32_hInstance;
1502  lpdi->lpszText = MAKEINTRESOURCEA(stringId);
1503  }
1504  return FALSE;
1505  }
1506  default :
1507  if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST)
1509  return FALSE;
1510  }
1511 }
1512 
1514 {
1515  return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) &&
1517 }
1518 
1519 /***********************************************************************
1520  * FILEDLG95_InitControls
1521  *
1522  * WM_INITDIALOG message handler (before hook notification)
1523  */
1525 {
1526  BOOL win2000plus = FALSE;
1527  BOOL win98plus = FALSE;
1528  BOOL handledPath = FALSE;
1529  OSVERSIONINFOW osVi;
1530  static const WCHAR szwSlash[] = { '\\', 0 };
1531  static const WCHAR szwStar[] = { '*',0 };
1532 
1533  static const TBBUTTON tbb[] =
1534  {
1535  {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1537  {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1539  {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1541  {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 },
1544  };
1545  static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR};
1546 
1547  RECT rectTB;
1548  RECT rectlook;
1549 
1550  HIMAGELIST toolbarImageList;
1551  ITEMIDLIST *desktopPidl;
1553 
1555 
1556  TRACE("%p\n", fodInfos);
1557 
1558  /* Get windows version emulating */
1559  osVi.dwOSVersionInfoSize = sizeof(osVi);
1560  GetVersionExW(&osVi);
1562  win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0)));
1563  } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1564  win2000plus = (osVi.dwMajorVersion > 4);
1565  if (win2000plus) win98plus = TRUE;
1566  }
1567  TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus);
1568 
1569 
1570  /* Use either the edit or the comboboxex for the filename control */
1571  if (filename_is_edit( fodInfos ))
1572  {
1574  fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 );
1575  }
1576  else
1577  {
1579  fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 );
1580  }
1581 
1582  /* Get the hwnd of the controls */
1583  fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE);
1584  fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN);
1585 
1586  GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook);
1587  MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2);
1588 
1589  /* construct the toolbar */
1591  MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2);
1592 
1593  rectTB.right = rectlook.right + rectTB.right - rectTB.left;
1594  rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top;
1595  rectTB.left = rectlook.right;
1596  rectTB.top = rectlook.top-1;
1597 
1598  if (fodInfos->unicode)
1599  fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
1601  rectTB.left, rectTB.top,
1602  rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1604  else
1605  fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL,
1607  rectTB.left, rectTB.top,
1608  rectTB.right - rectTB.left, rectTB.bottom - rectTB.top,
1610 
1611  SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
1612 
1613 /* FIXME: use TB_LOADIMAGES when implemented */
1614 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/
1615  SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0);
1616  SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba);
1617 
1618  /* Retrieve and add desktop icon to the toolbar */
1619  toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L);
1621  SHGetFileInfoW((const WCHAR *)desktopPidl, 0, &fileinfo, sizeof(fileinfo),
1623  ImageList_AddIcon(toolbarImageList, fileinfo.hIcon);
1624 
1625  DestroyIcon(fileinfo.hIcon);
1626  CoTaskMemFree(desktopPidl);
1627 
1628  /* Finish Toolbar Construction */
1629  SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb);
1630  SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0);
1631 
1632  if (is_places_bar_enabled(fodInfos))
1633  {
1634  TBBUTTON tb = { 0 };
1635  HIMAGELIST himl;
1636  RECT rect;
1637  int i, cx;
1638 
1641  cx = rect.right - rect.left;
1642 
1645 
1646  filedlg_collect_places_pidls(fodInfos);
1647  for (i = 0; i < ARRAY_SIZE(fodInfos->places); i++)
1648  {
1649  int index;
1650 
1651  if (!fodInfos->places[i])
1652  continue;
1653 
1654  memset(&fileinfo, 0, sizeof(fileinfo));
1655  SHGetFileInfoW((const WCHAR *)fodInfos->places[i], 0, &fileinfo, sizeof(fileinfo),
1658 
1659  tb.iBitmap = index;
1660  tb.iString = (INT_PTR)fileinfo.szDisplayName;
1661  tb.fsState = TBSTATE_ENABLED | TBSTATE_WRAP;
1662  tb.idCommand = TBPLACES_CMDID_PLACE0 + i;
1664 
1665  DestroyIcon(fileinfo.hIcon);
1666  }
1667 
1670  }
1671 
1672  /* Set the window text with the text specified in the OPENFILENAME structure */
1673  if(fodInfos->title)
1674  {
1675  SetWindowTextW(hwnd,fodInfos->title);
1676  }
1677  else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1678  {
1679  WCHAR buf[64];
1682  }
1683 
1684  /* Initialise the file name edit control */
1685  handledPath = FALSE;
1686  TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1687 
1688  if(fodInfos->filename)
1689  {
1690  /* 1. If win2000 or higher and filename contains a path, use it
1691  in preference over the lpstrInitialDir */
1692  if (win2000plus && *fodInfos->filename && wcspbrk(fodInfos->filename, szwSlash)) {
1693  WCHAR tmpBuf[MAX_PATH];
1694  WCHAR *nameBit;
1695  DWORD result;
1696 
1697  result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit);
1698  if (result) {
1699 
1700  /* nameBit is always shorter than the original filename. It may be NULL
1701  * when the filename contains only a drive name instead of file name */
1702  if (nameBit)
1703  {
1704  lstrcpyW(fodInfos->filename,nameBit);
1705  *nameBit = 0x00;
1706  }
1707  else
1708  *fodInfos->filename = '\0';
1709 
1710  heap_free(fodInfos->initdir);
1711  fodInfos->initdir = heap_alloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR));
1712  lstrcpyW(fodInfos->initdir, tmpBuf);
1713  handledPath = TRUE;
1714  TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n",
1715  debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1716  }
1717  SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1718 
1719  } else {
1720  SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1721  }
1722  }
1723 
1724  /* 2. (All platforms) If initdir is not null, then use it */
1725  if (!handledPath && fodInfos->initdir && *fodInfos->initdir)
1726  {
1727  /* Work out the proper path as supplied one might be relative */
1728  /* (Here because supplying '.' as dir browses to My Computer) */
1729  WCHAR tmpBuf[MAX_PATH];
1730  WCHAR tmpBuf2[MAX_PATH];
1731  WCHAR *nameBit;
1732  DWORD result;
1733 
1734  lstrcpyW(tmpBuf, fodInfos->initdir);
1735  if (PathFileExistsW(tmpBuf)) {
1736  /* initdir does not have to be a directory. If a file is
1737  * specified, the dir part is taken */
1738  if (PathIsDirectoryW(tmpBuf)) {
1739  PathAddBackslashW(tmpBuf);
1740  lstrcatW(tmpBuf, szwStar);
1741  }
1742  result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit);
1743  if (result) {
1744  *nameBit = 0x00;
1745  heap_free(fodInfos->initdir);
1746  fodInfos->initdir = heap_alloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR));
1747  lstrcpyW(fodInfos->initdir, tmpBuf2);
1748  handledPath = TRUE;
1749  TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir));
1750  }
1751  }
1752  else if (fodInfos->initdir)
1753  {
1754  heap_free(fodInfos->initdir);
1755  fodInfos->initdir = NULL;
1756  TRACE("Value in InitDir is not an existing path, changed to (nil)\n");
1757  }
1758  }
1759 
1760 #ifdef __REACTOS__
1761  if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1762  {
1763  /* 2.5. Win2000+: Recently used defext */
1764  if (win2000plus) {
1765  fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1766  fodInfos->initdir[0] = '\0';
1767 
1768  FILEDLG95_MRU_load_ext(fodInfos->initdir, MAX_PATH, fodInfos->defext);
1769 
1770  if (fodInfos->initdir[0] && PathIsDirectoryW(fodInfos->initdir)) {
1771  handledPath = TRUE;
1772  } else {
1773  heap_free(fodInfos->initdir);
1774  fodInfos->initdir = NULL;
1775  }
1776  }
1777  }
1778 #endif
1779 
1780  if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir))
1781  {
1782  /* 3. All except w2k+: if filename contains a path use it */
1783  if (!win2000plus && fodInfos->filename &&
1784  *fodInfos->filename &&
1785  wcspbrk(fodInfos->filename, szwSlash)) {
1786  WCHAR tmpBuf[MAX_PATH];
1787  WCHAR *nameBit;
1788  DWORD result;
1789 
1791  tmpBuf, &nameBit);
1792  if (result) {
1793  int len;
1794 
1795  /* nameBit is always shorter than the original filename */
1796  lstrcpyW(fodInfos->filename, nameBit);
1797  *nameBit = 0x00;
1798 
1799  len = lstrlenW(tmpBuf);
1800  heap_free(fodInfos->initdir);
1801  fodInfos->initdir = heap_alloc((len+1)*sizeof(WCHAR));
1802  lstrcpyW(fodInfos->initdir, tmpBuf);
1803 
1804  handledPath = TRUE;
1805  TRACE("Value in Filename includes path, overriding initdir: %s, %s\n",
1806  debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1807  }
1808  SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename );
1809  }
1810 
1811  /* 4. Win2000+: Recently used */
1812  if (!handledPath && win2000plus) {
1813  fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1814  fodInfos->initdir[0] = '\0';
1815 
1817 
1818  if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
1819  handledPath = TRUE;
1820  }else{
1821  heap_free(fodInfos->initdir);
1822  fodInfos->initdir = NULL;
1823  }
1824  }
1825 
1826  /* 5. win98+ and win2000+ if any files of specified filter types in
1827  current directory, use it */
1828  if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) {
1829 
1830  LPCWSTR lpstrPos = fodInfos->filter;
1831  WIN32_FIND_DATAW FindFileData;
1832  HANDLE hFind;
1833 
1834  while (1)
1835  {
1836  /* filter is a list... title\0ext\0......\0\0 */
1837 
1838  /* Skip the title */
1839  if(! *lpstrPos) break; /* end */
1840  lpstrPos += lstrlenW(lpstrPos) + 1;
1841 
1842  /* See if any files exist in the current dir with this extension */
1843  if(! *lpstrPos) break; /* end */
1844 
1845  hFind = FindFirstFileW(lpstrPos, &FindFileData);
1846 
1847  if (hFind == INVALID_HANDLE_VALUE) {
1848  /* None found - continue search */
1849  lpstrPos += lstrlenW(lpstrPos) + 1;
1850 
1851  } else {
1852 
1853  heap_free(fodInfos->initdir);
1854  fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1856 
1857  handledPath = TRUE;
1858  TRACE("No initial dir specified, but files of type %s found in current, so using it\n",
1859  debugstr_w(lpstrPos));
1860  FindClose(hFind);
1861  break;
1862  }
1863  }
1864  }
1865 
1866  /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
1867  if (!handledPath && (win2000plus || win98plus)) {
1868  fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1869 
1870  if (SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir) == S_OK)
1871  {
1873  {
1874  /* last fallback */
1876  TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir));
1877  }
1878  else
1879  TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir));
1880  }
1881  else
1882  TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir));
1883 
1884  handledPath = TRUE;
1885  } else if (!handledPath) {
1886  fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR));
1888  handledPath = TRUE;
1889  TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir));
1890  }
1891  }
1892  SetFocus( fodInfos->DlgInfos.hwndFileName );
1893  TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir));
1894 
1895  /* Must the open as read only check box be checked ?*/
1896  if(fodInfos->ofnInfos->Flags & OFN_READONLY)
1897  {
1899  }
1900 
1901  /* Must the open as read only check box be hidden? */
1902  if (filedialog_is_readonly_hidden(fodInfos))
1903  {
1906  }
1907 
1908  /* Must the help button be hidden? */
1909  if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP))
1910  {
1913  }
1914 
1915  /* change Open to Save */
1916  if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
1917  {
1918  WCHAR buf[16];
1923  }
1924 
1925  /* Initialize the filter combo box */
1927 
1928  return 0;
1929 }
1930 
1931 /***********************************************************************
1932  * FILEDLG95_ResizeControls
1933  *
1934  * WM_INITDIALOG message handler (after hook notification)
1935  */
1937 {
1938  FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1939 
1940  if (fodInfos->DlgInfos.hwndCustomDlg)
1941  {
1942  RECT rc;
1944 
1945  ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd,
1946  filedialog_is_readonly_hidden(fodInfos) && !(fodInfos->ofnInfos->Flags & OFN_SHOWHELP));
1947 
1948  /* resize the custom dialog to the parent size */
1950  GetClientRect(hwnd, &rc);
1951  else
1952  {
1953  /* our own fake template is zero sized and doesn't have children, so
1954  * there is no need to resize it. Picasa depends on it.
1955  */
1956  flags |= SWP_NOSIZE;
1957  SetRectEmpty(&rc);
1958  }
1959  SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM,
1960  0, 0, rc.right, rc.bottom, flags);
1961  }
1962  else
1963  {
1964  /* Resize the height; if opened as read-only, checkbox and help button are
1965  * hidden and we are not using a custom template nor a customDialog
1966  */
1967  if (filedialog_is_readonly_hidden(fodInfos) &&
1968  (!(fodInfos->ofnInfos->Flags &
1970  {
1971  RECT rectDlg, rectHelp, rectCancel;
1972  GetWindowRect(hwnd, &rectDlg);
1973  GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp);
1974  GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel);
1975  /* subtract the height of the help button plus the space between the help
1976  * button and the cancel button to the height of the dialog
1977  */
1978  SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left,
1979  (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom),
1981  }
1982  }
1983  return TRUE;
1984 }
1985 
1986 /***********************************************************************
1987  * FILEDLG95_FillControls
1988  *
1989  * WM_INITDIALOG message handler (after hook notification)
1990  */
1992 {
1993  LPITEMIDLIST pidlItemId = NULL;
1994 
1995  FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam;
1996 
1997  TRACE("dir=%s file=%s\n",
1998  debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename));
1999 
2000  /* Get the initial directory pidl */
2001 
2002  if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir)))
2003  {
2004  WCHAR path[MAX_PATH];
2005 
2007  pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path);
2008  }
2009 
2010  /* Initialise shell objects */
2012 
2013  /* Initialize the Look In combo box */
2014  FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB);
2015 
2016  /* Browse to the initial directory */
2017  IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE);
2018 
2019  ILFree(pidlItemId);
2020 
2021  return TRUE;
2022 }
2023 /***********************************************************************
2024  * FILEDLG95_Clean
2025  *
2026  * Regroups all the cleaning functions of the filedlg
2027  */
2029 {
2033 }
2034 
2035 
2036 /***********************************************************************
2037  * Browse to arbitrary pidl
2038  */
2040 {
2041  TRACE("%p, %p\n", info->ShellInfos.hwndOwner, pidl);
2042 
2043  IShellBrowser_BrowseObject(info->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE);
2044  if (info->ofnInfos->Flags & OFN_EXPLORER)
2045  SendCustomDlgNotificationMessage(info->ShellInfos.hwndOwner, CDN_FOLDERCHANGE);
2046 }
2047 
2048 /***********************************************************************
2049  * FILEDLG95_OnWMCommand
2050  *
2051  * WM_COMMAND message handler
2052  */
2054 {
2056  WORD wNotifyCode = HIWORD(wParam); /* notification code */
2057  WORD id = LOWORD(wParam); /* item, control, or accelerator identifier */
2058 
2059  switch (id)
2060  {
2061  /* OK button */
2062  case IDOK:
2064  break;
2065  /* Cancel button */
2066  case IDCANCEL:
2068  EndDialog(hwnd, FALSE);
2069  break;
2070  /* Filetype combo box */
2071  case IDC_FILETYPE:
2072  FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode);
2073  break;
2074  /* LookIn combo box */
2075  case IDC_LOOKIN:
2076  FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode);
2077  break;
2078 
2079  /* --- toolbar --- */
2080  /* Up folder button */
2081  case FCIDM_TB_UPFOLDER:
2083  break;
2084  /* New folder button */
2085  case FCIDM_TB_NEWFOLDER:
2086  FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA);
2087  break;
2088  /* List option button */
2089  case FCIDM_TB_SMALLICON:
2090  FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA);
2091  break;
2092  /* Details option button */
2093  case FCIDM_TB_REPORTVIEW:
2094  FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA);
2095  break;
2096 
2097  case FCIDM_TB_DESKTOP:
2098  {
2099  LPITEMIDLIST pidl;
2100 
2102  filedlg_browse_to_pidl(fodInfos, pidl);
2103  ILFree(pidl);
2104  break;
2105  }
2106 
2107  /* Places bar */
2108  case TBPLACES_CMDID_PLACE0:
2109  case TBPLACES_CMDID_PLACE1:
2110  case TBPLACES_CMDID_PLACE2:
2111  case TBPLACES_CMDID_PLACE3:
2112  case TBPLACES_CMDID_PLACE4:
2113  filedlg_browse_to_pidl(fodInfos, fodInfos->places[id - TBPLACES_CMDID_PLACE0]);
2114  break;
2115 
2116  case edt1:
2117  case cmb13:
2118  break;
2119 
2120  }
2121  /* Do not use the listview selection anymore */
2122  fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW;
2123  return 0;
2124 }
2125 
2126 /***********************************************************************
2127  * FILEDLG95_OnWMGetIShellBrowser
2128  *
2129  * WM_GETISHELLBROWSER message handler
2130  */
2132 {
2134 
2135  TRACE("\n");
2136 
2137  SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser);
2138 
2139  return TRUE;
2140 }
2141 
2142 
2143 /***********************************************************************
2144  * FILEDLG95_SendFileOK
2145  *
2146  * Sends the CDN_FILEOK notification if required
2147  *
2148  * RETURNS
2149  * TRUE if the dialog should close
2150  * FALSE if the dialog should not be closed
2151  */
2153 {
2154  /* ask the hook if we can close */
2155  if (is_dialog_hooked(fodInfos))
2156  {
2157  LRESULT retval = 0;
2158 
2159  TRACE("---\n");
2160  /* First send CDN_FILEOK as MSDN doc says */
2161  if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2163  if( retval)
2164  {
2165  TRACE("canceled\n");
2166  return FALSE;
2167  }
2168 
2169  /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */
2170  retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg,
2171  fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos);
2172  if( retval)
2173  {
2174  TRACE("canceled\n");
2175  return FALSE;
2176  }
2177  }
2178  return TRUE;
2179 }
2180 
2181 /***********************************************************************
2182  * FILEDLG95_OnOpenMultipleFiles
2183  *
2184  * Handles the opening of multiple files.
2185  *
2186  * FIXME
2187  * check destination buffer size
2188  */
2189 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed)
2190 {
2192  WCHAR lpstrPathSpec[MAX_PATH] = {0};
2193  UINT nCount, nSizePath;
2194 
2195  TRACE("\n");
2196 
2197  if(fodInfos->unicode)
2198  {
2199  LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2200  ofn->lpstrFile[0] = '\0';
2201  }
2202  else
2203  {
2205  ofn->lpstrFile[0] = '\0';
2206  }
2207 
2208  COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec );
2209 
2210  if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2211  ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) &&
2212  ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) )
2213  {
2214  LPWSTR lpstrTemp = lpstrFileList;
2215 
2216  for ( nCount = 0; nCount < nFileCount; nCount++ )
2217  {
2218  LPITEMIDLIST pidl;
2219 
2220  pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp);
2221  if (!pidl)
2222  {
2223  WCHAR lpstrNotFound[100];
2224  WCHAR lpstrMsg[100];
2225  WCHAR tmp[400];
2226  static const WCHAR nl[] = {'\n',0};
2227 
2228  LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100);
2230 
2231  lstrcpyW(tmp, lpstrTemp);
2232  lstrcatW(tmp, nl);
2233  lstrcatW(tmp, lpstrNotFound);
2234  lstrcatW(tmp, nl);
2235  lstrcatW(tmp, lpstrMsg);
2236 
2237  MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION);
2238  return FALSE;
2239  }
2240 
2241  /* move to the next file in the list of files */
2242  lpstrTemp += lstrlenW(lpstrTemp) + 1;
2243  ILFree(pidl);
2244  }
2245  }
2246 
2247  nSizePath = lstrlenW(lpstrPathSpec) + 1;
2248  if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) )
2249  {
2250  /* For "oldstyle" dialog the components have to
2251  be separated by blanks (not '\0'!) and short
2252  filenames have to be used! */
2253  FIXME("Components have to be separated by blanks\n");
2254  }
2255  if(fodInfos->unicode)
2256  {
2257  LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
2258  lstrcpyW( ofn->lpstrFile, lpstrPathSpec);
2259  memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) );
2260  }
2261  else
2262  {
2264 
2265  if (ofn->lpstrFile != NULL)
2266  {
2267  nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1,
2269  if (ofn->nMaxFile > nSizePath)
2270  {
2271  WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed,
2272  ofn->lpstrFile + nSizePath,
2273  ofn->nMaxFile - nSizePath, NULL, NULL);
2274  }
2275  }
2276  }
2277 
2278  fodInfos->ofnInfos->nFileOffset = nSizePath;
2279  fodInfos->ofnInfos->nFileExtension = 0;
2280 
2281  if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
2282  return FALSE;
2283 
2284  /* clean and exit */
2286  return EndDialog(hwnd,TRUE);
2287 }
2288 
2289 /* Returns the 'slot name' of the given module_name in the registry's
2290  * most-recently-used list. This will be an ASCII value in the
2291  * range ['a','z'). Returns zero on error.
2292  *
2293  * The slot's value in the registry has the form:
2294  * module_name\0mru_path\0
2295  *
2296  * If stored_path is given, then stored_path will contain the path name
2297  * stored in the registry's MRU list for the given module_name.
2298  *
2299  * If hkey_ret is given, then hkey_ret will be a handle to the registry's
2300  * MRU list key for the given module_name.
2301  */
2303 {
2304  WCHAR mru_list[32], *cur_mru_slot;
2305  BOOL taken[25] = {0};
2306  DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
2307  HKEY hkey_tmp, *hkey;
2308  LONG ret;
2309 
2310  if(hkey_ret)
2311  hkey = hkey_ret;
2312  else
2313  hkey = &hkey_tmp;
2314 
2315  if(stored_path)
2316  *stored_path = '\0';
2317 
2319  if(ret){
2320  WARN("Unable to create MRU key: %d\n", ret);
2321  return 0;
2322  }
2323 
2324  ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
2325  (LPBYTE)mru_list, &mru_list_size);
2326  if(ret || key_type != REG_SZ){
2327  if(ret == ERROR_FILE_NOT_FOUND)
2328  return 'a';
2329 
2330  WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2331  RegCloseKey(*hkey);
2332  return 0;
2333  }
2334 
2335  for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
2336  WCHAR value_data[MAX_PATH], value_name[2] = {0};
2337  DWORD value_data_size = sizeof(value_data);
2338 
2339  *value_name = *cur_mru_slot;
2340 
2341  ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
2342  &key_type, (LPBYTE)value_data, &value_data_size);
2343  if(ret || key_type != REG_BINARY){
2344  WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
2345  continue;
2346  }
2347 
2348  if(!wcsicmp(module_name, value_data)){
2349  if(!hkey_ret)
2350  RegCloseKey(*hkey);
2351  if(stored_path)
2352  lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
2353  return *value_name;
2354  }
2355  }
2356 
2357  if(!hkey_ret)
2358  RegCloseKey(*hkey);
2359 
2360  /* the module name isn't in the registry, so find the next open slot */
2361  for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
2362  taken[*cur_mru_slot - 'a'] = TRUE;
2363  for(i = 0; i < 25; ++i){
2364  if(!taken[i])
2365  return i + 'a';
2366  }
2367 
2368  /* all slots are taken, so return the last one in MRUList */
2369  --cur_mru_slot;
2370  return *cur_mru_slot;
2371 }
2372 
2373 /* save the given filename as most-recently-used path for this module */
2375 {
2376  WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
2377  LONG ret;
2378  HKEY hkey;
2379 
2380  /* get the current executable's name */
2381  if (!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, ARRAY_SIZE(module_path)))
2382  {
2383  WARN("GotModuleFileName failed: %d\n", GetLastError());
2384  return;
2385  }
2386  module_name = wcsrchr(module_path, '\\');
2387  if(!module_name)
2388  module_name = module_path;
2389  else
2390  module_name += 1;
2391 
2393  if(!slot)
2394  return;
2395  *slot_name = slot;
2396 
2397  { /* update the slot's info */
2398  WCHAR *path_ends, *final;
2399  DWORD path_len, final_len;
2400 
2401  /* use only the path segment of `filename' */
2402  path_ends = wcsrchr(filename, '\\');
2403  path_len = path_ends - filename;
2404 
2405  final_len = path_len + lstrlenW(module_name) + 2;
2406 
2407  final = heap_alloc(final_len * sizeof(WCHAR));
2408  if(!final)
2409  return;
2410  lstrcpyW(final, module_name);
2411  memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
2412  final[final_len-1] = '\0';
2413 
2414  ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
2415  final_len * sizeof(WCHAR));
2416  if(ret){
2417  WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
2418  heap_free(final);
2419  RegCloseKey(hkey);
2420  return;
2421  }
2422 
2423  heap_free(final);
2424  }
2425 
2426  { /* update MRUList value */
2427  WCHAR old_mru_list[32], new_mru_list[32];
2428  WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
2429  DWORD mru_list_size = sizeof(old_mru_list), key_type;
2430 
2431  ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
2432  (LPBYTE)old_mru_list, &mru_list_size);
2433  if(ret || key_type != REG_SZ){
2434  if(ret == ERROR_FILE_NOT_FOUND){
2435  new_mru_list[0] = slot;
2436  new_mru_list[1] = '\0';
2437  }else{
2438  WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
2439  RegCloseKey(hkey);
2440  return;
2441  }
2442  }else{
2443  /* copy old list data over so that the new slot is at the start
2444  * of the list */
2445  *new_mru_slot++ = slot;
2446  for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
2447  if(*old_mru_slot != slot)
2448  *new_mru_slot++ = *old_mru_slot;
2449  }
2450  *new_mru_slot = '\0';
2451  }
2452 
2453  ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
2454  (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
2455  if(ret){
2456  WARN("Error saving MRUList data: %d\n", ret);
2457  RegCloseKey(hkey);
2458  return;
2459  }
2460  }
2461 }
2462 
2463 /* load the most-recently-used path for this module */
2464 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
2465 {
2466  WCHAR module_path[MAX_PATH], *module_name;
2467 
2468  /* get the current executable's name */
2469  if (!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, ARRAY_SIZE(module_path)))
2470  {
2471  WARN("GotModuleFileName failed: %d\n", GetLastError());
2472  return;
2473  }
2474  module_name = wcsrchr(module_path, '\\');
2475  if(!module_name)
2476  module_name = module_path;
2477  else
2478  module_name += 1;
2479 
2480  FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
2481  TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
2482 }
2483 #ifdef __REACTOS__
2484 
2485 static const WCHAR s_subkey[] =
2486 {
2487  'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
2488  'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s',
2489  'i','o','n','\\','E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g',
2490  '3','2','\\','O','p','e','n','S','a','v','e','M','R','U',0
2491 };
2492 static const WCHAR s_szAst[] = { '*', 0 };
2493 
2494 typedef INT (CALLBACK *MRUStringCmpFnW)(LPCWSTR lhs, LPCWSTR rhs);
2495 typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length);
2496 
2497 /* https://docs.microsoft.com/en-us/windows/desktop/shell/mruinfo */
2498 typedef struct tagMRUINFOW
2499 {
2500  DWORD cbSize;
2501  UINT uMax;
2502  UINT fFlags;
2503  HKEY hKey;
2505  union
2506  {
2509  } u;
2510 } MRUINFOW, *LPMRUINFOW;
2511 
2512 /* flags for MRUINFOW.fFlags */
2513 #define MRU_STRING 0x0000
2514 #define MRU_BINARY 0x0001
2515 #define MRU_CACHEWRITE 0x0002
2516 
2517 static HINSTANCE s_hComCtl32 = NULL;
2518 
2519 /* comctl32.400: CreateMRUListW */
2520 typedef HANDLE (WINAPI *CREATEMRULISTW)(const MRUINFOW *);
2521 static CREATEMRULISTW s_pCreateMRUListW = NULL;
2522 
2523 /* comctl32.401: AddMRUStringW */
2524 typedef INT (WINAPI *ADDMRUSTRINGW)(HANDLE, LPCWSTR);
2525 static ADDMRUSTRINGW s_pAddMRUStringW = NULL;
2526 
2527 /* comctl32.402: FindMRUStringW */
2528 typedef INT (WINAPI *FINDMRUSTRINGW)(HANDLE, LPCWSTR, LPINT);
2529 static FINDMRUSTRINGW s_pFindMRUStringW = NULL;
2530 
2531 /* comctl32.403: EnumMRUListW */
2532 typedef INT (WINAPI *ENUMMRULISTW)(HANDLE, INT, LPVOID, DWORD);
2533 static ENUMMRULISTW s_pEnumMRUListW = NULL;
2534 
2535 /* comctl32.152: FreeMRUList */
2536 typedef void (WINAPI *FREEMRULIST)(HANDLE);
2537 static FREEMRULIST s_pFreeMRUList = NULL;
2538 
2539 static BOOL FILEDLG_InitMRUList(void)
2540 {
2541  if (s_hComCtl32)
2542  return TRUE;
2543 
2544  s_hComCtl32 = GetModuleHandleA("comctl32");
2545  if (!s_hComCtl32)
2546  return FALSE;
2547 
2548  s_pCreateMRUListW = (CREATEMRULISTW)GetProcAddress(s_hComCtl32, (LPCSTR)400);
2549  s_pAddMRUStringW = (ADDMRUSTRINGW)GetProcAddress(s_hComCtl32, (LPCSTR)401);
2550  s_pFindMRUStringW = (FINDMRUSTRINGW)GetProcAddress(s_hComCtl32, (LPCSTR)402);
2551  s_pEnumMRUListW = (ENUMMRULISTW)GetProcAddress(s_hComCtl32, (LPCSTR)403);
2552  s_pFreeMRUList = (FREEMRULIST)GetProcAddress(s_hComCtl32, (LPCSTR)152);
2553  if (!s_pCreateMRUListW ||
2554  !s_pAddMRUStringW ||
2555  !s_pFindMRUStringW ||
2556  !s_pEnumMRUListW ||
2557  !s_pFreeMRUList)
2558  {
2559  s_hComCtl32 = NULL;
2560  return FALSE;
2561  }
2562 
2563  return TRUE;
2564 }
2565 
2566 static BOOL ExtIsPicture(LPCWSTR ext)
2567 {
2568  static const WCHAR s_image_exts[][6] =
2569  {
2570  { 'b','m','p',0 },
2571  { 'd','i','b',0 },
2572  { 'j','p','g',0 },
2573  { 'j','p','e','g',0 },
2574  { 'j','p','e',0 },
2575  { 'j','f','i','f',0 },
2576  { 'p','n','g',0 },
2577  { 'g','i','f',0 },
2578  { 't','i','f',0 },
2579  { 't','i','f','f',0 }
2580  };
2581  size_t i;
2582 
2583  for (i = 0; i < ARRAY_SIZE(s_image_exts); ++i)
2584  {
2585  if (lstrcmpiW(ext, s_image_exts[i]) == 0)
2586  {
2587  return TRUE;
2588  }
2589  }
2590  return FALSE;
2591 }
2592 
2593 static void FILEDLG95_MRU_load_ext(LPWSTR stored_path, size_t cchMax, LPCWSTR defext)
2594 {
2595  HKEY hOpenSaveMRT = NULL;
2596  LONG result;
2597  MRUINFOW mi;
2598  HANDLE hList;
2600  INT ret = 0;
2601 
2602  stored_path[0] = 0;
2603 
2604  if (!defext || !*defext || !FILEDLG_InitMRUList())
2605  {
2606  return;
2607  }
2608 
2609  if (*defext == '.')
2610  ++defext;
2611 
2612  result = RegOpenKeyW(HKEY_CURRENT_USER, s_subkey, &hOpenSaveMRT);
2613  if (!result && hOpenSaveMRT)
2614  {
2615  ZeroMemory(&mi, sizeof(mi));
2616  mi.cbSize = sizeof(mi);
2617  mi.uMax = 26;
2618  mi.fFlags = MRU_STRING;
2619  mi.hKey = hOpenSaveMRT;
2620  mi.lpszSubKey = defext;
2621  mi.u.string_cmpfn = lstrcmpiW;
2622  hList = (*s_pCreateMRUListW)(&mi);
2623  if (hList)
2624  {
2625  ret = (*s_pEnumMRUListW)(hList, 0, szText, sizeof(szText));
2626  if (ret > 0)
2627  {
2628  lstrcpynW(stored_path, szText, cchMax);
2629  PathRemoveFileSpecW(stored_path);
2630  }
2631  (*s_pFreeMRUList)(hList);
2632  }
2633 
2634  RegCloseKey(hOpenSaveMRT);
2635  }
2636 
2637  if (stored_path[0] == 0)
2638  {
2639  LPITEMIDLIST pidl;
2640  if (ExtIsPicture(defext))
2641  {
2643  }
2644  else
2645  {
2647  }
2648  SHGetPathFromIDListW(pidl, stored_path);
2649  ILFree(pidl);
2650  }
2651 }
2652 
2653 static void FILEDLG95_MRU_save_ext(LPCWSTR filename)
2654 {
2655  HKEY hOpenSaveMRT = NULL;
2656  LONG result;
2657  MRUINFOW mi;
2658  HANDLE hList;
2660 
2661  if (!defext || !*defext || !FILEDLG_InitMRUList())
2662  {
2663  return;
2664  }
2665 
2666  if (*defext == '.')
2667  ++defext;
2668 
2669  result = RegOpenKeyW(HKEY_CURRENT_USER, s_subkey, &hOpenSaveMRT);
2670  if (!result && hOpenSaveMRT)
2671  {
2672  ZeroMemory(&mi, sizeof(mi));
2673  mi.cbSize = sizeof(mi);
2674  mi.uMax = 26;
2675  mi.fFlags = MRU_STRING;
2676  mi.hKey = hOpenSaveMRT;
2677  mi.lpszSubKey = defext;
2678  mi.u.string_cmpfn = lstrcmpiW;
2679  hList = (*s_pCreateMRUListW)(&mi);
2680  if (hList)
2681  {
2682  (*s_pAddMRUStringW)(hList, filename);
2683  (*s_pFreeMRUList)(hList);
2684  }
2685 
2686  mi.cbSize = sizeof(mi);
2687  mi.uMax = 26;
2688  mi.fFlags = MRU_STRING;
2689  mi.hKey = hOpenSaveMRT;
2690  mi.lpszSubKey = s_szAst;
2691  mi.u.string_cmpfn = lstrcmpiW;
2692  hList = (*s_pCreateMRUListW)(&mi);
2693  if (hList)
2694  {
2695  (*s_pAddMRUStringW)(hList, filename);
2696  (*s_pFreeMRUList)(hList);
2697  }
2698 
2699  RegCloseKey(hOpenSaveMRT);
2700  }
2701 }
2702 
2703 #endif /* __REACTOS__ */
2704 
2705 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText)
2706 {
2707  WCHAR strMsgTitle[MAX_PATH];
2708  WCHAR strMsgText [MAX_PATH];
2709  if (idCaption)
2710  LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, ARRAY_SIZE(strMsgTitle));
2711  else
2712  strMsgTitle[0] = '\0';
2713  LoadStringW(COMDLG32_hInstance, idText, strMsgText, ARRAY_SIZE(strMsgText));
2714  MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND);
2715 }
2716 
2717 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf,
2718  HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction)
2719 {
2720  int nOpenAction = defAction;
2721  LPWSTR lpszTemp, lpszTemp1;
2722  LPITEMIDLIST pidl = NULL;
2723  static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0};
2724 
2725  /* check for invalid chars */
2726  if((wcspbrk(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE))
2727  {
2729  return FALSE;
2730  }
2731 
2732  if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE;
2733 
2734  lpszTemp1 = lpszTemp = lpstrPathAndFile;
2735  while (lpszTemp1)
2736  {
2737  LPSHELLFOLDER lpsfChild;
2738  WCHAR lpwstrTemp[MAX_PATH];
2739  DWORD dwEaten, dwAttributes;
2740  LPWSTR p;
2741 
2742  lstrcpyW(lpwstrTemp, lpszTemp);
2743  p = PathFindNextComponentW(lpwstrTemp);
2744 
2745  if (!p) break; /* end of path */
2746 
2747  *p = 0;
2748  lpszTemp = lpszTemp + lstrlenW(lpwstrTemp);
2749 
2750  /* There are no wildcards when OFN_NOVALIDATE is set */
2751  if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE))
2752  {
2753  static const WCHAR wszWild[] = { '*', '?', 0 };
2754  /* if the last element is a wildcard do a search */
2755  if(wcspbrk(lpszTemp1, wszWild) != NULL)
2756  {
2757  nOpenAction = ONOPEN_SEARCH;
2758  break;
2759  }
2760  }
2761  lpszTemp1 = lpszTemp;
2762 
2763  TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf);
2764 
2765  /* append a backslash to drive letters */
2766  if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' &&
2767  ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') ||
2768  (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z')))
2769  {
2770  PathAddBackslashW(lpwstrTemp);
2771  }
2772 
2773  dwAttributes = SFGAO_FOLDER;
2774  if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes)))
2775  {
2776  /* the path component is valid, we have a pidl of the next path component */
2777  TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl);
2778  if(dwAttributes & SFGAO_FOLDER)
2779  {
2780  if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild)))
2781  {
2782  ERR("bind to failed\n"); /* should not fail */
2783  break;
2784  }
2785  IShellFolder_Release(*ppsf);
2786  *ppsf = lpsfChild;
2787  lpsfChild = NULL;
2788  }
2789  else
2790  {
2791  TRACE("value\n");
2792 
2793  /* end dialog, return value */
2794  nOpenAction = ONOPEN_OPEN;
2795  break;
2796  }
2797  ILFree(pidl);
2798  pidl = NULL;
2799  }
2800  else if (!(flags & OFN_NOVALIDATE))
2801  {
2802  if(*lpszTemp || /* points to trailing null for last path element */
2803  (lpwstrTemp[lstrlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */
2804  {
2805  if(flags & OFN_PATHMUSTEXIST)
2806  {
2808  break;
2809  }
2810  }
2811  else
2812  {
2813  if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg )
2814  {
2816  break;
2817  }
2818  }
2819  /* change to the current folder */
2820  nOpenAction = ONOPEN_OPEN;
2821  break;
2822  }
2823  else
2824  {
2825  nOpenAction = ONOPEN_OPEN;
2826  break;
2827  }
2828  }
2829  ILFree(pidl);
2830 
2831  return nOpenAction;
2832 }
2833 
2834 /***********************************************************************
2835  * FILEDLG95_OnOpen
2836  *
2837  * Ok button WM_COMMAND message handler
2838  *
2839  * If the function succeeds, the return value is nonzero.
2840  */
2842 {
2844  LPWSTR lpstrFileList;
2845  UINT nFileCount = 0;
2846  UINT sizeUsed = 0;
2847  BOOL ret = TRUE;
2848  WCHAR lpstrPathAndFile[MAX_PATH];
2849  LPSHELLFOLDER lpsf = NULL;
2850  int nOpenAction;
2851 
2852  TRACE("hwnd=%p\n", hwnd);
2853 
2854  /* try to browse the selected item */
2856  return FALSE;
2857 
2858  /* get the files from the edit control */
2859  nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed);
2860 
2861  if(nFileCount == 0)
2862  return FALSE;
2863 
2864  if(nFileCount > 1)
2865  {
2866  ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed);
2867  goto ret;
2868  }
2869 
2870  TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList));
2871 
2872 /*
2873  Step 1: Build a complete path name from the current folder and
2874  the filename or path in the edit box.
2875  Special cases:
2876  - the path in the edit box is a root path
2877  (with or without drive letter)
2878  - the edit box contains ".." (or a path with ".." in it)
2879 */
2880 
2881  COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile);
2882  heap_free(lpstrFileList);
2883 
2884 /*
2885  Step 2: here we have a cleaned up path
2886 
2887  We have to parse the path step by step to see if we have to browse
2888  to a folder if the path points to a directory or the last
2889  valid element is a directory.
2890 
2891  valid variables:
2892  lpstrPathAndFile: cleaned up path
2893  */
2894 
2895  if (nFileCount &&
2896  (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) &&
2897  !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST))
2898  nOpenAction = ONOPEN_OPEN;
2899  else
2900  nOpenAction = ONOPEN_BROWSE;
2901 
2902  nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd,
2903  fodInfos->ofnInfos->Flags,
2904  fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG,
2905  nOpenAction);
2906  if(!nOpenAction)
2907  goto ret;
2908 
2909 /*
2910  Step 3: here we have a cleaned up and validated path
2911 
2912  valid variables:
2913  lpsf: ShellFolder bound to the rightmost valid path component
2914  lpstrPathAndFile: cleaned up path
2915  nOpenAction: action to do
2916 */
2917  TRACE("end validate sf=%p\n", lpsf);
2918 
2919  switch(nOpenAction)
2920  {
2921  case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */
2922  TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile));
2923  {
2924  int iPos;
2925  LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile);
2926  DWORD len;
2927 
2928  /* replace the current filter */
2929  heap_free(fodInfos->ShellInfos.lpstrCurrentFilter);
2930  len = lstrlenW(lpszTemp)+1;
2931  fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc(len * sizeof(WCHAR));
2932  lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp);
2933 
2934  /* set the filter cb to the extension when possible */
2935  if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp)))
2936  SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETCURSEL, iPos, 0);
2937  }
2938  /* fall through */
2939  case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */
2940  TRACE("ONOPEN_BROWSE\n");
2941  {
2942  IPersistFolder2 * ppf2;
2943  if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2)))
2944  {
2945  LPITEMIDLIST pidlCurrent;
2946  IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent);
2947  IPersistFolder2_Release(ppf2);
2948  if (!ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent))
2949  {
2950  if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE))
2951  && fodInfos->ofnInfos->Flags & OFN_EXPLORER)
2952  {
2954  SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)"");
2955  }
2956  }
2957  else if( nOpenAction == ONOPEN_SEARCH )
2958  {
2959  if (fodInfos->Shell.FOIShellView)
2960  IShellView_Refresh(fodInfos->Shell.FOIShellView);
2961  }
2962  ILFree(pidlCurrent);
2963  if (filename_is_edit( fodInfos ))
2964  SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
2965  else
2966  {
2967  HWND hwnd;
2968 
2969  hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0);
2970  SendMessageW(hwnd, EM_SETSEL, 0, -1);
2971  }
2972  }
2973  }
2974  ret = FALSE;
2975  break;
2976  case ONOPEN_OPEN: /* fill in the return struct and close the dialog */
2977  TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile));
2978  {
2979  WCHAR *ext = NULL;
2980 
2981  /* update READONLY check box flag */
2983  fodInfos->ofnInfos->Flags |= OFN_READONLY;
2984  else
2985  fodInfos->ofnInfos->Flags &= ~OFN_READONLY;
2986 
2987  /* Attach the file extension with file name*/
2988  ext = PathFindExtensionW(lpstrPathAndFile);
2989 #ifdef __REACTOS__
2990  {
2991  LPWSTR filterExt = NULL, lpstrFilter = NULL, pch, pchNext;
2992  LPCWSTR the_ext = NULL;
2993  static const WCHAR szwDot[] = {'.',0};
2994  int PathLength = lstrlenW(lpstrPathAndFile);
2995 
2996  /* get filter extensions */
2997  lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
2998  fodInfos->ofnInfos->nFilterIndex - 1);
2999  if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
3000  {
3001  LPWSTR filterSearchIndex, pchFirst = NULL;
3002  filterExt = heap_alloc((lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
3003  if (filterExt)
3004  {
3005  strcpyW(filterExt, lpstrFilter);
3006 
3007  if (ext && *ext)
3008  {
3009  /* find ext in filter */
3010  for (pch = filterExt; pch && *pch; pch = pchNext)
3011  {
3012  filterSearchIndex = strchrW(pch, ';');
3013  if (filterSearchIndex)
3014  {
3015  filterSearchIndex[0] = 0;
3016  pchNext = filterSearchIndex + 1;
3017  }
3018  else
3019  {
3020  pchNext = NULL;
3021  }
3022 
3023  while (*pch == '*' || *pch == '.' || *pch == '?')
3024  {
3025  ++pch;
3026  }
3027 
3028  if (!pchFirst)
3029  pchFirst = pch;
3030 
3031  if (lstrcmpiW(pch, &ext[1]) == 0)
3032  {
3033  the_ext = pch;
3034  break;
3035  }
3036  }
3037 
3038  /* use first one if not found */
3039  if (!the_ext && pchFirst && *pchFirst)
3040  {
3041  the_ext = pchFirst;
3042  }
3043  }
3044  }
3045  }
3046 
3047  if (!the_ext)
3048  {
3049  /* use default extension if no extension in filter */
3050  the_ext = fodInfos->defext;
3051  }
3052 
3053  if (the_ext && *the_ext && lstrcmpiW(&ext[1], the_ext) != 0)
3054  {
3055  if (strlenW(lpstrPathAndFile) + 1 + strlenW(the_ext) + 1 <=
3056  fodInfos->ofnInfos->nMaxFile)
3057  {
3058  /* append the dot */
3059  lstrcatW(lpstrPathAndFile, szwDot);
3060  /* append the extension */
3061  lstrcatW(lpstrPathAndFile, the_ext);
3062  /* update ext */
3063  ext = PathFindExtensionW(lpstrPathAndFile);
3064  }
3065  }
3066 
3067  heap_free(filterExt);
3068 
3069  /* In Open dialog: if file does not exist try without extension */
3070  if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
3071  lpstrPathAndFile[PathLength] = 0;
3072 
3073  /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
3074  if (*ext)
3075  ext++;
3076  if (!lstrcmpiW(fodInfos->defext, ext))
3077  fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
3078  else
3079  fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
3080  }
3081 
3082  /* update dialog data */
3083  SetWindowTextW(fodInfos->DlgInfos.hwndFileName, PathFindFileNameW(lpstrPathAndFile));
3084 #else /* __REACTOS__ */
3085  if (! *ext && fodInfos->defext)
3086  {
3087  /* if no extension is specified with file name, then */
3088  /* attach the extension from file filter or default one */
3089 
3090  WCHAR *filterExt = NULL;
3091  LPWSTR lpstrFilter = NULL;
3092  static const WCHAR szwDot[] = {'.',0};
3093  int PathLength = lstrlenW(lpstrPathAndFile);
3094 
3095  /*Get the file extension from file type filter*/
3096  lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3097  fodInfos->ofnInfos->nFilterIndex-1);
3098 
3099  if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */
3100  {
3101  WCHAR* filterSearchIndex;
3102  filterExt = heap_alloc((lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR));
3103  lstrcpyW(filterExt, lpstrFilter);
3104 
3105  /* if a semicolon-separated list of file extensions was given, do not include the
3106  semicolon or anything after it in the extension.
3107  example: if filterExt was "*.abc;*.def", it will become "*.abc" */
3108  filterSearchIndex = wcschr(filterExt, ';');
3109  if (filterSearchIndex)
3110  {
3111  filterSearchIndex[0] = '\0';
3112  }
3113 
3114  /* find the file extension by searching for the first dot in filterExt */
3115  /* strip the * or anything else from the extension, "*.abc" becomes "abc" */
3116  /* if the extension is invalid or contains a glob, ignore it */
3117  filterSearchIndex = wcschr(filterExt, '.');
3118  if (filterSearchIndex++ && !wcschr(filterSearchIndex, '*') && !wcschr(filterSearchIndex, '?'))
3119  {
3120  lstrcpyW(filterExt, filterSearchIndex);
3121  }
3122  else
3123  {
3124  heap_free(filterExt);
3125  filterExt = NULL;
3126  }
3127  }
3128 
3129  if (!filterExt)
3130  {
3131  /* use the default file extension */
3132  filterExt = heap_alloc((lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR));
3133  lstrcpyW(filterExt, fodInfos->defext);
3134  }
3135 
3136  if (*filterExt) /* ignore filterExt="" */
3137  {
3138  /* Attach the dot*/
3139  lstrcatW(lpstrPathAndFile, szwDot);
3140  /* Attach the extension */
3141  lstrcatW(lpstrPathAndFile, filterExt);
3142  }
3143 
3144  heap_free(filterExt);
3145 
3146  /* In Open dialog: if file does not exist try without extension */
3147  if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile))
3148  lpstrPathAndFile[PathLength] = '\0';
3149 
3150  /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */
3151  if (*ext)
3152  ext++;
3153  if (!lstrcmpiW(fodInfos->defext, ext))
3154  fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT;
3155  else
3156  fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT;
3157  }
3158 #endif /* __REACTOS__ */
3159 
3160  /* In Save dialog: check if the file already exists */
3161  if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG
3162  && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT
3163  && PathFileExistsW(lpstrPathAndFile))
3164  {
3165  WCHAR lpstrOverwrite[100];
3166  int answer;
3167 
3168  LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100);
3169  answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title,
3171  if (answer == IDNO || answer == IDCANCEL)
3172  {
3173  ret = FALSE;
3174  goto ret;
3175  }
3176  }
3177 
3178  /* In Open dialog: check if it should be created if it doesn't exist */
3179  if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG)
3180  && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT
3181  && !PathFileExistsW(lpstrPathAndFile))
3182  {
3183  WCHAR lpstrCreate[100];
3184  int answer;
3185 
3186  LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100);
3187  answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title,
3189  if (answer == IDNO || answer == IDCANCEL)
3190  {
3191  ret = FALSE;
3192  goto ret;
3193  }
3194  }
3195 
3196  /* Check that the size of the file does not exceed buffer size.
3197  (Allow for extra \0 if OFN_MULTISELECT is set.) */
3198  if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile -
3199  ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0))
3200  {
3201 
3202  /* fill destination buffer */
3203  if (fodInfos->ofnInfos->lpstrFile)
3204  {
3205  if(fodInfos->unicode)
3206  {
3207  LPOPENFILENAMEW ofn = fodInfos->ofnInfos;
3208 
3209  lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile);
3211  ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0';
3212  }
3213  else
3214  {
3216 
3217  WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
3220  ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0';
3221  }
3222  }
3223 
3224  if(fodInfos->unicode)
3225  {
3226  LPWSTR lpszTemp;
3227 
3228  /* set filename offset */
3229  lpszTemp = PathFindFileNameW(lpstrPathAndFile);
3230  fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile);
3231 
3232  /* set extension offset */
3233  lpszTemp = PathFindExtensionW(lpstrPathAndFile);
3234  fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0;
3235  }
3236  else
3237  {
3238  LPSTR lpszTemp;
3239  CHAR tempFileA[MAX_PATH];
3240 
3241  /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */
3242  WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1,
3243  tempFileA, sizeof(tempFileA), NULL, NULL);
3244 
3245  /* set filename offset */
3246  lpszTemp = PathFindFileNameA(tempFileA);
3247  fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA);
3248 
3249  /* set extension offset */
3250  lpszTemp = PathFindExtensionA(tempFileA);
3251  fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0;
3252  }
3253 
3254  /* copy currently selected filter to lpstrCustomFilter */
3255  if (fodInfos->ofnInfos->lpstrCustomFilter)
3256  {
3258  int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
3259  NULL, 0, NULL, NULL);
3261  {
3263  s += strlen(ofn->lpstrCustomFilter)+1;
3264  WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1,
3265  s, len, NULL, NULL);
3266  }
3267  }
3268 
3269 
3270  if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
3271  goto ret;
3272 
3273  FILEDLG95_MRU_save_filename(lpstrPathAndFile);
3274 #ifdef __REACTOS__
3275  FILEDLG95_MRU_save_ext(lpstrPathAndFile);
3276 #endif
3277 
3278  TRACE("close\n");
3280  ret = EndDialog(hwnd, TRUE);
3281  }
3282  else
3283  {
3284  WORD size;
3285 
3286  size = lstrlenW(lpstrPathAndFile) + 1;
3287  if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)
3288  size += 1;
3289  /* return needed size in first two bytes of lpstrFile */
3290  if(fodInfos->ofnInfos->lpstrFile)
3291  *(WORD *)fodInfos->ofnInfos->lpstrFile = size;
3293  ret = EndDialog(hwnd, FALSE);
3295  }
3296  }
3297  break;
3298  }
3299 
3300 ret:
3301  if(lpsf) IShellFolder_Release(lpsf);
3302  return ret;
3303 }
3304 
3305 /***********************************************************************
3306  * FILEDLG95_SHELL_Init
3307  *
3308  * Initialisation of the shell objects
3309  */
3311 {
3313 
3314  TRACE("%p\n", hwnd);
3315 
3316  /*
3317  * Initialisation of the FileOpenDialogInfos structure
3318  */
3319 
3320  /* Shell */
3321 
3322  /*ShellInfos */
3323  fodInfos->ShellInfos.hwndOwner = hwnd;
3324 
3325  /* Disable multi-select if flag not set */
3326  if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT))
3327  {
3328  fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL;
3329  }
3330  fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT;
3331  fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST;
3332 
3333  /* Construct the IShellBrowser interface */
3334  fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd);
3335 
3336  return NOERROR;
3337 }
3338 
3339 /***********************************************************************
3340  * FILEDLG95_SHELL_ExecuteCommand
3341  *
3342  * Change the folder option and refresh the view
3343  * If the function succeeds, the return value is nonzero.
3344  */
3346 {
3348  IContextMenu * pcm;
3349 
3350  TRACE("(%p,%p)\n", hwnd, lpVerb);
3351 
3352  if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView,
3353  SVGIO_BACKGROUND,
3354  &IID_IContextMenu,
3355  (LPVOID*)&pcm)))
3356  {
3357  CMINVOKECOMMANDINFO ci;
3358  ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO));
3359  ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
3360  ci.lpVerb = lpVerb;
3361  ci.hwnd = hwnd;
3362 
3363  IContextMenu_InvokeCommand(pcm, &ci);
3364  IContextMenu_Release(pcm);
3365  }
3366 
3367  return FALSE;
3368 }
3369 
3370 /***********************************************************************
3371  * FILEDLG95_SHELL_UpFolder
3372  *
3373  * Browse to the specified object
3374  * If the function succeeds, the return value is nonzero.
3375  */
3377 {
3379 
3380  TRACE("\n");
3381 
3382  if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3383  NULL,
3384  SBSP_PARENT)))
3385  {
3386  if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3388  return TRUE;
3389  }
3390  return FALSE;
3391 }
3392 /***********************************************************************
3393  * FILEDLG95_SHELL_Clean
3394  *
3395  * Cleans the memory used by shell objects
3396  */
3398 {
3400 
3401  TRACE("\n");
3402 
3403  ILFree(fodInfos->ShellInfos.pidlAbsCurrent);
3404 
3405  /* clean Shell interfaces */
3406  if (fodInfos->Shell.FOIShellView)
3407  {
3408  IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView);
3409  IShellView_Release(fodInfos->Shell.FOIShellView);
3410  }
3411  if (fodInfos->Shell.FOIShellFolder)
3412  IShellFolder_Release(fodInfos->Shell.FOIShellFolder);
3413  IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser);
3414  if (fodInfos->Shell.FOIDataObject)
3415  IDataObject_Release(fodInfos->Shell.FOIDataObject);
3416 }
3417 
3418 /***********************************************************************
3419  * FILEDLG95_FILETYPE_Init
3420  *
3421  * Initialisation of the file type combo box
3422  */
3424 {
3426  int nFilters = 0; /* number of filters */
3427  int nFilterIndexCB;
3428 
3429  TRACE("%p\n", hwnd);
3430 
3431  if(fodInfos->customfilter)
3432  {
3433  /* customfilter has one entry... title\0ext\0
3434  * Set first entry of combo box item with customfilter
3435  */
3436  LPWSTR lpstrExt;
3437  LPCWSTR lpstrPos = fodInfos->customfilter;
3438 
3439  /* Get the title */
3440  lpstrPos += lstrlenW(fodInfos->customfilter) + 1;
3441 
3442  /* Copy the extensions */
3443  if (! *lpstrPos) return E_FAIL; /* malformed filter */
3444  if (!(lpstrExt = heap_alloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
3445  lstrcpyW(lpstrExt,lpstrPos);
3446 
3447  /* Add the item at the end of the combo */
3448  SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_ADDSTRING, 0, (LPARAM)fodInfos->customfilter);
3449  SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETITEMDATA, nFilters, (LPARAM)lpstrExt);
3450 
3451  nFilters++;
3452  }
3453  if(fodInfos->filter)
3454  {
3455  LPCWSTR lpstrPos = fodInfos->filter;
3456 
3457  for(;;)
3458  {
3459  /* filter is a list... title\0ext\0......\0\0
3460  * Set the combo item text to the title and the item data
3461  * to the ext
3462  */
3463  LPCWSTR lpstrDisplay;
3464  LPWSTR lpstrExt;
3465 
3466  /* Get the title */
3467  if(! *lpstrPos) break; /* end */
3468  lpstrDisplay = lpstrPos;
3469  lpstrPos += lstrlenW(lpstrPos) + 1;
3470 
3471  SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_ADDSTRING, 0, (LPARAM)lpstrDisplay);
3472 
3473  nFilters++;
3474 
3475  /* Copy the extensions */
3476  if (!(lpstrExt = heap_alloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL;
3477  lstrcpyW(lpstrExt,lpstrPos);
3478  lpstrPos += lstrlenW(lpstrPos) + 1;
3479 
3480  /* Add the item at the end of the combo */
3481  SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETITEMDATA, nFilters - 1, (LPARAM)lpstrExt);
3482 
3483  /* malformed filters are added anyway... */
3484  if (!*lpstrExt) break;
3485  }
3486  }
3487 
3488  /*
3489  * Set the current filter to the one specified
3490  * in the initialisation structure
3491  */
3492  if (fodInfos->filter || fodInfos->customfilter)
3493  {
3494  LPWSTR lpstrFilter;
3495 
3496  /* Check to make sure our index isn't out of bounds. */
3497  if ( fodInfos->ofnInfos->nFilterIndex >
3498  nFilters - (fodInfos->customfilter == NULL ? 0 : 1) )
3499  fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0);
3500 
3501  /* set default filter index */
3502  if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL)
3503  fodInfos->ofnInfos->nFilterIndex = 1;
3504 
3505  /* calculate index of Combo Box item */
3506  nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex;
3507  if (fodInfos->customfilter == NULL)
3508  nFilterIndexCB--;
3509 
3510  /* Set the current index selection. */
3511  SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETCURSEL, nFilterIndexCB, 0);
3512 
3513  /* Get the corresponding text string from the combo box. */
3514  lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3515  nFilterIndexCB);
3516 
3517  if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */
3518  lpstrFilter = NULL;
3519 
3520  if(lpstrFilter)
3521  {
3522  DWORD len;
3523  CharLowerW(lpstrFilter); /* lowercase */
3524  len = lstrlenW(lpstrFilter)+1;
3525  fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc( len * sizeof(WCHAR) );
3526  lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3527  }
3528  } else
3529  fodInfos->ofnInfos->nFilterIndex = 0;
3530  return S_OK;
3531 }
3532 
3533 /***********************************************************************
3534  * FILEDLG95_FILETYPE_OnCommand
3535  *
3536  * WM_COMMAND of the file type combo box
3537  * If the function succeeds, the return value is nonzero.
3538  */
3540 {
3542 
3543  switch(wNotifyCode)
3544  {
3545  case CBN_SELENDOK:
3546  {
3547  LPWSTR lpstrFilter;
3548 
3549  /* Get the current item of the filetype combo box */
3550  int iItem = SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_GETCURSEL, 0, 0);
3551 
3552  /* set the current filter index */
3553  fodInfos->ofnInfos->nFilterIndex = iItem +
3554  (fodInfos->customfilter == NULL ? 1 : 0);
3555 
3556  /* Set the current filter with the current selection */
3557  heap_free(fodInfos->ShellInfos.lpstrCurrentFilter);
3558 
3559  lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,
3560  iItem);
3561  if((INT_PTR)lpstrFilter != CB_ERR)
3562  {
3563  DWORD len;
3564  CharLowerW(lpstrFilter); /* lowercase */
3565  len = lstrlenW(lpstrFilter)+1;
3566  fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc( len * sizeof(WCHAR) );
3567  lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter);
3568  if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3570  }
3571 
3572  /* Refresh the actual view to display the included items*/
3573  if (fodInfos->Shell.FOIShellView)
3574  IShellView_Refresh(fodInfos->Shell.FOIShellView);
3575  }
3576  }
3577  return FALSE;
3578 }
3579 /***********************************************************************
3580  * FILEDLG95_FILETYPE_SearchExt
3581  *
3582  * searches for an extension in the filetype box
3583  */
3585 {
3586  int i, iCount;
3587 
3588  iCount = SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
3589 
3590  TRACE("%s\n", debugstr_w(lpstrExt));
3591 
3592  if(iCount != CB_ERR)
3593  {
3594  for(i=0;i<iCount;i++)
3595  {
3596  if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i)))
3597  return i;
3598  }
3599  }
3600  return -1;
3601 }
3602 
3603 /***********************************************************************
3604  * FILEDLG95_FILETYPE_Clean
3605  *
3606  * Clean the memory used by the filetype combo box
3607  */
3609 {
3611  int iPos;
3612  int iCount;
3613 
3614  iCount = SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_GETCOUNT, 0, 0);
3615 
3616  TRACE("\n");
3617 
3618  /* Delete each string of the combo and their associated data */
3619  if(iCount != CB_ERR)
3620  {
3621  for(iPos = iCount-1;iPos>=0;iPos--)
3622  {
3623  heap_free((void *)CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos));
3624  SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_DELETESTRING, iPos, 0);
3625  }
3626  }
3627  /* Current filter */
3628  heap_free(fodInfos->ShellInfos.lpstrCurrentFilter);
3629 }
3630 
3631 /***********************************************************************
3632  * FILEDLG95_LOOKIN_Init
3633  *
3634  * Initialisation of the look in combo box
3635  */
3636 
3637 /* Small helper function, to determine if the unixfs shell extension is rooted
3638  * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c.
3639  */
3641  HKEY hKey;
3642  static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\',
3643  'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3644  'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3645  'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
3646  'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8',
3647  '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-',
3648  '7','1','8','8','9','C','2','2','5','4','D','9','}',0 };
3649 
3650  if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
3651  return FALSE;
3652 
3653  RegCloseKey(hKey);
3654  return TRUE;
3655 }
3656 
3657 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo)
3658 {
3659  IShellFolder *psfRoot, *psfDrives;
3660  IEnumIDList *lpeRoot, *lpeDrives;
3661  LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp;
3662  HDC hdc;
3663  TEXTMETRICW tm;
3664  LookInInfos *liInfos = heap_alloc_zero(sizeof(*liInfos));
3665 
3666  TRACE("%p\n", hwndCombo);
3667 
3668  liInfos->iMaxIndentation = 0;
3669 
3670  SetPropA(hwndCombo, LookInInfosStr, liInfos);
3671 
3672  hdc = GetDC( hwndCombo );
3673  SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 ));
3674  GetTextMetricsW( hdc, &tm );
3675  ReleaseDC( hwndCombo, hdc );
3676 
3677  /* set item height for both text field and listbox */
3678  SendMessageW(hwndCombo, CB_SETITEMHEIGHT, -1, max(tm.tmHeight, GetSystemMetrics(SM_CYSMICON)));
3679  SendMessageW(hwndCombo, CB_SETITEMHEIGHT, 0, max(tm.tmHeight, GetSystemMetrics(SM_CYSMICON)));
3680 
3681  /* Turn on the extended UI for the combo box like Windows does */
3682  SendMessageW(hwndCombo, CB_SETEXTENDEDUI, TRUE, 0);
3683 
3684  /* Initialise data of Desktop folder */
3686  FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3687  ILFree(pidlTmp);
3688 
3690 
3691  SHGetDesktopFolder(&psfRoot);
3692 
3693  if (psfRoot)
3694  {
3695  /* enumerate the contents of the desktop */
3696  if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot)))
3697  {
3698  while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL))
3699  {
3700  FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND);
3701 
3702  /* If the unixfs extension is rooted, we don't expand the drives by default */
3704  {
3705  /* special handling for CSIDL_DRIVES */
3706  if (ILIsEqual(pidlTmp, pidlDrives))
3707  {
3708  if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives)))
3709  {
3710  /* enumerate the drives */
3711  if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives)))
3712  {
3713  while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL))
3714  {
3715  pidlAbsTmp = ILCombine(pidlTmp, pidlTmp1);
3716  FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND);
3717  ILFree(pidlAbsTmp);
3718  ILFree(pidlTmp1);
3719  }
3720  IEnumIDList_Release(lpeDrives);
3721  }
3722  IShellFolder_Release(psfDrives);
3723  }
3724  }
3725  }
3726 
3727  ILFree(pidlTmp);
3728  }
3729  IEnumIDList_Release(lpeRoot);
3730  }
3731  IShellFolder_Release(psfRoot);
3732  }
3733 
3734  ILFree(pidlDrives);
3735 }
3736 
3737 /***********************************************************************
3738  * FILEDLG95_LOOKIN_DrawItem
3739  *
3740  * WM_DRAWITEM message handler
3741  */
3743 {
3745  COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT);
3747  RECT rectText;
3748  RECT rectIcon;
3749  SHFILEINFOW sfi;
3750  HIMAGELIST ilItemImage;
3751  int iIndentation;
3752  TEXTMETRICW tm;
3753  LPSFOLDER tmpFolder;
3755  UINT icon_width, icon_height;
3756 
3757  TRACE("\n");
3758 
3759  if(pDIStruct->itemID == -1)
3760  return 0;
3761 
3762  if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem,
3763  pDIStruct->itemID)))
3764  return 0;
3765 
3766 
3767  icon_width = GetSystemMetrics(SM_CXICON);
3768  icon_height = GetSystemMetrics(SM_CYICON);
3769  if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height)
3770  {
3771  icon_width = GetSystemMetrics(SM_CXSMICON);
3772  icon_height = GetSystemMetrics(SM_CYSMICON);
3773  shgfi_flags |= SHGFI_SMALLICON;
3774  }
3775 
3776  ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem,
3777  0, &sfi, sizeof (sfi), shgfi_flags );
3778 
3779  /* Is this item selected ? */
3780  if(pDIStruct->itemState & ODS_SELECTED)
3781  {
3782  SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText)));
3783  SetBkColor(pDIStruct->hDC,crHighLight);
3784  FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
3785  }
3786  else
3787  {
3788  SetTextColor(pDIStruct->hDC,crText);
3789  SetBkColor(pDIStruct->hDC,crWin);
3790  FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW));
3791  }
3792 
3793  /* Do not indent item if drawing in the edit of the combo */
3794  if(pDIStruct->itemState & ODS_COMBOBOXEDIT)
3795  iIndentation = 0;
3796  else
3797  iIndentation = tmpFolder->m_iIndent;
3798 
3799  /* Draw text and icon */
3800 
3801  /* Initialise the icon display area */
3802  rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation;
3803  rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2;
3804  rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET;
3805  rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2;
3806 
3807  /* Initialise the text display area */
3808  GetTextMetricsW(pDIStruct->hDC, &tm);
3809  rectText.left = rectIcon.right;
3810  rectText.top =
3811  (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2;
3812  rectText.right = pDIStruct->rcItem.right;
3813  rectText.bottom =
3814  (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2;
3815 
3816  /* Draw the icon from the image list */
3817  ImageList_Draw(ilItemImage,
3818  sfi.iIcon,
3819  pDIStruct->hDC,
3820  rectIcon.left,
3821  rectIcon.top,
3822  ILD_TRANSPARENT );
3823 
3824  /* Draw the associated text */
3825  TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName));
3826  return NOERROR;
3827 }
3828 
3829 /***********************************************************************
3830  * FILEDLG95_LOOKIN_OnCommand
3831  *
3832  * LookIn combo box WM_COMMAND message handler
3833  * If the function succeeds, the return value is nonzero.
3834  */
3836 {
3838 
3839  TRACE("%p\n", fodInfos);
3840 
3841  switch(wNotifyCode)
3842  {
3843  case CBN_SELENDOK:
3844  {
3845  LPSFOLDER tmpFolder;
3846  int iItem;
3847 
3848  iItem = SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_GETCURSEL, 0, 0);
3849 
3850  if( iItem == CB_ERR) return FALSE;
3851 
3852  if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,
3853  iItem)))
3854  return FALSE;
3855 
3856 
3857  if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,
3858  tmpFolder->pidlItem,
3859  SBSP_ABSOLUTE)))
3860  {
3861  if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
3863  return TRUE;
3864  }
3865  break;
3866  }
3867 
3868  }
3869  return FALSE;
3870 }
3871 
3872 /***********************************************************************
3873  * FILEDLG95_LOOKIN_AddItem
3874  *
3875  * Adds an absolute pidl item to the lookin combo box
3876  * returns the index of the inserted item
3877  */
3878 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId)
3879 {
3880  LPITEMIDLIST pidlNext;
3881  SHFILEINFOW sfi;
3882  SFOLDER *tmpFolder;
3883  LookInInfos *liInfos;
3884 
3885  TRACE("%p, %p, %d\n", hwnd, pidl, iInsertId);
3886 
3887  if(!pidl)
3888  return -1;
3889 
3890  if(!(liInfos = GetPropA(hwnd,LookInInfosStr)))
3891  return -1;
3892 
3893  tmpFolder = heap_alloc_zero(sizeof(*tmpFolder));
3894  tmpFolder->m_iIndent = 0;
3895 
3896  /* Calculate the indentation of the item in the lookin*/
3897  pidlNext = pidl;
3898  while ((pidlNext = ILGetNext(pidlNext)))
3899  {
3900  tmpFolder->m_iIndent++;
3901  }
3902 
3903  tmpFolder->pidlItem = ILClone(pidl);
3904 
3905  if(tmpFolder->m_iIndent > liInfos->iMaxIndentation)
3906  liInfos->iMaxIndentation = tmpFolder->m_iIndent;
3907 
3908  sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
3909  SHGetFileInfoW((LPCWSTR)pidl,
3910  0,
3911  &sfi,
3912  sizeof(sfi),
3914 
3915  TRACE("-- Add %s attr=0x%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes);
3916 
3917  if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM))
3918  {
3919  int iItemID;
3920 
3921  TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent);
3922 
3923  /* Add the item at the end of the list */
3924  if(iInsertId < 0)
3925  {
3926  iItemID = SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)sfi.szDisplayName);
3927  }
3928  /* Insert the item at the iInsertId position*/
3929  else
3930  {
3931  iItemID = SendMessageW(hwnd, CB_INSERTSTRING, iInsertId, (LPARAM)sfi.szDisplayName);
3932  }
3933 
3934  SendMessageW(hwnd, CB_SETITEMDATA, iItemID, (LPARAM)tmpFolder);
3935  return iItemID;
3936  }
3937 
3938  ILFree( tmpFolder->pidlItem );
3939  heap_free( tmpFolder );
3940  return -1;
3941 
3942 }
3943 
3944 /***********************************************************************
3945  * FILEDLG95_LOOKIN_InsertItemAfterParent
3946  *
3947  * Insert an item below its parent
3948  */
3950 {
3951 
3952  LPITEMIDLIST pidlParent = GetParentPidl(pidl);
3953  int iParentPos;
3954 
3955  TRACE("\n");
3956 
3957  if (pidl == pidlParent)
3958  return -1;
3959 
3960  iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL);
3961 
3962  if(iParentPos < 0)
3963  {
3964  iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent);
3965  }
3966 
3967  ILFree(pidlParent);
3968 
3969  return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1);
3970 }
3971 
3972 /***********************************************************************
3973  * FILEDLG95_LOOKIN_SelectItem
3974  *
3975  * Adds an absolute pidl item to the lookin combo box
3976  * returns the index of the inserted item
3977  */
3979 {
3980  int iItemPos;
3981  LookInInfos *liInfos;
3982 
3983  TRACE("%p, %p\n", hwnd, pidl);
3984 
3986 
3987  liInfos = GetPropA(hwnd,LookInInfosStr);
3988 
3989  if(iItemPos < 0)
3990  {
3993  }
3994 
3995  else
3996  {
3997  SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
3998  while(liInfos->iMaxIndentation > tmpFolder->m_iIndent)
3999  {
4000  int iRemovedItem;
4001 
4002  if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd)))
4003  break;
4004  if(iRemovedItem < iItemPos)
4005  iItemPos--;
4006  }
4007  }
4008 
4009  SendMessageW(hwnd, CB_SETCURSEL, iItemPos, 0);
4010  liInfos->uSelectedItem = iItemPos;
4011 
4012  return 0;
4013 
4014 }
4015 
4016 /***********************************************************************
4017  * FILEDLG95_LOOKIN_RemoveMostExpandedItem
4018  *
4019  * Remove the item with an expansion level over iExpansionLevel
4020  */
4022 {
4023  int iItemPos;
4025 
4026  TRACE("\n");
4027 
4028  if(liInfos->iMaxIndentation <= 2)
4029  return -1;
4030 
4031  if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0)
4032  {
4033  SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos);
4034  ILFree(tmpFolder->pidlItem);
4035  heap_free(tmpFolder);
4036  SendMessageW(hwnd, CB_DELETESTRING, iItemPos, 0);
4037  liInfos->iMaxIndentation--;
4038 
4039  return iItemPos;
4040  }
4041 
4042  return -1;
4043 }
4044 
4045 /***********************************************************************
4046  * FILEDLG95_LOOKIN_SearchItem
4047  *
4048  * Search for pidl in the lookin combo box
4049  * returns the index of the found item
4050  */
4051 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod)
4052 {
4053  int i = 0;
4054  int iCount;
4055 
4056  iCount = SendMessageW(hwnd, CB_GETCOUNT, 0, 0);
4057 
4058  TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod);
4059 
4060  if (iCount != CB_ERR)
4061  {
4062  for(;i<iCount;i++)
4063  {
4064  LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i);
4065 
4066  if (iSearchMethod == SEARCH_PIDL && ILIsEqual((LPITEMIDLIST)searchArg, tmpFolder->pidlItem))
4067  return i;
4068  if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg)
4069  return i;
4070  }
4071  }
4072 
4073  return -1;
4074 }
4075 
4076 /***********************************************************************
4077  * FILEDLG95_LOOKIN_Clean
4078  *
4079  * Clean the memory used by the lookin combo box
4080  */
4082 {
4084  LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
4085  int iPos, iCount;
4086 
4087  iCount = SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_GETCOUNT, 0, 0);
4088 
4089  TRACE("\n");
4090 
4091  /* Delete each string of the combo and their associated data */
4092  if (iCount != CB_ERR)
4093  {
4094  for(iPos = iCount-1;iPos>=0;iPos--)
4095  {
4096  SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos);
4097  ILFree(tmpFolder->pidlItem);
4098  heap_free(tmpFolder);
4099  SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_DELETESTRING, iPos, 0);
4100  }
4101  }
4102 
4103  /* LookInInfos structure */
4104  heap_free(liInfos);
4105  RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr);
4106 }
4107 
4108 /***********************************************************************
4109  * get_def_format
4110  *
4111  * Fill the FORMATETC used in the shell id list
4112  */
4113 static FORMATETC get_def_format(void)
4114 {
4115  static CLIPFORMAT cfFormat;
4116  FORMATETC formatetc;
4117 
4118  if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA);
4119  formatetc.cfFormat = cfFormat;
4120  formatetc.ptd = 0;
4121  formatetc.dwAspect = DVASPECT_CONTENT;
4122  formatetc.lindex = -1;
4123  formatetc.tymed = TYMED_HGLOBAL;
4124  return formatetc;
4125 }
4126 
4127 /***********************************************************************
4128  * FILEDLG95_FILENAME_FillFromSelection
4129  *
4130  * fills the edit box from the cached DataObject
4131  */
4133 {
4135  LPITEMIDLIST pidl;
4136  LPWSTR lpstrAllFiles, lpstrTmp;
4137  UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength;
4138  STGMEDIUM medium;
4139  LPIDA cida;
4140  FORMATETC formatetc = get_def_format();
4141 
4142  TRACE("\n");
4143 
4144  if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium)))
4145  return;
4146 
4147  cida = GlobalLock(medium.u.hGlobal);
4148  nFileSelected = cida->cidl;
4149 
4150  /* Allocate a buffer */
4151  nAllFilesMaxLength = MAX_PATH + 3;
4152  lpstrAllFiles = heap_alloc_zero(nAllFilesMaxLength * sizeof(WCHAR));
4153  if (!lpstrAllFiles)
4154  goto ret;
4155 
4156  /* Loop through the selection, handle only files (not folders) */
4157  for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++)
4158  {
4159  pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]);
4160  if (pidl)
4161  {
4162  if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl))
4163  {
4164  if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength)
4165  {
4166  nAllFilesMaxLength *= 2;
4167  lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR));
4168  if (!lpstrTmp)
4169  goto ret;
4170  lpstrAllFiles = lpstrTmp;
4171  }
4172  nFiles += 1;
4173  lpstrAllFiles[nAllFilesLength++] = '"';
4174  GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength);
4175  nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength);
4176  nAllFilesLength += nThisFileLength;
4177  lpstrAllFiles[nAllFilesLength++] = '"';
4178  lpstrAllFiles[nAllFilesLength++] = ' ';
4179  }
4180  }
4181  }
4182 
4183  if (nFiles != 0)
4184  {
4185  /* If there's only one file, use the name as-is without quotes */
4186  lpstrTmp = lpstrAllFiles;
4187  if (nFiles == 1)
4188  {
4189  lpstrTmp += 1;
4190  lpstrTmp[nThisFileLength] = 0;
4191  }
4192  SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp);
4193  /* Select the file name like Windows does */
4194  if (filename_is_edit(fodInfos))
4195  SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1);
4196  }
4197 
4198 ret:
4199  heap_free(lpstrAllFiles);
4200  COMCTL32_ReleaseStgMedium(medium);
4201 }
4202 
4203 
4204 /* copied from shell32 to avoid linking to it
4205  * Although shell32 is already linked the behaviour of exported StrRetToStrN
4206  * is dependent on whether emulated OS is unicode or not.
4207  */
4209 {
4210  switch (src->uType)
4211  {
4212  case STRRET_WSTR:
4213  lstrcpynW(dest, src->u.pOleStr, len);
4214  CoTaskMemFree(src->u.pOleStr);
4215  break;
4216 
4217  case STRRET_CSTR:
4218  if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
4219  dest[len-1] = 0;
4220  break;
4221 
4222  case STRRET_OFFSET:
4223  if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len)
4224  dest[len-1] = 0;
4225  break;
4226 
4227  default:
4228  FIXME("unknown type %x!\n", src->uType);
4229  if (len) *dest = '\0';
4230  return E_FAIL;
4231  }
4232  return S_OK;
4233 }
4234 
4235 /***********************************************************************
4236  * FILEDLG95_FILENAME_GetFileNames
4237  *
4238  * Copies the filenames to a delimited string list.
4239  */
4240 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed)
4241 {
4243  UINT nFileCount = 0; /* number of files */
4244  UINT nStrLen = 0; /* length of string in edit control */
4245  LPWSTR lpstrEdit; /* buffer for string from edit control */
4246 
4247  TRACE("\n");
4248 
4249  /* get the filenames from the filename control */
4250  nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName );
4251  lpstrEdit = heap_alloc( (nStrLen+1)*sizeof(WCHAR) );
4252  GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1);
4253 
4254  TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit));
4255 
4256  nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed);
4257  heap_free(lpstrEdit);
4258  return nFileCount;
4259 }
4260 
4261 /*
4262  * DATAOBJECT Helper functions
4263  */
4264 
4265 /***********************************************************************
4266  * COMCTL32_ReleaseStgMedium
4267  *
4268  * like ReleaseStgMedium from ole32
4269  */
4270 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium)
4271 {
4272  if(medium.pUnkForRelease)
4273  {
4274  IUnknown_Release(medium.pUnkForRelease);
4275  }
4276  else
4277  {
4278  GlobalUnlock(medium.u.hGlobal);
4279  GlobalFree(medium.u.hGlobal);
4280  }
4281 }
4282 
4283 /***********************************************************************
4284  * GetPidlFromDataObject
4285  *
4286  * Return pidl(s) by number from the cached DataObject
4287  *
4288  * nPidlIndex=0 gets the fully qualified root path
4289  */
4291 {
4292 
4293  STGMEDIUM medium;
4294  FORMATETC formatetc = get_def_format();
4295  LPITEMIDLIST pidl = NULL;
4296 
4297  TRACE("sv=%p index=%u\n", doSelected, nPidlIndex);
4298 
4299  if (!doSelected)
4300  return NULL;
4301 
4302  /* Get the pidls from IDataObject */
4303  if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
4304  {
4305  LPIDA cida = GlobalLock(medium.u.hGlobal);
4306  if(nPidlIndex <= cida->cidl)
4307  {
4308  pidl = ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]]));
4309  }
4310  COMCTL32_ReleaseStgMedium(medium);
4311  }
4312  return pidl;
4313 }
4314 
4315 /***********************************************************************
4316  * GetNumSelected
4317  *
4318  * Return the number of selected items in the DataObject.
4319  *
4320 */
4321 static UINT GetNumSelected( IDataObject *doSelected )
4322 {
4323  UINT retVal = 0;
4324  STGMEDIUM medium;
4325  FORMATETC formatetc = get_def_format();
4326 
4327  TRACE("sv=%p\n", doSelected);
4328 
4329  if (!doSelected) return 0;
4330 
4331  /* Get the pidls from IDataObject */
4332  if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium)))
4333  {
4334  LPIDA cida = GlobalLock(medium.u.hGlobal);
4335  retVal = cida->cidl;
4336  COMCTL32_ReleaseStgMedium(medium);
4337  return retVal;
4338  }
4339  return 0;
4340 }
4341 
4342 /*
4343  * TOOLS
4344  */
4345 
4346 /***********************************************************************
4347  * GetName
4348  *
4349  * Get the pidl's display name (relative to folder) and
4350  * put it in lpstrFileName.
4351  *
4352  * Return NOERROR on success,
4353  * E_FAIL otherwise
4354  */
4355 
4356 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName)
4357 {
4358  STRRET str;
4359  HRESULT hRes;
4360 
4361  TRACE("sf=%p pidl=%p\n", lpsf, pidl);
4362 
4363  if(!lpsf)
4364  {
4365  SHGetDesktopFolder(&lpsf);
4366  hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName);
4367  IShellFolder_Release(lpsf);
4368  return hRes;
4369  }
4370 
4371  /* Get the display name of the pidl relative to the folder */
4372  if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str)))
4373  {
4374  return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl);
4375  }
4376  return E_FAIL;
4377 }
4378 
4379 /***********************************************************************
4380  * GetShellFolderFromPidl
4381  *
4382  * pidlRel is the item pidl relative
4383  * Return the IShellFolder of the absolute pidl
4384  */
4386 {
4387  IShellFolder *psf = NULL,*psfParent;
4388 
4389  TRACE("%p\n", pidlAbs);
4390 
4391  if(SUCCEEDED(SHGetDesktopFolder(&psfParent)))
4392  {
4393  psf = psfParent;
4394  if(pidlAbs && pidlAbs->mkid.cb)
4395  {
4396  if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf)))
4397  {
4398  IShellFolder_Release(psfParent);
4399  return psf;
4400  }
4401  }
4402  /* return the desktop */
4403  return psfParent;
4404  }
4405  return NULL;
4406 }
4407 
4408 /***********************************************************************
4409  * GetParentPidl
4410  *
4411  * Return the LPITEMIDLIST to the parent of the pidl in the list
4412  */
4414 {
4415  LPITEMIDLIST pidlParent;
4416 
4417  TRACE("%p\n", pidl);
4418 
4419  pidlParent = ILClone(pidl);
4420  ILRemoveLastID(pidlParent);
4421 
4422  return pidlParent;
4423 }
4424 
4425 /***********************************************************************
4426  * GetPidlFromName
4427  *
4428  * returns the pidl of the file name relative to folder
4429  * NULL if an error occurred
4430  */
4431 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName)
4432 {
4433  LPITEMIDLIST pidl = NULL;
4434  ULONG ulEaten;
4435 
4436  TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName));
4437 
4438  if(!lpcstrFileName) return NULL;
4439  if(!*lpcstrFileName) return NULL;
4440 
4441  if(!lpsf)
4442  {
4443  if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) {
4444  IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
4445  IShellFolder_Release(lpsf);
4446  }
4447  }
4448  else
4449  {
4450  IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL);
4451  }
4452  return pidl;
4453 }
4454 
4455 /*
4456 */
4457 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
4458 {
4459  ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
4460  HRESULT ret;
4461 
4462  TRACE("%p, %p\n", psf, pidl);
4463 
4464  ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr );
4465 
4466  TRACE("-- 0x%08x 0x%08x\n", uAttr, ret);
4467  /* see documentation shell 4.1*/
4468  return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
4469 }
4470 
4471 /***********************************************************************
4472  * BrowseSelectedFolder
4473  */
4475 {
4477  BOOL bBrowseSelFolder = FALSE;
4478 
4479  TRACE("\n");
4480 
4481  if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1)
4482  {
4483  LPITEMIDLIST pidlSelection;
4484 
4485  /* get the file selected */
4486  pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1);
4487  if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection))
4488  {
4489  if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser,
4490  pidlSelection, SBSP_RELATIVE ) ) )
4491  {
4492  WCHAR buf[64];
4494  MessageBoxW( hwnd, buf, fodInfos->title, MB_OK | MB_ICONEXCLAMATION );
4495  }
4496  bBrowseSelFolder = TRUE;
4497  if(fodInfos->ofnInfos->Flags & OFN_EXPLORER)
4499  }
4500  ILFree( pidlSelection );
4501  }
4502 
4503  return bBrowseSelFolder;
4504 }
4505 
4507 {
4508  return (size == OPENFILENAME_SIZE_VERSION_400W) ||
4509  (size == sizeof( OPENFILENAMEW ));
4510 }
4511 
4513 {
4515  !(flags & OFN_EXPLORER));
4516 }
4517 
4518 /* ------------------ APIs ---------------------- */
4519 
4520 /***********************************************************************
4521  * GetOpenFileNameA (COMDLG32.@)
4522  *
4523  * Creates a dialog box for the user to select a file to open.
4524  *
4525  * RETURNS
4526  * TRUE on success: user enters a valid file
4527  * FALSE on cancel, error, close or filename-does-not-fit-in-buffer.
4528  *
4529</