1 /* 2 * Copyright (c) 1996, 2014, 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_Scrollbar.h" 28 #include "awt_Canvas.h" 29 #include "awt_Window.h" 30 31 /* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code. 32 */ 33 34 /***********************************************************************/ 35 // struct for _SetValues() method 36 struct SetValuesStruct { 37 jobject scrollbar; 38 jint value; 39 jint visible; 40 jint min, max; 41 42 }; 43 /************************************************************************ 44 * AwtScrollbar fields 45 */ 46 47 jfieldID AwtScrollbar::lineIncrementID; 48 jfieldID AwtScrollbar::pageIncrementID; 49 jfieldID AwtScrollbar::orientationID; 50 51 BOOL AwtScrollbar::ms_isInsideMouseFilter = FALSE; 52 int AwtScrollbar::ms_instanceCounter = 0; 53 HHOOK AwtScrollbar::ms_hMouseFilter; 54 55 /************************************************************************ 56 * AwtScrollbar methods 57 */ 58 59 AwtScrollbar::AwtScrollbar() { 60 m_orientation = SB_HORZ; 61 m_lineIncr = 0; 62 m_pageIncr = 0; 63 m_prevCallback = NULL; 64 m_prevCallbackPos = 0; 65 ms_instanceCounter++; 66 67 /* 68 * Fix for 4515085. 69 * Use the hook to process WM_LBUTTONUP message. 70 */ 71 if (AwtScrollbar::ms_instanceCounter == 1) { 72 AwtScrollbar::ms_hMouseFilter = 73 ::SetWindowsHookEx(WH_MOUSE, (HOOKPROC)AwtScrollbar::MouseFilter, 74 0, AwtToolkit::MainThread()); 75 } 76 } 77 78 AwtScrollbar::~AwtScrollbar() 79 { 80 } 81 82 void AwtScrollbar::Dispose() 83 { 84 if (--ms_instanceCounter == 0) { 85 ::UnhookWindowsHookEx(ms_hMouseFilter); 86 } 87 AwtComponent::Dispose(); 88 } 89 90 LPCTSTR 91 AwtScrollbar::GetClassName() { 92 return TEXT("SCROLLBAR"); /* System provided scrollbar class */ 93 } 94 95 /* Create a new AwtScrollbar object and window. */ 96 AwtScrollbar * 97 AwtScrollbar::Create(jobject peer, jobject parent) 98 { 99 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 100 101 jobject target = NULL; 102 AwtScrollbar* c = NULL; 103 104 try { 105 if (env->EnsureLocalCapacity(1) < 0) { 106 return NULL; 107 } 108 109 PDATA pData; 110 AwtCanvas* awtParent; 111 JNI_CHECK_PEER_GOTO(parent, done); 112 113 awtParent = (AwtCanvas*)pData; 114 JNI_CHECK_NULL_GOTO(awtParent, "null awtParent", done); 115 116 target = env->GetObjectField(peer, AwtObject::targetID); 117 JNI_CHECK_NULL_GOTO(target, "null target", done); 118 119 c = new AwtScrollbar(); 120 121 { 122 jint orientation = 123 env->GetIntField(target, AwtScrollbar::orientationID); 124 c->m_orientation = (orientation == java_awt_Scrollbar_VERTICAL) ? 125 SB_VERT : SB_HORZ; 126 c->m_lineIncr = 127 env->GetIntField(target, AwtScrollbar::lineIncrementID); 128 c->m_pageIncr = 129 env->GetIntField(target, AwtScrollbar::pageIncrementID); 130 131 DWORD style = WS_CHILD | WS_CLIPSIBLINGS | 132 c->m_orientation;/* Note: SB_ and SBS_ are the same here */ 133 134 jint x = env->GetIntField(target, AwtComponent::xID); 135 jint y = env->GetIntField(target, AwtComponent::yID); 136 jint width = env->GetIntField(target, AwtComponent::widthID); 137 jint height = env->GetIntField(target, AwtComponent::heightID); 138 139 c->CreateHWnd(env, L"", style, 0, 140 x, y, width, height, 141 awtParent->GetHWnd(), 142 reinterpret_cast<HMENU>(static_cast<INT_PTR>( 143 awtParent->CreateControlID())), 144 ::GetSysColor(COLOR_SCROLLBAR), 145 ::GetSysColor(COLOR_SCROLLBAR), 146 peer); 147 c->m_backgroundColorSet = TRUE; 148 /* suppress inheriting parent's color. */ 149 c->UpdateBackground(env, target); 150 } 151 } catch (...) { 152 env->DeleteLocalRef(target); 153 throw; 154 } 155 156 done: 157 env->DeleteLocalRef(target); 158 return c; 159 } 160 161 LRESULT CALLBACK 162 AwtScrollbar::MouseFilter(int nCode, WPARAM wParam, LPARAM lParam) 163 { 164 if (((UINT)wParam == WM_LBUTTONUP || (UINT)wParam == WM_MOUSEMOVE) && 165 ms_isInsideMouseFilter != TRUE && 166 nCode >= 0) 167 { 168 HWND hwnd = ((PMOUSEHOOKSTRUCT)lParam)->hwnd; 169 AwtComponent *comp = AwtComponent::GetComponent(hwnd); 170 171 if (comp != NULL && comp->IsScrollbar()) { 172 MSG msg; 173 LPMSG lpMsg = (LPMSG)&msg; 174 UINT msgID = (UINT)wParam; 175 176 ms_isInsideMouseFilter = TRUE; 177 178 // Peek the message to get wParam containing the message's flags. 179 // <::PeekMessage> will call this hook again. To prevent recursive 180 // processing the <ms_isInsideMouseFilter> flag is used. 181 // Calling <::PeekMessage> is not so good desision but is the only one 182 // found to get those flags (used further in Java event creation). 183 // WARNING! If you are about to add new hook of WM_MOUSE type make 184 // it ready for recursive call, otherwise modify this one. 185 if (::PeekMessage(lpMsg, hwnd, msgID, msgID, PM_NOREMOVE)) { 186 comp->WindowProc(msgID, lpMsg->wParam, lpMsg->lParam); 187 } 188 189 ms_isInsideMouseFilter = FALSE; 190 } 191 } 192 return ::CallNextHookEx(AwtScrollbar::ms_hMouseFilter, nCode, wParam, lParam); 193 } 194 195 196 LRESULT 197 AwtScrollbar::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 198 { 199 // Delegate real work to super 200 LRESULT retValue = AwtComponent::WindowProc(message, wParam, lParam); 201 202 // After-hooks for workarounds 203 switch (message) { 204 205 // Work around a windows bug described in KB article Q73839. 206 // Need to update focus indicator on scrollbar if thumb 207 // proportion or thumb position was changed. 208 209 case WM_SIZE: 210 case SBM_SETSCROLLINFO: 211 case SBM_SETRANGE: 212 case SBM_SETRANGEREDRAW: 213 if (AwtComponent::sm_focusOwner == GetHWnd()) { 214 UpdateFocusIndicator(); 215 } 216 break; 217 } 218 219 return retValue; 220 } 221 222 MsgRouting 223 AwtScrollbar::WmNcHitTest(UINT x, UINT y, LRESULT& retVal) 224 { 225 if (::IsWindow(AwtWindow::GetModalBlocker(AwtComponent::GetTopLevelParentForWindow(GetHWnd())))) { 226 retVal = HTCLIENT; 227 return mrConsume; 228 } 229 return AwtComponent::WmNcHitTest(x, y, retVal); 230 } 231 232 // Fix for a race condition when the WM_LBUTTONUP is picked by the AWT 233 // message loop before(!) the windows internal message loop for the 234 // scrollbar is started in response to WM_LBUTTONDOWN. See KB article 235 // Q102552. 236 // 237 // Note that WM_LBUTTONUP is processed by the windows internal message 238 // loop. May be we can synthesize a MOUSE_RELEASED event but that 239 // seems kludgey, so we'd better left this as is for now. 240 241 MsgRouting 242 AwtScrollbar::WmMouseDown(UINT flags, int x, int y, int button) 243 { 244 // We pass the WM_LBUTTONDOWN up to Java, but we process it 245 // immediately as well to avoid the race. Later when this press 246 // event returns to us wrapped into a WM_AWT_HANDLE_EVENT we 247 // ignore it in the HandleEvent below. This means that we can not 248 // consume the mouse press in the Java world. 249 250 MsgRouting usualRoute = AwtComponent::WmMouseDown(flags, x, y, button); 251 252 if (::IsWindow(AwtWindow::GetModalBlocker(AwtComponent::GetTopLevelParentForWindow(GetHWnd())))) { 253 return mrConsume; 254 } 255 256 if (button == LEFT_BUTTON) 257 return mrDoDefault; // Force immediate processing to avoid the race. 258 else 259 return usualRoute; 260 } 261 262 MsgRouting 263 AwtScrollbar::HandleEvent(MSG *msg, BOOL synthetic) 264 { 265 // SCROLLBAR control doesn't cause activation on mouse/key events, 266 // so we can safely (for synthetic focus) pass them to the system proc. 267 268 if (IsFocusingMouseMessage(msg)) { 269 // Left button press was already routed to default window 270 // procedure in the WmMouseDown above. Propagating synthetic 271 // press seems like a bad idea as internal message loop 272 // doesn't know how to unwrap synthetic release. 273 delete msg; 274 return mrConsume; 275 } 276 return AwtComponent::HandleEvent(msg, synthetic); 277 } 278 279 // Work around a windows bug descrbed in KB article Q73839. Reset 280 // focus on scrollbars to update focus indicator. The article advises 281 // to disable/enable the scrollbar. 282 void 283 AwtScrollbar::UpdateFocusIndicator() 284 { 285 if (IsFocusable()) { 286 // todo: doesn't work 287 SendMessage((WPARAM)ESB_DISABLE_BOTH); 288 SendMessage((WPARAM)ESB_ENABLE_BOTH); 289 } 290 } 291 292 // In a windows app one would call SetScrollInfo from WM_[HV]SCROLL 293 // handler directly. Since we call SetScrollInfo from Java world 294 // after scroll handler is over next WM_[HV]SCROLL event can be 295 // delivered before SetScrollInfo was called in response to the 296 // previous one and thus we would fire exactly the same event which 297 // will only contribute to the growth of the backlog of scroll events. 298 299 const char * const AwtScrollbar::SbNlineDown = "lineDown"; 300 const char * const AwtScrollbar::SbNlineUp = "lineUp"; 301 const char * const AwtScrollbar::SbNpageDown = "pageDown"; 302 const char * const AwtScrollbar::SbNpageUp = "pageUp"; 303 const char * const AwtScrollbar::SbNdrag = "drag"; 304 const char * const AwtScrollbar::SbNdragEnd = "dragEnd"; 305 const char * const AwtScrollbar::SbNwarp = "warp"; 306 307 inline void 308 AwtScrollbar::DoScrollCallbackCoalesce(const char* methodName, int newPos) 309 { 310 if (methodName == m_prevCallback && newPos == m_prevCallbackPos) { 311 DTRACE_PRINTLN2("AwtScrollbar: ignoring duplicate callback %s(%d)", 312 methodName, newPos); 313 } 314 else { 315 DoCallback(methodName, "(I)V", newPos); 316 m_prevCallback = methodName; 317 m_prevCallbackPos = newPos; 318 } 319 } 320 321 322 MsgRouting 323 AwtScrollbar::WmVScroll(UINT scrollCode, UINT pos, HWND hScrollbar) 324 { 325 int minVal, maxVal; // scrollbar range 326 int minPos, maxPos; // thumb positions (max depends on visible amount) 327 int curPos, newPos; 328 329 // For drags we have old (static) and new (dynamic) thumb positions 330 int dragP = (scrollCode == SB_THUMBTRACK 331 || scrollCode == SB_THUMBPOSITION); 332 int thumbPos; 333 334 SCROLLINFO si; 335 si.cbSize = sizeof si; 336 si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE; 337 338 // From, _Win32 Programming_, by Rector and Newcommer, p. 185: 339 // "In some of the older documentation on Win32 scroll bars, 340 // including that published by Microsoft, you may read that 341 // you *cannot* obtain the scroll position while in a handler. 342 // The SIF_TRACKPOS flag was added after this documentation 343 // was published. Beware of this older documentation; it may 344 // have other obsolete features." 345 if (dragP) { 346 si.fMask |= SIF_TRACKPOS; 347 } 348 349 VERIFY(::GetScrollInfo(GetHWnd(), SB_CTL, &si)); 350 curPos = si.nPos; 351 minPos = minVal = si.nMin; 352 353 // Upper bound of the range. Note that adding 1 here is safe 354 // and won't cause a wrap, since we have substracted 1 in the 355 // SetValues above. 356 maxVal = si.nMax + 1; 357 358 // Meaningful maximum position is maximum - visible. 359 maxPos = maxVal - si.nPage; 360 361 // XXX: Documentation for SBM_SETRANGE says that scrollbar 362 // range is limited by MAXLONG, which is 2**31, but when a 363 // scroll range is greater than that, thumbPos is reported 364 // incorrectly due to integer arithmetic wrap(s). 365 thumbPos = dragP ? si.nTrackPos : curPos; 366 367 // NB: Beware arithmetic wrap when calculating newPos 368 switch (scrollCode) { 369 370 case SB_LINEUP: 371 if ((__int64)curPos - m_lineIncr > minPos) 372 newPos = curPos - m_lineIncr; 373 else 374 newPos = minPos; 375 if (newPos != curPos) 376 DoScrollCallbackCoalesce(SbNlineUp, newPos); 377 break; 378 379 case SB_LINEDOWN: 380 if ((__int64)curPos + m_lineIncr < maxPos) 381 newPos = curPos + m_lineIncr; 382 else 383 newPos = maxPos; 384 if (newPos != curPos) 385 DoScrollCallbackCoalesce(SbNlineDown, newPos); 386 break; 387 388 case SB_PAGEUP: 389 if ((__int64)curPos - m_pageIncr > minPos) 390 newPos = curPos - m_pageIncr; 391 else 392 newPos = minPos; 393 if (newPos != curPos) 394 DoScrollCallbackCoalesce(SbNpageUp, newPos); 395 break; 396 397 case SB_PAGEDOWN: 398 if ((__int64)curPos + m_pageIncr < maxPos) 399 newPos = curPos + m_pageIncr; 400 else 401 newPos = maxPos; 402 if (newPos != curPos) 403 DoScrollCallbackCoalesce(SbNpageDown, newPos); 404 break; 405 406 case SB_TOP: 407 if (minPos != curPos) 408 DoScrollCallbackCoalesce(SbNwarp, minPos); 409 break; 410 411 case SB_BOTTOM: 412 if (maxPos != curPos) 413 DoScrollCallbackCoalesce(SbNwarp, maxPos); 414 break; 415 416 case SB_THUMBTRACK: 417 if (thumbPos != curPos) 418 DoScrollCallbackCoalesce(SbNdrag, thumbPos); 419 break; 420 421 case SB_THUMBPOSITION: 422 DoScrollCallbackCoalesce(SbNdragEnd, thumbPos); 423 break; 424 425 case SB_ENDSCROLL: 426 // reset book-keeping info 427 m_prevCallback = NULL; 428 break; 429 } 430 return mrDoDefault; 431 } 432 433 MsgRouting 434 AwtScrollbar::WmHScroll(UINT scrollCode, UINT pos, HWND hScrollbar) 435 { 436 return WmVScroll(scrollCode, pos, hScrollbar); 437 } 438 439 void AwtScrollbar::_SetValues(void *param) 440 { 441 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 442 443 SetValuesStruct *svs = (SetValuesStruct *)param; 444 jobject self = svs->scrollbar; 445 446 SCROLLINFO si; 447 si.cbSize = sizeof si; 448 si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE; 449 si.nMin = svs->min; 450 si.nMax = svs->max - 1; 451 si.nPage = svs->visible; 452 si.nPos = svs->value; 453 454 AwtScrollbar *sb = NULL; 455 456 PDATA pData; 457 JNI_CHECK_PEER_GOTO(self, ret); 458 sb = (AwtScrollbar *)pData; 459 if (::IsWindow(sb->GetHWnd())) 460 { 461 BOOL update_p = ::IsWindowEnabled(sb->GetHWnd()); // don't redraw if disabled 462 DTRACE_PRINTLN5("AwtScrollbar::SetValues(val = %d, vis = %d,"//(ctd.) 463 " min = %d, max = %d)%s", 464 svs->value, svs->visible, svs->min, svs->max, 465 update_p ? "" : " - NOT redrawing"); 466 ::SetScrollInfo(sb->GetHWnd(), SB_CTL, &si, update_p); 467 } 468 ret: 469 env->DeleteGlobalRef(self); 470 471 delete svs; 472 } 473 474 /************************************************************************ 475 * Scrollbar native methods 476 */ 477 478 extern "C" { 479 480 /* 481 * Class: java_awt_Scrollbar 482 * Method: initIDs 483 * Signature: ()V 484 */ 485 JNIEXPORT void JNICALL 486 Java_java_awt_Scrollbar_initIDs(JNIEnv *env, jclass cls) 487 { 488 TRY; 489 490 AwtScrollbar::lineIncrementID = env->GetFieldID(cls, "lineIncrement", "I"); 491 DASSERT(AwtScrollbar::lineIncrementID != NULL); 492 CHECK_NULL(AwtScrollbar::lineIncrementID); 493 494 AwtScrollbar::pageIncrementID = env->GetFieldID(cls, "pageIncrement", "I"); 495 DASSERT(AwtScrollbar::pageIncrementID != NULL); 496 CHECK_NULL(AwtScrollbar::pageIncrementID); 497 498 AwtScrollbar::orientationID = env->GetFieldID(cls, "orientation", "I"); 499 DASSERT(AwtScrollbar::orientationID != NULL); 500 501 CATCH_BAD_ALLOC; 502 } 503 504 } /* extern "C" */ 505 506 507 /************************************************************************ 508 * WScrollbarPeer native methods 509 */ 510 511 extern "C" { 512 513 /* 514 * Class: sun_awt_windows_WScrollbarPeer 515 * Method: setValues 516 * Signature: (IIII)V 517 */ 518 JNIEXPORT void JNICALL 519 Java_sun_awt_windows_WScrollbarPeer_setValues(JNIEnv *env, jobject self, 520 jint value, jint visible, 521 jint minimum, jint maximum) 522 { 523 TRY; 524 525 SetValuesStruct *svs = new SetValuesStruct; 526 svs->scrollbar = env->NewGlobalRef(self); 527 svs->value = value; 528 svs->visible = visible; 529 svs->min = minimum; 530 svs->max = maximum; 531 532 AwtToolkit::GetInstance().SyncCall(AwtScrollbar::_SetValues, svs); 533 // global ref and svs are deleted in _SetValues 534 535 CATCH_BAD_ALLOC; 536 } 537 538 /* 539 * Class: sun_awt_windows_WScrollbarPeer 540 * Method: setLineIncrement 541 * Signature: (I)V 542 */ 543 JNIEXPORT void JNICALL 544 Java_sun_awt_windows_WScrollbarPeer_setLineIncrement(JNIEnv *env, jobject self, 545 jint increment) 546 { 547 TRY; 548 549 PDATA pData; 550 JNI_CHECK_PEER_RETURN(self); 551 AwtScrollbar* c = (AwtScrollbar*)pData; 552 c->SetLineIncrement(increment); 553 554 CATCH_BAD_ALLOC; 555 } 556 557 /* 558 * Class: sun_awt_windows_WScrollbarPeer 559 * Method: setPageIncrement 560 * Signature: (I)V 561 */ 562 JNIEXPORT void JNICALL 563 Java_sun_awt_windows_WScrollbarPeer_setPageIncrement(JNIEnv *env, jobject self, 564 jint increment) 565 { 566 TRY; 567 568 PDATA pData; 569 JNI_CHECK_PEER_RETURN(self); 570 AwtScrollbar* c = (AwtScrollbar*)pData; 571 c->SetPageIncrement(increment); 572 573 CATCH_BAD_ALLOC; 574 } 575 576 /* 577 * Class: sun_awt_windows_WScrollbarPeer 578 * Method: create 579 * Signature: (Lsun/awt/windows/WComponentPeer;)V 580 */ 581 JNIEXPORT void JNICALL 582 Java_sun_awt_windows_WScrollbarPeer_create(JNIEnv *env, jobject self, 583 jobject parent) 584 { 585 TRY; 586 587 PDATA pData; 588 JNI_CHECK_PEER_RETURN(parent); 589 AwtToolkit::CreateComponent(self, parent, 590 (AwtToolkit::ComponentFactory) 591 AwtScrollbar::Create); 592 JNI_CHECK_PEER_CREATION_RETURN(self); 593 594 CATCH_BAD_ALLOC; 595 } 596 597 /* 598 * Class: sun_awt_windows_WScrollbarPeer 599 * Method: getScrollbarSize 600 * Signature: (I)I 601 */ 602 JNIEXPORT jint JNICALL 603 Java_sun_awt_windows_WScrollbarPeer_getScrollbarSize(JNIEnv *env, jclass clazz, jint orientation) 604 { 605 if (orientation == java_awt_Scrollbar_VERTICAL) { 606 return ::GetSystemMetrics(SM_CXVSCROLL); 607 } else { 608 return ::GetSystemMetrics(SM_CYHSCROLL); 609 } 610 } 611 612 } /* extern "C" */