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