1 /*
   2  * Copyright (c) 1996, 2011, 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
  39 struct SetLabelStruct {
  40   jobject button;
  41   jstring label;
  42 };
  43 
  44 /************************************************************************
  45  * AwtButton fields
  46  */
  47 
  48 /* java.awt.Button fields */
  49 jfieldID AwtButton::labelID;
  50 
  51 
  52 /************************************************************************
  53  * AwtButton methods
  54  */
  55 
  56 AwtButton::AwtButton() {
  57     leftButtonDown = FALSE;
  58 }
  59 
  60 /* System provided button class */
  61 LPCTSTR AwtButton::GetClassName() {
  62     return TEXT("BUTTON");
  63 }
  64 
  65 /* Create a new AwtButton object and window. */
  66 AwtButton* 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 
 138 done:
 139     env->DeleteLocalRef(target);
 140     if (label != NULL)
 141         env->DeleteLocalRef(label);
 142     return c;
 143 }
 144 
 145 MsgRouting
 146 AwtButton::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 
 155 MsgRouting
 156 AwtButton::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 
 186 void
 187 AwtButton::NotifyListeners()
 188 {
 189     DoCallback("handleAction", "(JI)V", ::JVM_CurrentTimeMillis(NULL, 0),
 190                (jint)AwtComponent::GetJavaModifiers());
 191 }
 192 
 193 MsgRouting
 194 AwtButton::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 
 260 MsgRouting AwtButton::WmPaint(HDC)
 261 {
 262     /* Suppress peer notification, because it's handled in WmDrawItem. */
 263     return mrDoDefault;
 264 }
 265 
 266 BOOL AwtButton::IsFocusingMouseMessage(MSG *pMsg) {
 267     return pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_LBUTTONUP;
 268 }
 269 
 270 BOOL AwtButton::IsFocusingKeyMessage(MSG *pMsg) {
 271     return (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) &&
 272             pMsg->wParam == VK_SPACE;
 273 }
 274 
 275 MsgRouting 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 
 290 void 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 
 327 done:
 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 
 345 extern "C" {
 346 
 347 /*
 348  * Class:     sun_awt_windows_WButtonPeer
 349  * Method:    initIDs
 350  * Signature: ()V
 351  */
 352 JNIEXPORT void JNICALL
 353 Java_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  */
 372 JNIEXPORT void JNICALL
 373 Java_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  */
 396 JNIEXPORT void JNICALL
 397 Java_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" */