blob: 23b1e93c411bc1c7f114914e725a2bc43188a940 [file] [log] [blame]
/*
* Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "awt_Toolkit.h"
#include "awt_Scrollbar.h"
#include "awt_Canvas.h"
#include "awt_Window.h"
/* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
*/
/***********************************************************************/
// struct for _SetValues() method
struct SetValuesStruct {
jobject scrollbar;
jint value;
jint visible;
jint min, max;
};
/************************************************************************
* AwtScrollbar fields
*/
jfieldID AwtScrollbar::lineIncrementID;
jfieldID AwtScrollbar::pageIncrementID;
jfieldID AwtScrollbar::orientationID;
BOOL AwtScrollbar::ms_isInsideMouseFilter = FALSE;
int AwtScrollbar::ms_instanceCounter = 0;
HHOOK AwtScrollbar::ms_hMouseFilter;
/************************************************************************
* AwtScrollbar methods
*/
AwtScrollbar::AwtScrollbar() {
m_orientation = SB_HORZ;
m_lineIncr = 0;
m_pageIncr = 0;
m_prevCallback = NULL;
m_prevCallbackPos = 0;
ms_instanceCounter++;
/*
* Fix for 4515085.
* Use the hook to process WM_LBUTTONUP message.
*/
if (AwtScrollbar::ms_instanceCounter == 1) {
AwtScrollbar::ms_hMouseFilter =
::SetWindowsHookEx(WH_MOUSE, (HOOKPROC)AwtScrollbar::MouseFilter,
0, AwtToolkit::MainThread());
}
}
AwtScrollbar::~AwtScrollbar()
{
}
void AwtScrollbar::Dispose()
{
if (--ms_instanceCounter == 0) {
::UnhookWindowsHookEx(ms_hMouseFilter);
}
AwtComponent::Dispose();
}
LPCTSTR
AwtScrollbar::GetClassName() {
return TEXT("SCROLLBAR"); /* System provided scrollbar class */
}
/* Create a new AwtScrollbar object and window. */
AwtScrollbar *
AwtScrollbar::Create(jobject peer, jobject parent)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject target = NULL;
AwtScrollbar* c = NULL;
try {
if (env->EnsureLocalCapacity(1) < 0) {
return NULL;
}
PDATA pData;
AwtCanvas* awtParent;
JNI_CHECK_PEER_GOTO(parent, done);
awtParent = (AwtCanvas*)pData;
JNI_CHECK_NULL_GOTO(awtParent, "null awtParent", done);
target = env->GetObjectField(peer, AwtObject::targetID);
JNI_CHECK_NULL_GOTO(target, "null target", done);
c = new AwtScrollbar();
{
jint orientation =
env->GetIntField(target, AwtScrollbar::orientationID);
c->m_orientation = (orientation == java_awt_Scrollbar_VERTICAL) ?
SB_VERT : SB_HORZ;
c->m_lineIncr =
env->GetIntField(target, AwtScrollbar::lineIncrementID);
c->m_pageIncr =
env->GetIntField(target, AwtScrollbar::pageIncrementID);
DWORD style = WS_CHILD | WS_CLIPSIBLINGS |
c->m_orientation;/* Note: SB_ and SBS_ are the same here */
jint x = env->GetIntField(target, AwtComponent::xID);
jint y = env->GetIntField(target, AwtComponent::yID);
jint width = env->GetIntField(target, AwtComponent::widthID);
jint height = env->GetIntField(target, AwtComponent::heightID);
c->CreateHWnd(env, L"", style, 0,
x, y, width, height,
awtParent->GetHWnd(),
reinterpret_cast<HMENU>(static_cast<INT_PTR>(
awtParent->CreateControlID())),
::GetSysColor(COLOR_SCROLLBAR),
::GetSysColor(COLOR_SCROLLBAR),
peer);
c->m_backgroundColorSet = TRUE;
/* suppress inheriting parent's color. */
c->UpdateBackground(env, target);
}
} catch (...) {
env->DeleteLocalRef(target);
throw;
}
done:
env->DeleteLocalRef(target);
return c;
}
LRESULT CALLBACK
AwtScrollbar::MouseFilter(int nCode, WPARAM wParam, LPARAM lParam)
{
if (((UINT)wParam == WM_LBUTTONUP || (UINT)wParam == WM_MOUSEMOVE) &&
ms_isInsideMouseFilter != TRUE &&
nCode >= 0)
{
HWND hwnd = ((PMOUSEHOOKSTRUCT)lParam)->hwnd;
AwtComponent *comp = AwtComponent::GetComponent(hwnd);
if (comp != NULL && comp->IsScrollbar()) {
MSG msg;
LPMSG lpMsg = (LPMSG)&msg;
UINT msgID = (UINT)wParam;
ms_isInsideMouseFilter = TRUE;
// Peek the message to get wParam containing the message's flags.
// <::PeekMessage> will call this hook again. To prevent recursive
// processing the <ms_isInsideMouseFilter> flag is used.
// Calling <::PeekMessage> is not so good desision but is the only one
// found to get those flags (used further in Java event creation).
// WARNING! If you are about to add new hook of WM_MOUSE type make
// it ready for recursive call, otherwise modify this one.
if (::PeekMessage(lpMsg, hwnd, msgID, msgID, PM_NOREMOVE)) {
comp->WindowProc(msgID, lpMsg->wParam, lpMsg->lParam);
}
ms_isInsideMouseFilter = FALSE;
}
}
return ::CallNextHookEx(AwtScrollbar::ms_hMouseFilter, nCode, wParam, lParam);
}
LRESULT
AwtScrollbar::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// Delegate real work to super
LRESULT retValue = AwtComponent::WindowProc(message, wParam, lParam);
// After-hooks for workarounds
switch (message) {
// Work around a windows bug described in KB article Q73839.
// Need to update focus indicator on scrollbar if thumb
// proportion or thumb position was changed.
case WM_SIZE:
case SBM_SETSCROLLINFO:
case SBM_SETRANGE:
case SBM_SETRANGEREDRAW:
if (AwtComponent::sm_focusOwner == GetHWnd()) {
UpdateFocusIndicator();
}
break;
}
return retValue;
}
MsgRouting
AwtScrollbar::WmNcHitTest(UINT x, UINT y, LRESULT& retVal)
{
if (::IsWindow(AwtWindow::GetModalBlocker(AwtComponent::GetTopLevelParentForWindow(GetHWnd())))) {
retVal = HTCLIENT;
return mrConsume;
}
return AwtComponent::WmNcHitTest(x, y, retVal);
}
// Fix for a race condition when the WM_LBUTTONUP is picked by the AWT
// message loop before(!) the windows internal message loop for the
// scrollbar is started in response to WM_LBUTTONDOWN. See KB article
// Q102552.
//
// Note that WM_LBUTTONUP is processed by the windows internal message
// loop. May be we can synthesize a MOUSE_RELEASED event but that
// seems kludgey, so we'd better left this as is for now.
MsgRouting
AwtScrollbar::WmMouseDown(UINT flags, int x, int y, int button)
{
// We pass the WM_LBUTTONDOWN up to Java, but we process it
// immediately as well to avoid the race. Later when this press
// event returns to us wrapped into a WM_AWT_HANDLE_EVENT we
// ignore it in the HandleEvent below. This means that we can not
// consume the mouse press in the Java world.
MsgRouting usualRoute = AwtComponent::WmMouseDown(flags, x, y, button);
if (::IsWindow(AwtWindow::GetModalBlocker(AwtComponent::GetTopLevelParentForWindow(GetHWnd())))) {
return mrConsume;
}
if (button == LEFT_BUTTON)
return mrDoDefault; // Force immediate processing to avoid the race.
else
return usualRoute;
}
MsgRouting
AwtScrollbar::HandleEvent(MSG *msg, BOOL synthetic)
{
// SCROLLBAR control doesn't cause activation on mouse/key events,
// so we can safely (for synthetic focus) pass them to the system proc.
if (IsFocusingMouseMessage(msg)) {
// Left button press was already routed to default window
// procedure in the WmMouseDown above. Propagating synthetic
// press seems like a bad idea as internal message loop
// doesn't know how to unwrap synthetic release.
delete msg;
return mrConsume;
}
return AwtComponent::HandleEvent(msg, synthetic);
}
// Work around a windows bug descrbed in KB article Q73839. Reset
// focus on scrollbars to update focus indicator. The article advises
// to disable/enable the scrollbar.
void
AwtScrollbar::UpdateFocusIndicator()
{
if (IsFocusable()) {
// todo: doesn't work
SendMessage((WPARAM)ESB_DISABLE_BOTH);
SendMessage((WPARAM)ESB_ENABLE_BOTH);
}
}
// In a windows app one would call SetScrollInfo from WM_[HV]SCROLL
// handler directly. Since we call SetScrollInfo from Java world
// after scroll handler is over next WM_[HV]SCROLL event can be
// delivered before SetScrollInfo was called in response to the
// previous one and thus we would fire exactly the same event which
// will only contribute to the growth of the backlog of scroll events.
const char * const AwtScrollbar::SbNlineDown = "lineDown";
const char * const AwtScrollbar::SbNlineUp = "lineUp";
const char * const AwtScrollbar::SbNpageDown = "pageDown";
const char * const AwtScrollbar::SbNpageUp = "pageUp";
const char * const AwtScrollbar::SbNdrag = "drag";
const char * const AwtScrollbar::SbNdragEnd = "dragEnd";
const char * const AwtScrollbar::SbNwarp = "warp";
inline void
AwtScrollbar::DoScrollCallbackCoalesce(const char* methodName, int newPos)
{
if (methodName == m_prevCallback && newPos == m_prevCallbackPos) {
DTRACE_PRINTLN2("AwtScrollbar: ignoring duplicate callback %s(%d)",
methodName, newPos);
}
else {
DoCallback(methodName, "(I)V", newPos);
m_prevCallback = methodName;
m_prevCallbackPos = newPos;
}
}
MsgRouting
AwtScrollbar::WmVScroll(UINT scrollCode, UINT pos, HWND hScrollbar)
{
int minVal, maxVal; // scrollbar range
int minPos, maxPos; // thumb positions (max depends on visible amount)
int curPos, newPos;
// For drags we have old (static) and new (dynamic) thumb positions
int dragP = (scrollCode == SB_THUMBTRACK
|| scrollCode == SB_THUMBPOSITION);
int thumbPos;
SCROLLINFO si;
si.cbSize = sizeof si;
si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE;
// From, _Win32 Programming_, by Rector and Newcommer, p. 185:
// "In some of the older documentation on Win32 scroll bars,
// including that published by Microsoft, you may read that
// you *cannot* obtain the scroll position while in a handler.
// The SIF_TRACKPOS flag was added after this documentation
// was published. Beware of this older documentation; it may
// have other obsolete features."
if (dragP) {
si.fMask |= SIF_TRACKPOS;
}
VERIFY(::GetScrollInfo(GetHWnd(), SB_CTL, &si));
curPos = si.nPos;
minPos = minVal = si.nMin;
// Upper bound of the range. Note that adding 1 here is safe
// and won't cause a wrap, since we have substracted 1 in the
// SetValues above.
maxVal = si.nMax + 1;
// Meaningful maximum position is maximum - visible.
maxPos = maxVal - si.nPage;
// XXX: Documentation for SBM_SETRANGE says that scrollbar
// range is limited by MAXLONG, which is 2**31, but when a
// scroll range is greater than that, thumbPos is reported
// incorrectly due to integer arithmetic wrap(s).
thumbPos = dragP ? si.nTrackPos : curPos;
// NB: Beware arithmetic wrap when calculating newPos
switch (scrollCode) {
case SB_LINEUP:
if ((__int64)curPos - m_lineIncr > minPos)
newPos = curPos - m_lineIncr;
else
newPos = minPos;
if (newPos != curPos)
DoScrollCallbackCoalesce(SbNlineUp, newPos);
break;
case SB_LINEDOWN:
if ((__int64)curPos + m_lineIncr < maxPos)
newPos = curPos + m_lineIncr;
else
newPos = maxPos;
if (newPos != curPos)
DoScrollCallbackCoalesce(SbNlineDown, newPos);
break;
case SB_PAGEUP:
if ((__int64)curPos - m_pageIncr > minPos)
newPos = curPos - m_pageIncr;
else
newPos = minPos;
if (newPos != curPos)
DoScrollCallbackCoalesce(SbNpageUp, newPos);
break;
case SB_PAGEDOWN:
if ((__int64)curPos + m_pageIncr < maxPos)
newPos = curPos + m_pageIncr;
else
newPos = maxPos;
if (newPos != curPos)
DoScrollCallbackCoalesce(SbNpageDown, newPos);
break;
case SB_TOP:
if (minPos != curPos)
DoScrollCallbackCoalesce(SbNwarp, minPos);
break;
case SB_BOTTOM:
if (maxPos != curPos)
DoScrollCallbackCoalesce(SbNwarp, maxPos);
break;
case SB_THUMBTRACK:
if (thumbPos != curPos)
DoScrollCallbackCoalesce(SbNdrag, thumbPos);
break;
case SB_THUMBPOSITION:
DoScrollCallbackCoalesce(SbNdragEnd, thumbPos);
break;
case SB_ENDSCROLL:
// reset book-keeping info
m_prevCallback = NULL;
break;
}
return mrDoDefault;
}
MsgRouting
AwtScrollbar::WmHScroll(UINT scrollCode, UINT pos, HWND hScrollbar)
{
return WmVScroll(scrollCode, pos, hScrollbar);
}
void AwtScrollbar::_SetValues(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
SetValuesStruct *svs = (SetValuesStruct *)param;
jobject self = svs->scrollbar;
SCROLLINFO si;
si.cbSize = sizeof si;
si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE;
si.nMin = svs->min;
si.nMax = svs->max - 1;
si.nPage = svs->visible;
si.nPos = svs->value;
AwtScrollbar *sb = NULL;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
sb = (AwtScrollbar *)pData;
if (::IsWindow(sb->GetHWnd()))
{
BOOL update_p = ::IsWindowEnabled(sb->GetHWnd()); // don't redraw if disabled
DTRACE_PRINTLN5("AwtScrollbar::SetValues(val = %d, vis = %d,"//(ctd.)
" min = %d, max = %d)%s",
svs->value, svs->visible, svs->min, svs->max,
update_p ? "" : " - NOT redrawing");
::SetScrollInfo(sb->GetHWnd(), SB_CTL, &si, update_p);
}
ret:
env->DeleteGlobalRef(self);
delete svs;
}
/************************************************************************
* Scrollbar native methods
*/
extern "C" {
/*
* Class: java_awt_Scrollbar
* Method: initIDs
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_java_awt_Scrollbar_initIDs(JNIEnv *env, jclass cls)
{
TRY;
AwtScrollbar::lineIncrementID = env->GetFieldID(cls, "lineIncrement", "I");
AwtScrollbar::pageIncrementID = env->GetFieldID(cls, "pageIncrement", "I");
AwtScrollbar::orientationID = env->GetFieldID(cls, "orientation", "I");
DASSERT(AwtScrollbar::lineIncrementID != NULL);
DASSERT(AwtScrollbar::pageIncrementID != NULL);
DASSERT(AwtScrollbar::orientationID != NULL);
CATCH_BAD_ALLOC;
}
} /* extern "C" */
/************************************************************************
* WScrollbarPeer native methods
*/
extern "C" {
/*
* Class: sun_awt_windows_WScrollbarPeer
* Method: setValues
* Signature: (IIII)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WScrollbarPeer_setValues(JNIEnv *env, jobject self,
jint value, jint visible,
jint minimum, jint maximum)
{
TRY;
SetValuesStruct *svs = new SetValuesStruct;
svs->scrollbar = env->NewGlobalRef(self);
svs->value = value;
svs->visible = visible;
svs->min = minimum;
svs->max = maximum;
AwtToolkit::GetInstance().SyncCall(AwtScrollbar::_SetValues, svs);
// global ref and svs are deleted in _SetValues
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WScrollbarPeer
* Method: setLineIncrement
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WScrollbarPeer_setLineIncrement(JNIEnv *env, jobject self,
jint increment)
{
TRY;
PDATA pData;
JNI_CHECK_PEER_RETURN(self);
AwtScrollbar* c = (AwtScrollbar*)pData;
c->SetLineIncrement(increment);
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WScrollbarPeer
* Method: setPageIncrement
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WScrollbarPeer_setPageIncrement(JNIEnv *env, jobject self,
jint increment)
{
TRY;
PDATA pData;
JNI_CHECK_PEER_RETURN(self);
AwtScrollbar* c = (AwtScrollbar*)pData;
c->SetPageIncrement(increment);
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WScrollbarPeer
* Method: create
* Signature: (Lsun/awt/windows/WComponentPeer;)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WScrollbarPeer_create(JNIEnv *env, jobject self,
jobject parent)
{
TRY;
PDATA pData;
JNI_CHECK_PEER_RETURN(parent);
AwtToolkit::CreateComponent(self, parent,
(AwtToolkit::ComponentFactory)
AwtScrollbar::Create);
JNI_CHECK_PEER_CREATION_RETURN(self);
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WScrollbarPeer
* Method: getScrollbarSize
* Signature: (I)I
*/
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WScrollbarPeer_getScrollbarSize(JNIEnv *env, jclass clazz, jint orientation)
{
if (orientation == java_awt_Scrollbar_VERTICAL) {
return ::GetSystemMetrics(SM_CXVSCROLL);
} else {
return ::GetSystemMetrics(SM_CYHSCROLL);
}
}
} /* extern "C" */