1 /* 2 * Common Dialog Boxes interface (32 bit) 3 * Find/Replace 4 * 5 * Copyright 1998,1999 Bertho A. Stultiens 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 22 #include <stdarg.h> 23 #include <string.h> 24 #include "windef.h" 25 #include "winbase.h" 26 #include "winnls.h" 27 #include "wingdi.h" 28 #include "winuser.h" 29 #include "commdlg.h" 30 #include "cderr.h" 31 #include "dlgs.h" 32 #include "wine/debug.h" 33 34 WINE_DEFAULT_DEBUG_CHANNEL(commdlg); 35 36 #include "cdlg.h" 37 38 39 /*-----------------------------------------------------------------------*/ 40 41 static UINT FindReplaceMessage; 42 static UINT HelpMessage; 43 44 #define FR_MASK (FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD | FR_REPLACEALL | FR_REPLACE | FR_FINDNEXT | FR_DIALOGTERM) 45 /* CRITICAL_SECTION COMDLG32_CritSect; */ 46 47 /* Notes: 48 * MS uses a critical section at a few locations. However, I fail to 49 * see the reason for this. Their comdlg32.dll has a few race conditions 50 * but _not_ at those places that are protected with the mutex (there are 51 * globals that seem to hold info for the wndproc). 52 * 53 * FindText[AW]/ReplaceText[AW] 54 * The find/replace calls are passed a structure that is _not_ used 55 * internally. There is a local copy that holds the running info to 56 * be able to combine xxxA and xxxW calls. The passed pointer is 57 * returned upon sendmessage. Apps won't break this way when they rely 58 * on the original pointer. This will work as long as the sizes of 59 * FINDREPLACEA == FINDREPLACEW. The local copy will also prevent 60 * the app to see the wine-specific extra flags to distinguish between 61 * A/W and Find/Replace. 62 */ 63 64 65 /*********************************************************************** 66 * COMDLG32_FR_GetFlags [internal] 67 * Returns the button state that needs to be reported to the caller. 68 * RETURNS 69 * Current state of check and radio buttons 70 */ 71 static DWORD COMDLG32_FR_GetFlags(HWND hDlgWnd) 72 { 73 DWORD flags = 0; 74 if(IsDlgButtonChecked(hDlgWnd, rad2) == BST_CHECKED) 75 flags |= FR_DOWN; 76 if(IsDlgButtonChecked(hDlgWnd, chx1) == BST_CHECKED) 77 flags |= FR_WHOLEWORD; 78 if(IsDlgButtonChecked(hDlgWnd, chx2) == BST_CHECKED) 79 flags |= FR_MATCHCASE; 80 return flags; 81 } 82 83 /*********************************************************************** 84 * COMDLG32_FR_HandleWMCommand [internal] 85 * Handle WM_COMMAND messages... 86 */ 87 static void COMDLG32_FR_HandleWMCommand(HWND hDlgWnd, COMDLG32_FR_Data *pData, int Id, int NotifyCode) 88 { 89 DWORD flag; 90 91 pData->user_fr.fra->Flags &= ~FR_MASK; /* Clear return flags */ 92 if(pData->fr.Flags & FR_WINE_REPLACE) /* Replace always goes down... */ 93 pData->user_fr.fra->Flags |= FR_DOWN; 94 95 if(NotifyCode == BN_CLICKED) 96 { 97 switch(Id) 98 { 99 case IDOK: /* Find Next */ 100 if(GetDlgItemTextA(hDlgWnd, edt1, pData->fr.lpstrFindWhat, pData->fr.wFindWhatLen) > 0) 101 { 102 pData->user_fr.fra->Flags |= COMDLG32_FR_GetFlags(hDlgWnd) | FR_FINDNEXT; 103 if(pData->fr.Flags & FR_WINE_UNICODE) 104 { 105 MultiByteToWideChar( CP_ACP, 0, pData->fr.lpstrFindWhat, -1, 106 pData->user_fr.frw->lpstrFindWhat, 107 0x7fffffff ); 108 } 109 else 110 { 111 strcpy(pData->user_fr.fra->lpstrFindWhat, pData->fr.lpstrFindWhat); 112 } 113 SendMessageA(pData->fr.hwndOwner, FindReplaceMessage, 0, (LPARAM)pData->user_fr.fra); 114 } 115 break; 116 117 case IDCANCEL: 118 pData->user_fr.fra->Flags |= COMDLG32_FR_GetFlags(hDlgWnd) | FR_DIALOGTERM; 119 SendMessageA(pData->fr.hwndOwner, FindReplaceMessage, 0, (LPARAM)pData->user_fr.fra); 120 DestroyWindow(hDlgWnd); 121 break; 122 123 case psh2: /* Replace All */ 124 flag = FR_REPLACEALL; 125 goto Replace; 126 127 case psh1: /* Replace */ 128 flag = FR_REPLACE; 129 Replace: 130 if((pData->fr.Flags & FR_WINE_REPLACE) 131 && GetDlgItemTextA(hDlgWnd, edt1, pData->fr.lpstrFindWhat, pData->fr.wFindWhatLen) > 0) 132 { 133 pData->fr.lpstrReplaceWith[0] = 0; /* In case the next GetDlgItemText Fails */ 134 GetDlgItemTextA(hDlgWnd, edt2, pData->fr.lpstrReplaceWith, pData->fr.wReplaceWithLen); 135 pData->user_fr.fra->Flags |= COMDLG32_FR_GetFlags(hDlgWnd) | flag; 136 if(pData->fr.Flags & FR_WINE_UNICODE) 137 { 138 MultiByteToWideChar( CP_ACP, 0, pData->fr.lpstrFindWhat, -1, 139 pData->user_fr.frw->lpstrFindWhat, 140 0x7fffffff ); 141 MultiByteToWideChar( CP_ACP, 0, pData->fr.lpstrReplaceWith, -1, 142 pData->user_fr.frw->lpstrReplaceWith, 143 0x7fffffff ); 144 } 145 else 146 { 147 strcpy(pData->user_fr.fra->lpstrFindWhat, pData->fr.lpstrFindWhat); 148 strcpy(pData->user_fr.fra->lpstrReplaceWith, pData->fr.lpstrReplaceWith); 149 } 150 SendMessageA(pData->fr.hwndOwner, FindReplaceMessage, 0, (LPARAM)pData->user_fr.fra); 151 } 152 break; 153 154 case pshHelp: 155 pData->user_fr.fra->Flags |= COMDLG32_FR_GetFlags(hDlgWnd); 156 SendMessageA(pData->fr.hwndOwner, HelpMessage, (WPARAM)hDlgWnd, (LPARAM)pData->user_fr.fra); 157 break; 158 } 159 } 160 else if(NotifyCode == EN_CHANGE && Id == edt1) 161 { 162 BOOL enable = SendDlgItemMessageA(hDlgWnd, edt1, WM_GETTEXTLENGTH, 0, 0) > 0; 163 EnableWindow(GetDlgItem(hDlgWnd, IDOK), enable); 164 if(pData->fr.Flags & FR_WINE_REPLACE) 165 { 166 EnableWindow(GetDlgItem(hDlgWnd, psh1), enable); 167 EnableWindow(GetDlgItem(hDlgWnd, psh2), enable); 168 } 169 } 170 } 171 172 /*********************************************************************** 173 * COMDLG32_FindReplaceDlgProc [internal] 174 * [Find/Replace]Text32[A/W] window procedure. 175 */ 176 static INT_PTR CALLBACK COMDLG32_FindReplaceDlgProc(HWND hDlgWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) 177 { 178 COMDLG32_FR_Data *pdata = GetPropA(hDlgWnd, (LPSTR)COMDLG32_Atom); 179 INT_PTR retval = TRUE; 180 181 if(iMsg == WM_INITDIALOG) 182 { 183 pdata = (COMDLG32_FR_Data *)lParam; 184 if(!SetPropA(hDlgWnd, (LPSTR)COMDLG32_Atom, (HANDLE)pdata)) 185 { 186 ERR("Could not Set prop; invent a graceful exit?...\n"); 187 DestroyWindow(hDlgWnd); 188 return FALSE; 189 } 190 SendDlgItemMessageA(hDlgWnd, edt1, EM_SETLIMITTEXT, pdata->fr.wFindWhatLen, 0); 191 SendDlgItemMessageA(hDlgWnd, edt1, WM_SETTEXT, 0, (LPARAM)pdata->fr.lpstrFindWhat); 192 if(pdata->fr.Flags & FR_WINE_REPLACE) 193 { 194 SendDlgItemMessageA(hDlgWnd, edt2, EM_SETLIMITTEXT, pdata->fr.wReplaceWithLen, 0); 195 SendDlgItemMessageA(hDlgWnd, edt2, WM_SETTEXT, 0, (LPARAM)pdata->fr.lpstrReplaceWith); 196 } 197 198 if(!(pdata->fr.Flags & FR_SHOWHELP)) 199 ShowWindow(GetDlgItem(hDlgWnd, pshHelp), SW_HIDE); 200 if(pdata->fr.Flags & FR_HIDEUPDOWN) 201 { 202 ShowWindow(GetDlgItem(hDlgWnd, rad1), SW_HIDE); 203 ShowWindow(GetDlgItem(hDlgWnd, rad2), SW_HIDE); 204 ShowWindow(GetDlgItem(hDlgWnd, grp1), SW_HIDE); 205 } 206 else if(pdata->fr.Flags & FR_NOUPDOWN) 207 { 208 EnableWindow(GetDlgItem(hDlgWnd, rad1), FALSE); 209 EnableWindow(GetDlgItem(hDlgWnd, rad2), FALSE); 210 EnableWindow(GetDlgItem(hDlgWnd, grp1), FALSE); 211 } 212 else 213 { 214 SendDlgItemMessageA(hDlgWnd, rad1, BM_SETCHECK, pdata->fr.Flags & FR_DOWN ? 0 : BST_CHECKED, 0); 215 SendDlgItemMessageA(hDlgWnd, rad2, BM_SETCHECK, pdata->fr.Flags & FR_DOWN ? BST_CHECKED : 0, 0); 216 } 217 218 if(pdata->fr.Flags & FR_HIDEMATCHCASE) 219 ShowWindow(GetDlgItem(hDlgWnd, chx2), SW_HIDE); 220 else if(pdata->fr.Flags & FR_NOMATCHCASE) 221 EnableWindow(GetDlgItem(hDlgWnd, chx2), FALSE); 222 else 223 SendDlgItemMessageA(hDlgWnd, chx2, BM_SETCHECK, pdata->fr.Flags & FR_MATCHCASE ? BST_CHECKED : 0, 0); 224 225 if(pdata->fr.Flags & FR_HIDEWHOLEWORD) 226 ShowWindow(GetDlgItem(hDlgWnd, chx1), SW_HIDE); 227 else if(pdata->fr.Flags & FR_NOWHOLEWORD) 228 EnableWindow(GetDlgItem(hDlgWnd, chx1), FALSE); 229 else 230 SendDlgItemMessageA(hDlgWnd, chx1, BM_SETCHECK, pdata->fr.Flags & FR_WHOLEWORD ? BST_CHECKED : 0, 0); 231 232 /* We did the init here, now call the hook if requested */ 233 234 /* We do not do ShowWindow if hook exists and is FALSE */ 235 /* per MSDN Article Q96135 */ 236 if((pdata->fr.Flags & FR_ENABLEHOOK) 237 && ! pdata->fr.lpfnHook(hDlgWnd, iMsg, wParam, (LPARAM) &pdata->fr)) 238 return TRUE; 239 ShowWindow(hDlgWnd, SW_SHOWNORMAL); 240 UpdateWindow(hDlgWnd); 241 return TRUE; 242 } 243 244 if(pdata && (pdata->fr.Flags & FR_ENABLEHOOK)) 245 { 246 retval = pdata->fr.lpfnHook(hDlgWnd, iMsg, wParam, lParam); 247 } 248 else 249 retval = FALSE; 250 251 if(pdata && !retval) 252 { 253 retval = TRUE; 254 switch(iMsg) 255 { 256 case WM_COMMAND: 257 COMDLG32_FR_HandleWMCommand(hDlgWnd, pdata, LOWORD(wParam), HIWORD(wParam)); 258 break; 259 260 case WM_CLOSE: 261 COMDLG32_FR_HandleWMCommand(hDlgWnd, pdata, IDCANCEL, BN_CLICKED); 262 break; 263 264 case WM_HELP: 265 /* Heeeeelp! */ 266 FIXME("Got WM_HELP. Who is gonna supply it?\n"); 267 break; 268 269 case WM_CONTEXTMENU: 270 /* Heeeeelp! */ 271 FIXME("Got WM_CONTEXTMENU. Who is gonna supply it?\n"); 272 break; 273 /* FIXME: Handle F1 help */ 274 275 default: 276 retval = FALSE; /* We did not handle the message */ 277 } 278 } 279 280 /* WM_DESTROY is a special case. 281 * We need to ensure that the allocated memory is freed just before 282 * the dialog is killed. We also need to remove the added prop. 283 */ 284 if(iMsg == WM_DESTROY) 285 { 286 RemovePropA(hDlgWnd, (LPSTR)COMDLG32_Atom); 287 HeapFree(GetProcessHeap(), 0, pdata); 288 } 289 290 return retval; 291 } 292 293 /*********************************************************************** 294 * COMDLG32_FR_CheckPartial [internal] 295 * Check various fault conditions in the supplied parameters that 296 * cause an extended error to be reported. 297 * RETURNS 298 * TRUE: Success 299 * FALSE: Failure 300 */ 301 static BOOL COMDLG32_FR_CheckPartial( 302 const FINDREPLACEA *pfr, /* [in] Find structure */ 303 BOOL Replace /* [in] True if called as replace */ 304 ) { 305 if(!pfr) 306 { 307 COMDLG32_SetCommDlgExtendedError(CDERR_INITIALIZATION); 308 return FALSE; 309 } 310 311 if(pfr->lStructSize != sizeof(FINDREPLACEA)) 312 { 313 COMDLG32_SetCommDlgExtendedError(CDERR_STRUCTSIZE); 314 return FALSE; 315 } 316 317 if(!IsWindow(pfr->hwndOwner)) 318 { 319 COMDLG32_SetCommDlgExtendedError(CDERR_DIALOGFAILURE); 320 return FALSE; 321 } 322 323 if((pfr->wFindWhatLen < 1 || !pfr->lpstrFindWhat) 324 ||(Replace && !pfr->lpstrReplaceWith)) 325 { 326 COMDLG32_SetCommDlgExtendedError(FRERR_BUFFERLENGTHZERO); 327 return FALSE; 328 } 329 330 if((FindReplaceMessage = RegisterWindowMessageA(FINDMSGSTRINGA)) == 0) 331 { 332 COMDLG32_SetCommDlgExtendedError(CDERR_REGISTERMSGFAIL); 333 return FALSE; 334 } 335 if((HelpMessage = RegisterWindowMessageA(HELPMSGSTRINGA)) == 0) 336 { 337 COMDLG32_SetCommDlgExtendedError(CDERR_REGISTERMSGFAIL); 338 return FALSE; 339 } 340 341 if((pfr->Flags & FR_ENABLEHOOK) && !pfr->lpfnHook) 342 { 343 COMDLG32_SetCommDlgExtendedError(CDERR_NOHOOK); 344 return FALSE; 345 } 346 347 if((pfr->Flags & FR_ENABLETEMPLATEHANDLE) && !pfr->hInstance) 348 { 349 COMDLG32_SetCommDlgExtendedError(CDERR_NOHINSTANCE); 350 return FALSE; 351 } 352 353 return TRUE; 354 } 355 356 /*********************************************************************** 357 * COMDLG32_FR_DoFindReplace [internal] 358 * Actual load and creation of the Find/Replace dialog. 359 * RETURNS 360 * Window handle to created dialog:Success 361 * NULL:Failure 362 */ 363 static HWND COMDLG32_FR_DoFindReplace( 364 COMDLG32_FR_Data *pdata /* [in] Internal data structure */ 365 ) { 366 HWND hdlgwnd = 0; 367 HGLOBAL loadrc; 368 DWORD error; 369 LPDLGTEMPLATEW rcs; 370 371 TRACE("hInst=%p, Flags=%08x\n", pdata->fr.hInstance, pdata->fr.Flags); 372 373 if(!(pdata->fr.Flags & FR_ENABLETEMPLATEHANDLE)) 374 { 375 HMODULE hmod = COMDLG32_hInstance; 376 HRSRC htemplate; 377 if(pdata->fr.Flags & FR_ENABLETEMPLATE) 378 { 379 hmod = pdata->fr.hInstance; 380 if(pdata->fr.Flags & FR_WINE_UNICODE) 381 { 382 htemplate = FindResourceW(hmod, (LPCWSTR)pdata->fr.lpTemplateName, (LPWSTR)RT_DIALOG); 383 } 384 else 385 { 386 htemplate = FindResourceA(hmod, pdata->fr.lpTemplateName, (LPCSTR)RT_DIALOG); 387 } 388 } 389 else 390 { 391 int rcid = pdata->fr.Flags & FR_WINE_REPLACE ? REPLACEDLGORD 392 : FINDDLGORD; 393 htemplate = FindResourceA(hmod, MAKEINTRESOURCEA(rcid), (LPCSTR)RT_DIALOG); 394 } 395 if(!htemplate) 396 { 397 error = CDERR_FINDRESFAILURE; 398 goto cleanup; 399 } 400 401 loadrc = LoadResource(hmod, htemplate); 402 } 403 else 404 { 405 loadrc = pdata->fr.hInstance; 406 } 407 408 if(!loadrc) 409 { 410 error = CDERR_LOADRESFAILURE; 411 goto cleanup; 412 } 413 414 if((rcs = LockResource(loadrc)) == NULL) 415 { 416 error = CDERR_LOCKRESFAILURE; 417 goto cleanup; 418 } 419 420 hdlgwnd = CreateDialogIndirectParamA(COMDLG32_hInstance, 421 rcs, 422 pdata->fr.hwndOwner, 423 COMDLG32_FindReplaceDlgProc, 424 (LPARAM)pdata); 425 if(!hdlgwnd) 426 { 427 error = CDERR_DIALOGFAILURE; 428 cleanup: 429 COMDLG32_SetCommDlgExtendedError(error); 430 HeapFree(GetProcessHeap(), 0, pdata); 431 } 432 return hdlgwnd; 433 } 434 435 /*********************************************************************** 436 * FindTextA [COMDLG32.@] 437 * 438 * See FindTextW. 439 */ 440 HWND WINAPI FindTextA( 441 LPFINDREPLACEA pfr /* [in] Find/replace structure*/ 442 ) { 443 COMDLG32_FR_Data *pdata; 444 445 TRACE("LPFINDREPLACE=%p\n", pfr); 446 447 if(!COMDLG32_FR_CheckPartial(pfr, FALSE)) 448 return 0; 449 450 if((pdata = COMDLG32_AllocMem(sizeof(COMDLG32_FR_Data))) == NULL) 451 return 0; /* Error has been set */ 452 453 pdata->user_fr.fra = pfr; 454 pdata->fr = *pfr; 455 return COMDLG32_FR_DoFindReplace(pdata); 456 } 457 458 /*********************************************************************** 459 * ReplaceTextA [COMDLG32.@] 460 * 461 * See ReplaceTextW. 462 */ 463 HWND WINAPI ReplaceTextA( 464 LPFINDREPLACEA pfr /* [in] Find/replace structure*/ 465 ) { 466 COMDLG32_FR_Data *pdata; 467 468 TRACE("LPFINDREPLACE=%p\n", pfr); 469 470 if(!COMDLG32_FR_CheckPartial(pfr, TRUE)) 471 return 0; 472 473 if((pdata = COMDLG32_AllocMem(sizeof(COMDLG32_FR_Data))) == NULL) 474 return 0; /* Error has been set */ 475 476 pdata->user_fr.fra = pfr; 477 pdata->fr = *pfr; 478 pdata->fr.Flags |= FR_WINE_REPLACE; 479 return COMDLG32_FR_DoFindReplace(pdata); 480 } 481 482 /*********************************************************************** 483 * FindTextW [COMDLG32.@] 484 * 485 * Create a modeless find-text dialog box. 486 * 487 * RETURNS 488 * Window handle to created dialog: Success 489 * NULL: Failure 490 */ 491 HWND WINAPI FindTextW( 492 LPFINDREPLACEW pfr /* [in] Find/replace structure*/ 493 ) { 494 COMDLG32_FR_Data *pdata; 495 DWORD len; 496 497 TRACE("LPFINDREPLACE=%p\n", pfr); 498 499 if(!COMDLG32_FR_CheckPartial((LPFINDREPLACEA)pfr, FALSE)) 500 return 0; 501 502 len = WideCharToMultiByte( CP_ACP, 0, pfr->lpstrFindWhat, pfr->wFindWhatLen, 503 NULL, 0, NULL, NULL ); 504 if((pdata = COMDLG32_AllocMem(sizeof(COMDLG32_FR_Data) + len)) == NULL) 505 return 0; /* Error has been set */ 506 507 pdata->user_fr.frw = pfr; 508 pdata->fr = *(LPFINDREPLACEA)pfr; /* FINDREPLACEx have same size */ 509 pdata->fr.Flags |= FR_WINE_UNICODE; 510 pdata->fr.lpstrFindWhat = (LPSTR)(pdata + 1); /* Set string pointer */ 511 WideCharToMultiByte( CP_ACP, 0, pfr->lpstrFindWhat, pfr->wFindWhatLen, 512 pdata->fr.lpstrFindWhat, len, NULL, NULL ); 513 return COMDLG32_FR_DoFindReplace(pdata); 514 } 515 516 /*********************************************************************** 517 * ReplaceTextW [COMDLG32.@] 518 * 519 * Create a modeless replace-text dialog box. 520 * 521 * RETURNS 522 * Window handle to created dialog: Success 523 * NULL: Failure 524 */ 525 HWND WINAPI ReplaceTextW( 526 LPFINDREPLACEW pfr /* [in] Find/replace structure*/ 527 ) { 528 COMDLG32_FR_Data *pdata; 529 DWORD len1, len2; 530 531 TRACE("LPFINDREPLACE=%p\n", pfr); 532 533 if(!COMDLG32_FR_CheckPartial((LPFINDREPLACEA)pfr, TRUE)) 534 return 0; 535 536 len1 = WideCharToMultiByte( CP_ACP, 0, pfr->lpstrFindWhat, pfr->wFindWhatLen, 537 NULL, 0, NULL, NULL ); 538 len2 = WideCharToMultiByte( CP_ACP, 0, pfr->lpstrReplaceWith, pfr->wReplaceWithLen, 539 NULL, 0, NULL, NULL ); 540 if((pdata = COMDLG32_AllocMem(sizeof(COMDLG32_FR_Data) + len1 + len2)) == NULL) 541 return 0; /* Error has been set */ 542 543 pdata->user_fr.frw = pfr; 544 pdata->fr = *(LPFINDREPLACEA)pfr; /* FINDREPLACEx have same size */ 545 pdata->fr.Flags |= FR_WINE_REPLACE | FR_WINE_UNICODE; 546 pdata->fr.lpstrFindWhat = (LPSTR)(pdata + 1); /* Set string pointer */ 547 pdata->fr.lpstrReplaceWith = pdata->fr.lpstrFindWhat + len1; 548 549 WideCharToMultiByte( CP_ACP, 0, pfr->lpstrFindWhat, pfr->wFindWhatLen, 550 pdata->fr.lpstrFindWhat, len1, NULL, NULL ); 551 WideCharToMultiByte( CP_ACP, 0, pfr->lpstrReplaceWith, pfr->wReplaceWithLen, 552 pdata->fr.lpstrReplaceWith, len2, NULL, NULL ); 553 return COMDLG32_FR_DoFindReplace(pdata); 554 } 555