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