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