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     /*
  79      * RichEdit 1.0 control starts internal message loop if the
  80      * left mouse button is pressed while the cursor is not over
  81      * the current selection or the current selection is empty.
  82      * Because of this we don't receive WM_MOUSEMOVE messages
  83      * while the left mouse button is pressed. To work around
  84      * this behavior we process the relevant mouse messages
  85      * by ourselves.
  86      * By consuming WM_MOUSEMOVE messages we also don't give
  87      * the RichEdit control a chance to recognize a drag gesture
  88      * and initiate its own drag-n-drop operation.
  89      *
  90      * The workaround also allows us to implement synthetic focus mechanism.
  91      */
  92     if (IsFocusingMouseMessage(msg)) {
  93 
  94         LONG lCurPos = EditGetCharFromPos(msg->pt);
  95 
  96         /*
  97          * NOTE: Plain EDIT control always clears selection on mouse
  98          * button press. We are clearing the current selection only if
  99          * the mouse pointer is not over the selected region.
 100          * In this case we sacrifice backward compatibility
 101          * to allow dnd of the current selection.
 102          */
 103         if (msg->message == WM_LBUTTONDBLCLK) {
 104             jchar echo = SendMessage(EM_GETPASSWORDCHAR);
 105 
 106             if(echo == 0){
 107               SetStartSelectionPos(static_cast<LONG>(SendMessage(
 108                   EM_FINDWORDBREAK, WB_MOVEWORDLEFT, lCurPos)));
 109               SetEndSelectionPos(static_cast<LONG>(SendMessage(
 110                   EM_FINDWORDBREAK, WB_MOVEWORDRIGHT, lCurPos)));
 111             }else{
 112               SetStartSelectionPos(0);
 113               SetEndSelectionPos(GetTextLength());
 114             }
 115 
 116         } else {
 117             SetStartSelectionPos(lCurPos);
 118             SetEndSelectionPos(lCurPos);
 119         }
 120         CHARRANGE cr;
 121         cr.cpMin = GetStartSelectionPos();
 122         cr.cpMax = GetEndSelectionPos();
 123         EditSetSel(cr);
 124 
 125         delete msg;
 126         return mrConsume;
 127     } else if (msg->message == WM_LBUTTONUP) {
 128 
 129         /*
 130          * If the left mouse button is pressed on the selected region
 131          * we don't clear the current selection. We clear it on button
 132          * release instead. This is to allow dnd of the current selection.
 133          */
 134         if (GetStartSelectionPos() == -1 && GetEndSelectionPos() == -1) {
 135             CHARRANGE cr;
 136 
 137             LONG lCurPos = EditGetCharFromPos(msg->pt);
 138 
 139             cr.cpMin = lCurPos;
 140             cr.cpMax = lCurPos;
 141             EditSetSel(cr);
 142         }
 143 
 144         /*
 145          * Cleanup the state variables when left mouse button is released.
 146          * These state variables are designed to reflect the selection state
 147          * while the left mouse button is pressed and be set to -1 otherwise.
 148          */
 149         SetStartSelectionPos(-1);
 150         SetEndSelectionPos(-1);
 151         SetLastSelectionPos(-1);
 152 
 153         delete msg;
 154         return mrConsume;
 155     } else if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON)) {
 156 
 157         /*
 158          * We consume WM_MOUSEMOVE while the left mouse button is pressed,
 159          * so we have to simulate autoscrolling when mouse is moved outside
 160          * of the client area.
 161          */
 162         POINT p;
 163         RECT r;
 164         BOOL bScrollLeft = FALSE;
 165         BOOL bScrollRight = FALSE;
 166         BOOL bScrollUp = FALSE;
 167         BOOL bScrollDown = FALSE;
 168 
 169         p.x = msg->pt.x;
 170         p.y = msg->pt.y;
 171         VERIFY(::GetClientRect(GetHWnd(), &r));
 172 
 173         if (p.x < 0) {
 174             bScrollLeft = TRUE;
 175             p.x = 0;
 176         } else if (p.x > r.right) {
 177             bScrollRight = TRUE;
 178             p.x = r.right - 1;
 179         }
 180         LONG lCurPos = EditGetCharFromPos(p);
 181 
 182         if (GetStartSelectionPos() != -1 &&
 183             GetEndSelectionPos() != -1 &&
 184             lCurPos != GetLastSelectionPos()) {
 185 
 186             CHARRANGE cr;
 187 
 188             SetLastSelectionPos(lCurPos);
 189 
 190             cr.cpMin = GetStartSelectionPos();
 191             cr.cpMax = GetLastSelectionPos();
 192 
 193             EditSetSel(cr);
 194         }
 195 
 196         if (bScrollLeft == TRUE || bScrollRight == TRUE) {
 197             SCROLLINFO si;
 198             memset(&si, 0, sizeof(si));
 199             si.cbSize = sizeof(si);
 200             si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
 201 
 202             VERIFY(::GetScrollInfo(GetHWnd(), SB_HORZ, &si));
 203             if (bScrollLeft == TRUE) {
 204                 si.nPos = si.nPos - si.nPage / 2;
 205                 si.nPos = max(si.nMin, si.nPos);
 206             } else if (bScrollRight == TRUE) {
 207                 si.nPos = si.nPos + si.nPage / 2;
 208                 si.nPos = min(si.nPos, si.nMax);
 209             }
 210             /*
 211              * Okay to use 16-bit position since RichEdit control adjusts
 212              * its scrollbars so that their range is always 16-bit.
 213              */
 214             DASSERT(abs(si.nPos) < 0x8000);
 215             SendMessage(WM_HSCROLL,
 216                         MAKEWPARAM(SB_THUMBPOSITION, LOWORD(si.nPos)));
 217         }
 218         delete msg;
 219         return mrConsume;
 220     }
 221     /*
 222      * Store the 'synthetic' parameter so that the WM_PASTE security check
 223      * happens only for synthetic events.
 224      */
 225     m_synthetic = synthetic;
 226     returnVal = AwtComponent::HandleEvent(msg, synthetic);
 227     m_synthetic = FALSE;
 228 
 229     return returnVal;
 230 }
 231 
 232 void AwtTextField::_SetEchoChar(void *param)
 233 {
 234     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 235 
 236     SetEchoCharStruct *secs = (SetEchoCharStruct *)param;
 237     jobject self = secs->textfield;
 238     jchar echo = secs->echoChar;
 239 
 240     AwtTextField *c = NULL;
 241 
 242     PDATA pData;
 243     JNI_CHECK_PEER_GOTO(self, ret);
 244     c = (AwtTextField *)pData;
 245     if (::IsWindow(c->GetHWnd()))
 246     {
 247         c->SendMessage(EM_SETPASSWORDCHAR, echo);
 248         // Fix for 4307281: force redraw so that changes will take effect
 249         VERIFY(::InvalidateRect(c->GetHWnd(), NULL, FALSE));
 250     }
 251 ret:
 252     env->DeleteGlobalRef(self);
 253 
 254     delete secs;
 255 }
 256 
 257 
 258 /************************************************************************
 259  * WTextFieldPeer native methods
 260  */
 261 
 262 extern "C" {
 263 
 264 /*
 265  * Class:     sun_awt_windows_WTextFieldPeer
 266  * Method:    create
 267  * Signature: (Lsun/awt/windows/WComponentPeer;)V
 268  */
 269 JNIEXPORT void JNICALL
 270 Java_sun_awt_windows_WTextFieldPeer_create(JNIEnv *env, jobject self,
 271                                            jobject parent)
 272 {
 273     TRY;
 274 
 275     PDATA pData;
 276     JNI_CHECK_PEER_RETURN(parent);
 277     AwtToolkit::CreateComponent(self, parent,
 278                                 (AwtToolkit::ComponentFactory)
 279                                 AwtTextField::Create);
 280     JNI_CHECK_PEER_CREATION_RETURN(self);
 281 
 282     CATCH_BAD_ALLOC;
 283 }
 284 
 285 /*
 286  * Class:     sun_awt_windows_WTextFieldPeer
 287  * Method:    setEchoCharacter
 288  * Signature: (C)V
 289  */
 290 JNIEXPORT void JNICALL
 291 Java_sun_awt_windows_WTextFieldPeer_setEchoCharacter(JNIEnv *env, jobject self,
 292                                                      jchar ch)
 293 {
 294     TRY;
 295 
 296     SetEchoCharStruct *secs = new SetEchoCharStruct;
 297     secs->textfield = env->NewGlobalRef(self);
 298     secs->echoChar = ch;
 299 
 300     AwtToolkit::GetInstance().SyncCall(AwtTextField::_SetEchoChar, secs);
 301     // global ref and secs are deleted in _SetEchoChar()
 302 
 303     CATCH_BAD_ALLOC;
 304 }
 305 
 306 } /* extern "C" */