1 /*
   2  * Copyright (c) 1996, 2013, 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_TextField.h"
  28 #include "awt_TextComponent.h"
  29 #include "awt_Canvas.h"
  30 
  31 /* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
  32  */
  33 
  34 /***********************************************************************/
  35 // struct for _SetEchoChar() method
  36 struct SetEchoCharStruct {
  37     jobject textfield;
  38     jchar echoChar;
  39 };
  40 /************************************************************************
  41  * AwtTextField methods
  42  */
  43 
  44 AwtTextField::AwtTextField()
  45 {
  46 }
  47 
  48 /* Create a new AwtTextField object and window.   */
  49 AwtTextField* AwtTextField::Create(jobject peer, jobject parent)
  50 {
  51     return (AwtTextField*) AwtTextComponent::Create(peer, parent, false);
  52 }
  53 
  54 void AwtTextField::EditSetSel(CHARRANGE &cr) {
  55     SendMessage(EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&cr));
  56 
  57     // 6417581: force expected drawing
  58     if (IS_WINVISTA && cr.cpMin == cr.cpMax) {
  59         ::InvalidateRect(GetHWnd(), NULL, TRUE);
  60     }
  61 
  62 }
  63 
  64 LRESULT AwtTextField::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  65 {
  66     if (message == WM_UNDO || message == EM_UNDO || message == EM_CANUNDO) {
  67         if (GetWindowLong(GetHWnd(), GWL_STYLE) & ES_READONLY) {
  68             return FALSE;
  69         }
  70     }
  71     return AwtTextComponent::WindowProc(message, wParam, lParam);
  72 }
  73 
  74 MsgRouting
  75 AwtTextField::HandleEvent(MSG *msg, BOOL synthetic)
  76 {
  77     MsgRouting returnVal;
  78     BOOL systemBeeperEnabled = FALSE;
  79     /*
  80      * RichEdit 1.0 control starts internal message loop if the
  81      * left mouse button is pressed while the cursor is not over
  82      * the current selection or the current selection is empty.
  83      * Because of this we don't receive WM_MOUSEMOVE messages
  84      * while the left mouse button is pressed. To work around
  85      * this behavior we process the relevant mouse messages
  86      * by ourselves.
  87      * By consuming WM_MOUSEMOVE messages we also don't give
  88      * the RichEdit control a chance to recognize a drag gesture
  89      * and initiate its own drag-n-drop operation.
  90      *
  91      * The workaround also allows us to implement synthetic focus mechanism.
  92      */
  93     if (IsFocusingMouseMessage(msg)) {
  94 
  95         LONG lCurPos = EditGetCharFromPos(msg->pt);
  96 
  97         /*
  98          * NOTE: Plain EDIT control always clears selection on mouse
  99          * button press. We are clearing the current selection only if
 100          * the mouse pointer is not over the selected region.
 101          * In this case we sacrifice backward compatibility
 102          * to allow dnd of the current selection.
 103          */
 104         if (msg->message == WM_LBUTTONDBLCLK) {
 105             jchar echo = SendMessage(EM_GETPASSWORDCHAR);
 106 
 107             if(echo == 0){
 108               SetStartSelectionPos(static_cast<LONG>(SendMessage(
 109                   EM_FINDWORDBREAK, WB_MOVEWORDLEFT, lCurPos)));
 110               SetEndSelectionPos(static_cast<LONG>(SendMessage(
 111                   EM_FINDWORDBREAK, WB_MOVEWORDRIGHT, lCurPos)));
 112             }else{
 113               SetStartSelectionPos(0);
 114               SetEndSelectionPos(GetTextLength());
 115             }
 116 
 117         } else {
 118             SetStartSelectionPos(lCurPos);
 119             SetEndSelectionPos(lCurPos);
 120         }
 121         CHARRANGE cr;
 122         cr.cpMin = GetStartSelectionPos();
 123         cr.cpMax = GetEndSelectionPos();
 124         EditSetSel(cr);
 125 
 126         delete msg;
 127         return mrConsume;
 128     } else if (msg->message == WM_LBUTTONUP) {
 129 
 130         /*
 131          * If the left mouse button is pressed on the selected region
 132          * we don't clear the current selection. We clear it on button
 133          * release instead. This is to allow dnd of the current selection.
 134          */
 135         if (GetStartSelectionPos() == -1 && GetEndSelectionPos() == -1) {
 136             CHARRANGE cr;
 137 
 138             LONG lCurPos = EditGetCharFromPos(msg->pt);
 139 
 140             cr.cpMin = lCurPos;
 141             cr.cpMax = lCurPos;
 142             EditSetSel(cr);
 143         }
 144 
 145         /*
 146          * Cleanup the state variables when left mouse button is released.
 147          * These state variables are designed to reflect the selection state
 148          * while the left mouse button is pressed and be set to -1 otherwise.
 149          */
 150         SetStartSelectionPos(-1);
 151         SetEndSelectionPos(-1);
 152         SetLastSelectionPos(-1);
 153 
 154         delete msg;
 155         return mrConsume;
 156     } else if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON)) {
 157 
 158         /*
 159          * We consume WM_MOUSEMOVE while the left mouse button is pressed,
 160          * so we have to simulate autoscrolling when mouse is moved outside
 161          * of the client area.
 162          */
 163         POINT p;
 164         RECT r;
 165         BOOL bScrollLeft = FALSE;
 166         BOOL bScrollRight = FALSE;
 167         BOOL bScrollUp = FALSE;
 168         BOOL bScrollDown = FALSE;
 169 
 170         p.x = msg->pt.x;
 171         p.y = msg->pt.y;
 172         VERIFY(::GetClientRect(GetHWnd(), &r));
 173 
 174         if (p.x < 0) {
 175             bScrollLeft = TRUE;
 176             p.x = 0;
 177         } else if (p.x > r.right) {
 178             bScrollRight = TRUE;
 179             p.x = r.right - 1;
 180         }
 181         LONG lCurPos = EditGetCharFromPos(p);
 182 
 183         if (GetStartSelectionPos() != -1 &&
 184             GetEndSelectionPos() != -1 &&
 185             lCurPos != GetLastSelectionPos()) {
 186 
 187             CHARRANGE cr;
 188 
 189             SetLastSelectionPos(lCurPos);
 190 
 191             cr.cpMin = GetStartSelectionPos();
 192             cr.cpMax = GetLastSelectionPos();
 193 
 194             EditSetSel(cr);
 195         }
 196 
 197         if (bScrollLeft == TRUE || bScrollRight == TRUE) {
 198             SCROLLINFO si;
 199             memset(&si, 0, sizeof(si));
 200             si.cbSize = sizeof(si);
 201             si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
 202 
 203             VERIFY(::GetScrollInfo(GetHWnd(), SB_HORZ, &si));
 204             if (bScrollLeft == TRUE) {
 205                 si.nPos = si.nPos - si.nPage / 2;
 206                 si.nPos = max(si.nMin, si.nPos);
 207             } else if (bScrollRight == TRUE) {
 208                 si.nPos = si.nPos + si.nPage / 2;
 209                 si.nPos = min(si.nPos, si.nMax);
 210             }
 211             /*
 212              * Okay to use 16-bit position since RichEdit control adjusts
 213              * its scrollbars so that their range is always 16-bit.
 214              */
 215             DASSERT(abs(si.nPos) < 0x8000);
 216             SendMessage(WM_HSCROLL,
 217                         MAKEWPARAM(SB_THUMBPOSITION, LOWORD(si.nPos)));
 218         }
 219         delete msg;
 220         return mrConsume;
 221     } else if (msg->message == WM_KEYDOWN) {
 222         UINT virtualKey = (UINT) msg->wParam;
 223 
 224         switch(virtualKey){
 225           case VK_RETURN:
 226           case VK_UP:
 227           case VK_DOWN:
 228           case VK_LEFT:
 229           case VK_RIGHT:
 230           case VK_DELETE:
 231           case VK_BACK:
 232               SystemParametersInfo(SPI_GETBEEP, 0, &systemBeeperEnabled, 0);
 233               if(systemBeeperEnabled){
 234                   // disable system beeper for the RICHEDIT control to be compatible
 235                   // with the EDIT control behaviour
 236                   SystemParametersInfo(SPI_SETBEEP, 0, NULL, 0);
 237               }
 238               break;
 239           }
 240     } else if (msg->message == WM_SETTINGCHANGE) {
 241         if (msg->wParam == SPI_SETBEEP) {
 242             SystemParametersInfo(SPI_GETBEEP, 0, &systemBeeperEnabled, 0);
 243             if(systemBeeperEnabled){
 244                 SystemParametersInfo(SPI_SETBEEP, 1, NULL, 0);
 245             }
 246         }
 247     }
 248 
 249     /*
 250      * Store the 'synthetic' parameter so that the WM_PASTE security check
 251      * happens only for synthetic events.
 252      */
 253     m_synthetic = synthetic;
 254     returnVal = AwtComponent::HandleEvent(msg, synthetic);
 255     m_synthetic = FALSE;
 256 
 257     if(systemBeeperEnabled){
 258         SystemParametersInfo(SPI_SETBEEP, 1, NULL, 0);
 259     }
 260 
 261     return returnVal;
 262 }
 263 
 264 void AwtTextField::_SetEchoChar(void *param)
 265 {
 266     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 267 
 268     SetEchoCharStruct *secs = (SetEchoCharStruct *)param;
 269     jobject self = secs->textfield;
 270     jchar echo = secs->echoChar;
 271 
 272     AwtTextField *c = NULL;
 273 
 274     PDATA pData;
 275     JNI_CHECK_PEER_GOTO(self, ret);
 276     c = (AwtTextField *)pData;
 277     if (::IsWindow(c->GetHWnd()))
 278     {
 279         c->SendMessage(EM_SETPASSWORDCHAR, echo);
 280         // Fix for 4307281: force redraw so that changes will take effect
 281         VERIFY(::InvalidateRect(c->GetHWnd(), NULL, FALSE));
 282     }
 283 ret:
 284     env->DeleteGlobalRef(self);
 285 
 286     delete secs;
 287 }
 288 
 289 
 290 /************************************************************************
 291  * WTextFieldPeer native methods
 292  */
 293 
 294 extern "C" {
 295 
 296 /*
 297  * Class:     sun_awt_windows_WTextFieldPeer
 298  * Method:    create
 299  * Signature: (Lsun/awt/windows/WComponentPeer;)V
 300  */
 301 JNIEXPORT void JNICALL
 302 Java_sun_awt_windows_WTextFieldPeer_create(JNIEnv *env, jobject self,
 303                                            jobject parent)
 304 {
 305     TRY;
 306 
 307     PDATA pData;
 308     JNI_CHECK_PEER_RETURN(parent);
 309     AwtToolkit::CreateComponent(self, parent,
 310                                 (AwtToolkit::ComponentFactory)
 311                                 AwtTextField::Create);
 312     JNI_CHECK_PEER_CREATION_RETURN(self);
 313 
 314     CATCH_BAD_ALLOC;
 315 }
 316 
 317 /*
 318  * Class:     sun_awt_windows_WTextFieldPeer
 319  * Method:    setEchoChar
 320  * Signature: (C)V
 321  */
 322 JNIEXPORT void JNICALL
 323 Java_sun_awt_windows_WTextFieldPeer_setEchoChar(JNIEnv *env, jobject self,
 324                                                 jchar ch)
 325 {
 326     TRY;
 327 
 328     SetEchoCharStruct *secs = new SetEchoCharStruct;
 329     secs->textfield = env->NewGlobalRef(self);
 330     secs->echoChar = ch;
 331 
 332     AwtToolkit::GetInstance().SyncCall(AwtTextField::_SetEchoChar, secs);
 333     // global ref and secs are deleted in _SetEchoChar()
 334 
 335     CATCH_BAD_ALLOC;
 336 }
 337 
 338 } /* extern "C" */