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 
AwtTextField()44 AwtTextField::AwtTextField()
45 {
46 }
47 
48 /* Create a new AwtTextField object and window.   */
Create(jobject peer,jobject parent)49 AwtTextField* AwtTextField::Create(jobject peer, jobject parent)
50 {
51     return (AwtTextField*) AwtTextComponent::Create(peer, parent, false);
52 }
53 
EditSetSel(CHARRANGE & cr)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 
WindowProc(UINT message,WPARAM wParam,LPARAM lParam)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
HandleEvent(MSG * msg,BOOL synthetic)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             SendMessage(EM_SHOWSCROLLBAR, SB_HORZ, TRUE);
204             VERIFY(::GetScrollInfo(GetHWnd(), SB_HORZ, &si));
205             SendMessage(EM_SHOWSCROLLBAR, SB_HORZ, FALSE);
206 
207             if (bScrollLeft == TRUE) {
208                 si.nPos = si.nPos - si.nPage / 2;
209                 si.nPos = max(si.nMin, si.nPos);
210             } else if (bScrollRight == TRUE) {
211                 si.nPos = si.nPos + si.nPage / 2;
212                 si.nPos = min(si.nPos, si.nMax);
213             }
214             /*
215              * Okay to use 16-bit position since RichEdit control adjusts
216              * its scrollbars so that their range is always 16-bit.
217              */
218             DASSERT(abs(si.nPos) < 0x8000);
219             SendMessage(WM_HSCROLL,
220                         MAKEWPARAM(SB_THUMBPOSITION, LOWORD(si.nPos)));
221         }
222         delete msg;
223         return mrConsume;
224     } else if (msg->message == WM_KEYDOWN) {
225         UINT virtualKey = (UINT) msg->wParam;
226 
227         switch(virtualKey){
228           case VK_RETURN:
229           case VK_UP:
230           case VK_DOWN:
231           case VK_LEFT:
232           case VK_RIGHT:
233           case VK_DELETE:
234           case VK_BACK:
235               SystemParametersInfo(SPI_GETBEEP, 0, &systemBeeperEnabled, 0);
236               if(systemBeeperEnabled){
237                   // disable system beeper for the RICHEDIT control to be compatible
238                   // with the EDIT control behaviour
239                   SystemParametersInfo(SPI_SETBEEP, 0, NULL, 0);
240               }
241               break;
242           }
243     } else if (msg->message == WM_SETTINGCHANGE) {
244         if (msg->wParam == SPI_SETBEEP) {
245             SystemParametersInfo(SPI_GETBEEP, 0, &systemBeeperEnabled, 0);
246             if(systemBeeperEnabled){
247                 SystemParametersInfo(SPI_SETBEEP, 1, NULL, 0);
248             }
249         }
250     }
251 
252     returnVal = AwtTextComponent::HandleEvent(msg, synthetic);
253 
254     if(systemBeeperEnabled){
255         SystemParametersInfo(SPI_SETBEEP, 1, NULL, 0);
256     }
257 
258     return returnVal;
259 }
260 
_SetEchoChar(void * param)261 void AwtTextField::_SetEchoChar(void *param)
262 {
263     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
264 
265     SetEchoCharStruct *secs = (SetEchoCharStruct *)param;
266     jobject self = secs->textfield;
267     jchar echo = secs->echoChar;
268 
269     AwtTextField *c = NULL;
270 
271     PDATA pData;
272     JNI_CHECK_PEER_GOTO(self, ret);
273     c = (AwtTextField *)pData;
274     if (::IsWindow(c->GetHWnd()))
275     {
276         c->SendMessage(EM_SETPASSWORDCHAR, echo);
277         // Fix for 4307281: force redraw so that changes will take effect
278         VERIFY(::InvalidateRect(c->GetHWnd(), NULL, FALSE));
279     }
280 ret:
281     env->DeleteGlobalRef(self);
282 
283     delete secs;
284 }
285 
286 
287 /************************************************************************
288  * WTextFieldPeer native methods
289  */
290 
291 extern "C" {
292 
293 /*
294  * Class:     sun_awt_windows_WTextFieldPeer
295  * Method:    create
296  * Signature: (Lsun/awt/windows/WComponentPeer;)V
297  */
298 JNIEXPORT void JNICALL
Java_sun_awt_windows_WTextFieldPeer_create(JNIEnv * env,jobject self,jobject parent)299 Java_sun_awt_windows_WTextFieldPeer_create(JNIEnv *env, jobject self,
300                                            jobject parent)
301 {
302     TRY;
303 
304     AwtToolkit::CreateComponent(self, parent,
305                                 (AwtToolkit::ComponentFactory)
306                                 AwtTextField::Create);
307 
308     CATCH_BAD_ALLOC;
309 }
310 
311 /*
312  * Class:     sun_awt_windows_WTextFieldPeer
313  * Method:    setEchoChar
314  * Signature: (C)V
315  */
316 JNIEXPORT void JNICALL
Java_sun_awt_windows_WTextFieldPeer_setEchoChar(JNIEnv * env,jobject self,jchar ch)317 Java_sun_awt_windows_WTextFieldPeer_setEchoChar(JNIEnv *env, jobject self,
318                                                 jchar ch)
319 {
320     TRY;
321 
322     SetEchoCharStruct *secs = new SetEchoCharStruct;
323     secs->textfield = env->NewGlobalRef(self);
324     secs->echoChar = ch;
325 
326     AwtToolkit::GetInstance().SyncCall(AwtTextField::_SetEchoChar, secs);
327     // global ref and secs are deleted in _SetEchoChar()
328 
329     CATCH_BAD_ALLOC;
330 }
331 
332 } /* extern "C" */
333