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 selection autoscrolling when mouse is moved
 161          * outside of the client area.
 162          */
 163         POINT p;
 164         p.x = msg->pt.x;
 165         p.y = msg->pt.y;
 166         LONG lCurPos = EditGetCharFromPos(p);
 167 
 168         if (GetStartSelectionPos() != -1 &&
 169             GetEndSelectionPos() != -1 &&
 170             lCurPos != GetLastSelectionPos()) {
 171 
 172             CHARRANGE cr;
 173 
 174             SetLastSelectionPos(lCurPos);
 175 
 176             cr.cpMin = GetStartSelectionPos();
 177             cr.cpMax = GetLastSelectionPos();
 178 
 179             EditSetSel(cr);
 180         }
 181         delete msg;
 182         return mrConsume;
 183     } else if (msg->message == WM_KEYDOWN) {
 184         UINT virtualKey = (UINT) msg->wParam;
 185 
 186         switch(virtualKey){
 187           case VK_RETURN:
 188           case VK_UP:
 189           case VK_DOWN:
 190           case VK_LEFT:
 191           case VK_RIGHT:
 192           case VK_DELETE:
 193           case VK_BACK:
 194               SystemParametersInfo(SPI_GETBEEP, 0, &systemBeeperEnabled, 0);
 195               if(systemBeeperEnabled){
 196                   // disable system beeper for the RICHEDIT control to be compatible
 197                   // with the EDIT control behaviour
 198                   SystemParametersInfo(SPI_SETBEEP, 0, NULL, 0);
 199               }
 200               break;
 201           }
 202     } else if (msg->message == WM_SETTINGCHANGE) {
 203         if (msg->wParam == SPI_SETBEEP) {
 204             SystemParametersInfo(SPI_GETBEEP, 0, &systemBeeperEnabled, 0);
 205             if(systemBeeperEnabled){
 206                 SystemParametersInfo(SPI_SETBEEP, 1, NULL, 0);
 207             }
 208         }
 209     }
 210 
 211     returnVal = AwtTextComponent::HandleEvent(msg, synthetic);
 212 
 213     if(systemBeeperEnabled){
 214         SystemParametersInfo(SPI_SETBEEP, 1, NULL, 0);
 215     }
 216 
 217     return returnVal;
 218 }
 219 
 220 void AwtTextField::_SetEchoChar(void *param)
 221 {
 222     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 223 
 224     SetEchoCharStruct *secs = (SetEchoCharStruct *)param;
 225     jobject self = secs->textfield;
 226     jchar echo = secs->echoChar;
 227 
 228     AwtTextField *c = NULL;
 229 
 230     PDATA pData;
 231     JNI_CHECK_PEER_GOTO(self, ret);
 232     c = (AwtTextField *)pData;
 233     if (::IsWindow(c->GetHWnd()))
 234     {
 235         c->SendMessage(EM_SETPASSWORDCHAR, echo);
 236         // Fix for 4307281: force redraw so that changes will take effect
 237         VERIFY(::InvalidateRect(c->GetHWnd(), NULL, FALSE));
 238     }
 239 ret:
 240     env->DeleteGlobalRef(self);
 241 
 242     delete secs;
 243 }
 244 
 245 
 246 /************************************************************************
 247  * WTextFieldPeer native methods
 248  */
 249 
 250 extern "C" {
 251 
 252 /*
 253  * Class:     sun_awt_windows_WTextFieldPeer
 254  * Method:    create
 255  * Signature: (Lsun/awt/windows/WComponentPeer;)V
 256  */
 257 JNIEXPORT void JNICALL
 258 Java_sun_awt_windows_WTextFieldPeer_create(JNIEnv *env, jobject self,
 259                                            jobject parent)
 260 {
 261     TRY;
 262 
 263     AwtToolkit::CreateComponent(self, parent,
 264                                 (AwtToolkit::ComponentFactory)
 265                                 AwtTextField::Create);
 266 
 267     CATCH_BAD_ALLOC;
 268 }
 269 
 270 /*
 271  * Class:     sun_awt_windows_WTextFieldPeer
 272  * Method:    setEchoChar
 273  * Signature: (C)V
 274  */
 275 JNIEXPORT void JNICALL
 276 Java_sun_awt_windows_WTextFieldPeer_setEchoChar(JNIEnv *env, jobject self,
 277                                                 jchar ch)
 278 {
 279     TRY;
 280 
 281     SetEchoCharStruct *secs = new SetEchoCharStruct;
 282     secs->textfield = env->NewGlobalRef(self);
 283     secs->echoChar = ch;
 284 
 285     AwtToolkit::GetInstance().SyncCall(AwtTextField::_SetEchoChar, secs);
 286     // global ref and secs are deleted in _SetEchoChar()
 287 
 288     CATCH_BAD_ALLOC;
 289 }
 290 
 291 } /* extern "C" */