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