1 /* 2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "awt_Toolkit.h" 27 #include "awt_TextArea.h" 28 #include "awt_TextComponent.h" 29 #include "awt_Canvas.h" 30 #include "awt_Window.h" 31 #include "awt_Frame.h" 32 33 /* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code. 34 */ 35 36 /***********************************************************************/ 37 // Struct for _ReplaceText() method 38 struct ReplaceTextStruct { 39 jobject textComponent; 40 jstring text; 41 int start, end; 42 }; 43 44 /************************************************************************ 45 * AwtTextArea fields 46 */ 47 48 jfieldID AwtTextArea::scrollbarVisibilityID; 49 50 WNDPROC AwtTextArea::sm_pDefWindowProc = NULL; 51 52 /************************************************************************ 53 * AwtTextArea methods 54 */ 55 56 AwtTextArea::AwtTextArea() { 57 m_bIgnoreEnChange = FALSE; 58 m_bCanUndo = FALSE; 59 m_hEditCtrl = NULL; 60 m_lHDeltaAccum = 0; 61 m_lVDeltaAccum = 0; 62 } 63 64 AwtTextArea::~AwtTextArea() 65 { 66 } 67 68 void AwtTextArea::Dispose() 69 { 70 if (m_hEditCtrl != NULL) { 71 VERIFY(::DestroyWindow(m_hEditCtrl)); 72 m_hEditCtrl = NULL; 73 } 74 AwtTextComponent::Dispose(); 75 } 76 77 /* Create a new AwtTextArea object and window. */ 78 AwtTextArea* AwtTextArea::Create(jobject peer, jobject parent) 79 { 80 return (AwtTextArea*) AwtTextComponent::Create(peer, parent, true); 81 } 82 83 void AwtTextArea::EditSetSel(CHARRANGE &cr) { 84 // Fix for 5003402: added restoring/hiding selection to enable automatic scrolling 85 SendMessage(EM_HIDESELECTION, FALSE, TRUE); 86 SendMessage(EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&cr)); 87 SendMessage(EM_HIDESELECTION, TRUE, TRUE); 88 // 6417581: force expected drawing 89 if (IS_WINVISTA && cr.cpMin == cr.cpMax) { 90 ::InvalidateRect(GetHWnd(), NULL, TRUE); 91 } 92 } 93 94 void AwtTextArea::EditGetSel(CHARRANGE &cr) { 95 SendMessage(EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&cr)); 96 } 97 98 /* Count how many '\n's are there in jStr */ 99 size_t AwtTextArea::CountNewLines(JNIEnv *env, jstring jStr, size_t maxlen) 100 { 101 size_t nNewlines = 0; 102 103 if (jStr == NULL) { 104 return nNewlines; 105 } 106 /* 107 * Fix for BugTraq Id 4260109. 108 * Don't use TO_WSTRING since it allocates memory on the stack 109 * causing stack overflow when the text is very long. 110 */ 111 size_t length = env->GetStringLength(jStr) + 1; 112 WCHAR *string = new WCHAR[length]; 113 env->GetStringRegion(jStr, 0, static_cast<jsize>(length - 1), reinterpret_cast<jchar*>(string)); 114 string[length-1] = '\0'; 115 for (size_t i = 0; i < maxlen && i < length - 1; i++) { 116 if (string[i] == L'\n') { 117 nNewlines++; 118 } 119 } 120 delete[] string; 121 return nNewlines; 122 } 123 124 BOOL AwtTextArea::InheritsNativeMouseWheelBehavior() {return true;} 125 126 127 LRESULT 128 AwtTextArea::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { 129 130 LRESULT retValue = 0; 131 MsgRouting mr = mrDoDefault; 132 133 switch (message) { 134 case WM_PRINTCLIENT: 135 { 136 FORMATRANGE fr; 137 HDC hPrinterDC = (HDC)wParam; 138 int nHorizRes = ::GetDeviceCaps(hPrinterDC, HORZRES); 139 int nVertRes = ::GetDeviceCaps(hPrinterDC, VERTRES); 140 int nLogPixelsX = ::GetDeviceCaps(hPrinterDC, LOGPIXELSX); 141 int nLogPixelsY = ::GetDeviceCaps(hPrinterDC, LOGPIXELSY); 142 143 // Ensure the printer DC is in MM_TEXT mode. 144 ::SetMapMode ( hPrinterDC, MM_TEXT ); 145 146 // Rendering to the same DC we are measuring. 147 ::ZeroMemory(&fr, sizeof(fr)); 148 fr.hdc = fr.hdcTarget = hPrinterDC; 149 // Set up the page. 150 fr.rcPage.left = fr.rcPage.top = 0; 151 fr.rcPage.right = (nHorizRes/nLogPixelsX) * 1440; // in twips 152 fr.rcPage.bottom = (nVertRes/nLogPixelsY) * 1440; 153 fr.rc.left = fr.rcPage.left; 154 fr.rc.top = fr.rcPage.top; 155 fr.rc.right = fr.rcPage.right; 156 fr.rc.bottom = fr.rcPage.bottom; 157 158 // start printing from the first visible line 159 LRESULT nLine = SendMessage(EM_GETFIRSTVISIBLELINE, 0, 0); 160 LONG startCh = static_cast<LONG>(SendMessage(EM_LINEINDEX, 161 (WPARAM)nLine, 0)); 162 fr.chrg.cpMin = startCh; 163 fr.chrg.cpMax = -1; 164 165 SendMessage(EM_FORMATRANGE, TRUE, (LPARAM)&fr); 166 } 167 168 break; 169 case EM_SETCHARFORMAT: 170 case WM_SETFONT: 171 SetIgnoreEnChange(TRUE); 172 break; 173 } 174 175 retValue = AwtComponent::WindowProc(message, wParam, lParam); 176 177 switch (message) { 178 case EM_SETCHARFORMAT: 179 case WM_SETFONT: 180 SetIgnoreEnChange(FALSE); 181 break; 182 } 183 184 return retValue; 185 } 186 187 /* 188 * This routine is a window procedure for the subclass of the standard edit control 189 * used to generate context menu. RichEdit controls don't have built-in context menu. 190 * To implement this functionality we have to create an invisible edit control and 191 * forward WM_CONTEXTMENU messages from a RichEdit control to this helper edit control. 192 * While the edit control context menu is active we intercept the message generated in 193 * response to particular item selection and forward it back to the RichEdit control. 194 * (See AwtTextArea::WmContextMenu for more details). 195 */ 196 LRESULT 197 AwtTextArea::EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { 198 199 static BOOL bContextMenuActive = FALSE; 200 201 LRESULT retValue = 0; 202 MsgRouting mr = mrDoDefault; 203 204 DASSERT(::IsWindow(::GetParent(hWnd))); 205 206 switch (message) { 207 case WM_UNDO: 208 case WM_CUT: 209 case WM_COPY: 210 case WM_PASTE: 211 case WM_CLEAR: 212 case EM_SETSEL: 213 if (bContextMenuActive) { 214 ::SendMessage(::GetParent(hWnd), message, wParam, lParam); 215 mr = mrConsume; 216 } 217 break; 218 case WM_CONTEXTMENU: 219 bContextMenuActive = TRUE; 220 break; 221 } 222 223 if (mr == mrDoDefault) { 224 DASSERT(sm_pDefWindowProc != NULL); 225 retValue = ::CallWindowProc(sm_pDefWindowProc, 226 hWnd, message, wParam, lParam); 227 } 228 229 if (message == WM_CONTEXTMENU) { 230 bContextMenuActive = FALSE; 231 } 232 233 return retValue; 234 } 235 236 MsgRouting 237 AwtTextArea::WmContextMenu(HWND hCtrl, UINT xPos, UINT yPos) { 238 /* Use the system provided edit control class to generate context menu. */ 239 if (m_hEditCtrl == NULL) { 240 DWORD dwStyle = WS_CHILD; 241 DWORD dwExStyle = 0; 242 m_hEditCtrl = ::CreateWindowEx(dwExStyle, 243 L"EDIT", 244 L"TEXT", 245 dwStyle, 246 0, 0, 0, 0, 247 GetHWnd(), 248 reinterpret_cast<HMENU>( 249 static_cast<INT_PTR>( 250 CreateControlID())), 251 AwtToolkit::GetInstance().GetModuleHandle(), 252 NULL); 253 DASSERT(m_hEditCtrl != NULL); 254 if (sm_pDefWindowProc == NULL) { 255 sm_pDefWindowProc = (WNDPROC)::GetWindowLongPtr(m_hEditCtrl, 256 GWLP_WNDPROC); 257 } 258 ::SetLastError(0); 259 INT_PTR ret = ::SetWindowLongPtr(m_hEditCtrl, GWLP_WNDPROC, 260 (INT_PTR)AwtTextArea::EditProc); 261 DASSERT(ret != 0 || ::GetLastError() == 0); 262 } 263 264 /* 265 * Tricks on the edit control to ensure that its context menu has 266 * the correct set of enabled items according to the RichEdit state. 267 */ 268 ::SetWindowText(m_hEditCtrl, TEXT("TEXT")); 269 270 if (m_bCanUndo == TRUE && SendMessage(EM_CANUNDO)) { 271 /* Enable 'Undo' item. */ 272 ::SendMessage(m_hEditCtrl, WM_CHAR, 'A', 0); 273 } 274 275 { 276 /* 277 * Initial selection for the edit control - (0,1). 278 * This enables 'Cut', 'Copy' and 'Delete' and 'Select All'. 279 */ 280 INT nStart = 0; 281 INT nEnd = 1; 282 if (SendMessage(EM_SELECTIONTYPE) == SEL_EMPTY) { 283 /* 284 * RichEdit selection is empty - clear selection of the edit control. 285 * This disables 'Cut', 'Copy' and 'Delete'. 286 */ 287 nStart = -1; 288 nEnd = 0; 289 } else { 290 291 CHARRANGE cr; 292 EditGetSel(cr); 293 /* Check if all the text is selected. */ 294 if (cr.cpMin == 0) { 295 296 int len = ::GetWindowTextLength(GetHWnd()); 297 if (cr.cpMin == 0 && cr.cpMax >= len) { 298 /* 299 * All the text is selected in RichEdit - select all the 300 * text in the edit control. This disables 'Select All'. 301 */ 302 nStart = 0; 303 nEnd = -1; 304 } 305 } 306 } 307 ::SendMessage(m_hEditCtrl, EM_SETSEL, (WPARAM)nStart, (LPARAM)nEnd); 308 } 309 310 /* Disable 'Paste' item if the RichEdit control is read-only. */ 311 ::SendMessage(m_hEditCtrl, EM_SETREADONLY, 312 GetStyle() & ES_READONLY ? TRUE : FALSE, 0); 313 314 POINT p; 315 p.x = xPos; 316 p.y = yPos; 317 318 /* 319 * If the context menu is requested with SHIFT+F10 or VK_APPS key, 320 * we position its top left corner to the center of the RichEdit 321 * client rect. 322 */ 323 if (p.x == -1 && p.y == -1) { 324 RECT r; 325 VERIFY(::GetClientRect(GetHWnd(), &r)); 326 p.x = (r.left + r.right) / 2; 327 p.y = (r.top + r.bottom) / 2; 328 VERIFY(::ClientToScreen(GetHWnd(), &p)); 329 } 330 331 // The context menu steals focus from the proxy. 332 // So, set the focus-restore flag up. 333 SetRestoreFocus(TRUE); 334 ::SendMessage(m_hEditCtrl, WM_CONTEXTMENU, (WPARAM)m_hEditCtrl, MAKELPARAM(p.x, p.y)); 335 SetRestoreFocus(FALSE); 336 337 return mrConsume; 338 } 339 340 MsgRouting 341 AwtTextArea::WmNcHitTest(UINT x, UINT y, LRESULT& retVal) 342 { 343 if (::IsWindow(AwtWindow::GetModalBlocker(AwtComponent::GetTopLevelParentForWindow(GetHWnd())))) { 344 retVal = HTCLIENT; 345 return mrConsume; 346 } 347 return AwtTextComponent::WmNcHitTest(x, y, retVal); 348 } 349 350 351 MsgRouting 352 AwtTextArea::WmNotify(UINT notifyCode) 353 { 354 if (notifyCode == EN_CHANGE) { 355 /* 356 * Ignore notifications if the text hasn't been changed. 357 * EN_CHANGE sent on character formatting changes as well. 358 */ 359 if (m_bIgnoreEnChange == FALSE) { 360 m_bCanUndo = TRUE; 361 DoCallback("valueChanged", "()V"); 362 } else { 363 m_bCanUndo = FALSE; 364 } 365 } 366 return mrDoDefault; 367 } 368 369 MsgRouting 370 AwtTextArea::HandleEvent(MSG *msg, BOOL synthetic) 371 { 372 MsgRouting returnVal; 373 /* 374 * RichEdit 1.0 control starts internal message loop if the 375 * left mouse button is pressed while the cursor is not over 376 * the current selection or the current selection is empty. 377 * Because of this we don't receive WM_MOUSEMOVE messages 378 * while the left mouse button is pressed. To work around 379 * this behavior we process the relevant mouse messages 380 * by ourselves. 381 * By consuming WM_MOUSEMOVE messages we also don't give 382 * the RichEdit control a chance to recognize a drag gesture 383 * and initiate its own drag-n-drop operation. 384 * 385 * The workaround also allows us to implement synthetic focus mechanism. 386 * 387 */ 388 if (IsFocusingMouseMessage(msg)) { 389 CHARRANGE cr; 390 391 LONG lCurPos = EditGetCharFromPos(msg->pt); 392 393 EditGetSel(cr); 394 /* 395 * NOTE: Plain EDIT control always clears selection on mouse 396 * button press. We are clearing the current selection only if 397 * the mouse pointer is not over the selected region. 398 * In this case we sacrifice backward compatibility 399 * to allow dnd of the current selection. 400 */ 401 if (lCurPos < cr.cpMin || cr.cpMax <= lCurPos) { 402 if (msg->message == WM_LBUTTONDBLCLK) { 403 SetStartSelectionPos(static_cast<LONG>(SendMessage( 404 EM_FINDWORDBREAK, WB_MOVEWORDLEFT, lCurPos))); 405 SetEndSelectionPos(static_cast<LONG>(SendMessage( 406 EM_FINDWORDBREAK, WB_MOVEWORDRIGHT, lCurPos))); 407 } else { 408 SetStartSelectionPos(lCurPos); 409 SetEndSelectionPos(lCurPos); 410 } 411 cr.cpMin = GetStartSelectionPos(); 412 cr.cpMax = GetEndSelectionPos(); 413 EditSetSel(cr); 414 } 415 416 delete msg; 417 return mrConsume; 418 } else if (msg->message == WM_LBUTTONUP) { 419 420 /* 421 * If the left mouse button is pressed on the selected region 422 * we don't clear the current selection. We clear it on button 423 * release instead. This is to allow dnd of the current selection. 424 */ 425 if (GetStartSelectionPos() == -1 && GetEndSelectionPos() == -1) { 426 CHARRANGE cr; 427 428 LONG lCurPos = EditGetCharFromPos(msg->pt); 429 430 cr.cpMin = lCurPos; 431 cr.cpMax = lCurPos; 432 EditSetSel(cr); 433 } 434 435 /* 436 * Cleanup the state variables when left mouse button is released. 437 * These state variables are designed to reflect the selection state 438 * while the left mouse button is pressed and be set to -1 otherwise. 439 */ 440 SetStartSelectionPos(-1); 441 SetEndSelectionPos(-1); 442 SetLastSelectionPos(-1); 443 444 delete msg; 445 return mrConsume; 446 } else if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON)) { 447 448 /* 449 * We consume WM_MOUSEMOVE while the left mouse button is pressed, 450 * so we have to simulate autoscrolling when mouse is moved outside 451 * of the client area. 452 */ 453 POINT p; 454 RECT r; 455 BOOL bScrollLeft = FALSE; 456 BOOL bScrollRight = FALSE; 457 BOOL bScrollUp = FALSE; 458 BOOL bScrollDown = FALSE; 459 460 p.x = msg->pt.x; 461 p.y = msg->pt.y; 462 VERIFY(::GetClientRect(GetHWnd(), &r)); 463 464 if (p.x < 0) { 465 bScrollLeft = TRUE; 466 p.x = 0; 467 } else if (p.x > r.right) { 468 bScrollRight = TRUE; 469 p.x = r.right - 1; 470 } 471 if (p.y < 0) { 472 bScrollUp = TRUE; 473 p.y = 0; 474 } else if (p.y > r.bottom) { 475 bScrollDown = TRUE; 476 p.y = r.bottom - 1; 477 } 478 479 LONG lCurPos = EditGetCharFromPos(p); 480 481 if (GetStartSelectionPos() != -1 && 482 GetEndSelectionPos() != -1 && 483 lCurPos != GetLastSelectionPos()) { 484 485 CHARRANGE cr; 486 487 SetLastSelectionPos(lCurPos); 488 489 cr.cpMin = GetStartSelectionPos(); 490 cr.cpMax = GetLastSelectionPos(); 491 492 EditSetSel(cr); 493 } 494 495 if (bScrollLeft == TRUE || bScrollRight == TRUE) { 496 SCROLLINFO si; 497 memset(&si, 0, sizeof(si)); 498 si.cbSize = sizeof(si); 499 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; 500 501 VERIFY(::GetScrollInfo(GetHWnd(), SB_HORZ, &si)); 502 if (bScrollLeft == TRUE) { 503 si.nPos = si.nPos - si.nPage / 2; 504 si.nPos = max(si.nMin, si.nPos); 505 } else if (bScrollRight == TRUE) { 506 si.nPos = si.nPos + si.nPage / 2; 507 si.nPos = min(si.nPos, si.nMax); 508 } 509 /* 510 * Okay to use 16-bit position since RichEdit control adjusts 511 * its scrollbars so that their range is always 16-bit. 512 */ 513 DASSERT(abs(si.nPos) < 0x8000); 514 SendMessage(WM_HSCROLL, 515 MAKEWPARAM(SB_THUMBPOSITION, LOWORD(si.nPos))); 516 } 517 if (bScrollUp == TRUE) { 518 SendMessage(EM_LINESCROLL, 0, -1); 519 } else if (bScrollDown == TRUE) { 520 SendMessage(EM_LINESCROLL, 0, 1); 521 } 522 delete msg; 523 return mrConsume; 524 } else if (msg->message == WM_RBUTTONUP || 525 (msg->message == WM_SYSKEYDOWN && msg->wParam == VK_F10 && 526 HIBYTE(::GetKeyState(VK_SHIFT)))) { 527 POINT p; 528 if (msg->message == WM_RBUTTONUP) { 529 VERIFY(::GetCursorPos(&p)); 530 } else { 531 p.x = -1; 532 p.y = -1; 533 } 534 535 if (!::PostMessage(GetHWnd(), WM_CONTEXTMENU, (WPARAM)GetHWnd(), 536 MAKELPARAM(p.x, p.y))) { 537 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 538 JNU_ThrowInternalError(env, "Message not posted, native event queue may be full."); 539 env->ExceptionDescribe(); 540 env->ExceptionClear(); 541 } 542 delete msg; 543 return mrConsume; 544 } else if (msg->message == WM_MOUSEWHEEL) { 545 // 4417236: If there is an old version of RichEd32.dll which 546 // does not provide the mouse wheel scrolling we have to 547 // interpret WM_MOUSEWHEEL as a sequence of scroll messages. 548 // kdm@sparc.spb.su 549 UINT platfScrollLines = 3; 550 // Retrieve a number of scroll lines. 551 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, 552 &platfScrollLines, 0); 553 554 if (platfScrollLines > 0) { 555 HWND hWnd = GetHWnd(); 556 LONG styles = ::GetWindowLong(hWnd, GWL_STYLE); 557 558 RECT rect; 559 // rect.left and rect.top are zero. 560 // rect.right and rect.bottom contain the width and height 561 VERIFY(::GetClientRect(hWnd, &rect)); 562 563 // calculate a number of visible lines 564 TEXTMETRIC tm; 565 HDC hDC = ::GetDC(hWnd); 566 DASSERT(hDC != NULL); 567 VERIFY(::GetTextMetrics(hDC, &tm)); 568 VERIFY(::ReleaseDC(hWnd, hDC)); 569 LONG visibleLines = rect.bottom / tm.tmHeight + 1; 570 571 LONG lineCount = static_cast<LONG>(::SendMessage(hWnd, 572 EM_GETLINECOUNT, 0, 0)); 573 BOOL sb_vert_disabled = (styles & WS_VSCROLL) == 0 574 || (lineCount <= visibleLines); 575 576 LONG *delta_accum = &m_lVDeltaAccum; 577 UINT wm_msg = WM_VSCROLL; 578 int sb_type = SB_VERT; 579 580 if (sb_vert_disabled && (styles & WS_HSCROLL)) { 581 delta_accum = &m_lHDeltaAccum; 582 wm_msg = WM_HSCROLL; 583 sb_type = SB_HORZ; 584 } 585 586 int delta = (short) HIWORD(msg->wParam); 587 *delta_accum += delta; 588 if (abs(*delta_accum) >= WHEEL_DELTA) { 589 if (platfScrollLines == WHEEL_PAGESCROLL) { 590 // Synthesize a page down or a page up message. 591 ::SendMessage(hWnd, wm_msg, 592 (delta > 0) ? SB_PAGEUP : SB_PAGEDOWN, 0); 593 *delta_accum = 0; 594 } else { 595 // We provide a friendly behavior of text scrolling. 596 // During of scrolling the text can be out of the client 597 // area's boundary. Here we try to prevent this behavior. 598 SCROLLINFO si; 599 ::ZeroMemory(&si, sizeof(si)); 600 si.cbSize = sizeof(SCROLLINFO); 601 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; 602 int actualScrollLines = 603 abs((int)(platfScrollLines * (*delta_accum / WHEEL_DELTA))); 604 for (int i = 0; i < actualScrollLines; i++) { 605 if (::GetScrollInfo(hWnd, sb_type, &si)) { 606 if ((wm_msg == WM_VSCROLL) 607 && ((*delta_accum < 0 608 && si.nPos >= (si.nMax - (int) si.nPage)) 609 || (*delta_accum > 0 610 && si.nPos <= si.nMin))) { 611 break; 612 } 613 } 614 // Here we don't send EM_LINESCROLL or EM_SCROLL 615 // messages to rich edit because it doesn't 616 // provide horizontal scrolling. 617 // So it's only possible to scroll by 1 line 618 // at a time to prevent scrolling when the 619 // scrollbar thumb reaches its boundary position. 620 ::SendMessage(hWnd, wm_msg, 621 (*delta_accum>0) ? SB_LINEUP : SB_LINEDOWN, 0); 622 } 623 *delta_accum %= WHEEL_DELTA; 624 } 625 } else { 626 *delta_accum = 0; 627 } 628 } 629 delete msg; 630 return mrConsume; 631 // 4417236: end of fix 632 } 633 634 /* 635 * Store the 'synthetic' parameter so that the WM_PASTE security check 636 * happens only for synthetic events. 637 */ 638 m_synthetic = synthetic; 639 returnVal = AwtComponent::HandleEvent(msg, synthetic); 640 m_synthetic = FALSE; 641 642 return returnVal; 643 } 644 645 646 /* Fix for 4776535, 4648702 647 * If width is 0 or 1 Windows hides the horizontal scroll bar even 648 * if the WS_HSCROLL style is set. It is a bug in Windows. 649 * As a workaround we should set an initial width to 2. 650 * kdm@sparc.spb.su 651 */ 652 void AwtTextArea::Reshape(int x, int y, int w, int h) 653 { 654 if (w < 2) { 655 w = 2; 656 } 657 AwtTextComponent::Reshape(x, y, w, h); 658 } 659 660 LONG AwtTextArea::getJavaSelPos(LONG orgPos) 661 { 662 long wlen; 663 long pos = orgPos; 664 long cur = 0; 665 long retval = 0; 666 LPTSTR wbuf; 667 668 if ((wlen = GetTextLength()) == 0) 669 return 0; 670 wbuf = new TCHAR[wlen + 1]; 671 GetText(wbuf, wlen + 1); 672 if (m_isLFonly == TRUE) { 673 wlen = RemoveCR(wbuf); 674 } 675 676 while (cur < pos && cur < wlen) { 677 if (wbuf[cur] == _T('\r') && wbuf[cur + 1] == _T('\n')) { 678 pos++; 679 } 680 cur++; 681 retval++; 682 } 683 delete[] wbuf; 684 return retval; 685 } 686 687 LONG AwtTextArea::getWin32SelPos(LONG orgPos) 688 { 689 if (GetTextLength() == 0) 690 return 0; 691 return orgPos; 692 } 693 694 void AwtTextArea::SetSelRange(LONG start, LONG end) 695 { 696 CHARRANGE cr; 697 cr.cpMin = getWin32SelPos(start); 698 cr.cpMax = getWin32SelPos(end); 699 EditSetSel(cr); 700 } 701 702 703 void AwtTextArea::_ReplaceText(void *param) 704 { 705 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 706 707 ReplaceTextStruct *rts = (ReplaceTextStruct *)param; 708 709 jobject textComponent = rts->textComponent; 710 jstring text = rts->text; 711 jint start = rts->start; 712 jint end = rts->end; 713 714 AwtTextComponent *c = NULL; 715 716 PDATA pData; 717 JNI_CHECK_PEER_GOTO(textComponent, done); 718 JNI_CHECK_NULL_GOTO(text, "null string", done); 719 720 c = (AwtTextComponent *)pData; 721 if (::IsWindow(c->GetHWnd())) 722 { 723 jsize length = env->GetStringLength(text) + 1; 724 // Bugid 4141477 - Can't use TO_WSTRING here because it uses alloca 725 // WCHAR* buffer = TO_WSTRING(text); 726 TCHAR *buffer = new TCHAR[length]; 727 env->GetStringRegion(text, 0, length-1, reinterpret_cast<jchar*>(buffer)); 728 buffer[length-1] = '\0'; 729 730 c->CheckLineSeparator(buffer); 731 c->RemoveCR(buffer); 732 // Fix for 5003402: added restoring/hiding selection to enable automatic scrolling 733 c->SendMessage(EM_HIDESELECTION, FALSE, TRUE); 734 c->SendMessage(EM_SETSEL, start, end); 735 c->SendMessage(EM_REPLACESEL, FALSE, (LPARAM)buffer); 736 c->SendMessage(EM_HIDESELECTION, TRUE, TRUE); 737 738 delete[] buffer; 739 } 740 741 done: 742 env->DeleteGlobalRef(textComponent); 743 env->DeleteGlobalRef(text); 744 745 delete rts; 746 } 747 748 749 /************************************************************************ 750 * TextArea native methods 751 */ 752 753 extern "C" { 754 755 /* 756 * Class: java_awt_TextArea 757 * Method: initIDs 758 * Signature: ()V 759 */ 760 JNIEXPORT void JNICALL 761 Java_java_awt_TextArea_initIDs(JNIEnv *env, jclass cls) 762 { 763 TRY; 764 765 AwtTextArea::scrollbarVisibilityID = 766 env->GetFieldID(cls, "scrollbarVisibility", "I"); 767 768 DASSERT(AwtTextArea::scrollbarVisibilityID != NULL); 769 770 CATCH_BAD_ALLOC; 771 } 772 773 } /* extern "C" */ 774 775 776 /************************************************************************ 777 * WTextAreaPeer native methods 778 */ 779 780 extern "C" { 781 782 /* 783 * Class: sun_awt_windows_WTextAreaPeer 784 * Method: create 785 * Signature: (Lsun/awt/windows/WComponentPeer;)V 786 */ 787 JNIEXPORT void JNICALL 788 Java_sun_awt_windows_WTextAreaPeer_create(JNIEnv *env, jobject self, 789 jobject parent) 790 { 791 TRY; 792 793 PDATA pData; 794 JNI_CHECK_PEER_RETURN(parent); 795 AwtToolkit::CreateComponent(self, parent, 796 (AwtToolkit::ComponentFactory) 797 AwtTextArea::Create); 798 JNI_CHECK_PEER_CREATION_RETURN(self); 799 800 CATCH_BAD_ALLOC; 801 } 802 803 /* 804 * Class: sun_awt_windows_WTextAreaPeer 805 * Method: replaceText 806 * Signature: (Ljava/lang/String;II)V 807 */ 808 JNIEXPORT void JNICALL 809 Java_sun_awt_windows_WTextAreaPeer_replaceText(JNIEnv *env, jobject self, 810 jstring text, 811 jint start, jint end) 812 { 813 TRY; 814 815 jobject selfGlobalRef = env->NewGlobalRef(self); 816 jstring textGlobalRef = (jstring)env->NewGlobalRef(text); 817 818 ReplaceTextStruct *rts = new ReplaceTextStruct; 819 rts->textComponent = selfGlobalRef; 820 rts->text = textGlobalRef; 821 rts->start = start; 822 rts->end = end; 823 824 AwtToolkit::GetInstance().SyncCall(AwtTextArea::_ReplaceText, rts); 825 // global refs and rts are deleted in _ReplaceText() 826 827 CATCH_BAD_ALLOC; 828 } 829 830 /* 831 * Class: sun_awt_windows_WTextAreaPeer 832 * Method: insertText 833 * Signature: (Ljava/lang/String;I)V 834 */ 835 JNIEXPORT void JNICALL 836 Java_sun_awt_windows_WTextAreaPeer_insertText(JNIEnv *env, jobject self, 837 jstring text, jint pos) 838 { 839 Java_sun_awt_windows_WTextAreaPeer_replaceText(env, self, text, pos, pos); 840 } 841 842 } /* extern "C" */ 843 844