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