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 LPCTSTR AwtTextArea::GetClassName() { 78 static BOOL richedLibraryLoaded = FALSE; 79 if (!richedLibraryLoaded) { 80 JDK_LoadSystemLibrary("RICHED20.DLL"); 81 richedLibraryLoaded = TRUE; 82 } 83 return RICHEDIT_CLASS; 84 } 85 86 /* Create a new AwtTextArea object and window. */ 87 AwtTextArea* AwtTextArea::Create(jobject peer, jobject parent) 88 { 89 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 90 91 jobject target = NULL; 92 AwtTextArea* c = NULL; 93 94 try { 95 if (env->EnsureLocalCapacity(1) < 0) { 96 return NULL; 97 } 98 99 PDATA pData; 100 AwtCanvas* awtParent; 101 JNI_CHECK_PEER_GOTO(parent, done); 102 103 awtParent = (AwtCanvas*)pData; 104 JNI_CHECK_NULL_GOTO(awtParent, "null awtParent", done); 105 106 target = env->GetObjectField(peer, AwtObject::targetID); 107 JNI_CHECK_NULL_GOTO(target, "null target", done); 108 109 c = new AwtTextArea(); 110 111 { 112 /* Adjust style for scrollbar visibility and word wrap */ 113 DWORD scroll_style; 114 jint scrollbarVisibility = 115 env->GetIntField(target, AwtTextArea::scrollbarVisibilityID); 116 117 switch (scrollbarVisibility) { 118 case java_awt_TextArea_SCROLLBARS_NONE: 119 scroll_style = ES_AUTOVSCROLL; 120 break; 121 case java_awt_TextArea_SCROLLBARS_VERTICAL_ONLY: 122 scroll_style = WS_VSCROLL | ES_AUTOVSCROLL; 123 break; 124 case java_awt_TextArea_SCROLLBARS_HORIZONTAL_ONLY: 125 scroll_style = WS_HSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL; 126 break; 127 case java_awt_TextArea_SCROLLBARS_BOTH: 128 scroll_style = WS_VSCROLL | WS_HSCROLL | 129 ES_AUTOVSCROLL | ES_AUTOHSCROLL; 130 break; 131 } 132 133 /* 134 * Specify ES_DISABLENOSCROLL - RichEdit control style to disable 135 * scrollbars instead of hiding them when not needed. 136 */ 137 DWORD style = WS_CHILD | WS_CLIPSIBLINGS | ES_LEFT | ES_MULTILINE | 138 ES_WANTRETURN | scroll_style | ES_DISABLENOSCROLL; 139 DWORD exStyle = WS_EX_CLIENTEDGE; 140 if (GetRTL()) { 141 exStyle |= WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR; 142 if (GetRTLReadingOrder()) 143 exStyle |= WS_EX_RTLREADING; 144 } 145 146 jint x = env->GetIntField(target, AwtComponent::xID); 147 jint y = env->GetIntField(target, AwtComponent::yID); 148 jint width = env->GetIntField(target, AwtComponent::widthID); 149 jint height = env->GetIntField(target, AwtComponent::heightID); 150 151 c->CreateHWnd(env, L"", style, exStyle, 152 x, y, width, height, 153 awtParent->GetHWnd(), 154 reinterpret_cast<HMENU>(static_cast<INT_PTR>( 155 awtParent->CreateControlID())), 156 ::GetSysColor(COLOR_WINDOWTEXT), 157 ::GetSysColor(COLOR_WINDOW), 158 peer); 159 160 // Fix for 4753116. 161 // If it is not win95 (we are using Richedit 2.0) 162 // we set plain text mode, in which the control is 163 // similar to a standard edit control: 164 // - The text in a plain text control can have only 165 // one format. 166 // - The user cannot paste rich text formats, such as RTF 167 // or embedded objects into a plain text control. 168 // - Rich text mode controls always have a default 169 // end-of-document marker or carriage return, 170 // to format paragraphs. 171 // kdm@sparc.spb.su 172 c->SendMessage(EM_SETTEXTMODE, TM_PLAINTEXT, 0); 173 174 c->m_backgroundColorSet = TRUE; 175 /* suppress inheriting parent's color. */ 176 c->UpdateBackground(env, target); 177 c->SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, 178 MAKELPARAM(1, 1)); 179 /* 180 * Fix for BugTraq Id 4260109. 181 * Set the text limit to the maximum. 182 * Use EM_EXLIMITTEXT for RichEdit controls. 183 * For some reason RichEdit 1.0 becomes read-only if the 184 * specified limit is greater than 0x7FFFFFFD. 185 */ 186 c->SendMessage(EM_EXLIMITTEXT, 0, 0x7FFFFFFD); 187 188 /* Unregister RichEdit built-in drop target. */ 189 VERIFY(::RevokeDragDrop(c->GetHWnd()) != DRAGDROP_E_INVALIDHWND); 190 191 /* To enforce CF_TEXT format for paste operations. */ 192 VERIFY(c->SendMessage(EM_SETOLECALLBACK, 0, 193 (LPARAM)&GetOleCallback())); 194 195 c->SendMessage(EM_SETEVENTMASK, 0, ENM_CHANGE); 196 } 197 } catch (...) { 198 env->DeleteLocalRef(target); 199 throw; 200 } 201 202 done: 203 env->DeleteLocalRef(target); 204 205 return c; 206 } 207 208 void AwtTextArea::EditSetSel(CHARRANGE &cr) { 209 // Fix for 5003402: added restoring/hiding selection to enable automatic scrolling 210 SendMessage(EM_HIDESELECTION, FALSE, TRUE); 211 SendMessage(EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&cr)); 212 SendMessage(EM_HIDESELECTION, TRUE, TRUE); 213 // 6417581: force expected drawing 214 if (IS_WINVISTA && cr.cpMin == cr.cpMax) { 215 ::InvalidateRect(GetHWnd(), NULL, TRUE); 216 } 217 } 218 219 void AwtTextArea::EditGetSel(CHARRANGE &cr) { 220 SendMessage(EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&cr)); 221 } 222 223 LONG AwtTextArea::EditGetCharFromPos(POINT& pt) { 224 return static_cast<LONG>(SendMessage(EM_CHARFROMPOS, 0, 225 reinterpret_cast<LPARAM>(&pt))); 226 } 227 228 /* Count how many '\n's are there in jStr */ 229 size_t AwtTextArea::CountNewLines(JNIEnv *env, jstring jStr, size_t maxlen) 230 { 231 size_t nNewlines = 0; 232 233 if (jStr == NULL) { 234 return nNewlines; 235 } 236 /* 237 * Fix for BugTraq Id 4260109. 238 * Don't use TO_WSTRING since it allocates memory on the stack 239 * causing stack overflow when the text is very long. 240 */ 241 size_t length = env->GetStringLength(jStr) + 1; 242 WCHAR *string = new WCHAR[length]; 243 env->GetStringRegion(jStr, 0, static_cast<jsize>(length - 1), reinterpret_cast<jchar*>(string)); 244 string[length-1] = '\0'; 245 for (size_t i = 0; i < maxlen && i < length - 1; i++) { 246 if (string[i] == L'\n') { 247 nNewlines++; 248 } 249 } 250 delete[] string; 251 return nNewlines; 252 } 253 254 BOOL AwtTextArea::InheritsNativeMouseWheelBehavior() {return true;} 255 256 MsgRouting 257 AwtTextArea::PreProcessMsg(MSG& msg) 258 { 259 MsgRouting mr = mrPassAlong; 260 static BOOL bPassAlongWmLButtonUp = TRUE; 261 262 if (msg.message == WM_LBUTTONDBLCLK) { 263 bPassAlongWmLButtonUp = FALSE; 264 } 265 266 /* 267 * For some reason RichEdit 1.0 filters out WM_LBUTTONUP after 268 * WM_LBUTTONDBLCLK. To work around this "feature" we send WM_LBUTTONUP 269 * directly to the window procedure and consume instead of passing it 270 * to the next hook. 271 */ 272 if (msg.message == WM_LBUTTONUP && bPassAlongWmLButtonUp == FALSE) { 273 SendMessage(WM_LBUTTONUP, msg.wParam, msg.lParam); 274 bPassAlongWmLButtonUp = TRUE; 275 mr = mrConsume; 276 } 277 278 if (mr == mrPassAlong) { 279 mr = AwtComponent::PreProcessMsg(msg); 280 } 281 282 return mr; 283 } 284 285 LRESULT 286 AwtTextArea::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { 287 288 LRESULT retValue = 0; 289 MsgRouting mr = mrDoDefault; 290 291 switch (message) { 292 case WM_PRINTCLIENT: 293 { 294 FORMATRANGE fr; 295 HDC hPrinterDC = (HDC)wParam; 296 int nHorizRes = ::GetDeviceCaps(hPrinterDC, HORZRES); 297 int nVertRes = ::GetDeviceCaps(hPrinterDC, VERTRES); 298 int nLogPixelsX = ::GetDeviceCaps(hPrinterDC, LOGPIXELSX); 299 int nLogPixelsY = ::GetDeviceCaps(hPrinterDC, LOGPIXELSY); 300 301 // Ensure the printer DC is in MM_TEXT mode. 302 ::SetMapMode ( hPrinterDC, MM_TEXT ); 303 304 // Rendering to the same DC we are measuring. 305 ::ZeroMemory(&fr, sizeof(fr)); 306 fr.hdc = fr.hdcTarget = hPrinterDC; 307 // Set up the page. 308 fr.rcPage.left = fr.rcPage.top = 0; 309 fr.rcPage.right = (nHorizRes/nLogPixelsX) * 1440; // in twips 310 fr.rcPage.bottom = (nVertRes/nLogPixelsY) * 1440; 311 fr.rc.left = fr.rcPage.left; 312 fr.rc.top = fr.rcPage.top; 313 fr.rc.right = fr.rcPage.right; 314 fr.rc.bottom = fr.rcPage.bottom; 315 316 // start printing from the first visible line 317 LRESULT nLine = SendMessage(EM_GETFIRSTVISIBLELINE, 0, 0); 318 LONG startCh = static_cast<LONG>(SendMessage(EM_LINEINDEX, 319 (WPARAM)nLine, 0)); 320 fr.chrg.cpMin = startCh; 321 fr.chrg.cpMax = -1; 322 323 SendMessage(EM_FORMATRANGE, TRUE, (LPARAM)&fr); 324 } 325 326 break; 327 case EM_SETCHARFORMAT: 328 case WM_SETFONT: 329 SetIgnoreEnChange(TRUE); 330 break; 331 } 332 333 retValue = AwtComponent::WindowProc(message, wParam, lParam); 334 335 switch (message) { 336 case EM_SETCHARFORMAT: 337 case WM_SETFONT: 338 SetIgnoreEnChange(FALSE); 339 break; 340 } 341 342 return retValue; 343 } 344 345 /* 346 * This routine is a window procedure for the subclass of the standard edit control 347 * used to generate context menu. RichEdit controls don't have built-in context menu. 348 * To implement this functionality we have to create an invisible edit control and 349 * forward WM_CONTEXTMENU messages from a RichEdit control to this helper edit control. 350 * While the edit control context menu is active we intercept the message generated in 351 * response to particular item selection and forward it back to the RichEdit control. 352 * (See AwtTextArea::WmContextMenu for more details). 353 */ 354 LRESULT 355 AwtTextArea::EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { 356 357 static BOOL bContextMenuActive = FALSE; 358 359 LRESULT retValue = 0; 360 MsgRouting mr = mrDoDefault; 361 362 DASSERT(::IsWindow(::GetParent(hWnd))); 363 364 switch (message) { 365 case WM_UNDO: 366 case WM_CUT: 367 case WM_COPY: 368 case WM_PASTE: 369 case WM_CLEAR: 370 case EM_SETSEL: 371 if (bContextMenuActive) { 372 ::SendMessage(::GetParent(hWnd), message, wParam, lParam); 373 mr = mrConsume; 374 } 375 break; 376 case WM_CONTEXTMENU: 377 bContextMenuActive = TRUE; 378 break; 379 } 380 381 if (mr == mrDoDefault) { 382 DASSERT(sm_pDefWindowProc != NULL); 383 retValue = ::CallWindowProc(sm_pDefWindowProc, 384 hWnd, message, wParam, lParam); 385 } 386 387 if (message == WM_CONTEXTMENU) { 388 bContextMenuActive = FALSE; 389 } 390 391 return retValue; 392 } 393 394 MsgRouting 395 AwtTextArea::WmContextMenu(HWND hCtrl, UINT xPos, UINT yPos) { 396 /* Use the system provided edit control class to generate context menu. */ 397 if (m_hEditCtrl == NULL) { 398 DWORD dwStyle = WS_CHILD; 399 DWORD dwExStyle = 0; 400 m_hEditCtrl = ::CreateWindowEx(dwExStyle, 401 L"EDIT", 402 L"TEXT", 403 dwStyle, 404 0, 0, 0, 0, 405 GetHWnd(), 406 reinterpret_cast<HMENU>( 407 static_cast<INT_PTR>( 408 CreateControlID())), 409 AwtToolkit::GetInstance().GetModuleHandle(), 410 NULL); 411 DASSERT(m_hEditCtrl != NULL); 412 if (sm_pDefWindowProc == NULL) { 413 sm_pDefWindowProc = (WNDPROC)::GetWindowLongPtr(m_hEditCtrl, 414 GWLP_WNDPROC); 415 } 416 ::SetLastError(0); 417 INT_PTR ret = ::SetWindowLongPtr(m_hEditCtrl, GWLP_WNDPROC, 418 (INT_PTR)AwtTextArea::EditProc); 419 DASSERT(ret != 0 || ::GetLastError() == 0); 420 } 421 422 /* 423 * Tricks on the edit control to ensure that its context menu has 424 * the correct set of enabled items according to the RichEdit state. 425 */ 426 ::SetWindowText(m_hEditCtrl, TEXT("TEXT")); 427 428 if (m_bCanUndo == TRUE && SendMessage(EM_CANUNDO)) { 429 /* Enable 'Undo' item. */ 430 ::SendMessage(m_hEditCtrl, WM_CHAR, 'A', 0); 431 } 432 433 { 434 /* 435 * Initial selection for the edit control - (0,1). 436 * This enables 'Cut', 'Copy' and 'Delete' and 'Select All'. 437 */ 438 INT nStart = 0; 439 INT nEnd = 1; 440 if (SendMessage(EM_SELECTIONTYPE) == SEL_EMPTY) { 441 /* 442 * RichEdit selection is empty - clear selection of the edit control. 443 * This disables 'Cut', 'Copy' and 'Delete'. 444 */ 445 nStart = -1; 446 nEnd = 0; 447 } else { 448 449 CHARRANGE cr; 450 EditGetSel(cr); 451 /* Check if all the text is selected. */ 452 if (cr.cpMin == 0) { 453 454 int len = ::GetWindowTextLength(GetHWnd()); 455 if (cr.cpMin == 0 && cr.cpMax >= len) { 456 /* 457 * All the text is selected in RichEdit - select all the 458 * text in the edit control. This disables 'Select All'. 459 */ 460 nStart = 0; 461 nEnd = -1; 462 } 463 } 464 } 465 ::SendMessage(m_hEditCtrl, EM_SETSEL, (WPARAM)nStart, (LPARAM)nEnd); 466 } 467 468 /* Disable 'Paste' item if the RichEdit control is read-only. */ 469 ::SendMessage(m_hEditCtrl, EM_SETREADONLY, 470 GetStyle() & ES_READONLY ? TRUE : FALSE, 0); 471 472 POINT p; 473 p.x = xPos; 474 p.y = yPos; 475 476 /* 477 * If the context menu is requested with SHIFT+F10 or VK_APPS key, 478 * we position its top left corner to the center of the RichEdit 479 * client rect. 480 */ 481 if (p.x == -1 && p.y == -1) { 482 RECT r; 483 VERIFY(::GetClientRect(GetHWnd(), &r)); 484 p.x = (r.left + r.right) / 2; 485 p.y = (r.top + r.bottom) / 2; 486 VERIFY(::ClientToScreen(GetHWnd(), &p)); 487 } 488 489 // The context menu steals focus from the proxy. 490 // So, set the focus-restore flag up. 491 SetRestoreFocus(TRUE); 492 ::SendMessage(m_hEditCtrl, WM_CONTEXTMENU, (WPARAM)m_hEditCtrl, MAKELPARAM(p.x, p.y)); 493 SetRestoreFocus(FALSE); 494 495 return mrConsume; 496 } 497 498 MsgRouting 499 AwtTextArea::WmNcHitTest(UINT x, UINT y, LRESULT& retVal) 500 { 501 if (::IsWindow(AwtWindow::GetModalBlocker(AwtComponent::GetTopLevelParentForWindow(GetHWnd())))) { 502 retVal = HTCLIENT; 503 return mrConsume; 504 } 505 return AwtTextComponent::WmNcHitTest(x, y, retVal); 506 } 507 508 509 MsgRouting 510 AwtTextArea::WmNotify(UINT notifyCode) 511 { 512 if (notifyCode == EN_CHANGE) { 513 /* 514 * Ignore notifications if the text hasn't been changed. 515 * EN_CHANGE sent on character formatting changes as well. 516 */ 517 if (m_bIgnoreEnChange == FALSE) { 518 m_bCanUndo = TRUE; 519 DoCallback("valueChanged", "()V"); 520 } else { 521 m_bCanUndo = FALSE; 522 } 523 } 524 return mrDoDefault; 525 } 526 527 MsgRouting 528 AwtTextArea::HandleEvent(MSG *msg, BOOL synthetic) 529 { 530 MsgRouting returnVal; 531 /* 532 * RichEdit 1.0 control starts internal message loop if the 533 * left mouse button is pressed while the cursor is not over 534 * the current selection or the current selection is empty. 535 * Because of this we don't receive WM_MOUSEMOVE messages 536 * while the left mouse button is pressed. To work around 537 * this behavior we process the relevant mouse messages 538 * by ourselves. 539 * By consuming WM_MOUSEMOVE messages we also don't give 540 * the RichEdit control a chance to recognize a drag gesture 541 * and initiate its own drag-n-drop operation. 542 * 543 * The workaround also allows us to implement synthetic focus mechanism. 544 * 545 */ 546 if (IsFocusingMouseMessage(msg)) { 547 CHARRANGE cr; 548 549 LONG lCurPos = EditGetCharFromPos(msg->pt); 550 551 EditGetSel(cr); 552 /* 553 * NOTE: Plain EDIT control always clears selection on mouse 554 * button press. We are clearing the current selection only if 555 * the mouse pointer is not over the selected region. 556 * In this case we sacrifice backward compatibility 557 * to allow dnd of the current selection. 558 */ 559 if (lCurPos < cr.cpMin || cr.cpMax <= lCurPos) { 560 if (msg->message == WM_LBUTTONDBLCLK) { 561 SetStartSelectionPos(static_cast<LONG>(SendMessage( 562 EM_FINDWORDBREAK, WB_MOVEWORDLEFT, lCurPos))); 563 SetEndSelectionPos(static_cast<LONG>(SendMessage( 564 EM_FINDWORDBREAK, WB_MOVEWORDRIGHT, lCurPos))); 565 } else { 566 SetStartSelectionPos(lCurPos); 567 SetEndSelectionPos(lCurPos); 568 } 569 cr.cpMin = GetStartSelectionPos(); 570 cr.cpMax = GetEndSelectionPos(); 571 EditSetSel(cr); 572 } 573 574 delete msg; 575 return mrConsume; 576 } else if (msg->message == WM_LBUTTONUP) { 577 578 /* 579 * If the left mouse button is pressed on the selected region 580 * we don't clear the current selection. We clear it on button 581 * release instead. This is to allow dnd of the current selection. 582 */ 583 if (GetStartSelectionPos() == -1 && GetEndSelectionPos() == -1) { 584 CHARRANGE cr; 585 586 LONG lCurPos = EditGetCharFromPos(msg->pt); 587 588 cr.cpMin = lCurPos; 589 cr.cpMax = lCurPos; 590 EditSetSel(cr); 591 } 592 593 /* 594 * Cleanup the state variables when left mouse button is released. 595 * These state variables are designed to reflect the selection state 596 * while the left mouse button is pressed and be set to -1 otherwise. 597 */ 598 SetStartSelectionPos(-1); 599 SetEndSelectionPos(-1); 600 SetLastSelectionPos(-1); 601 602 delete msg; 603 return mrConsume; 604 } else if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON)) { 605 606 /* 607 * We consume WM_MOUSEMOVE while the left mouse button is pressed, 608 * so we have to simulate autoscrolling when mouse is moved outside 609 * of the client area. 610 */ 611 POINT p; 612 RECT r; 613 BOOL bScrollLeft = FALSE; 614 BOOL bScrollRight = FALSE; 615 BOOL bScrollUp = FALSE; 616 BOOL bScrollDown = FALSE; 617 618 p.x = msg->pt.x; 619 p.y = msg->pt.y; 620 VERIFY(::GetClientRect(GetHWnd(), &r)); 621 622 if (p.x < 0) { 623 bScrollLeft = TRUE; 624 p.x = 0; 625 } else if (p.x > r.right) { 626 bScrollRight = TRUE; 627 p.x = r.right - 1; 628 } 629 if (p.y < 0) { 630 bScrollUp = TRUE; 631 p.y = 0; 632 } else if (p.y > r.bottom) { 633 bScrollDown = TRUE; 634 p.y = r.bottom - 1; 635 } 636 637 LONG lCurPos = EditGetCharFromPos(p); 638 639 if (GetStartSelectionPos() != -1 && 640 GetEndSelectionPos() != -1 && 641 lCurPos != GetLastSelectionPos()) { 642 643 CHARRANGE cr; 644 645 SetLastSelectionPos(lCurPos); 646 647 cr.cpMin = GetStartSelectionPos(); 648 cr.cpMax = GetLastSelectionPos(); 649 650 EditSetSel(cr); 651 } 652 653 if (bScrollLeft == TRUE || bScrollRight == TRUE) { 654 SCROLLINFO si; 655 memset(&si, 0, sizeof(si)); 656 si.cbSize = sizeof(si); 657 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; 658 659 VERIFY(::GetScrollInfo(GetHWnd(), SB_HORZ, &si)); 660 if (bScrollLeft == TRUE) { 661 si.nPos = si.nPos - si.nPage / 2; 662 si.nPos = max(si.nMin, si.nPos); 663 } else if (bScrollRight == TRUE) { 664 si.nPos = si.nPos + si.nPage / 2; 665 si.nPos = min(si.nPos, si.nMax); 666 } 667 /* 668 * Okay to use 16-bit position since RichEdit control adjusts 669 * its scrollbars so that their range is always 16-bit. 670 */ 671 DASSERT(abs(si.nPos) < 0x8000); 672 SendMessage(WM_HSCROLL, 673 MAKEWPARAM(SB_THUMBPOSITION, LOWORD(si.nPos))); 674 } 675 if (bScrollUp == TRUE) { 676 SendMessage(EM_LINESCROLL, 0, -1); 677 } else if (bScrollDown == TRUE) { 678 SendMessage(EM_LINESCROLL, 0, 1); 679 } 680 delete msg; 681 return mrConsume; 682 } else if (msg->message == WM_RBUTTONUP || 683 (msg->message == WM_SYSKEYDOWN && msg->wParam == VK_F10 && 684 HIBYTE(::GetKeyState(VK_SHIFT)))) { 685 POINT p; 686 if (msg->message == WM_RBUTTONUP) { 687 VERIFY(::GetCursorPos(&p)); 688 } else { 689 p.x = -1; 690 p.y = -1; 691 } 692 693 if (!::PostMessage(GetHWnd(), WM_CONTEXTMENU, (WPARAM)GetHWnd(), 694 MAKELPARAM(p.x, p.y))) { 695 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 696 JNU_ThrowInternalError(env, "Message not posted, native event queue may be full."); 697 env->ExceptionDescribe(); 698 env->ExceptionClear(); 699 } 700 delete msg; 701 return mrConsume; 702 } else if (msg->message == WM_MOUSEWHEEL) { 703 // 4417236: If there is an old version of RichEd32.dll which 704 // does not provide the mouse wheel scrolling we have to 705 // interpret WM_MOUSEWHEEL as a sequence of scroll messages. 706 // kdm@sparc.spb.su 707 UINT platfScrollLines = 3; 708 // Retrieve a number of scroll lines. 709 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, 710 &platfScrollLines, 0); 711 712 if (platfScrollLines > 0) { 713 HWND hWnd = GetHWnd(); 714 LONG styles = ::GetWindowLong(hWnd, GWL_STYLE); 715 716 RECT rect; 717 // rect.left and rect.top are zero. 718 // rect.right and rect.bottom contain the width and height 719 VERIFY(::GetClientRect(hWnd, &rect)); 720 721 // calculate a number of visible lines 722 TEXTMETRIC tm; 723 HDC hDC = ::GetDC(hWnd); 724 DASSERT(hDC != NULL); 725 VERIFY(::GetTextMetrics(hDC, &tm)); 726 VERIFY(::ReleaseDC(hWnd, hDC)); 727 LONG visibleLines = rect.bottom / tm.tmHeight + 1; 728 729 LONG lineCount = static_cast<LONG>(::SendMessage(hWnd, 730 EM_GETLINECOUNT, 0, 0)); 731 BOOL sb_vert_disabled = (styles & WS_VSCROLL) == 0 732 || (lineCount <= visibleLines); 733 734 LONG *delta_accum = &m_lVDeltaAccum; 735 UINT wm_msg = WM_VSCROLL; 736 int sb_type = SB_VERT; 737 738 if (sb_vert_disabled && (styles & WS_HSCROLL)) { 739 delta_accum = &m_lHDeltaAccum; 740 wm_msg = WM_HSCROLL; 741 sb_type = SB_HORZ; 742 } 743 744 int delta = (short) HIWORD(msg->wParam); 745 *delta_accum += delta; 746 if (abs(*delta_accum) >= WHEEL_DELTA) { 747 if (platfScrollLines == WHEEL_PAGESCROLL) { 748 // Synthesize a page down or a page up message. 749 ::SendMessage(hWnd, wm_msg, 750 (delta > 0) ? SB_PAGEUP : SB_PAGEDOWN, 0); 751 *delta_accum = 0; 752 } else { 753 // We provide a friendly behavior of text scrolling. 754 // During of scrolling the text can be out of the client 755 // area's boundary. Here we try to prevent this behavior. 756 SCROLLINFO si; 757 ::ZeroMemory(&si, sizeof(si)); 758 si.cbSize = sizeof(SCROLLINFO); 759 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; 760 int actualScrollLines = 761 abs((int)(platfScrollLines * (*delta_accum / WHEEL_DELTA))); 762 for (int i = 0; i < actualScrollLines; i++) { 763 if (::GetScrollInfo(hWnd, sb_type, &si)) { 764 if ((wm_msg == WM_VSCROLL) 765 && ((*delta_accum < 0 766 && si.nPos >= (si.nMax - (int) si.nPage)) 767 || (*delta_accum > 0 768 && si.nPos <= si.nMin))) { 769 break; 770 } 771 } 772 // Here we don't send EM_LINESCROLL or EM_SCROLL 773 // messages to rich edit because it doesn't 774 // provide horizontal scrolling. 775 // So it's only possible to scroll by 1 line 776 // at a time to prevent scrolling when the 777 // scrollbar thumb reaches its boundary position. 778 ::SendMessage(hWnd, wm_msg, 779 (*delta_accum>0) ? SB_LINEUP : SB_LINEDOWN, 0); 780 } 781 *delta_accum %= WHEEL_DELTA; 782 } 783 } else { 784 *delta_accum = 0; 785 } 786 } 787 delete msg; 788 return mrConsume; 789 // 4417236: end of fix 790 } 791 792 /* 793 * Store the 'synthetic' parameter so that the WM_PASTE security check 794 * happens only for synthetic events. 795 */ 796 m_synthetic = synthetic; 797 returnVal = AwtComponent::HandleEvent(msg, synthetic); 798 m_synthetic = FALSE; 799 800 return returnVal; 801 } 802 803 /* 804 * WM_CTLCOLOR is not sent by rich edit controls. 805 * Use EM_SETCHARFORMAT and EM_SETBKGNDCOLOR to set 806 * respectively foreground and background color. 807 */ 808 void AwtTextArea::SetColor(COLORREF c) { 809 AwtComponent::SetColor(c); 810 811 CHARFORMAT cf; 812 memset(&cf, 0, sizeof(cf)); 813 cf.cbSize = sizeof(cf); 814 cf.dwMask = CFM_COLOR; 815 816 cf.crTextColor = ::IsWindowEnabled(GetHWnd()) ? GetColor() : ::GetSysColor(COLOR_3DSHADOW); 817 818 /* 819 * The documentation for EM_GETCHARFORMAT is not exactly 820 * correct. It appears that wParam has the same meaning 821 * as for EM_SETCHARFORMAT. Our task is to secure that 822 * all the characters in the control have the required 823 * formatting. That's why we use SCF_ALL. 824 */ 825 VERIFY(SendMessage(EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf)); 826 VERIFY(SendMessage(EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf)); 827 } 828 829 /* 830 * In responce to EM_SETBKGNDCOLOR rich edit changes 831 * its bg color and repaints itself so we don't need 832 * to force repaint. 833 */ 834 void AwtTextArea::SetBackgroundColor(COLORREF c) { 835 AwtComponent::SetBackgroundColor(c); 836 SendMessage(EM_SETBKGNDCOLOR, (WPARAM)FALSE, (LPARAM)GetBackgroundColor()); 837 } 838 839 /* 840 * Disabled edit control has grayed foreground. 841 * Disabled RichEdit 1.0 control has original foreground. 842 * Thus we have to set grayed foreground manually. 843 */ 844 void AwtTextArea::Enable(BOOL bEnable) 845 { 846 AwtComponent::Enable(bEnable); 847 848 SetColor(GetColor()); 849 } 850 851 852 /* Fix for 4776535, 4648702 853 * If width is 0 or 1 Windows hides the horizontal scroll bar even 854 * if the WS_HSCROLL style is set. It is a bug in Windows. 855 * As a workaround we should set an initial width to 2. 856 * kdm@sparc.spb.su 857 */ 858 void AwtTextArea::Reshape(int x, int y, int w, int h) 859 { 860 if (w < 2) { 861 w = 2; 862 } 863 AwtTextComponent::Reshape(x, y, w, h); 864 } 865 866 LONG AwtTextArea::getJavaSelPos(LONG orgPos) 867 { 868 long wlen; 869 long pos = orgPos; 870 long cur = 0; 871 long retval = 0; 872 LPTSTR wbuf; 873 874 if ((wlen = GetTextLength()) == 0) 875 return 0; 876 wbuf = new TCHAR[wlen + 1]; 877 GetText(wbuf, wlen + 1); 878 if (m_isLFonly == TRUE) { 879 wlen = RemoveCR(wbuf); 880 } 881 882 while (cur < pos && cur < wlen) { 883 if (wbuf[cur] == _T('\r') && wbuf[cur + 1] == _T('\n')) { 884 pos++; 885 } 886 cur++; 887 retval++; 888 } 889 delete[] wbuf; 890 return retval; 891 } 892 893 LONG AwtTextArea::getWin32SelPos(LONG orgPos) 894 { 895 if (GetTextLength() == 0) 896 return 0; 897 return orgPos; 898 } 899 900 void AwtTextArea::SetSelRange(LONG start, LONG end) 901 { 902 CHARRANGE cr; 903 cr.cpMin = getWin32SelPos(start); 904 cr.cpMax = getWin32SelPos(end); 905 EditSetSel(cr); 906 } 907 908 909 void AwtTextArea::_ReplaceText(void *param) 910 { 911 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 912 913 ReplaceTextStruct *rts = (ReplaceTextStruct *)param; 914 915 jobject textComponent = rts->textComponent; 916 jstring text = rts->text; 917 jint start = rts->start; 918 jint end = rts->end; 919 920 AwtTextComponent *c = NULL; 921 922 PDATA pData; 923 JNI_CHECK_PEER_GOTO(textComponent, done); 924 JNI_CHECK_NULL_GOTO(text, "null string", done); 925 926 c = (AwtTextComponent *)pData; 927 if (::IsWindow(c->GetHWnd())) 928 { 929 jsize length = env->GetStringLength(text) + 1; 930 // Bugid 4141477 - Can't use TO_WSTRING here because it uses alloca 931 // WCHAR* buffer = TO_WSTRING(text); 932 TCHAR *buffer = new TCHAR[length]; 933 env->GetStringRegion(text, 0, length-1, reinterpret_cast<jchar*>(buffer)); 934 buffer[length-1] = '\0'; 935 936 c->CheckLineSeparator(buffer); 937 c->RemoveCR(buffer); 938 // Fix for 5003402: added restoring/hiding selection to enable automatic scrolling 939 c->SendMessage(EM_HIDESELECTION, FALSE, TRUE); 940 c->SendMessage(EM_SETSEL, start, end); 941 c->SendMessage(EM_REPLACESEL, FALSE, (LPARAM)buffer); 942 c->SendMessage(EM_HIDESELECTION, TRUE, TRUE); 943 944 delete[] buffer; 945 } 946 947 done: 948 env->DeleteGlobalRef(textComponent); 949 env->DeleteGlobalRef(text); 950 951 delete rts; 952 } 953 954 955 /************************************************************************ 956 * TextArea native methods 957 */ 958 959 extern "C" { 960 961 /* 962 * Class: java_awt_TextArea 963 * Method: initIDs 964 * Signature: ()V 965 */ 966 JNIEXPORT void JNICALL 967 Java_java_awt_TextArea_initIDs(JNIEnv *env, jclass cls) 968 { 969 TRY; 970 971 AwtTextArea::scrollbarVisibilityID = 972 env->GetFieldID(cls, "scrollbarVisibility", "I"); 973 974 DASSERT(AwtTextArea::scrollbarVisibilityID != NULL); 975 976 CATCH_BAD_ALLOC; 977 } 978 979 } /* extern "C" */ 980 981 982 /************************************************************************ 983 * WTextAreaPeer native methods 984 */ 985 986 extern "C" { 987 988 /* 989 * Class: sun_awt_windows_WTextAreaPeer 990 * Method: create 991 * Signature: (Lsun/awt/windows/WComponentPeer;)V 992 */ 993 JNIEXPORT void JNICALL 994 Java_sun_awt_windows_WTextAreaPeer_create(JNIEnv *env, jobject self, 995 jobject parent) 996 { 997 TRY; 998 999 PDATA pData; 1000 JNI_CHECK_PEER_RETURN(parent); 1001 AwtToolkit::CreateComponent(self, parent, 1002 (AwtToolkit::ComponentFactory) 1003 AwtTextArea::Create); 1004 JNI_CHECK_PEER_CREATION_RETURN(self); 1005 1006 CATCH_BAD_ALLOC; 1007 } 1008 1009 /* 1010 * Class: sun_awt_windows_WTextAreaPeer 1011 * Method: replaceText 1012 * Signature: (Ljava/lang/String;II)V 1013 */ 1014 JNIEXPORT void JNICALL 1015 Java_sun_awt_windows_WTextAreaPeer_replaceText(JNIEnv *env, jobject self, 1016 jstring text, 1017 jint start, jint end) 1018 { 1019 TRY; 1020 1021 jobject selfGlobalRef = env->NewGlobalRef(self); 1022 jstring textGlobalRef = (jstring)env->NewGlobalRef(text); 1023 1024 ReplaceTextStruct *rts = new ReplaceTextStruct; 1025 rts->textComponent = selfGlobalRef; 1026 rts->text = textGlobalRef; 1027 rts->start = start; 1028 rts->end = end; 1029 1030 AwtToolkit::GetInstance().SyncCall(AwtTextArea::_ReplaceText, rts); 1031 // global refs and rts are deleted in _ReplaceText() 1032 1033 CATCH_BAD_ALLOC; 1034 } 1035 1036 /* 1037 * Class: sun_awt_windows_WTextAreaPeer 1038 * Method: insertText 1039 * Signature: (Ljava/lang/String;I)V 1040 */ 1041 JNIEXPORT void JNICALL 1042 Java_sun_awt_windows_WTextAreaPeer_insertText(JNIEnv *env, jobject self, 1043 jstring text, jint pos) 1044 { 1045 Java_sun_awt_windows_WTextAreaPeer_replaceText(env, self, text, pos, pos); 1046 } 1047 1048 } /* extern "C" */ 1049 1050 1051 AwtTextArea::OleCallback AwtTextArea::sm_oleCallback; 1052 1053 /************************************************************************ 1054 * Inner class OleCallback definition. 1055 */ 1056 1057 AwtTextArea::OleCallback::OleCallback() { 1058 m_refs = 0; 1059 AddRef(); 1060 } 1061 1062 STDMETHODIMP 1063 AwtTextArea::OleCallback::QueryInterface(REFIID riid, LPVOID * ppvObj) { 1064 1065 TRY; 1066 1067 if (::IsEqualIID(riid, IID_IUnknown)) { 1068 *ppvObj = (void __RPC_FAR *__RPC_FAR)(IUnknown*)this; 1069 AddRef(); 1070 return S_OK; 1071 } else if (::IsEqualIID(riid, IID_IRichEditOleCallback)) { 1072 *ppvObj = (void __RPC_FAR *__RPC_FAR)(IRichEditOleCallback*)this; 1073 AddRef(); 1074 return S_OK; 1075 } else { 1076 *ppvObj = NULL; 1077 return E_NOINTERFACE; 1078 } 1079 1080 CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY); 1081 } 1082 1083 STDMETHODIMP_(ULONG) 1084 AwtTextArea::OleCallback::AddRef() { 1085 return ++m_refs; 1086 } 1087 1088 STDMETHODIMP_(ULONG) 1089 AwtTextArea::OleCallback::Release() { 1090 int refs; 1091 1092 if ((refs = --m_refs) == 0) delete this; 1093 1094 return (ULONG)refs; 1095 } 1096 1097 STDMETHODIMP 1098 AwtTextArea::OleCallback::GetNewStorage(LPSTORAGE FAR * ppstg) { 1099 return E_NOTIMPL; 1100 } 1101 1102 STDMETHODIMP 1103 AwtTextArea::OleCallback::GetInPlaceContext(LPOLEINPLACEFRAME FAR * ppipframe, 1104 LPOLEINPLACEUIWINDOW FAR* ppipuiDoc, 1105 LPOLEINPLACEFRAMEINFO pipfinfo) 1106 { 1107 return E_NOTIMPL; 1108 } 1109 1110 STDMETHODIMP 1111 AwtTextArea::OleCallback::ShowContainerUI(BOOL fShow) { 1112 return E_NOTIMPL; 1113 } 1114 1115 STDMETHODIMP 1116 AwtTextArea::OleCallback::QueryInsertObject(LPCLSID pclsid, 1117 LPSTORAGE pstg, 1118 LONG cp) { 1119 return NOERROR; 1120 } 1121 1122 STDMETHODIMP 1123 AwtTextArea::OleCallback::DeleteObject(LPOLEOBJECT poleobj) { 1124 return NOERROR; 1125 } 1126 1127 STDMETHODIMP 1128 AwtTextArea::OleCallback::QueryAcceptData(LPDATAOBJECT pdataobj, 1129 CLIPFORMAT *pcfFormat, 1130 DWORD reco, 1131 BOOL fReally, 1132 HGLOBAL hMetaPict) { 1133 if (reco == RECO_PASTE) { 1134 // If CF_TEXT format is available edit controls will select it, 1135 // otherwise if it is CF_UNICODETEXT is available it will be 1136 // selected, otherwise if CF_OEMTEXT is available it will be selected. 1137 if (::IsClipboardFormatAvailable(CF_TEXT)) { 1138 *pcfFormat = CF_TEXT; 1139 } else if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) { 1140 *pcfFormat = CF_UNICODETEXT; 1141 } else if (::IsClipboardFormatAvailable(CF_OEMTEXT)) { 1142 *pcfFormat = CF_OEMTEXT; 1143 } else { 1144 // Don't allow rich edit to paste clipboard data 1145 // in other formats. 1146 *pcfFormat = CF_TEXT; 1147 } 1148 } 1149 1150 return NOERROR; 1151 } 1152 1153 STDMETHODIMP 1154 AwtTextArea::OleCallback::ContextSensitiveHelp(BOOL fEnterMode) { 1155 return NOERROR; 1156 } 1157 1158 STDMETHODIMP 1159 AwtTextArea::OleCallback::GetClipboardData(CHARRANGE *pchrg, 1160 DWORD reco, 1161 LPDATAOBJECT *ppdataobj) { 1162 return E_NOTIMPL; 1163 } 1164 1165 STDMETHODIMP 1166 AwtTextArea::OleCallback::GetDragDropEffect(BOOL fDrag, 1167 DWORD grfKeyState, 1168 LPDWORD pdwEffect) { 1169 1170 return E_NOTIMPL; 1171 } 1172 1173 1174 STDMETHODIMP 1175 AwtTextArea::OleCallback::GetContextMenu(WORD seltype, 1176 LPOLEOBJECT lpoleobj, 1177 CHARRANGE FAR * lpchrg, 1178 HMENU FAR * lphmenu) { 1179 return E_NOTIMPL; 1180 }