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