1/*
2 * Copyright (c) 1996, 2015, 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.h"
27
28#include "awt_Object.h"    /* wop_pDataID */
29#include "awt_Toolkit.h"
30#include "awt_Button.h"
31#include "awt_Canvas.h"
32#include "awt_Window.h"
33
34/* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
35 */
36
37/***********************************************************************/
38// Struct for _SetLabel() method
39struct SetLabelStruct {
40  jobject button;
41  jstring label;
42};
43
44/************************************************************************
45 * AwtButton fields
46 */
47
48/* java.awt.Button fields */
49jfieldID AwtButton::labelID;
50
51
52/************************************************************************
53 * AwtButton methods
54 */
55
56AwtButton::AwtButton() {
57    leftButtonDown = FALSE;
58}
59
60/* System provided button class */
61LPCTSTR AwtButton::GetClassName() {
62    return TEXT("BUTTON");
63}
64
65/* Create a new AwtButton object and window. */
66AwtButton* AwtButton::Create(jobject self, jobject parent)
67{
68    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
69
70    /* the result */
71    AwtButton *c = NULL;
72
73    jobject target = NULL;
74    jstring label = NULL;
75
76    try {
77        LPCWSTR labelStr;
78        DWORD style;
79        DWORD exStyle = 0;
80        jint x, y, height, width;
81
82        if (env->EnsureLocalCapacity(2) < 0) {
83            return NULL;
84        }
85
86        PDATA pData;
87        AwtCanvas* awtParent;
88
89        JNI_CHECK_PEER_GOTO(parent, done);
90        awtParent = (AwtCanvas*)pData;
91        JNI_CHECK_NULL_GOTO(awtParent, "awtParent", done);
92
93        target = env->GetObjectField(self, AwtObject::targetID);
94        JNI_CHECK_NULL_GOTO(target, "target", done);
95
96        c = new AwtButton();
97
98        label = (jstring)env->GetObjectField(target, AwtButton::labelID);
99
100        x = env->GetIntField(target, AwtComponent::xID);
101        y = env->GetIntField(target, AwtComponent::yID);
102        width = env->GetIntField(target, AwtComponent::widthID);
103        height = env->GetIntField(target, AwtComponent::heightID);
104
105        if (label == NULL) {
106            labelStr = L"";
107        } else {
108            labelStr = JNU_GetStringPlatformChars(env, label, JNI_FALSE);
109        }
110        style = 0;
111
112        if (labelStr == NULL) {
113            throw std::bad_alloc();
114        }
115
116        style = WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON | BS_OWNERDRAW;
117        if (GetRTLReadingOrder())
118            exStyle |= WS_EX_RTLREADING;
119
120        c->CreateHWnd(env, labelStr, style, exStyle, x, y, width, height,
121                      awtParent->GetHWnd(),
122                      reinterpret_cast<HMENU>(static_cast<INT_PTR>(
123                  awtParent->CreateControlID())),
124                      ::GetSysColor(COLOR_BTNTEXT),
125                      ::GetSysColor(COLOR_BTNFACE),
126                      self);
127        c->m_backgroundColorSet = TRUE;  // suppress inheriting parent's color
128        c->UpdateBackground(env, target);
129        if (label != NULL)
130            JNU_ReleaseStringPlatformChars(env, label, labelStr);
131    } catch (...) {
132        env->DeleteLocalRef(target);
133        if (label != NULL)
134            env->DeleteLocalRef(label);
135        throw;
136    }
137
138done:
139    env->DeleteLocalRef(target);
140    if (label != NULL)
141        env->DeleteLocalRef(label);
142    return c;
143}
144
145MsgRouting
146AwtButton::WmMouseDown(UINT flags, int x, int y, int button)
147{
148    // 4530087: keep track of the when the left mouse button is pressed
149    if (button == LEFT_BUTTON) {
150        leftButtonDown = TRUE;
151    }
152    return AwtComponent::WmMouseDown(flags, x, y, button);
153}
154
155MsgRouting
156AwtButton::WmMouseUp(UINT flags, int x, int y, int button)
157{
158    MsgRouting mrResult = AwtComponent::WmMouseUp(flags, x, y, button);
159
160    if (::IsWindow(AwtWindow::GetModalBlocker(AwtComponent::GetTopLevelParentForWindow(GetHWnd()))))
161    {
162        return mrConsume;
163    }
164
165    // 4530087: It is possible that a left mouse press happened on a Window
166    // obscuring this AwtButton, and during event handling the Window was
167    // removed.  This causes a WmMouseUp call to this AwtButton, even though
168    // there was no accompanying WmMouseDown.  ActionEvents should ONLY be
169    // notified (via NotifyListeners()) if the left button press happened on
170    // this AwtButton.  --bchristi
171    if (button == LEFT_BUTTON && leftButtonDown) {
172        leftButtonDown = FALSE;
173
174        POINT p = {x, y};
175        RECT rect;
176        ::GetClientRect(GetHWnd(), &rect);
177
178        if (::PtInRect(&rect, p)) {
179            NotifyListeners();
180        }
181    }
182
183    return mrResult;
184}
185
186void
187AwtButton::NotifyListeners()
188{
189    DoCallback("handleAction", "(JI)V", ::JVM_CurrentTimeMillis(NULL, 0),
190               (jint)AwtComponent::GetActionModifiers());
191}
192
193MsgRouting
194AwtButton::OwnerDrawItem(UINT /*ctrlId*/, DRAWITEMSTRUCT& drawInfo)
195{
196    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
197
198    if (env->EnsureLocalCapacity(3) < 0) {
199        /* is this OK? */
200        return mrConsume;
201    }
202
203    jobject self = GetPeer(env);
204    jobject target = env->GetObjectField(self, AwtObject::targetID);
205
206    HDC hDC = drawInfo.hDC;
207    RECT rect = drawInfo.rcItem;
208    UINT nState;
209    SIZE size;
210
211    /* Draw Button */
212    nState = DFCS_BUTTONPUSH;
213    if (drawInfo.itemState & ODS_SELECTED)
214        nState |= DFCS_PUSHED;
215
216    ::FillRect(hDC, &rect, GetBackgroundBrush());
217    UINT edgeType = (nState & DFCS_PUSHED) ? EDGE_SUNKEN : EDGE_RAISED;
218    ::DrawEdge(hDC, &rect, edgeType, BF_RECT | BF_SOFT);
219
220    /* Draw WindowText */
221    jobject font = GET_FONT(target, self);
222    jstring str = (jstring)env->GetObjectField(target, AwtButton::labelID);
223
224    size = AwtFont::getMFStringSize(hDC, font, str);
225
226    /* Check whether the button is disabled. */
227    BOOL bEnabled = isEnabled();
228
229    int adjust = (nState & DFCS_PUSHED) ? 1 : 0;
230    int x = (rect.left + rect.right-size.cx) / 2 + adjust;
231    int y = (rect.top + rect.bottom-size.cy) / 2 + adjust;
232
233    if (bEnabled) {
234        AwtComponent::DrawWindowText(hDC, font, str, x, y);
235    } else {
236        AwtComponent::DrawGrayText(hDC, font, str, x, y);
237    }
238
239    /* Draw focus rect */
240    if (drawInfo.itemState & ODS_FOCUS){
241        const int inf = 3; /* heuristic decision */
242        RECT focusRect;
243        VERIFY(::CopyRect(&focusRect, &rect));
244        VERIFY(::InflateRect(&focusRect,-inf,-inf));
245        if(::DrawFocusRect(hDC, &focusRect) == 0)
246            VERIFY(::GetLastError() == 0);
247    }
248
249    /* Notify any subclasses */
250    DoCallback("handlePaint", "(IIII)V", rect.left, rect.top,
251               rect.right-rect.left, rect.bottom-rect.top);
252
253    env->DeleteLocalRef(target);
254    env->DeleteLocalRef(font);
255    env->DeleteLocalRef(str);
256
257    return mrConsume;
258}
259
260MsgRouting AwtButton::WmPaint(HDC)
261{
262    /* Suppress peer notification, because it's handled in WmDrawItem. */
263    return mrDoDefault;
264}
265
266BOOL AwtButton::IsFocusingMouseMessage(MSG *pMsg) {
267    return pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_LBUTTONUP;
268}
269
270BOOL AwtButton::IsFocusingKeyMessage(MSG *pMsg) {
271    return (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) &&
272            pMsg->wParam == VK_SPACE;
273}
274
275MsgRouting AwtButton::HandleEvent(MSG *msg, BOOL synthetic)
276{
277    if (IsFocusingMouseMessage(msg)) {
278        SendMessage(BM_SETSTATE, msg->message == WM_LBUTTONDOWN ? TRUE : FALSE, 0);
279        delete msg;
280        return mrConsume;
281    }
282    if (IsFocusingKeyMessage(msg)) {
283        SendMessage(BM_SETSTATE, msg->message == WM_KEYDOWN ? TRUE : FALSE, 0);
284        delete msg;
285        return mrConsume;
286    }
287    return AwtComponent::HandleEvent(msg, synthetic);
288}
289
290void AwtButton::_SetLabel(void *param)
291{
292    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
293
294    SetLabelStruct *sls = (SetLabelStruct *)param;
295
296    jobject button = sls->button;
297    jstring label = sls->label;
298
299    int badAlloc = 0;
300    AwtComponent *c = NULL;
301
302    PDATA pData;
303    JNI_CHECK_PEER_GOTO(button, done);
304
305    c = (AwtComponent*)pData;
306    if (::IsWindow(c->GetHWnd()))
307    {
308        LPCTSTR labelStr = NULL;
309
310        // By convension null label means empty string
311        if (label == NULL) {
312            labelStr = TEXT("");
313        } else {
314            labelStr = JNU_GetStringPlatformChars(env, label, JNI_FALSE);
315        }
316
317        if (labelStr == NULL) {
318            badAlloc = 1;
319        } else {
320            c->SetText(labelStr);
321            if (label != NULL) {
322                JNU_ReleaseStringPlatformChars(env, label, labelStr);
323            }
324        }
325    }
326
327done:
328    env->DeleteGlobalRef(button);
329    if (label != NULL)
330    {
331        env->DeleteGlobalRef(label);
332    }
333
334    delete sls;
335
336    if (badAlloc) {
337        throw std::bad_alloc();
338    }
339}
340
341/************************************************************************
342 * WButtonPeer native methods
343 */
344
345extern "C" {
346
347/*
348 * Class:     sun_awt_windows_WButtonPeer
349 * Method:    initIDs
350 * Signature: ()V
351 */
352JNIEXPORT void JNICALL
353Java_sun_awt_windows_WButtonPeer_initIDs(JNIEnv *env, jclass cls)
354{
355    TRY;
356
357    cls = env->FindClass("java/awt/Button");
358    if (cls == NULL) {
359        return;
360    }
361    AwtButton::labelID = env->GetFieldID(cls, "label", "Ljava/lang/String;");
362    DASSERT(AwtButton::labelID != NULL);
363
364    CATCH_BAD_ALLOC;
365}
366
367/*
368 * Class:     sun_awt_windows_WButtonPeer
369 * Method:    setLabel
370 * Signature: (Ljava/lang/String;)V
371 */
372JNIEXPORT void JNICALL
373Java_sun_awt_windows_WButtonPeer_setLabel(JNIEnv *env, jobject self,
374                                          jstring label)
375{
376    TRY;
377
378    PDATA pData;
379    JNI_CHECK_PEER_RETURN(self);
380
381    SetLabelStruct *sls = new SetLabelStruct;
382    sls->button = env->NewGlobalRef(self);
383    sls->label = (label != NULL) ? (jstring)env->NewGlobalRef(label) : NULL;
384
385    AwtToolkit::GetInstance().SyncCall(AwtButton::_SetLabel, sls);
386    // global refs and sls are deleted in _SetLabel()
387
388    CATCH_BAD_ALLOC;
389}
390
391/*
392 * Class:     sun_awt_windows_WButtonPeer
393 * Method:    create
394 * Signature: (Lsun/awt/windows/WComponentPeer;)V
395 */
396JNIEXPORT void JNICALL
397Java_sun_awt_windows_WButtonPeer_create(JNIEnv *env, jobject self,
398                                        jobject parent)
399{
400    TRY;
401
402    PDATA pData;
403    JNI_CHECK_PEER_RETURN(parent);
404
405    AwtToolkit::CreateComponent(
406        self, parent, (AwtToolkit::ComponentFactory)AwtButton::Create);
407
408    JNI_CHECK_PEER_CREATION_RETURN(self);
409
410    CATCH_BAD_ALLOC;
411}
412
413}  /* extern "C" */
414