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, file=%s\n", debugstr_w(lpstrPathAndFile), debugstr_w(lpstrFile)); 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 case IDC_TOOLBARPLACES: 1267 DeferWindowPos( hdwp, ctrl, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top + chgy, 1268 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); 1269 break; 1270 } 1271 } 1272 } 1273 if(fodInfos->DlgInfos.hwndCustomDlg && 1274 (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))) 1275 { 1276 for( ctrl = GetWindow( fodInfos->DlgInfos.hwndCustomDlg, GW_CHILD); 1277 ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT)) 1278 { 1279 GetWindowRect( ctrl, &rc); 1280 MapWindowPoints( NULL, hwnd, (LPPOINT) &rc, 2); 1281 if( rc.top > rcview.bottom) 1282 { 1283 /* if it was below the shell view 1284 * move to bottom */ 1285 DeferWindowPos( hdwp, ctrl, NULL, rc.left, rc.top + chgy, 1286 rc.right - rc.left, rc.bottom - rc.top, 1287 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); 1288 } 1289 else if( rc.left > rcview.right) 1290 { 1291 /* if it was to the right of the shell view 1292 * move to right */ 1293 DeferWindowPos( hdwp, ctrl, NULL, rc.left + chgx, rc.top, 1294 rc.right - rc.left, rc.bottom - rc.top, 1295 SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); 1296 } 1297 } 1298 /* size the custom dialog at the end: some applications do some 1299 * control re-arranging at this point */ 1300 GetClientRect(hwnd, &rc); 1301 DeferWindowPos( hdwp,fodInfos->DlgInfos.hwndCustomDlg, NULL, 1302 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); 1303 } 1304 EndDeferWindowPos( hdwp); 1305 /* should not be needed */ 1306 RedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE ); 1307 return TRUE; 1308 } 1309 1310 /*********************************************************************** 1311 * FileOpenDlgProc95 1312 * 1313 * File open dialog procedure 1314 */ 1315 INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1316 { 1317 #if 0 1318 TRACE("%p 0x%04x\n", hwnd, uMsg); 1319 #endif 1320 1321 switch(uMsg) 1322 { 1323 case WM_INITDIALOG: 1324 { 1325 FileOpenDlgInfos * fodInfos = (FileOpenDlgInfos *)lParam; 1326 RECT rc, rcstc; 1327 int gripx = GetSystemMetrics( SM_CYHSCROLL); 1328 int gripy = GetSystemMetrics( SM_CYVSCROLL); 1329 1330 /* Some shell namespace extensions depend on COM being initialized. */ 1331 if (SUCCEEDED(OleInitialize(NULL))) 1332 fodInfos->ole_initialized = TRUE; 1333 1334 SetPropW(hwnd, filedlg_info_propnameW, fodInfos); 1335 1336 FILEDLG95_InitControls(hwnd); 1337 1338 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING) 1339 { 1340 DWORD style = GetWindowLongW(hwnd, GWL_STYLE); 1341 DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE); 1342 RECT client, client_adjusted; 1343 1344 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING) 1345 { 1346 style |= WS_SIZEBOX; 1347 ex_style |= WS_EX_WINDOWEDGE; 1348 } 1349 else 1350 style &= ~WS_SIZEBOX; 1351 SetWindowLongW(hwnd, GWL_STYLE, style); 1352 SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style); 1353 1354 GetClientRect( hwnd, &client ); 1355 GetClientRect( hwnd, &client_adjusted ); 1356 AdjustWindowRectEx( &client_adjusted, style, FALSE, ex_style ); 1357 1358 GetWindowRect( hwnd, &rc ); 1359 rc.right += client_adjusted.right - client.right; 1360 rc.bottom += client_adjusted.bottom - client.bottom; 1361 SetWindowPos(hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_FRAMECHANGED | SWP_NOACTIVATE | 1362 SWP_NOZORDER | SWP_NOMOVE); 1363 1364 GetWindowRect( hwnd, &rc ); 1365 fodInfos->DlgInfos.hwndGrip = 1366 CreateWindowExA( 0, "SCROLLBAR", NULL, 1367 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | 1368 SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, 1369 rc.right - gripx, rc.bottom - gripy, 1370 gripx, gripy, hwnd, (HMENU) -1, COMDLG32_hInstance, NULL); 1371 } 1372 1373 fodInfos->DlgInfos.hwndCustomDlg = 1374 CreateTemplateDialog((FileOpenDlgInfos *)lParam, hwnd); 1375 1376 FILEDLG95_ResizeControls(hwnd, wParam, lParam); 1377 FILEDLG95_FillControls(hwnd, wParam, lParam); 1378 1379 if( fodInfos->DlgInfos.hwndCustomDlg) 1380 ShowWindow( fodInfos->DlgInfos.hwndCustomDlg, SW_SHOW); 1381 1382 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) { 1383 SendCustomDlgNotificationMessage(hwnd,CDN_INITDONE); 1384 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE); 1385 } 1386 1387 /* if the app has changed the position of the invisible listbox, 1388 * change that of the listview (browser) as well */ 1389 GetWindowRect( fodInfos->ShellInfos.hwndView, &rc); 1390 GetWindowRect( GetDlgItem( hwnd, IDC_SHELLSTATIC ), &rcstc); 1391 if( !EqualRect( &rc, &rcstc)) 1392 { 1393 MapWindowPoints( NULL, hwnd, (LPPOINT) &rcstc, 2); 1394 SetWindowPos( fodInfos->ShellInfos.hwndView, NULL, 1395 rcstc.left, rcstc.top, rcstc.right - rcstc.left, rcstc.bottom - rcstc.top, 1396 SWP_NOACTIVATE | SWP_NOZORDER); 1397 } 1398 1399 if (fodInfos->ofnInfos->Flags & OFN_ENABLESIZING) 1400 { 1401 GetWindowRect( hwnd, &rc); 1402 fodInfos->sizedlg.cx = rc.right - rc.left; 1403 fodInfos->sizedlg.cy = rc.bottom - rc.top; 1404 fodInfos->initial_size.x = fodInfos->sizedlg.cx; 1405 fodInfos->initial_size.y = fodInfos->sizedlg.cy; 1406 GetClientRect( hwnd, &rc); 1407 SetWindowPos( fodInfos->DlgInfos.hwndGrip, NULL, 1408 rc.right - gripx, rc.bottom - gripy, 1409 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); 1410 /* resize the dialog to the previous invocation */ 1411 if( MemDialogSize.cx && MemDialogSize.cy) 1412 SetWindowPos( hwnd, NULL, 1413 0, 0, MemDialogSize.cx, MemDialogSize.cy, 1414 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); 1415 } 1416 1417 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) 1418 SendCustomDlgNotificationMessage(hwnd,CDN_SELCHANGE); 1419 1420 return 0; 1421 } 1422 case WM_SIZE: 1423 return FILEDLG95_OnWMSize(hwnd, wParam); 1424 case WM_GETMINMAXINFO: 1425 return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam); 1426 case WM_COMMAND: 1427 return FILEDLG95_OnWMCommand(hwnd, wParam); 1428 case WM_DRAWITEM: 1429 { 1430 switch(((LPDRAWITEMSTRUCT)lParam)->CtlID) 1431 { 1432 case IDC_LOOKIN: 1433 FILEDLG95_LOOKIN_DrawItem((LPDRAWITEMSTRUCT) lParam); 1434 return TRUE; 1435 } 1436 } 1437 return FALSE; 1438 1439 case WM_GETISHELLBROWSER: 1440 return FILEDLG95_OnWMGetIShellBrowser(hwnd); 1441 1442 case WM_DESTROY: 1443 { 1444 FileOpenDlgInfos * fodInfos = get_filedlg_infoptr(hwnd); 1445 HWND places_bar = GetDlgItem(hwnd, IDC_TOOLBARPLACES); 1446 HIMAGELIST himl; 1447 1448 if (fodInfos && fodInfos->ofnInfos->Flags & OFN_ENABLESIZING) 1449 MemDialogSize = fodInfos->sizedlg; 1450 1451 if (places_bar) 1452 { 1453 himl = (HIMAGELIST)SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_GETIMAGELIST, 0, 0); 1454 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETIMAGELIST, 0, 0); 1455 ImageList_Destroy(himl); 1456 } 1457 return FALSE; 1458 } 1459 1460 case WM_NCDESTROY: 1461 RemovePropW(hwnd, filedlg_info_propnameW); 1462 return 0; 1463 1464 case WM_NOTIFY: 1465 { 1466 LPNMHDR lpnmh = (LPNMHDR)lParam; 1467 UINT stringId = -1; 1468 1469 /* set up the button tooltips strings */ 1470 if(TTN_GETDISPINFOA == lpnmh->code ) 1471 { 1472 LPNMTTDISPINFOA lpdi = (LPNMTTDISPINFOA)lParam; 1473 switch(lpnmh->idFrom ) 1474 { 1475 /* Up folder button */ 1476 case FCIDM_TB_UPFOLDER: 1477 stringId = IDS_UPFOLDER; 1478 break; 1479 /* New folder button */ 1480 case FCIDM_TB_NEWFOLDER: 1481 stringId = IDS_NEWFOLDER; 1482 break; 1483 /* List option button */ 1484 case FCIDM_TB_SMALLICON: 1485 stringId = IDS_LISTVIEW; 1486 break; 1487 /* Details option button */ 1488 case FCIDM_TB_REPORTVIEW: 1489 stringId = IDS_REPORTVIEW; 1490 break; 1491 /* Desktop button */ 1492 case FCIDM_TB_DESKTOP: 1493 stringId = IDS_TODESKTOP; 1494 break; 1495 default: 1496 stringId = 0; 1497 } 1498 lpdi->hinst = COMDLG32_hInstance; 1499 lpdi->lpszText = MAKEINTRESOURCEA(stringId); 1500 } 1501 return FALSE; 1502 } 1503 default : 1504 if(uMsg >= CDM_FIRST && uMsg <= CDM_LAST) 1505 return FILEDLG95_HandleCustomDialogMessages(hwnd, uMsg, wParam, lParam); 1506 return FALSE; 1507 } 1508 } 1509 1510 static inline BOOL filename_is_edit( const FileOpenDlgInfos *info ) 1511 { 1512 return (info->ofnInfos->lStructSize == OPENFILENAME_SIZE_VERSION_400W) && 1513 (info->ofnInfos->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)); 1514 } 1515 1516 /*********************************************************************** 1517 * FILEDLG95_InitControls 1518 * 1519 * WM_INITDIALOG message handler (before hook notification) 1520 */ 1521 static LRESULT FILEDLG95_InitControls(HWND hwnd) 1522 { 1523 BOOL win2000plus = FALSE; 1524 BOOL win98plus = FALSE; 1525 BOOL handledPath = FALSE; 1526 OSVERSIONINFOW osVi; 1527 static const WCHAR szwSlash[] = { '\\', 0 }; 1528 static const WCHAR szwStar[] = { '*',0 }; 1529 1530 static const TBBUTTON tbb[] = 1531 { 1532 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 }, 1533 {VIEW_PARENTFOLDER, FCIDM_TB_UPFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 }, 1534 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 }, 1535 {VIEW_NEWFOLDER+1, FCIDM_TB_DESKTOP, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 }, 1536 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 }, 1537 {VIEW_NEWFOLDER, FCIDM_TB_NEWFOLDER, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 }, 1538 {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0 }, 1539 {VIEW_LIST, FCIDM_TB_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 }, 1540 {VIEW_DETAILS, FCIDM_TB_REPORTVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0 }, 1541 }; 1542 static const TBADDBITMAP tba = {HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR}; 1543 1544 RECT rectTB; 1545 RECT rectlook; 1546 1547 HIMAGELIST toolbarImageList; 1548 ITEMIDLIST *desktopPidl; 1549 SHFILEINFOW fileinfo; 1550 1551 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 1552 1553 TRACE("%p\n", fodInfos); 1554 1555 /* Get windows version emulating */ 1556 osVi.dwOSVersionInfoSize = sizeof(osVi); 1557 GetVersionExW(&osVi); 1558 if (osVi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { 1559 win98plus = ((osVi.dwMajorVersion > 4) || ((osVi.dwMajorVersion == 4) && (osVi.dwMinorVersion > 0))); 1560 } else if (osVi.dwPlatformId == VER_PLATFORM_WIN32_NT) { 1561 win2000plus = (osVi.dwMajorVersion > 4); 1562 if (win2000plus) win98plus = TRUE; 1563 } 1564 TRACE("Running on 2000+ %d, 98+ %d\n", win2000plus, win98plus); 1565 1566 1567 /* Use either the edit or the comboboxex for the filename control */ 1568 if (filename_is_edit( fodInfos )) 1569 { 1570 DestroyWindow( GetDlgItem( hwnd, cmb13 ) ); 1571 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, edt1 ); 1572 } 1573 else 1574 { 1575 DestroyWindow( GetDlgItem( hwnd, edt1 ) ); 1576 fodInfos->DlgInfos.hwndFileName = GetDlgItem( hwnd, cmb13 ); 1577 } 1578 1579 /* Get the hwnd of the controls */ 1580 fodInfos->DlgInfos.hwndFileTypeCB = GetDlgItem(hwnd,IDC_FILETYPE); 1581 fodInfos->DlgInfos.hwndLookInCB = GetDlgItem(hwnd,IDC_LOOKIN); 1582 1583 GetWindowRect( fodInfos->DlgInfos.hwndLookInCB,&rectlook); 1584 MapWindowPoints( 0, hwnd,(LPPOINT)&rectlook,2); 1585 1586 /* construct the toolbar */ 1587 GetWindowRect(GetDlgItem(hwnd,IDC_TOOLBARSTATIC),&rectTB); 1588 MapWindowPoints( 0, hwnd,(LPPOINT)&rectTB,2); 1589 1590 rectTB.right = rectlook.right + rectTB.right - rectTB.left; 1591 rectTB.bottom = rectlook.top - 1 + rectTB.bottom - rectTB.top; 1592 rectTB.left = rectlook.right; 1593 rectTB.top = rectlook.top-1; 1594 1595 if (fodInfos->unicode) 1596 fodInfos->DlgInfos.hwndTB = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, 1597 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NORESIZE, 1598 rectTB.left, rectTB.top, 1599 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top, 1600 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL); 1601 else 1602 fodInfos->DlgInfos.hwndTB = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL, 1603 WS_CHILD | WS_GROUP | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NORESIZE, 1604 rectTB.left, rectTB.top, 1605 rectTB.right - rectTB.left, rectTB.bottom - rectTB.top, 1606 hwnd, (HMENU)IDC_TOOLBAR, COMDLG32_hInstance, NULL); 1607 1608 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); 1609 1610 /* FIXME: use TB_LOADIMAGES when implemented */ 1611 /* SendMessageW(fodInfos->DlgInfos.hwndTB, TB_LOADIMAGES, IDB_VIEW_SMALL_COLOR, HINST_COMMCTRL);*/ 1612 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_SETMAXTEXTROWS, 0, 0); 1613 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBITMAP, 12, (LPARAM) &tba); 1614 1615 /* Retrieve and add desktop icon to the toolbar */ 1616 toolbarImageList = (HIMAGELIST)SendMessageW(fodInfos->DlgInfos.hwndTB, TB_GETIMAGELIST, 0, 0L); 1617 SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, &desktopPidl); 1618 SHGetFileInfoW((const WCHAR *)desktopPidl, 0, &fileinfo, sizeof(fileinfo), 1619 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON); 1620 ImageList_AddIcon(toolbarImageList, fileinfo.hIcon); 1621 1622 DestroyIcon(fileinfo.hIcon); 1623 CoTaskMemFree(desktopPidl); 1624 1625 /* Finish Toolbar Construction */ 1626 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_ADDBUTTONSW, 9, (LPARAM) tbb); 1627 SendMessageW(fodInfos->DlgInfos.hwndTB, TB_AUTOSIZE, 0, 0); 1628 1629 if (is_places_bar_enabled(fodInfos)) 1630 { 1631 TBBUTTON tb = { 0 }; 1632 HIMAGELIST himl; 1633 RECT rect; 1634 int i, cx; 1635 1636 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_BUTTONSTRUCTSIZE, 0, 0); 1637 GetClientRect(GetDlgItem(hwnd, IDC_TOOLBARPLACES), &rect); 1638 cx = rect.right - rect.left; 1639 1640 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETBUTTONWIDTH, 0, MAKELPARAM(cx, cx)); 1641 himl = ImageList_Create(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), ILC_COLOR32, 4, 1); 1642 1643 filedlg_collect_places_pidls(fodInfos); 1644 for (i = 0; i < ARRAY_SIZE(fodInfos->places); i++) 1645 { 1646 int index; 1647 1648 if (!fodInfos->places[i]) 1649 continue; 1650 1651 memset(&fileinfo, 0, sizeof(fileinfo)); 1652 SHGetFileInfoW((const WCHAR *)fodInfos->places[i], 0, &fileinfo, sizeof(fileinfo), 1653 SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_ICON); 1654 index = ImageList_AddIcon(himl, fileinfo.hIcon); 1655 1656 tb.iBitmap = index; 1657 tb.iString = (INT_PTR)fileinfo.szDisplayName; 1658 tb.fsState = TBSTATE_ENABLED | TBSTATE_WRAP; 1659 tb.idCommand = TBPLACES_CMDID_PLACE0 + i; 1660 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_ADDBUTTONSW, 1, (LPARAM)&tb); 1661 1662 DestroyIcon(fileinfo.hIcon); 1663 } 1664 1665 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETIMAGELIST, 0, (LPARAM)himl); 1666 SendDlgItemMessageW(hwnd, IDC_TOOLBARPLACES, TB_SETBUTTONSIZE, 0, MAKELPARAM(cx, cx * 3 / 4)); 1667 } 1668 1669 /* Set the window text with the text specified in the OPENFILENAME structure */ 1670 if(fodInfos->title) 1671 { 1672 SetWindowTextW(hwnd,fodInfos->title); 1673 } 1674 else if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) 1675 { 1676 WCHAR buf[64]; 1677 LoadStringW(COMDLG32_hInstance, IDS_SAVE_AS, buf, ARRAY_SIZE(buf)); 1678 SetWindowTextW(hwnd, buf); 1679 } 1680 1681 /* Initialise the file name edit control */ 1682 handledPath = FALSE; 1683 TRACE("Before manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir)); 1684 1685 if(fodInfos->filename) 1686 { 1687 /* 1. If win2000 or higher and filename contains a path, use it 1688 in preference over the lpstrInitialDir */ 1689 if (win2000plus && *fodInfos->filename && strpbrkW(fodInfos->filename, szwSlash)) { 1690 WCHAR tmpBuf[MAX_PATH]; 1691 WCHAR *nameBit; 1692 DWORD result; 1693 1694 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, tmpBuf, &nameBit); 1695 if (result) { 1696 1697 /* nameBit is always shorter than the original filename. It may be NULL 1698 * when the filename contains only a drive name instead of file name */ 1699 if (nameBit) 1700 { 1701 lstrcpyW(fodInfos->filename,nameBit); 1702 *nameBit = 0x00; 1703 } 1704 else 1705 *fodInfos->filename = '\0'; 1706 1707 heap_free(fodInfos->initdir); 1708 fodInfos->initdir = heap_alloc((lstrlenW(tmpBuf) + 1)*sizeof(WCHAR)); 1709 lstrcpyW(fodInfos->initdir, tmpBuf); 1710 handledPath = TRUE; 1711 TRACE("Value in Filename includes path, overriding InitialDir: %s, %s\n", 1712 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir)); 1713 } 1714 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename ); 1715 1716 } else { 1717 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename ); 1718 } 1719 } 1720 1721 /* 2. (All platforms) If initdir is not null, then use it */ 1722 if (!handledPath && fodInfos->initdir && *fodInfos->initdir) 1723 { 1724 /* Work out the proper path as supplied one might be relative */ 1725 /* (Here because supplying '.' as dir browses to My Computer) */ 1726 WCHAR tmpBuf[MAX_PATH]; 1727 WCHAR tmpBuf2[MAX_PATH]; 1728 WCHAR *nameBit; 1729 DWORD result; 1730 1731 lstrcpyW(tmpBuf, fodInfos->initdir); 1732 if (PathFileExistsW(tmpBuf)) { 1733 /* initdir does not have to be a directory. If a file is 1734 * specified, the dir part is taken */ 1735 if (PathIsDirectoryW(tmpBuf)) { 1736 PathAddBackslashW(tmpBuf); 1737 lstrcatW(tmpBuf, szwStar); 1738 } 1739 result = GetFullPathNameW(tmpBuf, MAX_PATH, tmpBuf2, &nameBit); 1740 if (result) { 1741 *nameBit = 0x00; 1742 heap_free(fodInfos->initdir); 1743 fodInfos->initdir = heap_alloc((lstrlenW(tmpBuf2) + 1) * sizeof(WCHAR)); 1744 lstrcpyW(fodInfos->initdir, tmpBuf2); 1745 handledPath = TRUE; 1746 TRACE("Value in InitDir changed to %s\n", debugstr_w(fodInfos->initdir)); 1747 } 1748 } 1749 else if (fodInfos->initdir) 1750 { 1751 heap_free(fodInfos->initdir); 1752 fodInfos->initdir = NULL; 1753 TRACE("Value in InitDir is not an existing path, changed to (nil)\n"); 1754 } 1755 } 1756 1757 if (!handledPath && (!fodInfos->initdir || !*fodInfos->initdir)) 1758 { 1759 /* 3. All except w2k+: if filename contains a path use it */ 1760 if (!win2000plus && fodInfos->filename && 1761 *fodInfos->filename && 1762 strpbrkW(fodInfos->filename, szwSlash)) { 1763 WCHAR tmpBuf[MAX_PATH]; 1764 WCHAR *nameBit; 1765 DWORD result; 1766 1767 result = GetFullPathNameW(fodInfos->filename, MAX_PATH, 1768 tmpBuf, &nameBit); 1769 if (result) { 1770 int len; 1771 1772 /* nameBit is always shorter than the original filename */ 1773 lstrcpyW(fodInfos->filename, nameBit); 1774 *nameBit = 0x00; 1775 1776 len = lstrlenW(tmpBuf); 1777 heap_free(fodInfos->initdir); 1778 fodInfos->initdir = heap_alloc((len+1)*sizeof(WCHAR)); 1779 lstrcpyW(fodInfos->initdir, tmpBuf); 1780 1781 handledPath = TRUE; 1782 TRACE("Value in Filename includes path, overriding initdir: %s, %s\n", 1783 debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir)); 1784 } 1785 SetWindowTextW( fodInfos->DlgInfos.hwndFileName, fodInfos->filename ); 1786 } 1787 1788 /* 4. Win2000+: Recently used */ 1789 if (!handledPath && win2000plus) { 1790 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR)); 1791 fodInfos->initdir[0] = '\0'; 1792 1793 FILEDLG95_MRU_load_filename(fodInfos->initdir); 1794 1795 if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){ 1796 handledPath = TRUE; 1797 }else{ 1798 heap_free(fodInfos->initdir); 1799 fodInfos->initdir = NULL; 1800 } 1801 } 1802 1803 /* 5. win98+ and win2000+ if any files of specified filter types in 1804 current directory, use it */ 1805 if (win98plus && !handledPath && fodInfos->filter && *fodInfos->filter) { 1806 1807 LPCWSTR lpstrPos = fodInfos->filter; 1808 WIN32_FIND_DATAW FindFileData; 1809 HANDLE hFind; 1810 1811 while (1) 1812 { 1813 /* filter is a list... title\0ext\0......\0\0 */ 1814 1815 /* Skip the title */ 1816 if(! *lpstrPos) break; /* end */ 1817 lpstrPos += lstrlenW(lpstrPos) + 1; 1818 1819 /* See if any files exist in the current dir with this extension */ 1820 if(! *lpstrPos) break; /* end */ 1821 1822 hFind = FindFirstFileW(lpstrPos, &FindFileData); 1823 1824 if (hFind == INVALID_HANDLE_VALUE) { 1825 /* None found - continue search */ 1826 lpstrPos += lstrlenW(lpstrPos) + 1; 1827 1828 } else { 1829 1830 heap_free(fodInfos->initdir); 1831 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR)); 1832 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir); 1833 1834 handledPath = TRUE; 1835 TRACE("No initial dir specified, but files of type %s found in current, so using it\n", 1836 debugstr_w(lpstrPos)); 1837 FindClose(hFind); 1838 break; 1839 } 1840 } 1841 } 1842 1843 /* 6. Win98+ and 2000+: Use personal files dir, others use current dir */ 1844 if (!handledPath && (win2000plus || win98plus)) { 1845 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR)); 1846 1847 if (SHGetFolderPathW(hwnd, CSIDL_PERSONAL, 0, 0, fodInfos->initdir) == S_OK) 1848 { 1849 if (SHGetFolderPathW(hwnd, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, 0, 0, fodInfos->initdir) == S_OK) 1850 { 1851 /* last fallback */ 1852 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir); 1853 TRACE("No personal or desktop dir, using cwd as failsafe: %s\n", debugstr_w(fodInfos->initdir)); 1854 } 1855 else 1856 TRACE("No personal dir, using desktop instead: %s\n", debugstr_w(fodInfos->initdir)); 1857 } 1858 else 1859 TRACE("No initial dir specified, using personal files dir of %s\n", debugstr_w(fodInfos->initdir)); 1860 1861 handledPath = TRUE; 1862 } else if (!handledPath) { 1863 fodInfos->initdir = heap_alloc(MAX_PATH * sizeof(WCHAR)); 1864 GetCurrentDirectoryW(MAX_PATH, fodInfos->initdir); 1865 handledPath = TRUE; 1866 TRACE("No initial dir specified, using current dir of %s\n", debugstr_w(fodInfos->initdir)); 1867 } 1868 } 1869 SetFocus( fodInfos->DlgInfos.hwndFileName ); 1870 TRACE("After manipulation, file = %s, dir = %s\n", debugstr_w(fodInfos->filename), debugstr_w(fodInfos->initdir)); 1871 1872 /* Must the open as read only check box be checked ?*/ 1873 if(fodInfos->ofnInfos->Flags & OFN_READONLY) 1874 { 1875 SendDlgItemMessageW(hwnd,IDC_OPENREADONLY,BM_SETCHECK,TRUE,0); 1876 } 1877 1878 /* Must the open as read only check box be hidden? */ 1879 if (filedialog_is_readonly_hidden(fodInfos)) 1880 { 1881 ShowWindow(GetDlgItem(hwnd,IDC_OPENREADONLY),SW_HIDE); 1882 EnableWindow(GetDlgItem(hwnd, IDC_OPENREADONLY), FALSE); 1883 } 1884 1885 /* Must the help button be hidden? */ 1886 if (!(fodInfos->ofnInfos->Flags & OFN_SHOWHELP)) 1887 { 1888 ShowWindow(GetDlgItem(hwnd, pshHelp), SW_HIDE); 1889 EnableWindow(GetDlgItem(hwnd, pshHelp), FALSE); 1890 } 1891 1892 /* change Open to Save */ 1893 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) 1894 { 1895 WCHAR buf[16]; 1896 LoadStringW(COMDLG32_hInstance, IDS_SAVE_BUTTON, buf, ARRAY_SIZE(buf)); 1897 SetDlgItemTextW(hwnd, IDOK, buf); 1898 LoadStringW(COMDLG32_hInstance, IDS_SAVE_IN, buf, ARRAY_SIZE(buf)); 1899 SetDlgItemTextW(hwnd, IDC_LOOKINSTATIC, buf); 1900 } 1901 1902 /* Initialize the filter combo box */ 1903 FILEDLG95_FILETYPE_Init(hwnd); 1904 1905 return 0; 1906 } 1907 1908 /*********************************************************************** 1909 * FILEDLG95_ResizeControls 1910 * 1911 * WM_INITDIALOG message handler (after hook notification) 1912 */ 1913 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam) 1914 { 1915 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam; 1916 1917 if (fodInfos->DlgInfos.hwndCustomDlg) 1918 { 1919 RECT rc; 1920 UINT flags = SWP_NOACTIVATE; 1921 1922 ArrangeCtrlPositions(fodInfos->DlgInfos.hwndCustomDlg, hwnd, 1923 filedialog_is_readonly_hidden(fodInfos) && !(fodInfos->ofnInfos->Flags & OFN_SHOWHELP)); 1924 1925 /* resize the custom dialog to the parent size */ 1926 if (fodInfos->ofnInfos->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)) 1927 GetClientRect(hwnd, &rc); 1928 else 1929 { 1930 /* our own fake template is zero sized and doesn't have children, so 1931 * there is no need to resize it. Picasa depends on it. 1932 */ 1933 flags |= SWP_NOSIZE; 1934 SetRectEmpty(&rc); 1935 } 1936 SetWindowPos(fodInfos->DlgInfos.hwndCustomDlg, HWND_BOTTOM, 1937 0, 0, rc.right, rc.bottom, flags); 1938 } 1939 else 1940 { 1941 /* Resize the height; if opened as read-only, checkbox and help button are 1942 * hidden and we are not using a custom template nor a customDialog 1943 */ 1944 if (filedialog_is_readonly_hidden(fodInfos) && 1945 (!(fodInfos->ofnInfos->Flags & 1946 (OFN_SHOWHELP|OFN_ENABLETEMPLATE|OFN_ENABLETEMPLATEHANDLE)))) 1947 { 1948 RECT rectDlg, rectHelp, rectCancel; 1949 GetWindowRect(hwnd, &rectDlg); 1950 GetWindowRect(GetDlgItem(hwnd, pshHelp), &rectHelp); 1951 GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rectCancel); 1952 /* subtract the height of the help button plus the space between the help 1953 * button and the cancel button to the height of the dialog 1954 */ 1955 SetWindowPos(hwnd, 0, 0, 0, rectDlg.right-rectDlg.left, 1956 (rectDlg.bottom-rectDlg.top) - (rectHelp.bottom - rectCancel.bottom), 1957 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER); 1958 } 1959 } 1960 return TRUE; 1961 } 1962 1963 /*********************************************************************** 1964 * FILEDLG95_FillControls 1965 * 1966 * WM_INITDIALOG message handler (after hook notification) 1967 */ 1968 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam) 1969 { 1970 LPITEMIDLIST pidlItemId = NULL; 1971 1972 FileOpenDlgInfos *fodInfos = (FileOpenDlgInfos *) lParam; 1973 1974 TRACE("dir=%s file=%s\n", 1975 debugstr_w(fodInfos->initdir), debugstr_w(fodInfos->filename)); 1976 1977 /* Get the initial directory pidl */ 1978 1979 if(!(pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder,fodInfos->initdir))) 1980 { 1981 WCHAR path[MAX_PATH]; 1982 1983 GetCurrentDirectoryW(MAX_PATH,path); 1984 pidlItemId = GetPidlFromName(fodInfos->Shell.FOIShellFolder, path); 1985 } 1986 1987 /* Initialise shell objects */ 1988 FILEDLG95_SHELL_Init(hwnd); 1989 1990 /* Initialize the Look In combo box */ 1991 FILEDLG95_LOOKIN_Init(fodInfos->DlgInfos.hwndLookInCB); 1992 1993 /* Browse to the initial directory */ 1994 IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser,pidlItemId, SBSP_ABSOLUTE); 1995 1996 ILFree(pidlItemId); 1997 1998 return TRUE; 1999 } 2000 /*********************************************************************** 2001 * FILEDLG95_Clean 2002 * 2003 * Regroups all the cleaning functions of the filedlg 2004 */ 2005 void FILEDLG95_Clean(HWND hwnd) 2006 { 2007 FILEDLG95_FILETYPE_Clean(hwnd); 2008 FILEDLG95_LOOKIN_Clean(hwnd); 2009 FILEDLG95_SHELL_Clean(hwnd); 2010 } 2011 2012 2013 /*********************************************************************** 2014 * Browse to arbitrary pidl 2015 */ 2016 static void filedlg_browse_to_pidl(const FileOpenDlgInfos *info, LPITEMIDLIST pidl) 2017 { 2018 TRACE("%p, %p\n", info->ShellInfos.hwndOwner, pidl); 2019 2020 IShellBrowser_BrowseObject(info->Shell.FOIShellBrowser, pidl, SBSP_ABSOLUTE); 2021 if (info->ofnInfos->Flags & OFN_EXPLORER) 2022 SendCustomDlgNotificationMessage(info->ShellInfos.hwndOwner, CDN_FOLDERCHANGE); 2023 } 2024 2025 /*********************************************************************** 2026 * FILEDLG95_OnWMCommand 2027 * 2028 * WM_COMMAND message handler 2029 */ 2030 static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam) 2031 { 2032 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 2033 WORD wNotifyCode = HIWORD(wParam); /* notification code */ 2034 WORD id = LOWORD(wParam); /* item, control, or accelerator identifier */ 2035 2036 switch (id) 2037 { 2038 /* OK button */ 2039 case IDOK: 2040 FILEDLG95_OnOpen(hwnd); 2041 break; 2042 /* Cancel button */ 2043 case IDCANCEL: 2044 FILEDLG95_Clean(hwnd); 2045 EndDialog(hwnd, FALSE); 2046 break; 2047 /* Filetype combo box */ 2048 case IDC_FILETYPE: 2049 FILEDLG95_FILETYPE_OnCommand(hwnd,wNotifyCode); 2050 break; 2051 /* LookIn combo box */ 2052 case IDC_LOOKIN: 2053 FILEDLG95_LOOKIN_OnCommand(hwnd,wNotifyCode); 2054 break; 2055 2056 /* --- toolbar --- */ 2057 /* Up folder button */ 2058 case FCIDM_TB_UPFOLDER: 2059 FILEDLG95_SHELL_UpFolder(hwnd); 2060 break; 2061 /* New folder button */ 2062 case FCIDM_TB_NEWFOLDER: 2063 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_NEWFOLDERA); 2064 break; 2065 /* List option button */ 2066 case FCIDM_TB_SMALLICON: 2067 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWLISTA); 2068 break; 2069 /* Details option button */ 2070 case FCIDM_TB_REPORTVIEW: 2071 FILEDLG95_SHELL_ExecuteCommand(hwnd,CMDSTR_VIEWDETAILSA); 2072 break; 2073 2074 case FCIDM_TB_DESKTOP: 2075 { 2076 LPITEMIDLIST pidl; 2077 2078 SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, &pidl); 2079 filedlg_browse_to_pidl(fodInfos, pidl); 2080 ILFree(pidl); 2081 break; 2082 } 2083 2084 /* Places bar */ 2085 case TBPLACES_CMDID_PLACE0: 2086 case TBPLACES_CMDID_PLACE1: 2087 case TBPLACES_CMDID_PLACE2: 2088 case TBPLACES_CMDID_PLACE3: 2089 case TBPLACES_CMDID_PLACE4: 2090 filedlg_browse_to_pidl(fodInfos, fodInfos->places[id - TBPLACES_CMDID_PLACE0]); 2091 break; 2092 2093 case edt1: 2094 case cmb13: 2095 break; 2096 2097 } 2098 /* Do not use the listview selection anymore */ 2099 fodInfos->DlgInfos.dwDlgProp &= ~FODPROP_USEVIEW; 2100 return 0; 2101 } 2102 2103 /*********************************************************************** 2104 * FILEDLG95_OnWMGetIShellBrowser 2105 * 2106 * WM_GETISHELLBROWSER message handler 2107 */ 2108 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd) 2109 { 2110 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 2111 2112 TRACE("\n"); 2113 2114 SetWindowLongPtrW(hwnd,DWLP_MSGRESULT,(LONG_PTR)fodInfos->Shell.FOIShellBrowser); 2115 2116 return TRUE; 2117 } 2118 2119 2120 /*********************************************************************** 2121 * FILEDLG95_SendFileOK 2122 * 2123 * Sends the CDN_FILEOK notification if required 2124 * 2125 * RETURNS 2126 * TRUE if the dialog should close 2127 * FALSE if the dialog should not be closed 2128 */ 2129 static BOOL FILEDLG95_SendFileOK( HWND hwnd, FileOpenDlgInfos *fodInfos ) 2130 { 2131 /* ask the hook if we can close */ 2132 if (is_dialog_hooked(fodInfos)) 2133 { 2134 LRESULT retval = 0; 2135 2136 TRACE("---\n"); 2137 /* First send CDN_FILEOK as MSDN doc says */ 2138 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) 2139 retval = SendCustomDlgNotificationMessage(hwnd,CDN_FILEOK); 2140 if( retval) 2141 { 2142 TRACE("canceled\n"); 2143 return FALSE; 2144 } 2145 2146 /* fodInfos->ofnInfos points to an ASCII or UNICODE structure as appropriate */ 2147 retval = SendMessageW(fodInfos->DlgInfos.hwndCustomDlg, 2148 fodInfos->HookMsg.fileokstring, 0, (LPARAM)fodInfos->ofnInfos); 2149 if( retval) 2150 { 2151 TRACE("canceled\n"); 2152 return FALSE; 2153 } 2154 } 2155 return TRUE; 2156 } 2157 2158 /*********************************************************************** 2159 * FILEDLG95_OnOpenMultipleFiles 2160 * 2161 * Handles the opening of multiple files. 2162 * 2163 * FIXME 2164 * check destination buffer size 2165 */ 2166 BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCount, UINT sizeUsed) 2167 { 2168 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 2169 WCHAR lpstrPathSpec[MAX_PATH] = {0}; 2170 UINT nCount, nSizePath; 2171 2172 TRACE("\n"); 2173 2174 if(fodInfos->unicode) 2175 { 2176 LPOPENFILENAMEW ofn = fodInfos->ofnInfos; 2177 ofn->lpstrFile[0] = '\0'; 2178 } 2179 else 2180 { 2181 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA) fodInfos->ofnInfos; 2182 ofn->lpstrFile[0] = '\0'; 2183 } 2184 2185 COMDLG32_GetDisplayNameOf( fodInfos->ShellInfos.pidlAbsCurrent, lpstrPathSpec ); 2186 2187 if ( !(fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) && 2188 ( fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST) && 2189 ! ( fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG ) ) 2190 { 2191 LPWSTR lpstrTemp = lpstrFileList; 2192 2193 for ( nCount = 0; nCount < nFileCount; nCount++ ) 2194 { 2195 LPITEMIDLIST pidl; 2196 2197 pidl = GetPidlFromName(fodInfos->Shell.FOIShellFolder, lpstrTemp); 2198 if (!pidl) 2199 { 2200 WCHAR lpstrNotFound[100]; 2201 WCHAR lpstrMsg[100]; 2202 WCHAR tmp[400]; 2203 static const WCHAR nl[] = {'\n',0}; 2204 2205 LoadStringW(COMDLG32_hInstance, IDS_FILENOTFOUND, lpstrNotFound, 100); 2206 LoadStringW(COMDLG32_hInstance, IDS_VERIFYFILE, lpstrMsg, 100); 2207 2208 lstrcpyW(tmp, lpstrTemp); 2209 lstrcatW(tmp, nl); 2210 lstrcatW(tmp, lpstrNotFound); 2211 lstrcatW(tmp, nl); 2212 lstrcatW(tmp, lpstrMsg); 2213 2214 MessageBoxW(hwnd, tmp, fodInfos->title, MB_OK | MB_ICONEXCLAMATION); 2215 return FALSE; 2216 } 2217 2218 /* move to the next file in the list of files */ 2219 lpstrTemp += lstrlenW(lpstrTemp) + 1; 2220 ILFree(pidl); 2221 } 2222 } 2223 2224 nSizePath = lstrlenW(lpstrPathSpec) + 1; 2225 if ( !(fodInfos->ofnInfos->Flags & OFN_EXPLORER) ) 2226 { 2227 /* For "oldstyle" dialog the components have to 2228 be separated by blanks (not '\0'!) and short 2229 filenames have to be used! */ 2230 FIXME("Components have to be separated by blanks\n"); 2231 } 2232 if(fodInfos->unicode) 2233 { 2234 LPOPENFILENAMEW ofn = fodInfos->ofnInfos; 2235 lstrcpyW( ofn->lpstrFile, lpstrPathSpec); 2236 memcpy( ofn->lpstrFile + nSizePath, lpstrFileList, sizeUsed*sizeof(WCHAR) ); 2237 } 2238 else 2239 { 2240 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos; 2241 2242 if (ofn->lpstrFile != NULL) 2243 { 2244 nSizePath = WideCharToMultiByte(CP_ACP, 0, lpstrPathSpec, -1, 2245 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL); 2246 if (ofn->nMaxFile > nSizePath) 2247 { 2248 WideCharToMultiByte(CP_ACP, 0, lpstrFileList, sizeUsed, 2249 ofn->lpstrFile + nSizePath, 2250 ofn->nMaxFile - nSizePath, NULL, NULL); 2251 } 2252 } 2253 } 2254 2255 fodInfos->ofnInfos->nFileOffset = nSizePath; 2256 fodInfos->ofnInfos->nFileExtension = 0; 2257 2258 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) ) 2259 return FALSE; 2260 2261 /* clean and exit */ 2262 FILEDLG95_Clean(hwnd); 2263 return EndDialog(hwnd,TRUE); 2264 } 2265 2266 /* Returns the 'slot name' of the given module_name in the registry's 2267 * most-recently-used list. This will be an ASCII value in the 2268 * range ['a','z'). Returns zero on error. 2269 * 2270 * The slot's value in the registry has the form: 2271 * module_name\0mru_path\0 2272 * 2273 * If stored_path is given, then stored_path will contain the path name 2274 * stored in the registry's MRU list for the given module_name. 2275 * 2276 * If hkey_ret is given, then hkey_ret will be a handle to the registry's 2277 * MRU list key for the given module_name. 2278 */ 2279 static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret) 2280 { 2281 WCHAR mru_list[32], *cur_mru_slot; 2282 BOOL taken[25] = {0}; 2283 DWORD mru_list_size = sizeof(mru_list), key_type = -1, i; 2284 HKEY hkey_tmp, *hkey; 2285 LONG ret; 2286 2287 if(hkey_ret) 2288 hkey = hkey_ret; 2289 else 2290 hkey = &hkey_tmp; 2291 2292 if(stored_path) 2293 *stored_path = '\0'; 2294 2295 ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey); 2296 if(ret){ 2297 WARN("Unable to create MRU key: %d\n", ret); 2298 return 0; 2299 } 2300 2301 ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type, 2302 (LPBYTE)mru_list, &mru_list_size); 2303 if(ret || key_type != REG_SZ){ 2304 if(ret == ERROR_FILE_NOT_FOUND) 2305 return 'a'; 2306 2307 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret); 2308 RegCloseKey(*hkey); 2309 return 0; 2310 } 2311 2312 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){ 2313 WCHAR value_data[MAX_PATH], value_name[2] = {0}; 2314 DWORD value_data_size = sizeof(value_data); 2315 2316 *value_name = *cur_mru_slot; 2317 2318 ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY, 2319 &key_type, (LPBYTE)value_data, &value_data_size); 2320 if(ret || key_type != REG_BINARY){ 2321 WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret); 2322 continue; 2323 } 2324 2325 if(!strcmpiW(module_name, value_data)){ 2326 if(!hkey_ret) 2327 RegCloseKey(*hkey); 2328 if(stored_path) 2329 lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1); 2330 return *value_name; 2331 } 2332 } 2333 2334 if(!hkey_ret) 2335 RegCloseKey(*hkey); 2336 2337 /* the module name isn't in the registry, so find the next open slot */ 2338 for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot) 2339 taken[*cur_mru_slot - 'a'] = TRUE; 2340 for(i = 0; i < 25; ++i){ 2341 if(!taken[i]) 2342 return i + 'a'; 2343 } 2344 2345 /* all slots are taken, so return the last one in MRUList */ 2346 --cur_mru_slot; 2347 return *cur_mru_slot; 2348 } 2349 2350 /* save the given filename as most-recently-used path for this module */ 2351 static void FILEDLG95_MRU_save_filename(LPCWSTR filename) 2352 { 2353 WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0}; 2354 LONG ret; 2355 HKEY hkey; 2356 2357 /* get the current executable's name */ 2358 if (!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, ARRAY_SIZE(module_path))) 2359 { 2360 WARN("GotModuleFileName failed: %d\n", GetLastError()); 2361 return; 2362 } 2363 module_name = strrchrW(module_path, '\\'); 2364 if(!module_name) 2365 module_name = module_path; 2366 else 2367 module_name += 1; 2368 2369 slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey); 2370 if(!slot) 2371 return; 2372 *slot_name = slot; 2373 2374 { /* update the slot's info */ 2375 WCHAR *path_ends, *final; 2376 DWORD path_len, final_len; 2377 2378 /* use only the path segment of `filename' */ 2379 path_ends = strrchrW(filename, '\\'); 2380 path_len = path_ends - filename; 2381 2382 final_len = path_len + lstrlenW(module_name) + 2; 2383 2384 final = heap_alloc(final_len * sizeof(WCHAR)); 2385 if(!final) 2386 return; 2387 lstrcpyW(final, module_name); 2388 memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR)); 2389 final[final_len-1] = '\0'; 2390 2391 ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final, 2392 final_len * sizeof(WCHAR)); 2393 if(ret){ 2394 WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret); 2395 heap_free(final); 2396 RegCloseKey(hkey); 2397 return; 2398 } 2399 2400 heap_free(final); 2401 } 2402 2403 { /* update MRUList value */ 2404 WCHAR old_mru_list[32], new_mru_list[32]; 2405 WCHAR *old_mru_slot, *new_mru_slot = new_mru_list; 2406 DWORD mru_list_size = sizeof(old_mru_list), key_type; 2407 2408 ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type, 2409 (LPBYTE)old_mru_list, &mru_list_size); 2410 if(ret || key_type != REG_SZ){ 2411 if(ret == ERROR_FILE_NOT_FOUND){ 2412 new_mru_list[0] = slot; 2413 new_mru_list[1] = '\0'; 2414 }else{ 2415 WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret); 2416 RegCloseKey(hkey); 2417 return; 2418 } 2419 }else{ 2420 /* copy old list data over so that the new slot is at the start 2421 * of the list */ 2422 *new_mru_slot++ = slot; 2423 for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){ 2424 if(*old_mru_slot != slot) 2425 *new_mru_slot++ = *old_mru_slot; 2426 } 2427 *new_mru_slot = '\0'; 2428 } 2429 2430 ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list, 2431 (lstrlenW(new_mru_list) + 1) * sizeof(WCHAR)); 2432 if(ret){ 2433 WARN("Error saving MRUList data: %d\n", ret); 2434 RegCloseKey(hkey); 2435 return; 2436 } 2437 } 2438 } 2439 2440 /* load the most-recently-used path for this module */ 2441 static void FILEDLG95_MRU_load_filename(LPWSTR stored_path) 2442 { 2443 WCHAR module_path[MAX_PATH], *module_name; 2444 2445 /* get the current executable's name */ 2446 if (!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, ARRAY_SIZE(module_path))) 2447 { 2448 WARN("GotModuleFileName failed: %d\n", GetLastError()); 2449 return; 2450 } 2451 module_name = strrchrW(module_path, '\\'); 2452 if(!module_name) 2453 module_name = module_path; 2454 else 2455 module_name += 1; 2456 2457 FILEDLG95_MRU_get_slot(module_name, stored_path, NULL); 2458 TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path)); 2459 } 2460 2461 void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText) 2462 { 2463 WCHAR strMsgTitle[MAX_PATH]; 2464 WCHAR strMsgText [MAX_PATH]; 2465 if (idCaption) 2466 LoadStringW(COMDLG32_hInstance, idCaption, strMsgTitle, ARRAY_SIZE(strMsgTitle)); 2467 else 2468 strMsgTitle[0] = '\0'; 2469 LoadStringW(COMDLG32_hInstance, idText, strMsgText, ARRAY_SIZE(strMsgText)); 2470 MessageBoxW(hwnd,strMsgText, strMsgTitle, MB_OK | MB_ICONHAND); 2471 } 2472 2473 int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf, 2474 HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction) 2475 { 2476 int nOpenAction = defAction; 2477 LPWSTR lpszTemp, lpszTemp1; 2478 LPITEMIDLIST pidl = NULL; 2479 static const WCHAR szwInvalid[] = { '/',':','<','>','|', 0}; 2480 2481 /* check for invalid chars */ 2482 if((strpbrkW(lpstrPathAndFile+3, szwInvalid) != NULL) && !(flags & OFN_NOVALIDATE)) 2483 { 2484 FILEDLG95_OnOpenMessage(hwnd, IDS_INVALID_FILENAME_TITLE, IDS_INVALID_FILENAME); 2485 return FALSE; 2486 } 2487 2488 if (FAILED (SHGetDesktopFolder(ppsf))) return FALSE; 2489 2490 lpszTemp1 = lpszTemp = lpstrPathAndFile; 2491 while (lpszTemp1) 2492 { 2493 LPSHELLFOLDER lpsfChild; 2494 WCHAR lpwstrTemp[MAX_PATH]; 2495 DWORD dwEaten, dwAttributes; 2496 LPWSTR p; 2497 2498 lstrcpyW(lpwstrTemp, lpszTemp); 2499 p = PathFindNextComponentW(lpwstrTemp); 2500 2501 if (!p) break; /* end of path */ 2502 2503 *p = 0; 2504 lpszTemp = lpszTemp + lstrlenW(lpwstrTemp); 2505 2506 /* There are no wildcards when OFN_NOVALIDATE is set */ 2507 if(*lpszTemp==0 && !(flags & OFN_NOVALIDATE)) 2508 { 2509 static const WCHAR wszWild[] = { '*', '?', 0 }; 2510 /* if the last element is a wildcard do a search */ 2511 if(strpbrkW(lpszTemp1, wszWild) != NULL) 2512 { 2513 nOpenAction = ONOPEN_SEARCH; 2514 break; 2515 } 2516 } 2517 lpszTemp1 = lpszTemp; 2518 2519 TRACE("parse now=%s next=%s sf=%p\n",debugstr_w(lpwstrTemp), debugstr_w(lpszTemp), *ppsf); 2520 2521 /* append a backslash to drive letters */ 2522 if(lstrlenW(lpwstrTemp)==2 && lpwstrTemp[1] == ':' && 2523 ((lpwstrTemp[0] >= 'a' && lpwstrTemp[0] <= 'z') || 2524 (lpwstrTemp[0] >= 'A' && lpwstrTemp[0] <= 'Z'))) 2525 { 2526 PathAddBackslashW(lpwstrTemp); 2527 } 2528 2529 dwAttributes = SFGAO_FOLDER; 2530 if(SUCCEEDED(IShellFolder_ParseDisplayName(*ppsf, hwnd, NULL, lpwstrTemp, &dwEaten, &pidl, &dwAttributes))) 2531 { 2532 /* the path component is valid, we have a pidl of the next path component */ 2533 TRACE("parse OK attr=0x%08x pidl=%p\n", dwAttributes, pidl); 2534 if(dwAttributes & SFGAO_FOLDER) 2535 { 2536 if(FAILED(IShellFolder_BindToObject(*ppsf, pidl, 0, &IID_IShellFolder, (LPVOID*)&lpsfChild))) 2537 { 2538 ERR("bind to failed\n"); /* should not fail */ 2539 break; 2540 } 2541 IShellFolder_Release(*ppsf); 2542 *ppsf = lpsfChild; 2543 lpsfChild = NULL; 2544 } 2545 else 2546 { 2547 TRACE("value\n"); 2548 2549 /* end dialog, return value */ 2550 nOpenAction = ONOPEN_OPEN; 2551 break; 2552 } 2553 ILFree(pidl); 2554 pidl = NULL; 2555 } 2556 else if (!(flags & OFN_NOVALIDATE)) 2557 { 2558 if(*lpszTemp || /* points to trailing null for last path element */ 2559 (lpwstrTemp[strlenW(lpwstrTemp)-1] == '\\')) /* or if last element ends in '\' */ 2560 { 2561 if(flags & OFN_PATHMUSTEXIST) 2562 { 2563 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_PATHNOTEXISTING); 2564 break; 2565 } 2566 } 2567 else 2568 { 2569 if( (flags & OFN_FILEMUSTEXIST) && !isSaveDlg ) 2570 { 2571 FILEDLG95_OnOpenMessage(hwnd, 0, IDS_FILENOTEXISTING); 2572 break; 2573 } 2574 } 2575 /* change to the current folder */ 2576 nOpenAction = ONOPEN_OPEN; 2577 break; 2578 } 2579 else 2580 { 2581 nOpenAction = ONOPEN_OPEN; 2582 break; 2583 } 2584 } 2585 ILFree(pidl); 2586 2587 return nOpenAction; 2588 } 2589 2590 /*********************************************************************** 2591 * FILEDLG95_OnOpen 2592 * 2593 * Ok button WM_COMMAND message handler 2594 * 2595 * If the function succeeds, the return value is nonzero. 2596 */ 2597 BOOL FILEDLG95_OnOpen(HWND hwnd) 2598 { 2599 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 2600 LPWSTR lpstrFileList; 2601 UINT nFileCount = 0; 2602 UINT sizeUsed = 0; 2603 BOOL ret = TRUE; 2604 WCHAR lpstrPathAndFile[MAX_PATH]; 2605 LPSHELLFOLDER lpsf = NULL; 2606 int nOpenAction; 2607 2608 TRACE("hwnd=%p\n", hwnd); 2609 2610 /* try to browse the selected item */ 2611 if(BrowseSelectedFolder(hwnd)) 2612 return FALSE; 2613 2614 /* get the files from the edit control */ 2615 nFileCount = FILEDLG95_FILENAME_GetFileNames(hwnd, &lpstrFileList, &sizeUsed); 2616 2617 if(nFileCount == 0) 2618 return FALSE; 2619 2620 if(nFileCount > 1) 2621 { 2622 ret = FILEDLG95_OnOpenMultipleFiles(hwnd, lpstrFileList, nFileCount, sizeUsed); 2623 goto ret; 2624 } 2625 2626 TRACE("count=%u len=%u file=%s\n", nFileCount, sizeUsed, debugstr_w(lpstrFileList)); 2627 2628 /* 2629 Step 1: Build a complete path name from the current folder and 2630 the filename or path in the edit box. 2631 Special cases: 2632 - the path in the edit box is a root path 2633 (with or without drive letter) 2634 - the edit box contains ".." (or a path with ".." in it) 2635 */ 2636 2637 COMDLG32_GetCanonicalPath(fodInfos->ShellInfos.pidlAbsCurrent, lpstrFileList, lpstrPathAndFile); 2638 heap_free(lpstrFileList); 2639 2640 /* 2641 Step 2: here we have a cleaned up path 2642 2643 We have to parse the path step by step to see if we have to browse 2644 to a folder if the path points to a directory or the last 2645 valid element is a directory. 2646 2647 valid variables: 2648 lpstrPathAndFile: cleaned up path 2649 */ 2650 2651 if (nFileCount && 2652 (fodInfos->ofnInfos->Flags & OFN_NOVALIDATE) && 2653 !(fodInfos->ofnInfos->Flags & OFN_FILEMUSTEXIST)) 2654 nOpenAction = ONOPEN_OPEN; 2655 else 2656 nOpenAction = ONOPEN_BROWSE; 2657 2658 nOpenAction = FILEDLG95_ValidatePathAction(lpstrPathAndFile, &lpsf, hwnd, 2659 fodInfos->ofnInfos->Flags, 2660 fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG, 2661 nOpenAction); 2662 if(!nOpenAction) 2663 goto ret; 2664 2665 /* 2666 Step 3: here we have a cleaned up and validated path 2667 2668 valid variables: 2669 lpsf: ShellFolder bound to the rightmost valid path component 2670 lpstrPathAndFile: cleaned up path 2671 nOpenAction: action to do 2672 */ 2673 TRACE("end validate sf=%p\n", lpsf); 2674 2675 switch(nOpenAction) 2676 { 2677 case ONOPEN_SEARCH: /* set the current filter to the file mask and refresh */ 2678 TRACE("ONOPEN_SEARCH %s\n", debugstr_w(lpstrPathAndFile)); 2679 { 2680 int iPos; 2681 LPWSTR lpszTemp = PathFindFileNameW(lpstrPathAndFile); 2682 DWORD len; 2683 2684 /* replace the current filter */ 2685 heap_free(fodInfos->ShellInfos.lpstrCurrentFilter); 2686 len = lstrlenW(lpszTemp)+1; 2687 fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc(len * sizeof(WCHAR)); 2688 lstrcpyW( fodInfos->ShellInfos.lpstrCurrentFilter, lpszTemp); 2689 2690 /* set the filter cb to the extension when possible */ 2691 if(-1 < (iPos = FILEDLG95_FILETYPE_SearchExt(fodInfos->DlgInfos.hwndFileTypeCB, lpszTemp))) 2692 SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETCURSEL, iPos, 0); 2693 } 2694 /* fall through */ 2695 case ONOPEN_BROWSE: /* browse to the highest folder we could bind to */ 2696 TRACE("ONOPEN_BROWSE\n"); 2697 { 2698 IPersistFolder2 * ppf2; 2699 if(SUCCEEDED(IShellFolder_QueryInterface( lpsf, &IID_IPersistFolder2, (LPVOID*)&ppf2))) 2700 { 2701 LPITEMIDLIST pidlCurrent; 2702 IPersistFolder2_GetCurFolder(ppf2, &pidlCurrent); 2703 IPersistFolder2_Release(ppf2); 2704 if (!ILIsEqual(pidlCurrent, fodInfos->ShellInfos.pidlAbsCurrent)) 2705 { 2706 if (SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, pidlCurrent, SBSP_ABSOLUTE)) 2707 && fodInfos->ofnInfos->Flags & OFN_EXPLORER) 2708 { 2709 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE); 2710 SendMessageA(fodInfos->DlgInfos.hwndFileName, WM_SETTEXT, 0, (LPARAM)""); 2711 } 2712 } 2713 else if( nOpenAction == ONOPEN_SEARCH ) 2714 { 2715 if (fodInfos->Shell.FOIShellView) 2716 IShellView_Refresh(fodInfos->Shell.FOIShellView); 2717 } 2718 ILFree(pidlCurrent); 2719 if (filename_is_edit( fodInfos )) 2720 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1); 2721 else 2722 { 2723 HWND hwnd; 2724 2725 hwnd = (HWND)SendMessageA(fodInfos->DlgInfos.hwndFileName, CBEM_GETEDITCONTROL, 0, 0); 2726 SendMessageW(hwnd, EM_SETSEL, 0, -1); 2727 } 2728 } 2729 } 2730 ret = FALSE; 2731 break; 2732 case ONOPEN_OPEN: /* fill in the return struct and close the dialog */ 2733 TRACE("ONOPEN_OPEN %s\n", debugstr_w(lpstrPathAndFile)); 2734 { 2735 WCHAR *ext = NULL; 2736 2737 /* update READONLY check box flag */ 2738 if ((SendMessageW(GetDlgItem(hwnd,IDC_OPENREADONLY),BM_GETCHECK,0,0) & 0x03) == BST_CHECKED) 2739 fodInfos->ofnInfos->Flags |= OFN_READONLY; 2740 else 2741 fodInfos->ofnInfos->Flags &= ~OFN_READONLY; 2742 2743 /* Attach the file extension with file name*/ 2744 ext = PathFindExtensionW(lpstrPathAndFile); 2745 if (! *ext && fodInfos->defext) 2746 { 2747 /* if no extension is specified with file name, then */ 2748 /* attach the extension from file filter or default one */ 2749 2750 WCHAR *filterExt = NULL; 2751 LPWSTR lpstrFilter = NULL; 2752 static const WCHAR szwDot[] = {'.',0}; 2753 int PathLength = lstrlenW(lpstrPathAndFile); 2754 2755 /*Get the file extension from file type filter*/ 2756 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, 2757 fodInfos->ofnInfos->nFilterIndex-1); 2758 2759 if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */ 2760 { 2761 WCHAR* filterSearchIndex; 2762 filterExt = heap_alloc((lstrlenW(lpstrFilter) + 1) * sizeof(WCHAR)); 2763 strcpyW(filterExt, lpstrFilter); 2764 2765 /* if a semicolon-separated list of file extensions was given, do not include the 2766 semicolon or anything after it in the extension. 2767 example: if filterExt was "*.abc;*.def", it will become "*.abc" */ 2768 filterSearchIndex = strchrW(filterExt, ';'); 2769 if (filterSearchIndex) 2770 { 2771 filterSearchIndex[0] = '\0'; 2772 } 2773 2774 /* find the file extension by searching for the first dot in filterExt */ 2775 /* strip the * or anything else from the extension, "*.abc" becomes "abc" */ 2776 /* if the extension is invalid or contains a glob, ignore it */ 2777 filterSearchIndex = strchrW(filterExt, '.'); 2778 if (filterSearchIndex++ && !strchrW(filterSearchIndex, '*') && !strchrW(filterSearchIndex, '?')) 2779 { 2780 strcpyW(filterExt, filterSearchIndex); 2781 } 2782 else 2783 { 2784 heap_free(filterExt); 2785 filterExt = NULL; 2786 } 2787 } 2788 2789 if (!filterExt) 2790 { 2791 /* use the default file extension */ 2792 filterExt = heap_alloc((lstrlenW(fodInfos->defext) + 1) * sizeof(WCHAR)); 2793 strcpyW(filterExt, fodInfos->defext); 2794 } 2795 2796 if (*filterExt) /* ignore filterExt="" */ 2797 { 2798 /* Attach the dot*/ 2799 lstrcatW(lpstrPathAndFile, szwDot); 2800 /* Attach the extension */ 2801 lstrcatW(lpstrPathAndFile, filterExt); 2802 } 2803 2804 heap_free(filterExt); 2805 2806 /* In Open dialog: if file does not exist try without extension */ 2807 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile)) 2808 lpstrPathAndFile[PathLength] = '\0'; 2809 2810 /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */ 2811 if (*ext) 2812 ext++; 2813 if (!lstrcmpiW(fodInfos->defext, ext)) 2814 fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT; 2815 else 2816 fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT; 2817 } 2818 2819 /* In Save dialog: check if the file already exists */ 2820 if (fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG 2821 && fodInfos->ofnInfos->Flags & OFN_OVERWRITEPROMPT 2822 && PathFileExistsW(lpstrPathAndFile)) 2823 { 2824 WCHAR lpstrOverwrite[100]; 2825 int answer; 2826 2827 LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, lpstrOverwrite, 100); 2828 answer = MessageBoxW(hwnd, lpstrOverwrite, fodInfos->title, 2829 MB_YESNO | MB_ICONEXCLAMATION); 2830 if (answer == IDNO || answer == IDCANCEL) 2831 { 2832 ret = FALSE; 2833 goto ret; 2834 } 2835 } 2836 2837 /* In Open dialog: check if it should be created if it doesn't exist */ 2838 if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) 2839 && fodInfos->ofnInfos->Flags & OFN_CREATEPROMPT 2840 && !PathFileExistsW(lpstrPathAndFile)) 2841 { 2842 WCHAR lpstrCreate[100]; 2843 int answer; 2844 2845 LoadStringW(COMDLG32_hInstance, IDS_CREATEFILE, lpstrCreate, 100); 2846 answer = MessageBoxW(hwnd, lpstrCreate, fodInfos->title, 2847 MB_YESNO | MB_ICONEXCLAMATION); 2848 if (answer == IDNO || answer == IDCANCEL) 2849 { 2850 ret = FALSE; 2851 goto ret; 2852 } 2853 } 2854 2855 /* Check that the size of the file does not exceed buffer size. 2856 (Allow for extra \0 if OFN_MULTISELECT is set.) */ 2857 if(lstrlenW(lpstrPathAndFile) < fodInfos->ofnInfos->nMaxFile - 2858 ((fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) ? 1 : 0)) 2859 { 2860 2861 /* fill destination buffer */ 2862 if (fodInfos->ofnInfos->lpstrFile) 2863 { 2864 if(fodInfos->unicode) 2865 { 2866 LPOPENFILENAMEW ofn = fodInfos->ofnInfos; 2867 2868 lstrcpynW(ofn->lpstrFile, lpstrPathAndFile, ofn->nMaxFile); 2869 if (ofn->Flags & OFN_ALLOWMULTISELECT) 2870 ofn->lpstrFile[lstrlenW(ofn->lpstrFile) + 1] = '\0'; 2871 } 2872 else 2873 { 2874 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos; 2875 2876 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1, 2877 ofn->lpstrFile, ofn->nMaxFile, NULL, NULL); 2878 if (ofn->Flags & OFN_ALLOWMULTISELECT) 2879 ofn->lpstrFile[lstrlenA(ofn->lpstrFile) + 1] = '\0'; 2880 } 2881 } 2882 2883 if(fodInfos->unicode) 2884 { 2885 LPWSTR lpszTemp; 2886 2887 /* set filename offset */ 2888 lpszTemp = PathFindFileNameW(lpstrPathAndFile); 2889 fodInfos->ofnInfos->nFileOffset = (lpszTemp - lpstrPathAndFile); 2890 2891 /* set extension offset */ 2892 lpszTemp = PathFindExtensionW(lpstrPathAndFile); 2893 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - lpstrPathAndFile) + 1 : 0; 2894 } 2895 else 2896 { 2897 LPSTR lpszTemp; 2898 CHAR tempFileA[MAX_PATH]; 2899 2900 /* avoid using fodInfos->ofnInfos->lpstrFile since it can be NULL */ 2901 WideCharToMultiByte(CP_ACP, 0, lpstrPathAndFile, -1, 2902 tempFileA, sizeof(tempFileA), NULL, NULL); 2903 2904 /* set filename offset */ 2905 lpszTemp = PathFindFileNameA(tempFileA); 2906 fodInfos->ofnInfos->nFileOffset = (lpszTemp - tempFileA); 2907 2908 /* set extension offset */ 2909 lpszTemp = PathFindExtensionA(tempFileA); 2910 fodInfos->ofnInfos->nFileExtension = (*lpszTemp) ? (lpszTemp - tempFileA) + 1 : 0; 2911 } 2912 2913 /* copy currently selected filter to lpstrCustomFilter */ 2914 if (fodInfos->ofnInfos->lpstrCustomFilter) 2915 { 2916 LPOPENFILENAMEA ofn = (LPOPENFILENAMEA)fodInfos->ofnInfos; 2917 int len = WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1, 2918 NULL, 0, NULL, NULL); 2919 if (len + strlen(ofn->lpstrCustomFilter) + 1 <= ofn->nMaxCustFilter) 2920 { 2921 LPSTR s = ofn->lpstrCustomFilter; 2922 s += strlen(ofn->lpstrCustomFilter)+1; 2923 WideCharToMultiByte(CP_ACP, 0, fodInfos->ShellInfos.lpstrCurrentFilter, -1, 2924 s, len, NULL, NULL); 2925 } 2926 } 2927 2928 2929 if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) ) 2930 goto ret; 2931 2932 FILEDLG95_MRU_save_filename(lpstrPathAndFile); 2933 2934 TRACE("close\n"); 2935 FILEDLG95_Clean(hwnd); 2936 ret = EndDialog(hwnd, TRUE); 2937 } 2938 else 2939 { 2940 WORD size; 2941 2942 size = lstrlenW(lpstrPathAndFile) + 1; 2943 if (fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT) 2944 size += 1; 2945 /* return needed size in first two bytes of lpstrFile */ 2946 if(fodInfos->ofnInfos->lpstrFile) 2947 *(WORD *)fodInfos->ofnInfos->lpstrFile = size; 2948 FILEDLG95_Clean(hwnd); 2949 ret = EndDialog(hwnd, FALSE); 2950 COMDLG32_SetCommDlgExtendedError(FNERR_BUFFERTOOSMALL); 2951 } 2952 } 2953 break; 2954 } 2955 2956 ret: 2957 if(lpsf) IShellFolder_Release(lpsf); 2958 return ret; 2959 } 2960 2961 /*********************************************************************** 2962 * FILEDLG95_SHELL_Init 2963 * 2964 * Initialisation of the shell objects 2965 */ 2966 static LRESULT FILEDLG95_SHELL_Init(HWND hwnd) 2967 { 2968 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 2969 2970 TRACE("%p\n", hwnd); 2971 2972 /* 2973 * Initialisation of the FileOpenDialogInfos structure 2974 */ 2975 2976 /* Shell */ 2977 2978 /*ShellInfos */ 2979 fodInfos->ShellInfos.hwndOwner = hwnd; 2980 2981 /* Disable multi-select if flag not set */ 2982 if (!(fodInfos->ofnInfos->Flags & OFN_ALLOWMULTISELECT)) 2983 { 2984 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_SINGLESEL; 2985 } 2986 fodInfos->ShellInfos.folderSettings.fFlags |= FWF_AUTOARRANGE | FWF_ALIGNLEFT; 2987 fodInfos->ShellInfos.folderSettings.ViewMode = FVM_LIST; 2988 2989 /* Construct the IShellBrowser interface */ 2990 fodInfos->Shell.FOIShellBrowser = IShellBrowserImpl_Construct(hwnd); 2991 2992 return NOERROR; 2993 } 2994 2995 /*********************************************************************** 2996 * FILEDLG95_SHELL_ExecuteCommand 2997 * 2998 * Change the folder option and refresh the view 2999 * If the function succeeds, the return value is nonzero. 3000 */ 3001 static BOOL FILEDLG95_SHELL_ExecuteCommand(HWND hwnd, LPCSTR lpVerb) 3002 { 3003 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3004 IContextMenu * pcm; 3005 3006 TRACE("(%p,%p)\n", hwnd, lpVerb); 3007 3008 if(SUCCEEDED(IShellView_GetItemObject(fodInfos->Shell.FOIShellView, 3009 SVGIO_BACKGROUND, 3010 &IID_IContextMenu, 3011 (LPVOID*)&pcm))) 3012 { 3013 CMINVOKECOMMANDINFO ci; 3014 ZeroMemory(&ci, sizeof(CMINVOKECOMMANDINFO)); 3015 ci.cbSize = sizeof(CMINVOKECOMMANDINFO); 3016 ci.lpVerb = lpVerb; 3017 ci.hwnd = hwnd; 3018 3019 IContextMenu_InvokeCommand(pcm, &ci); 3020 IContextMenu_Release(pcm); 3021 } 3022 3023 return FALSE; 3024 } 3025 3026 /*********************************************************************** 3027 * FILEDLG95_SHELL_UpFolder 3028 * 3029 * Browse to the specified object 3030 * If the function succeeds, the return value is nonzero. 3031 */ 3032 static BOOL FILEDLG95_SHELL_UpFolder(HWND hwnd) 3033 { 3034 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3035 3036 TRACE("\n"); 3037 3038 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, 3039 NULL, 3040 SBSP_PARENT))) 3041 { 3042 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) 3043 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE); 3044 return TRUE; 3045 } 3046 return FALSE; 3047 } 3048 /*********************************************************************** 3049 * FILEDLG95_SHELL_Clean 3050 * 3051 * Cleans the memory used by shell objects 3052 */ 3053 static void FILEDLG95_SHELL_Clean(HWND hwnd) 3054 { 3055 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3056 3057 TRACE("\n"); 3058 3059 ILFree(fodInfos->ShellInfos.pidlAbsCurrent); 3060 3061 /* clean Shell interfaces */ 3062 if (fodInfos->Shell.FOIShellView) 3063 { 3064 IShellView_DestroyViewWindow(fodInfos->Shell.FOIShellView); 3065 IShellView_Release(fodInfos->Shell.FOIShellView); 3066 } 3067 if (fodInfos->Shell.FOIShellFolder) 3068 IShellFolder_Release(fodInfos->Shell.FOIShellFolder); 3069 IShellBrowser_Release(fodInfos->Shell.FOIShellBrowser); 3070 if (fodInfos->Shell.FOIDataObject) 3071 IDataObject_Release(fodInfos->Shell.FOIDataObject); 3072 } 3073 3074 /*********************************************************************** 3075 * FILEDLG95_FILETYPE_Init 3076 * 3077 * Initialisation of the file type combo box 3078 */ 3079 static HRESULT FILEDLG95_FILETYPE_Init(HWND hwnd) 3080 { 3081 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3082 int nFilters = 0; /* number of filters */ 3083 int nFilterIndexCB; 3084 3085 TRACE("%p\n", hwnd); 3086 3087 if(fodInfos->customfilter) 3088 { 3089 /* customfilter has one entry... title\0ext\0 3090 * Set first entry of combo box item with customfilter 3091 */ 3092 LPWSTR lpstrExt; 3093 LPCWSTR lpstrPos = fodInfos->customfilter; 3094 3095 /* Get the title */ 3096 lpstrPos += lstrlenW(fodInfos->customfilter) + 1; 3097 3098 /* Copy the extensions */ 3099 if (! *lpstrPos) return E_FAIL; /* malformed filter */ 3100 if (!(lpstrExt = heap_alloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL; 3101 lstrcpyW(lpstrExt,lpstrPos); 3102 3103 /* Add the item at the end of the combo */ 3104 SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_ADDSTRING, 0, (LPARAM)fodInfos->customfilter); 3105 SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETITEMDATA, nFilters, (LPARAM)lpstrExt); 3106 3107 nFilters++; 3108 } 3109 if(fodInfos->filter) 3110 { 3111 LPCWSTR lpstrPos = fodInfos->filter; 3112 3113 for(;;) 3114 { 3115 /* filter is a list... title\0ext\0......\0\0 3116 * Set the combo item text to the title and the item data 3117 * to the ext 3118 */ 3119 LPCWSTR lpstrDisplay; 3120 LPWSTR lpstrExt; 3121 3122 /* Get the title */ 3123 if(! *lpstrPos) break; /* end */ 3124 lpstrDisplay = lpstrPos; 3125 lpstrPos += lstrlenW(lpstrPos) + 1; 3126 3127 SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_ADDSTRING, 0, (LPARAM)lpstrDisplay); 3128 3129 nFilters++; 3130 3131 /* Copy the extensions */ 3132 if (!(lpstrExt = heap_alloc((lstrlenW(lpstrPos)+1)*sizeof(WCHAR)))) return E_FAIL; 3133 lstrcpyW(lpstrExt,lpstrPos); 3134 lpstrPos += lstrlenW(lpstrPos) + 1; 3135 3136 /* Add the item at the end of the combo */ 3137 SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETITEMDATA, nFilters - 1, (LPARAM)lpstrExt); 3138 3139 /* malformed filters are added anyway... */ 3140 if (!*lpstrExt) break; 3141 } 3142 } 3143 3144 /* 3145 * Set the current filter to the one specified 3146 * in the initialisation structure 3147 */ 3148 if (fodInfos->filter || fodInfos->customfilter) 3149 { 3150 LPWSTR lpstrFilter; 3151 3152 /* Check to make sure our index isn't out of bounds. */ 3153 if ( fodInfos->ofnInfos->nFilterIndex > 3154 nFilters - (fodInfos->customfilter == NULL ? 0 : 1) ) 3155 fodInfos->ofnInfos->nFilterIndex = (fodInfos->customfilter == NULL ? 1 : 0); 3156 3157 /* set default filter index */ 3158 if(fodInfos->ofnInfos->nFilterIndex == 0 && fodInfos->customfilter == NULL) 3159 fodInfos->ofnInfos->nFilterIndex = 1; 3160 3161 /* calculate index of Combo Box item */ 3162 nFilterIndexCB = fodInfos->ofnInfos->nFilterIndex; 3163 if (fodInfos->customfilter == NULL) 3164 nFilterIndexCB--; 3165 3166 /* Set the current index selection. */ 3167 SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_SETCURSEL, nFilterIndexCB, 0); 3168 3169 /* Get the corresponding text string from the combo box. */ 3170 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, 3171 nFilterIndexCB); 3172 3173 if ((INT_PTR)lpstrFilter == CB_ERR) /* control is empty */ 3174 lpstrFilter = NULL; 3175 3176 if(lpstrFilter) 3177 { 3178 DWORD len; 3179 CharLowerW(lpstrFilter); /* lowercase */ 3180 len = lstrlenW(lpstrFilter)+1; 3181 fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc( len * sizeof(WCHAR) ); 3182 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter); 3183 } 3184 } else 3185 fodInfos->ofnInfos->nFilterIndex = 0; 3186 return S_OK; 3187 } 3188 3189 /*********************************************************************** 3190 * FILEDLG95_FILETYPE_OnCommand 3191 * 3192 * WM_COMMAND of the file type combo box 3193 * If the function succeeds, the return value is nonzero. 3194 */ 3195 static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode) 3196 { 3197 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3198 3199 switch(wNotifyCode) 3200 { 3201 case CBN_SELENDOK: 3202 { 3203 LPWSTR lpstrFilter; 3204 3205 /* Get the current item of the filetype combo box */ 3206 int iItem = SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_GETCURSEL, 0, 0); 3207 3208 /* set the current filter index */ 3209 fodInfos->ofnInfos->nFilterIndex = iItem + 3210 (fodInfos->customfilter == NULL ? 1 : 0); 3211 3212 /* Set the current filter with the current selection */ 3213 heap_free(fodInfos->ShellInfos.lpstrCurrentFilter); 3214 3215 lpstrFilter = (LPWSTR) CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB, 3216 iItem); 3217 if((INT_PTR)lpstrFilter != CB_ERR) 3218 { 3219 DWORD len; 3220 CharLowerW(lpstrFilter); /* lowercase */ 3221 len = lstrlenW(lpstrFilter)+1; 3222 fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc( len * sizeof(WCHAR) ); 3223 lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter); 3224 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) 3225 SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE); 3226 } 3227 3228 /* Refresh the actual view to display the included items*/ 3229 if (fodInfos->Shell.FOIShellView) 3230 IShellView_Refresh(fodInfos->Shell.FOIShellView); 3231 } 3232 } 3233 return FALSE; 3234 } 3235 /*********************************************************************** 3236 * FILEDLG95_FILETYPE_SearchExt 3237 * 3238 * searches for an extension in the filetype box 3239 */ 3240 static int FILEDLG95_FILETYPE_SearchExt(HWND hwnd,LPCWSTR lpstrExt) 3241 { 3242 int i, iCount; 3243 3244 iCount = SendMessageW(hwnd, CB_GETCOUNT, 0, 0); 3245 3246 TRACE("%s\n", debugstr_w(lpstrExt)); 3247 3248 if(iCount != CB_ERR) 3249 { 3250 for(i=0;i<iCount;i++) 3251 { 3252 if(!lstrcmpiW(lpstrExt,(LPWSTR)CBGetItemDataPtr(hwnd,i))) 3253 return i; 3254 } 3255 } 3256 return -1; 3257 } 3258 3259 /*********************************************************************** 3260 * FILEDLG95_FILETYPE_Clean 3261 * 3262 * Clean the memory used by the filetype combo box 3263 */ 3264 static void FILEDLG95_FILETYPE_Clean(HWND hwnd) 3265 { 3266 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3267 int iPos; 3268 int iCount; 3269 3270 iCount = SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_GETCOUNT, 0, 0); 3271 3272 TRACE("\n"); 3273 3274 /* Delete each string of the combo and their associated data */ 3275 if(iCount != CB_ERR) 3276 { 3277 for(iPos = iCount-1;iPos>=0;iPos--) 3278 { 3279 heap_free((void *)CBGetItemDataPtr(fodInfos->DlgInfos.hwndFileTypeCB,iPos)); 3280 SendMessageW(fodInfos->DlgInfos.hwndFileTypeCB, CB_DELETESTRING, iPos, 0); 3281 } 3282 } 3283 /* Current filter */ 3284 heap_free(fodInfos->ShellInfos.lpstrCurrentFilter); 3285 } 3286 3287 /*********************************************************************** 3288 * FILEDLG95_LOOKIN_Init 3289 * 3290 * Initialisation of the look in combo box 3291 */ 3292 3293 /* Small helper function, to determine if the unixfs shell extension is rooted 3294 * at the desktop. Copied from dlls/shell32/shfldr_unixfs.c. 3295 */ 3296 static inline BOOL FILEDLG95_unixfs_is_rooted_at_desktop(void) { 3297 HKEY hKey; 3298 static const WCHAR wszRootedAtDesktop[] = { 'S','o','f','t','w','a','r','e','\\', 3299 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', 3300 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', 3301 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\', 3302 'N','a','m','e','S','p','a','c','e','\\','{','9','D','2','0','A','A','E','8', 3303 '-','0','6','2','5','-','4','4','B','0','-','9','C','A','7','-', 3304 '7','1','8','8','9','C','2','2','5','4','D','9','}',0 }; 3305 3306 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS) 3307 return FALSE; 3308 3309 RegCloseKey(hKey); 3310 return TRUE; 3311 } 3312 3313 static void FILEDLG95_LOOKIN_Init(HWND hwndCombo) 3314 { 3315 IShellFolder *psfRoot, *psfDrives; 3316 IEnumIDList *lpeRoot, *lpeDrives; 3317 LPITEMIDLIST pidlDrives, pidlTmp, pidlTmp1, pidlAbsTmp; 3318 HDC hdc; 3319 TEXTMETRICW tm; 3320 LookInInfos *liInfos = heap_alloc_zero(sizeof(*liInfos)); 3321 3322 TRACE("%p\n", hwndCombo); 3323 3324 liInfos->iMaxIndentation = 0; 3325 3326 SetPropA(hwndCombo, LookInInfosStr, liInfos); 3327 3328 hdc = GetDC( hwndCombo ); 3329 SelectObject( hdc, (HFONT)SendMessageW( hwndCombo, WM_GETFONT, 0, 0 )); 3330 GetTextMetricsW( hdc, &tm ); 3331 ReleaseDC( hwndCombo, hdc ); 3332 3333 /* set item height for both text field and listbox */ 3334 SendMessageW(hwndCombo, CB_SETITEMHEIGHT, -1, max(tm.tmHeight, GetSystemMetrics(SM_CYSMICON))); 3335 SendMessageW(hwndCombo, CB_SETITEMHEIGHT, 0, max(tm.tmHeight, GetSystemMetrics(SM_CYSMICON))); 3336 3337 /* Turn on the extended UI for the combo box like Windows does */ 3338 SendMessageW(hwndCombo, CB_SETEXTENDEDUI, TRUE, 0); 3339 3340 /* Initialise data of Desktop folder */ 3341 SHGetSpecialFolderLocation(0,CSIDL_DESKTOP,&pidlTmp); 3342 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND); 3343 ILFree(pidlTmp); 3344 3345 SHGetSpecialFolderLocation(0,CSIDL_DRIVES,&pidlDrives); 3346 3347 SHGetDesktopFolder(&psfRoot); 3348 3349 if (psfRoot) 3350 { 3351 /* enumerate the contents of the desktop */ 3352 if(SUCCEEDED(IShellFolder_EnumObjects(psfRoot, hwndCombo, SHCONTF_FOLDERS, &lpeRoot))) 3353 { 3354 while (S_OK == IEnumIDList_Next(lpeRoot, 1, &pidlTmp, NULL)) 3355 { 3356 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlTmp,LISTEND); 3357 3358 /* If the unixfs extension is rooted, we don't expand the drives by default */ 3359 if (!FILEDLG95_unixfs_is_rooted_at_desktop()) 3360 { 3361 /* special handling for CSIDL_DRIVES */ 3362 if (ILIsEqual(pidlTmp, pidlDrives)) 3363 { 3364 if(SUCCEEDED(IShellFolder_BindToObject(psfRoot, pidlTmp, NULL, &IID_IShellFolder, (LPVOID*)&psfDrives))) 3365 { 3366 /* enumerate the drives */ 3367 if(SUCCEEDED(IShellFolder_EnumObjects(psfDrives, hwndCombo,SHCONTF_FOLDERS, &lpeDrives))) 3368 { 3369 while (S_OK == IEnumIDList_Next(lpeDrives, 1, &pidlTmp1, NULL)) 3370 { 3371 pidlAbsTmp = ILCombine(pidlTmp, pidlTmp1); 3372 FILEDLG95_LOOKIN_AddItem(hwndCombo, pidlAbsTmp,LISTEND); 3373 ILFree(pidlAbsTmp); 3374 ILFree(pidlTmp1); 3375 } 3376 IEnumIDList_Release(lpeDrives); 3377 } 3378 IShellFolder_Release(psfDrives); 3379 } 3380 } 3381 } 3382 3383 ILFree(pidlTmp); 3384 } 3385 IEnumIDList_Release(lpeRoot); 3386 } 3387 IShellFolder_Release(psfRoot); 3388 } 3389 3390 ILFree(pidlDrives); 3391 } 3392 3393 /*********************************************************************** 3394 * FILEDLG95_LOOKIN_DrawItem 3395 * 3396 * WM_DRAWITEM message handler 3397 */ 3398 static LRESULT FILEDLG95_LOOKIN_DrawItem(LPDRAWITEMSTRUCT pDIStruct) 3399 { 3400 COLORREF crWin = GetSysColor(COLOR_WINDOW); 3401 COLORREF crHighLight = GetSysColor(COLOR_HIGHLIGHT); 3402 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT); 3403 RECT rectText; 3404 RECT rectIcon; 3405 SHFILEINFOW sfi; 3406 HIMAGELIST ilItemImage; 3407 int iIndentation; 3408 TEXTMETRICW tm; 3409 LPSFOLDER tmpFolder; 3410 UINT shgfi_flags = SHGFI_PIDL | SHGFI_OPENICON | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME; 3411 UINT icon_width, icon_height; 3412 3413 TRACE("\n"); 3414 3415 if(pDIStruct->itemID == -1) 3416 return 0; 3417 3418 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(pDIStruct->hwndItem, 3419 pDIStruct->itemID))) 3420 return 0; 3421 3422 3423 icon_width = GetSystemMetrics(SM_CXICON); 3424 icon_height = GetSystemMetrics(SM_CYICON); 3425 if (pDIStruct->rcItem.bottom - pDIStruct->rcItem.top < icon_height) 3426 { 3427 icon_width = GetSystemMetrics(SM_CXSMICON); 3428 icon_height = GetSystemMetrics(SM_CYSMICON); 3429 shgfi_flags |= SHGFI_SMALLICON; 3430 } 3431 3432 ilItemImage = (HIMAGELIST) SHGetFileInfoW ((LPCWSTR) tmpFolder->pidlItem, 3433 0, &sfi, sizeof (sfi), shgfi_flags ); 3434 3435 /* Is this item selected ? */ 3436 if(pDIStruct->itemState & ODS_SELECTED) 3437 { 3438 SetTextColor(pDIStruct->hDC,(0x00FFFFFF & ~(crText))); 3439 SetBkColor(pDIStruct->hDC,crHighLight); 3440 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT)); 3441 } 3442 else 3443 { 3444 SetTextColor(pDIStruct->hDC,crText); 3445 SetBkColor(pDIStruct->hDC,crWin); 3446 FillRect(pDIStruct->hDC,&pDIStruct->rcItem,GetSysColorBrush(COLOR_WINDOW)); 3447 } 3448 3449 /* Do not indent item if drawing in the edit of the combo */ 3450 if(pDIStruct->itemState & ODS_COMBOBOXEDIT) 3451 iIndentation = 0; 3452 else 3453 iIndentation = tmpFolder->m_iIndent; 3454 3455 /* Draw text and icon */ 3456 3457 /* Initialise the icon display area */ 3458 rectIcon.left = pDIStruct->rcItem.left + 1 + icon_width/2 * iIndentation; 3459 rectIcon.top = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - icon_height) / 2; 3460 rectIcon.right = rectIcon.left + icon_width + XTEXTOFFSET; 3461 rectIcon.bottom = (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + icon_height) / 2; 3462 3463 /* Initialise the text display area */ 3464 GetTextMetricsW(pDIStruct->hDC, &tm); 3465 rectText.left = rectIcon.right; 3466 rectText.top = 3467 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom - tm.tmHeight) / 2; 3468 rectText.right = pDIStruct->rcItem.right; 3469 rectText.bottom = 3470 (pDIStruct->rcItem.top + pDIStruct->rcItem.bottom + tm.tmHeight) / 2; 3471 3472 /* Draw the icon from the image list */ 3473 ImageList_Draw(ilItemImage, 3474 sfi.iIcon, 3475 pDIStruct->hDC, 3476 rectIcon.left, 3477 rectIcon.top, 3478 ILD_TRANSPARENT ); 3479 3480 /* Draw the associated text */ 3481 TextOutW(pDIStruct->hDC,rectText.left,rectText.top,sfi.szDisplayName,lstrlenW(sfi.szDisplayName)); 3482 return NOERROR; 3483 } 3484 3485 /*********************************************************************** 3486 * FILEDLG95_LOOKIN_OnCommand 3487 * 3488 * LookIn combo box WM_COMMAND message handler 3489 * If the function succeeds, the return value is nonzero. 3490 */ 3491 static BOOL FILEDLG95_LOOKIN_OnCommand(HWND hwnd, WORD wNotifyCode) 3492 { 3493 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3494 3495 TRACE("%p\n", fodInfos); 3496 3497 switch(wNotifyCode) 3498 { 3499 case CBN_SELENDOK: 3500 { 3501 LPSFOLDER tmpFolder; 3502 int iItem; 3503 3504 iItem = SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_GETCURSEL, 0, 0); 3505 3506 if( iItem == CB_ERR) return FALSE; 3507 3508 if(!(tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB, 3509 iItem))) 3510 return FALSE; 3511 3512 3513 if(SUCCEEDED(IShellBrowser_BrowseObject(fodInfos->Shell.FOIShellBrowser, 3514 tmpFolder->pidlItem, 3515 SBSP_ABSOLUTE))) 3516 { 3517 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) 3518 SendCustomDlgNotificationMessage(hwnd, CDN_FOLDERCHANGE); 3519 return TRUE; 3520 } 3521 break; 3522 } 3523 3524 } 3525 return FALSE; 3526 } 3527 3528 /*********************************************************************** 3529 * FILEDLG95_LOOKIN_AddItem 3530 * 3531 * Adds an absolute pidl item to the lookin combo box 3532 * returns the index of the inserted item 3533 */ 3534 static int FILEDLG95_LOOKIN_AddItem(HWND hwnd,LPITEMIDLIST pidl, int iInsertId) 3535 { 3536 LPITEMIDLIST pidlNext; 3537 SHFILEINFOW sfi; 3538 SFOLDER *tmpFolder; 3539 LookInInfos *liInfos; 3540 3541 TRACE("%p, %p, %d\n", hwnd, pidl, iInsertId); 3542 3543 if(!pidl) 3544 return -1; 3545 3546 if(!(liInfos = GetPropA(hwnd,LookInInfosStr))) 3547 return -1; 3548 3549 tmpFolder = heap_alloc_zero(sizeof(*tmpFolder)); 3550 tmpFolder->m_iIndent = 0; 3551 3552 /* Calculate the indentation of the item in the lookin*/ 3553 pidlNext = pidl; 3554 while ((pidlNext = ILGetNext(pidlNext))) 3555 { 3556 tmpFolder->m_iIndent++; 3557 } 3558 3559 tmpFolder->pidlItem = ILClone(pidl); 3560 3561 if(tmpFolder->m_iIndent > liInfos->iMaxIndentation) 3562 liInfos->iMaxIndentation = tmpFolder->m_iIndent; 3563 3564 sfi.dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM; 3565 SHGetFileInfoW((LPCWSTR)pidl, 3566 0, 3567 &sfi, 3568 sizeof(sfi), 3569 SHGFI_DISPLAYNAME | SHGFI_PIDL | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED); 3570 3571 TRACE("-- Add %s attr=0x%08x\n", debugstr_w(sfi.szDisplayName), sfi.dwAttributes); 3572 3573 if((sfi.dwAttributes & SFGAO_FILESYSANCESTOR) || (sfi.dwAttributes & SFGAO_FILESYSTEM)) 3574 { 3575 int iItemID; 3576 3577 TRACE("-- Add %s at %u\n", debugstr_w(sfi.szDisplayName), tmpFolder->m_iIndent); 3578 3579 /* Add the item at the end of the list */ 3580 if(iInsertId < 0) 3581 { 3582 iItemID = SendMessageW(hwnd, CB_ADDSTRING, 0, (LPARAM)sfi.szDisplayName); 3583 } 3584 /* Insert the item at the iInsertId position*/ 3585 else 3586 { 3587 iItemID = SendMessageW(hwnd, CB_INSERTSTRING, iInsertId, (LPARAM)sfi.szDisplayName); 3588 } 3589 3590 SendMessageW(hwnd, CB_SETITEMDATA, iItemID, (LPARAM)tmpFolder); 3591 return iItemID; 3592 } 3593 3594 ILFree( tmpFolder->pidlItem ); 3595 heap_free( tmpFolder ); 3596 return -1; 3597 3598 } 3599 3600 /*********************************************************************** 3601 * FILEDLG95_LOOKIN_InsertItemAfterParent 3602 * 3603 * Insert an item below its parent 3604 */ 3605 static int FILEDLG95_LOOKIN_InsertItemAfterParent(HWND hwnd,LPITEMIDLIST pidl) 3606 { 3607 3608 LPITEMIDLIST pidlParent = GetParentPidl(pidl); 3609 int iParentPos; 3610 3611 TRACE("\n"); 3612 3613 if (pidl == pidlParent) 3614 return -1; 3615 3616 iParentPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidlParent,SEARCH_PIDL); 3617 3618 if(iParentPos < 0) 3619 { 3620 iParentPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidlParent); 3621 } 3622 3623 ILFree(pidlParent); 3624 3625 return FILEDLG95_LOOKIN_AddItem(hwnd,pidl,iParentPos + 1); 3626 } 3627 3628 /*********************************************************************** 3629 * FILEDLG95_LOOKIN_SelectItem 3630 * 3631 * Adds an absolute pidl item to the lookin combo box 3632 * returns the index of the inserted item 3633 */ 3634 int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl) 3635 { 3636 int iItemPos; 3637 LookInInfos *liInfos; 3638 3639 TRACE("%p, %p\n", hwnd, pidl); 3640 3641 iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,(WPARAM)pidl,SEARCH_PIDL); 3642 3643 liInfos = GetPropA(hwnd,LookInInfosStr); 3644 3645 if(iItemPos < 0) 3646 { 3647 while(FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd) > -1); 3648 iItemPos = FILEDLG95_LOOKIN_InsertItemAfterParent(hwnd,pidl); 3649 } 3650 3651 else 3652 { 3653 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos); 3654 while(liInfos->iMaxIndentation > tmpFolder->m_iIndent) 3655 { 3656 int iRemovedItem; 3657 3658 if(-1 == (iRemovedItem = FILEDLG95_LOOKIN_RemoveMostExpandedItem(hwnd))) 3659 break; 3660 if(iRemovedItem < iItemPos) 3661 iItemPos--; 3662 } 3663 } 3664 3665 SendMessageW(hwnd, CB_SETCURSEL, iItemPos, 0); 3666 liInfos->uSelectedItem = iItemPos; 3667 3668 return 0; 3669 3670 } 3671 3672 /*********************************************************************** 3673 * FILEDLG95_LOOKIN_RemoveMostExpandedItem 3674 * 3675 * Remove the item with an expansion level over iExpansionLevel 3676 */ 3677 static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd) 3678 { 3679 int iItemPos; 3680 LookInInfos *liInfos = GetPropA(hwnd,LookInInfosStr); 3681 3682 TRACE("\n"); 3683 3684 if(liInfos->iMaxIndentation <= 2) 3685 return -1; 3686 3687 if((iItemPos = FILEDLG95_LOOKIN_SearchItem(hwnd,liInfos->iMaxIndentation,SEARCH_EXP)) >=0) 3688 { 3689 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,iItemPos); 3690 ILFree(tmpFolder->pidlItem); 3691 heap_free(tmpFolder); 3692 SendMessageW(hwnd, CB_DELETESTRING, iItemPos, 0); 3693 liInfos->iMaxIndentation--; 3694 3695 return iItemPos; 3696 } 3697 3698 return -1; 3699 } 3700 3701 /*********************************************************************** 3702 * FILEDLG95_LOOKIN_SearchItem 3703 * 3704 * Search for pidl in the lookin combo box 3705 * returns the index of the found item 3706 */ 3707 static int FILEDLG95_LOOKIN_SearchItem(HWND hwnd,WPARAM searchArg,int iSearchMethod) 3708 { 3709 int i = 0; 3710 int iCount; 3711 3712 iCount = SendMessageW(hwnd, CB_GETCOUNT, 0, 0); 3713 3714 TRACE("0x%08lx 0x%x\n",searchArg, iSearchMethod); 3715 3716 if (iCount != CB_ERR) 3717 { 3718 for(;i<iCount;i++) 3719 { 3720 LPSFOLDER tmpFolder = (LPSFOLDER) CBGetItemDataPtr(hwnd,i); 3721 3722 if (iSearchMethod == SEARCH_PIDL && ILIsEqual((LPITEMIDLIST)searchArg, tmpFolder->pidlItem)) 3723 return i; 3724 if(iSearchMethod == SEARCH_EXP && tmpFolder->m_iIndent == (int)searchArg) 3725 return i; 3726 } 3727 } 3728 3729 return -1; 3730 } 3731 3732 /*********************************************************************** 3733 * FILEDLG95_LOOKIN_Clean 3734 * 3735 * Clean the memory used by the lookin combo box 3736 */ 3737 static void FILEDLG95_LOOKIN_Clean(HWND hwnd) 3738 { 3739 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3740 LookInInfos *liInfos = GetPropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr); 3741 int iPos, iCount; 3742 3743 iCount = SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_GETCOUNT, 0, 0); 3744 3745 TRACE("\n"); 3746 3747 /* Delete each string of the combo and their associated data */ 3748 if (iCount != CB_ERR) 3749 { 3750 for(iPos = iCount-1;iPos>=0;iPos--) 3751 { 3752 SFOLDER *tmpFolder = (LPSFOLDER) CBGetItemDataPtr(fodInfos->DlgInfos.hwndLookInCB,iPos); 3753 ILFree(tmpFolder->pidlItem); 3754 heap_free(tmpFolder); 3755 SendMessageW(fodInfos->DlgInfos.hwndLookInCB, CB_DELETESTRING, iPos, 0); 3756 } 3757 } 3758 3759 /* LookInInfos structure */ 3760 heap_free(liInfos); 3761 RemovePropA(fodInfos->DlgInfos.hwndLookInCB,LookInInfosStr); 3762 } 3763 3764 /*********************************************************************** 3765 * get_def_format 3766 * 3767 * Fill the FORMATETC used in the shell id list 3768 */ 3769 static FORMATETC get_def_format(void) 3770 { 3771 static CLIPFORMAT cfFormat; 3772 FORMATETC formatetc; 3773 3774 if (!cfFormat) cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA); 3775 formatetc.cfFormat = cfFormat; 3776 formatetc.ptd = 0; 3777 formatetc.dwAspect = DVASPECT_CONTENT; 3778 formatetc.lindex = -1; 3779 formatetc.tymed = TYMED_HGLOBAL; 3780 return formatetc; 3781 } 3782 3783 /*********************************************************************** 3784 * FILEDLG95_FILENAME_FillFromSelection 3785 * 3786 * fills the edit box from the cached DataObject 3787 */ 3788 void FILEDLG95_FILENAME_FillFromSelection (HWND hwnd) 3789 { 3790 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3791 LPITEMIDLIST pidl; 3792 LPWSTR lpstrAllFiles, lpstrTmp; 3793 UINT nFiles = 0, nFileToOpen, nFileSelected, nAllFilesLength = 0, nThisFileLength, nAllFilesMaxLength; 3794 STGMEDIUM medium; 3795 LPIDA cida; 3796 FORMATETC formatetc = get_def_format(); 3797 3798 TRACE("\n"); 3799 3800 if (FAILED(IDataObject_GetData(fodInfos->Shell.FOIDataObject, &formatetc, &medium))) 3801 return; 3802 3803 cida = GlobalLock(medium.u.hGlobal); 3804 nFileSelected = cida->cidl; 3805 3806 /* Allocate a buffer */ 3807 nAllFilesMaxLength = MAX_PATH + 3; 3808 lpstrAllFiles = heap_alloc_zero(nAllFilesMaxLength * sizeof(WCHAR)); 3809 if (!lpstrAllFiles) 3810 goto ret; 3811 3812 /* Loop through the selection, handle only files (not folders) */ 3813 for (nFileToOpen = 0; nFileToOpen < nFileSelected; nFileToOpen++) 3814 { 3815 pidl = (LPITEMIDLIST)((LPBYTE)cida + cida->aoffset[nFileToOpen + 1]); 3816 if (pidl) 3817 { 3818 if (!IsPidlFolder(fodInfos->Shell.FOIShellFolder, pidl)) 3819 { 3820 if (nAllFilesLength + MAX_PATH + 3 > nAllFilesMaxLength) 3821 { 3822 nAllFilesMaxLength *= 2; 3823 lpstrTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpstrAllFiles, nAllFilesMaxLength * sizeof(WCHAR)); 3824 if (!lpstrTmp) 3825 goto ret; 3826 lpstrAllFiles = lpstrTmp; 3827 } 3828 nFiles += 1; 3829 lpstrAllFiles[nAllFilesLength++] = '"'; 3830 GetName(fodInfos->Shell.FOIShellFolder, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, lpstrAllFiles + nAllFilesLength); 3831 nThisFileLength = lstrlenW(lpstrAllFiles + nAllFilesLength); 3832 nAllFilesLength += nThisFileLength; 3833 lpstrAllFiles[nAllFilesLength++] = '"'; 3834 lpstrAllFiles[nAllFilesLength++] = ' '; 3835 } 3836 } 3837 } 3838 3839 if (nFiles != 0) 3840 { 3841 /* If there's only one file, use the name as-is without quotes */ 3842 lpstrTmp = lpstrAllFiles; 3843 if (nFiles == 1) 3844 { 3845 lpstrTmp += 1; 3846 lpstrTmp[nThisFileLength] = 0; 3847 } 3848 SetWindowTextW(fodInfos->DlgInfos.hwndFileName, lpstrTmp); 3849 /* Select the file name like Windows does */ 3850 if (filename_is_edit(fodInfos)) 3851 SendMessageW(fodInfos->DlgInfos.hwndFileName, EM_SETSEL, 0, -1); 3852 } 3853 3854 ret: 3855 heap_free(lpstrAllFiles); 3856 COMCTL32_ReleaseStgMedium(medium); 3857 } 3858 3859 3860 /* copied from shell32 to avoid linking to it 3861 * Although shell32 is already linked the behaviour of exported StrRetToStrN 3862 * is dependent on whether emulated OS is unicode or not. 3863 */ 3864 static HRESULT COMDLG32_StrRetToStrNW (LPWSTR dest, DWORD len, LPSTRRET src, const ITEMIDLIST *pidl) 3865 { 3866 switch (src->uType) 3867 { 3868 case STRRET_WSTR: 3869 lstrcpynW(dest, src->u.pOleStr, len); 3870 CoTaskMemFree(src->u.pOleStr); 3871 break; 3872 3873 case STRRET_CSTR: 3874 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len) 3875 dest[len-1] = 0; 3876 break; 3877 3878 case STRRET_OFFSET: 3879 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1, dest, len ) && len) 3880 dest[len-1] = 0; 3881 break; 3882 3883 default: 3884 FIXME("unknown type %x!\n", src->uType); 3885 if (len) *dest = '\0'; 3886 return E_FAIL; 3887 } 3888 return S_OK; 3889 } 3890 3891 /*********************************************************************** 3892 * FILEDLG95_FILENAME_GetFileNames 3893 * 3894 * Copies the filenames to a delimited string list. 3895 */ 3896 static int FILEDLG95_FILENAME_GetFileNames (HWND hwnd, LPWSTR * lpstrFileList, UINT * sizeUsed) 3897 { 3898 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 3899 UINT nFileCount = 0; /* number of files */ 3900 UINT nStrLen = 0; /* length of string in edit control */ 3901 LPWSTR lpstrEdit; /* buffer for string from edit control */ 3902 3903 TRACE("\n"); 3904 3905 /* get the filenames from the filename control */ 3906 nStrLen = GetWindowTextLengthW( fodInfos->DlgInfos.hwndFileName ); 3907 lpstrEdit = heap_alloc( (nStrLen+1)*sizeof(WCHAR) ); 3908 GetWindowTextW( fodInfos->DlgInfos.hwndFileName, lpstrEdit, nStrLen+1); 3909 3910 TRACE("nStrLen=%u str=%s\n", nStrLen, debugstr_w(lpstrEdit)); 3911 3912 nFileCount = COMDLG32_SplitFileNames(lpstrEdit, nStrLen, lpstrFileList, sizeUsed); 3913 heap_free(lpstrEdit); 3914 return nFileCount; 3915 } 3916 3917 /* 3918 * DATAOBJECT Helper functions 3919 */ 3920 3921 /*********************************************************************** 3922 * COMCTL32_ReleaseStgMedium 3923 * 3924 * like ReleaseStgMedium from ole32 3925 */ 3926 static void COMCTL32_ReleaseStgMedium (STGMEDIUM medium) 3927 { 3928 if(medium.pUnkForRelease) 3929 { 3930 IUnknown_Release(medium.pUnkForRelease); 3931 } 3932 else 3933 { 3934 GlobalUnlock(medium.u.hGlobal); 3935 GlobalFree(medium.u.hGlobal); 3936 } 3937 } 3938 3939 /*********************************************************************** 3940 * GetPidlFromDataObject 3941 * 3942 * Return pidl(s) by number from the cached DataObject 3943 * 3944 * nPidlIndex=0 gets the fully qualified root path 3945 */ 3946 LPITEMIDLIST GetPidlFromDataObject ( IDataObject *doSelected, UINT nPidlIndex) 3947 { 3948 3949 STGMEDIUM medium; 3950 FORMATETC formatetc = get_def_format(); 3951 LPITEMIDLIST pidl = NULL; 3952 3953 TRACE("sv=%p index=%u\n", doSelected, nPidlIndex); 3954 3955 if (!doSelected) 3956 return NULL; 3957 3958 /* Get the pidls from IDataObject */ 3959 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium))) 3960 { 3961 LPIDA cida = GlobalLock(medium.u.hGlobal); 3962 if(nPidlIndex <= cida->cidl) 3963 { 3964 pidl = ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[nPidlIndex]])); 3965 } 3966 COMCTL32_ReleaseStgMedium(medium); 3967 } 3968 return pidl; 3969 } 3970 3971 /*********************************************************************** 3972 * GetNumSelected 3973 * 3974 * Return the number of selected items in the DataObject. 3975 * 3976 */ 3977 static UINT GetNumSelected( IDataObject *doSelected ) 3978 { 3979 UINT retVal = 0; 3980 STGMEDIUM medium; 3981 FORMATETC formatetc = get_def_format(); 3982 3983 TRACE("sv=%p\n", doSelected); 3984 3985 if (!doSelected) return 0; 3986 3987 /* Get the pidls from IDataObject */ 3988 if(SUCCEEDED(IDataObject_GetData(doSelected,&formatetc,&medium))) 3989 { 3990 LPIDA cida = GlobalLock(medium.u.hGlobal); 3991 retVal = cida->cidl; 3992 COMCTL32_ReleaseStgMedium(medium); 3993 return retVal; 3994 } 3995 return 0; 3996 } 3997 3998 /* 3999 * TOOLS 4000 */ 4001 4002 /*********************************************************************** 4003 * GetName 4004 * 4005 * Get the pidl's display name (relative to folder) and 4006 * put it in lpstrFileName. 4007 * 4008 * Return NOERROR on success, 4009 * E_FAIL otherwise 4010 */ 4011 4012 static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName) 4013 { 4014 STRRET str; 4015 HRESULT hRes; 4016 4017 TRACE("sf=%p pidl=%p\n", lpsf, pidl); 4018 4019 if(!lpsf) 4020 { 4021 SHGetDesktopFolder(&lpsf); 4022 hRes = GetName(lpsf,pidl,dwFlags,lpstrFileName); 4023 IShellFolder_Release(lpsf); 4024 return hRes; 4025 } 4026 4027 /* Get the display name of the pidl relative to the folder */ 4028 if (SUCCEEDED(hRes = IShellFolder_GetDisplayNameOf(lpsf, pidl, dwFlags, &str))) 4029 { 4030 return COMDLG32_StrRetToStrNW(lpstrFileName, MAX_PATH, &str, pidl); 4031 } 4032 return E_FAIL; 4033 } 4034 4035 /*********************************************************************** 4036 * GetShellFolderFromPidl 4037 * 4038 * pidlRel is the item pidl relative 4039 * Return the IShellFolder of the absolute pidl 4040 */ 4041 IShellFolder *GetShellFolderFromPidl(LPITEMIDLIST pidlAbs) 4042 { 4043 IShellFolder *psf = NULL,*psfParent; 4044 4045 TRACE("%p\n", pidlAbs); 4046 4047 if(SUCCEEDED(SHGetDesktopFolder(&psfParent))) 4048 { 4049 psf = psfParent; 4050 if(pidlAbs && pidlAbs->mkid.cb) 4051 { 4052 if(SUCCEEDED(IShellFolder_BindToObject(psfParent, pidlAbs, NULL, &IID_IShellFolder, (LPVOID*)&psf))) 4053 { 4054 IShellFolder_Release(psfParent); 4055 return psf; 4056 } 4057 } 4058 /* return the desktop */ 4059 return psfParent; 4060 } 4061 return NULL; 4062 } 4063 4064 /*********************************************************************** 4065 * GetParentPidl 4066 * 4067 * Return the LPITEMIDLIST to the parent of the pidl in the list 4068 */ 4069 LPITEMIDLIST GetParentPidl(LPITEMIDLIST pidl) 4070 { 4071 LPITEMIDLIST pidlParent; 4072 4073 TRACE("%p\n", pidl); 4074 4075 pidlParent = ILClone(pidl); 4076 ILRemoveLastID(pidlParent); 4077 4078 return pidlParent; 4079 } 4080 4081 /*********************************************************************** 4082 * GetPidlFromName 4083 * 4084 * returns the pidl of the file name relative to folder 4085 * NULL if an error occurred 4086 */ 4087 static LPITEMIDLIST GetPidlFromName(IShellFolder *lpsf,LPWSTR lpcstrFileName) 4088 { 4089 LPITEMIDLIST pidl = NULL; 4090 ULONG ulEaten; 4091 4092 TRACE("sf=%p file=%s\n", lpsf, debugstr_w(lpcstrFileName)); 4093 4094 if(!lpcstrFileName) return NULL; 4095 if(!*lpcstrFileName) return NULL; 4096 4097 if(!lpsf) 4098 { 4099 if (SUCCEEDED(SHGetDesktopFolder(&lpsf))) { 4100 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL); 4101 IShellFolder_Release(lpsf); 4102 } 4103 } 4104 else 4105 { 4106 IShellFolder_ParseDisplayName(lpsf, 0, NULL, lpcstrFileName, &ulEaten, &pidl, NULL); 4107 } 4108 return pidl; 4109 } 4110 4111 /* 4112 */ 4113 static BOOL IsPidlFolder (LPSHELLFOLDER psf, LPCITEMIDLIST pidl) 4114 { 4115 ULONG uAttr = SFGAO_FOLDER | SFGAO_HASSUBFOLDER; 4116 HRESULT ret; 4117 4118 TRACE("%p, %p\n", psf, pidl); 4119 4120 ret = IShellFolder_GetAttributesOf( psf, 1, &pidl, &uAttr ); 4121 4122 TRACE("-- 0x%08x 0x%08x\n", uAttr, ret); 4123 /* see documentation shell 4.1*/ 4124 return uAttr & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER); 4125 } 4126 4127 /*********************************************************************** 4128 * BrowseSelectedFolder 4129 */ 4130 static BOOL BrowseSelectedFolder(HWND hwnd) 4131 { 4132 FileOpenDlgInfos *fodInfos = get_filedlg_infoptr(hwnd); 4133 BOOL bBrowseSelFolder = FALSE; 4134 4135 TRACE("\n"); 4136 4137 if (GetNumSelected(fodInfos->Shell.FOIDataObject) == 1) 4138 { 4139 LPITEMIDLIST pidlSelection; 4140 4141 /* get the file selected */ 4142 pidlSelection = GetPidlFromDataObject( fodInfos->Shell.FOIDataObject, 1); 4143 if (IsPidlFolder (fodInfos->Shell.FOIShellFolder, pidlSelection)) 4144 { 4145 if ( FAILED( IShellBrowser_BrowseObject( fodInfos->Shell.FOIShellBrowser, 4146 pidlSelection, SBSP_RELATIVE ) ) ) 4147 { 4148 WCHAR buf[64]; 4149 LoadStringW( COMDLG32_hInstance, IDS_PATHNOTEXISTING, buf, ARRAY_SIZE(buf)); 4150 MessageBoxW( hwnd, buf, fodInfos->title, MB_OK | MB_ICONEXCLAMATION ); 4151 } 4152 bBrowseSelFolder = TRUE; 4153 if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) 4154 SendCustomDlgNotificationMessage(hwnd,CDN_FOLDERCHANGE); 4155 } 4156 ILFree( pidlSelection ); 4157 } 4158 4159 return bBrowseSelFolder; 4160 } 4161 4162 static inline BOOL valid_struct_size( DWORD size ) 4163 { 4164 return (size == OPENFILENAME_SIZE_VERSION_400W) || 4165 (size == sizeof( OPENFILENAMEW )); 4166 } 4167 4168 static inline BOOL is_win16_looks(DWORD flags) 4169 { 4170 return (flags & (OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLETEMPLATE) && 4171 !(flags & OFN_EXPLORER)); 4172 } 4173 4174 /* ------------------ APIs ---------------------- */ 4175 4176 /*********************************************************************** 4177 * GetOpenFileNameA (COMDLG32.@) 4178 * 4179 * Creates a dialog box for the user to select a file to open. 4180 * 4181 * RETURNS 4182 * TRUE on success: user enters a valid file 4183 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer. 4184 * 4185 */ 4186 BOOL WINAPI GetOpenFileNameA(OPENFILENAMEA *ofn) 4187 { 4188 TRACE("flags 0x%08x\n", ofn->Flags); 4189 4190 if (!valid_struct_size( ofn->lStructSize )) 4191 { 4192 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE ); 4193 return FALSE; 4194 } 4195 4196 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */ 4197 if (ofn->Flags & OFN_FILEMUSTEXIST) 4198 ofn->Flags |= OFN_PATHMUSTEXIST; 4199 4200 if (is_win16_looks(ofn->Flags)) 4201 return GetFileName31A(ofn, OPEN_DIALOG); 4202 else 4203 { 4204 FileOpenDlgInfos info; 4205 4206 init_filedlg_infoA(ofn, &info); 4207 return GetFileDialog95(&info, OPEN_DIALOG); 4208 } 4209 } 4210 4211 /*********************************************************************** 4212 * GetOpenFileNameW (COMDLG32.@) 4213 * 4214 * Creates a dialog box for the user to select a file to open. 4215 * 4216 * RETURNS 4217 * TRUE on success: user enters a valid file 4218 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer. 4219 * 4220 */ 4221 BOOL WINAPI GetOpenFileNameW(OPENFILENAMEW *ofn) 4222 { 4223 TRACE("flags 0x%08x\n", ofn->Flags); 4224 4225 if (!valid_struct_size( ofn->lStructSize )) 4226 { 4227 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE ); 4228 return FALSE; 4229 } 4230 4231 /* OFN_FILEMUSTEXIST implies OFN_PATHMUSTEXIST */ 4232 if (ofn->Flags & OFN_FILEMUSTEXIST) 4233 ofn->Flags |= OFN_PATHMUSTEXIST; 4234 4235 if (is_win16_looks(ofn->Flags)) 4236 return GetFileName31W(ofn, OPEN_DIALOG); 4237 else 4238 { 4239 FileOpenDlgInfos info; 4240 4241 init_filedlg_infoW(ofn, &info); 4242 return GetFileDialog95(&info, OPEN_DIALOG); 4243 } 4244 } 4245 4246 4247 /*********************************************************************** 4248 * GetSaveFileNameA (COMDLG32.@) 4249 * 4250 * Creates a dialog box for the user to select a file to save. 4251 * 4252 * RETURNS 4253 * TRUE on success: user enters a valid file 4254 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer. 4255 * 4256 */ 4257 BOOL WINAPI GetSaveFileNameA(OPENFILENAMEA *ofn) 4258 { 4259 if (!valid_struct_size( ofn->lStructSize )) 4260 { 4261 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE ); 4262 return FALSE; 4263 } 4264 4265 if (is_win16_looks(ofn->Flags)) 4266 return GetFileName31A(ofn, SAVE_DIALOG); 4267 else 4268 { 4269 FileOpenDlgInfos info; 4270 4271 init_filedlg_infoA(ofn, &info); 4272 return GetFileDialog95(&info, SAVE_DIALOG); 4273 } 4274 } 4275 4276 /*********************************************************************** 4277 * GetSaveFileNameW (COMDLG32.@) 4278 * 4279 * Creates a dialog box for the user to select a file to save. 4280 * 4281 * RETURNS 4282 * TRUE on success: user enters a valid file 4283 * FALSE on cancel, error, close or filename-does-not-fit-in-buffer. 4284 * 4285 */ 4286 BOOL WINAPI GetSaveFileNameW( 4287 LPOPENFILENAMEW ofn) /* [in/out] address of init structure */ 4288 { 4289 if (!valid_struct_size( ofn->lStructSize )) 4290 { 4291 COMDLG32_SetCommDlgExtendedError( CDERR_STRUCTSIZE ); 4292 return FALSE; 4293 } 4294 4295 if (is_win16_looks(ofn->Flags)) 4296 return GetFileName31W(ofn, SAVE_DIALOG); 4297 else 4298 { 4299 FileOpenDlgInfos info; 4300 4301 init_filedlg_infoW(ofn, &info); 4302 return GetFileDialog95(&info, SAVE_DIALOG); 4303 } 4304 } 4305 4306 /*********************************************************************** 4307 * GetFileTitleA (COMDLG32.@) 4308 * 4309 * See GetFileTitleW. 4310 */ 4311 short WINAPI GetFileTitleA(LPCSTR lpFile, LPSTR lpTitle, WORD cbBuf) 4312 { 4313 int ret; 4314 UNICODE_STRING strWFile; 4315 LPWSTR lpWTitle; 4316 4317 RtlCreateUnicodeStringFromAsciiz(&strWFile, lpFile); 4318 lpWTitle = heap_alloc(cbBuf * sizeof(WCHAR)); 4319 ret = GetFileTitleW(strWFile.Buffer, lpWTitle, cbBuf); 4320 if (!ret) WideCharToMultiByte( CP_ACP, 0, lpWTitle, -1, lpTitle, cbBuf, NULL, NULL ); 4321 RtlFreeUnicodeString( &strWFile ); 4322 heap_free( lpWTitle ); 4323 return ret; 4324 } 4325 4326 4327 /*********************************************************************** 4328 * GetFileTitleW (COMDLG32.@) 4329 * 4330 * Get the name of a file. 4331 * 4332 * PARAMS 4333 * lpFile [I] name and location of file 4334 * lpTitle [O] returned file name 4335 * cbBuf [I] buffer size of lpTitle 4336 * 4337 * RETURNS 4338 * Success: zero 4339 * Failure: negative number. 4340 */ 4341 short WINAPI GetFileTitleW(LPCWSTR lpFile, LPWSTR lpTitle, WORD cbBuf) 4342 { 4343 int i, len; 4344 static const WCHAR brkpoint[] = {'*','[',']',0}; 4345 TRACE("(%p %p %d);\n", lpFile, lpTitle, cbBuf); 4346 4347 if(lpFile == NULL || lpTitle == NULL) 4348 return -1; 4349 4350 len = lstrlenW(lpFile); 4351 4352 if (len == 0) 4353 return -1; 4354 4355 if(strpbrkW(lpFile, brkpoint)) 4356 return -1; 4357 4358 len--; 4359 4360 if(lpFile[len] == '/' || lpFile[len] == '\\' || lpFile[len] == ':') 4361 return -1; 4362 4363 for(i = len; i >= 0; i--) 4364 { 4365 if (lpFile[i] == '/' || lpFile[i] == '\\' || lpFile[i] == ':') 4366 { 4367 i++; 4368 break; 4369 } 4370 } 4371 4372 if(i == -1) 4373 i++; 4374 4375 TRACE("---> %s\n", debugstr_w(&lpFile[i])); 4376 4377 len = lstrlenW(lpFile+i)+1; 4378 if(cbBuf < len) 4379 return len; 4380 4381 lstrcpyW(lpTitle, &lpFile[i]); 4382 return 0; 4383 } 4384