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 }