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_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     }
 241     /*
 242      * Store the 'synthetic' parameter so that the WM_PASTE security check
 243      * happens only for synthetic events.
 244      */
 245     m_synthetic = synthetic;
 246     returnVal = AwtComponent::HandleEvent(msg, synthetic);
 247     m_synthetic = FALSE;
 248 
 249     if(systemBeeperEnabled){
 250       SystemParametersInfo(SPI_SETBEEP, 1, NULL, 0);
 251     }
 252 
 253     return returnVal;
 254 }
 255 
 256 void AwtTextField::_SetEchoChar(void *param)
 257 {
 258     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 259 
 260     SetEchoCharStruct *secs = (SetEchoCharStruct *)param;
 261     jobject self = secs->textfield;
 262     jchar echo = secs->echoChar;
 263 
 264     AwtTextField *c = NULL;
 265 
 266     PDATA pData;
 267     JNI_CHECK_PEER_GOTO(self, ret);
 268     c = (AwtTextField *)pData;
 269     if (::IsWindow(c->GetHWnd()))
 270     {
 271         c->SendMessage(EM_SETPASSWORDCHAR, echo);
 272         // Fix for 4307281: force redraw so that changes will take effect
 273         VERIFY(::InvalidateRect(c->GetHWnd(), NULL, FALSE));
 274     }
 275 ret:
 276     env->DeleteGlobalRef(self);
 277 
 278     delete secs;
 279 }
 280 
 281 
 282 /************************************************************************
 283  * WTextFieldPeer native methods
 284  */
 285 
 286 extern "C" {
 287 
 288 /*
 289  * Class:     sun_awt_windows_WTextFieldPeer
 290  * Method:    create
 291  * Signature: (Lsun/awt/windows/WComponentPeer;)V
 292  */
 293 JNIEXPORT void JNICALL
 294 Java_sun_awt_windows_WTextFieldPeer_create(JNIEnv *env, jobject self,
 295                                            jobject parent)
 296 {
 297     TRY;
 298 
 299     PDATA pData;
 300     JNI_CHECK_PEER_RETURN(parent);
 301     AwtToolkit::CreateComponent(self, parent,
 302                                 (AwtToolkit::ComponentFactory)
 303                                 AwtTextField::Create);
 304     JNI_CHECK_PEER_CREATION_RETURN(self);
 305 
 306     CATCH_BAD_ALLOC;
 307 }
 308 
 309 /*
 310  * Class:     sun_awt_windows_WTextFieldPeer
 311  * Method:    setEchoCharacter
 312  * Signature: (C)V
 313  */
 314 JNIEXPORT void JNICALL
 315 Java_sun_awt_windows_WTextFieldPeer_setEchoCharacter(JNIEnv *env, jobject self,
 316                                                      jchar ch)
 317 {
 318     TRY;
 319 
 320     SetEchoCharStruct *secs = new SetEchoCharStruct;
 321     secs->textfield = env->NewGlobalRef(self);
 322     secs->echoChar = ch;
 323 
 324     AwtToolkit::GetInstance().SyncCall(AwtTextField::_SetEchoChar, secs);
 325     // global ref and secs are deleted in _SetEchoChar()
 326 
 327     CATCH_BAD_ALLOC;
 328 }
 329 
 330 } /* extern "C" */