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" */