1 /*
   2  * Copyright (c) 1996, 2020, 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 <windowsx.h>
  27 
  28 #include "awt_Toolkit.h"
  29 #include "awt_Choice.h"
  30 #include "awt_Canvas.h"
  31 
  32 #include "awt_Dimension.h"
  33 #include "awt_Container.h"
  34 
  35 #include "ComCtl32Util.h"
  36 
  37 #include <java_awt_Toolkit.h>
  38 #include <java_awt_FontMetrics.h>
  39 #include <java_awt_event_InputEvent.h>
  40 
  41 /* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
  42  */
  43 
  44 /************************************************************************/
  45 // Struct for _Reshape() method
  46 struct ReshapeStruct {
  47     jobject choice;
  48     jint x, y;
  49     jint width, height;
  50 };
  51 // Struct for _Select() method
  52 struct SelectStruct {
  53     jobject choice;
  54     jint index;
  55 };
  56 // Struct for _AddItems() method
  57 struct AddItemsStruct {
  58     jobject choice;
  59     jobjectArray items;
  60     jint index;
  61 };
  62 // Struct for _Remove() method
  63 struct RemoveStruct {
  64     jobject choice;
  65     jint index;
  66 };
  67 
  68 /************************************************************************/
  69 
  70 /* Bug #4509045: set if SetDragCapture captured mouse */
  71 
  72 BOOL AwtChoice::mouseCapture = FALSE;
  73 
  74 /* Bug #4338368: consume the spurious MouseUp when the choice loses focus */
  75 
  76 BOOL AwtChoice::skipNextMouseUp = FALSE;
  77 
  78 BOOL AwtChoice::sm_isMouseMoveInList = FALSE;
  79 
  80 static const UINT MINIMUM_NUMBER_OF_VISIBLE_ITEMS = 8;
  81 
  82 namespace {
  83     jfieldID selectedIndexID;
  84 }
  85 
  86 /*************************************************************************
  87  * AwtChoice class methods
  88  */
  89 
  90 AwtChoice::AwtChoice() {
  91     m_hList = NULL;
  92     m_listDefWindowProc = NULL;
  93 }
  94 
  95 LPCTSTR AwtChoice::GetClassName() {
  96     return TEXT("COMBOBOX");  /* System provided combobox class */
  97 }
  98 
  99 void AwtChoice::Dispose() {
 100     if (m_hList != NULL && m_listDefWindowProc != NULL) {
 101         ComCtl32Util::GetInstance().UnsubclassHWND(m_hList, ListWindowProc, m_listDefWindowProc);
 102     }
 103     AwtComponent::Dispose();
 104 }
 105 
 106 AwtChoice* AwtChoice::Create(jobject peer, jobject parent) {
 107     DASSERT(AwtToolkit::IsMainThread());
 108     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 109 
 110     jobject target = NULL;
 111     AwtChoice* c = NULL;
 112     RECT rc;
 113 
 114     try {
 115         if (env->EnsureLocalCapacity(1) < 0) {
 116             return NULL;
 117         }
 118         PDATA pData;
 119         AwtCanvas* awtParent;
 120         JNI_CHECK_PEER_GOTO(parent, done);
 121         awtParent = (AwtCanvas*)pData;
 122 
 123         target = env->GetObjectField(peer, AwtObject::targetID);
 124         JNI_CHECK_NULL_GOTO(target, "null target", done);
 125 
 126         c = new AwtChoice();
 127 
 128         {
 129             DWORD style = WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL |
 130                           CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED;
 131             DWORD exStyle = 0;
 132             if (GetRTL()) {
 133                 exStyle |= WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR;
 134                 if (GetRTLReadingOrder())
 135                     exStyle |= WS_EX_RTLREADING;
 136             }
 137 
 138             /*
 139              * In OWNER_DRAW, the size of the edit control part of the
 140              * choice must be determinded in its creation, when the parent
 141              * cannot get the choice's instance from its handle.  So
 142              * record the pair of the ID and the instance of the choice.
 143              */
 144             UINT myId = awtParent->CreateControlID();
 145             DASSERT(myId > 0);
 146             c->m_myControlID = myId;
 147             awtParent->PushChild(myId, c);
 148 
 149             jint x = env->GetIntField(target, AwtComponent::xID);
 150             jint y = env->GetIntField(target, AwtComponent::yID);
 151             jint width = env->GetIntField(target, AwtComponent::widthID);
 152             jint height = env->GetIntField(target, AwtComponent::heightID);
 153 
 154             jobject dimension = JNU_CallMethodByName(env, NULL, peer,
 155                                                      "getPreferredSize",
 156                                                      "()Ljava/awt/Dimension;").l;
 157             DASSERT(!safe_ExceptionOccurred(env));
 158             if (env->ExceptionCheck()) goto done;
 159 
 160             if (dimension != NULL && width == 0) {
 161                 width = env->GetIntField(dimension, AwtDimension::widthID);
 162             }
 163             c->CreateHWnd(env, L"", style, exStyle,
 164                           x, y, width, height,
 165                           awtParent->GetHWnd(),
 166                           reinterpret_cast<HMENU>(static_cast<INT_PTR>(myId)),
 167                           ::GetSysColor(COLOR_WINDOWTEXT),
 168                           ::GetSysColor(COLOR_WINDOW),
 169                           peer);
 170 
 171             /* suppress inheriting parent's color. */
 172             c->m_backgroundColorSet = TRUE;
 173             c->UpdateBackground(env, target);
 174 
 175             /* Bug 4255631 Solaris: Size returned by Choice.getSize() does not match
 176              * actual size
 177              * Fix: Set the Choice to its actual size in the component.
 178              */
 179             ::GetClientRect(c->GetHWnd(), &rc);
 180             env->SetIntField(target, AwtComponent::widthID, c->ScaleDownX(rc.right));
 181             env->SetIntField(target, AwtComponent::heightID, c->ScaleDownY(rc.bottom));
 182 
 183             if (IS_WINXP) {
 184                 ::SendMessage(c->GetHWnd(), CB_SETMINVISIBLE, (WPARAM) MINIMUM_NUMBER_OF_VISIBLE_ITEMS, 0);
 185             }
 186 
 187             env->DeleteLocalRef(dimension);
 188         }
 189     } catch (...) {
 190         env->DeleteLocalRef(target);
 191         throw;
 192     }
 193 
 194 done:
 195     env->DeleteLocalRef(target);
 196 
 197     return c;
 198 }
 199 
 200 // calculate height of drop-down list part of the combobox
 201 // to show all the items up to a maximum of eight
 202 int AwtChoice::GetDropDownHeight()
 203 {
 204     int itemHeight =(int)::SendMessage(GetHWnd(), CB_GETITEMHEIGHT, (UINT)0,0);
 205     int numItemsToShow = (int)::SendMessage(GetHWnd(), CB_GETCOUNT, 0,0);
 206     numItemsToShow = min(MINIMUM_NUMBER_OF_VISIBLE_ITEMS, numItemsToShow);
 207 
 208     // drop-down height snaps to nearest line, so add a
 209     // fudge factor of 1/2 line to ensure last line shows
 210     return ScaleDownY(itemHeight * numItemsToShow + itemHeight / 2);
 211 }
 212 
 213 // get the height of the field portion of the combobox
 214 int AwtChoice::GetFieldHeight()
 215 {
 216     int fieldHeight;
 217     int borderHeight;
 218     fieldHeight =(int)::SendMessage(GetHWnd(), CB_GETITEMHEIGHT, (UINT)-1, 0);
 219     // add top and bottom border lines; border size is different for
 220     // Win 4.x (3d edge) vs 3.x (1 pixel line)
 221     borderHeight = ::GetSystemMetrics(SM_CYEDGE);
 222     fieldHeight += borderHeight*2;
 223     return ScaleDownY(fieldHeight);
 224 }
 225 
 226 // gets the total height of the combobox, including drop down
 227 int AwtChoice::GetTotalHeight()
 228 {
 229     int dropHeight = GetDropDownHeight();
 230     int fieldHeight = GetFieldHeight();
 231 
 232     // border on drop-down portion is always non-3d (so don't use SM_CYEDGE)
 233     int borderHeight = ScaleDownY(::GetSystemMetrics(SM_CYBORDER));
 234     // total height = drop down height + field height + top+bottom drop down border lines
 235     return dropHeight + fieldHeight + borderHeight * 2;
 236 }
 237 
 238 // Recalculate and set the drop-down height for the Choice.
 239 void AwtChoice::ResetDropDownHeight()
 240 {
 241     RECT    rcWindow;
 242 
 243     ::GetWindowRect(GetHWnd(), &rcWindow);
 244     // resize the drop down to accommodate added/removed items
 245     int totalHeight = ScaleUpY(GetTotalHeight());
 246     ::SetWindowPos(GetHWnd(), NULL,
 247                     0, 0, rcWindow.right - rcWindow.left, totalHeight,
 248                     SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
 249 }
 250 
 251 /* Fix for the bug 4327666: set the capture for middle
 252    and right mouse buttons, but leave left button alone */
 253 void AwtChoice::SetDragCapture(UINT flags)
 254 {
 255     if ((flags & MK_LBUTTON) != 0) {
 256         if ((::GetCapture() == GetHWnd()) && mouseCapture) {
 257             /* On MK_LBUTTON ComboBox captures mouse itself
 258                so we should release capture and clear flag to
 259                prevent releasing capture by ReleaseDragCapture
 260              */
 261             ::ReleaseCapture();
 262             mouseCapture = FALSE;
 263         }
 264         return;
 265     }
 266 
 267     // don't want to interfere with other controls
 268     if (::GetCapture() == NULL) {
 269         ::SetCapture(GetHWnd());
 270         mouseCapture = TRUE;
 271     }
 272 }
 273 
 274 /* Fix for Bug 4509045: should release capture only if it is set by SetDragCapture */
 275 void AwtChoice::ReleaseDragCapture(UINT flags)
 276 {
 277     if ((::GetCapture() == GetHWnd()) && ((flags & ALL_MK_BUTTONS) == 0) && mouseCapture) {
 278         ::ReleaseCapture();
 279         mouseCapture = FALSE;
 280     }
 281 }
 282 
 283 void AwtChoice::Reshape(int x, int y, int w, int h)
 284 {
 285     // Choice component height is fixed (when rolled up)
 286     // so vertically center the choice in it's bounding box
 287     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 288     jobject target = GetTarget(env);
 289     jobject parent = env->GetObjectField(target, AwtComponent::parentID);
 290     RECT rc;
 291 
 292     int fieldHeight = GetFieldHeight();
 293     if ((parent != NULL && env->GetObjectField(parent, AwtContainer::layoutMgrID) != NULL) &&
 294         fieldHeight > 0 && fieldHeight < h) {
 295         y += (h - fieldHeight) / 2;
 296     }
 297 
 298     /* Fix for 4783342
 299      * Choice should ignore reshape on height changes,
 300      * as height is dependent on Font size only.
 301      */
 302     AwtComponent* awtParent = GetParent();
 303     BOOL bReshape = true;
 304     if (awtParent != NULL) {
 305         ::GetWindowRect(GetHWnd(), &rc);
 306         int oldW = ScaleDownX(rc.right - rc.left);
 307         RECT parentRc;
 308         ::GetWindowRect(awtParent->GetHWnd(), &parentRc);
 309         int oldX = ScaleDownX(rc.left - parentRc.left);
 310         int oldY = ScaleDownY(rc.top - parentRc.top);
 311         bReshape = (x != oldX || y != oldY || w != oldW);
 312     }
 313 
 314     if (bReshape)
 315     {
 316         int totalHeight = GetTotalHeight();
 317         AwtComponent::Reshape(x, y, w, totalHeight);
 318     }
 319 
 320     /* Bug 4255631 Solaris: Size returned by Choice.getSize() does not match
 321      * actual size
 322      * Fix: Set the Choice to its actual size in the component.
 323      */
 324     ::GetClientRect(GetHWnd(), &rc);
 325     env->SetIntField(target, AwtComponent::widthID, ScaleDownX(rc.right));
 326     env->SetIntField(target, AwtComponent::heightID, ScaleDownY(rc.bottom));
 327 
 328     env->DeleteLocalRef(target);
 329     env->DeleteLocalRef(parent);
 330 }
 331 
 332 jobject AwtChoice::PreferredItemSize(JNIEnv *env)
 333 {
 334     jobject dimension = JNU_CallMethodByName(env, NULL, GetPeer(env),
 335                                              "getPreferredSize",
 336                                              "()Ljava/awt/Dimension;").l;
 337     DASSERT(!safe_ExceptionOccurred(env));
 338     CHECK_NULL_RETURN(dimension, NULL);
 339 
 340     /* This size is window size of choice and it's too big for each
 341      * drop down item height.
 342      */
 343     env->SetIntField(dimension, AwtDimension::heightID,
 344                        ScaleUpY(GetFontHeight(env)));
 345     return dimension;
 346 }
 347 
 348 void AwtChoice::SetFont(AwtFont* font)
 349 {
 350     AwtComponent::SetFont(font);
 351 
 352     //Get the text metrics and change the height of each item.
 353     HDC hDC = ::GetDC(GetHWnd());
 354     DASSERT(hDC != NULL);
 355     TEXTMETRIC tm;
 356 
 357     HANDLE hFont = font->GetHFont();
 358     VERIFY(::SelectObject(hDC, hFont) != NULL);
 359     VERIFY(::GetTextMetrics(hDC, &tm));
 360     long h = tm.tmHeight + tm.tmExternalLeading;
 361     VERIFY(::ReleaseDC(GetHWnd(), hDC) != 0);
 362 
 363     int nCount = (int)::SendMessage(GetHWnd(), CB_GETCOUNT, 0, 0);
 364     for(int i = 0; i < nCount; ++i) {
 365         VERIFY(::SendMessage(GetHWnd(), CB_SETITEMHEIGHT, i, MAKELPARAM(h, 0)) != CB_ERR);
 366     }
 367     //Change the height of the Edit Box.
 368     VERIFY(::SendMessage(GetHWnd(), CB_SETITEMHEIGHT, (UINT)-1,
 369                          MAKELPARAM(h, 0)) != CB_ERR);
 370 
 371     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 372     jobject target = GetTarget(env);
 373     jint height = env->GetIntField(target, AwtComponent::heightID);
 374 
 375     Reshape(env->GetIntField(target, AwtComponent::xID),
 376             env->GetIntField(target, AwtComponent::yID),
 377             env->GetIntField(target, AwtComponent::widthID),
 378             h);
 379 
 380     env->DeleteLocalRef(target);
 381 }
 382 
 383 static int lastClickX = -1;
 384 static int lastClickY = -1;
 385 
 386 LRESULT CALLBACK AwtChoice::ListWindowProc(HWND hwnd, UINT message,
 387                                            WPARAM wParam, LPARAM lParam)
 388 {
 389     /*
 390      * We don't pass the choice WM_LBUTTONDOWN message. As the result the choice's list
 391      * doesn't forward mouse messages it captures. Below we do forward what we need.
 392      */
 393 
 394     TRY;
 395 
 396     DASSERT(::IsWindow(hwnd));
 397 
 398     switch (message) {
 399         case WM_LBUTTONDOWN: {
 400             DWORD curPos = ::GetMessagePos();
 401             lastClickX = GET_X_LPARAM(curPos);
 402             lastClickY = GET_Y_LPARAM(curPos);
 403             break;
 404         }
 405         case WM_MOUSEMOVE: {
 406             RECT rect;
 407             ::GetClientRect(hwnd, &rect);
 408 
 409             POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
 410             if (::PtInRect(&rect, pt)) {
 411                 sm_isMouseMoveInList = TRUE;
 412             }
 413 
 414             POINT lastPt = {lastClickX, lastClickY};
 415             ::ScreenToClient(hwnd, &lastPt);
 416             if (::PtInRect(&rect, lastPt)) {
 417                 break; // ignore when dragging inside the list
 418             }
 419         }
 420         case WM_LBUTTONUP: {
 421             lastClickX = -1;
 422             lastClickY = -1;
 423 
 424             AwtChoice *c = (AwtChoice *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
 425             if (c != NULL) {
 426                 // forward the msg to the choice
 427                 c->WindowProc(message, wParam, lParam);
 428             }
 429         }
 430     }
 431     return ComCtl32Util::GetInstance().DefWindowProc(NULL, hwnd, message, wParam, lParam);
 432 
 433     CATCH_BAD_ALLOC_RET(0);
 434 }
 435 
 436 
 437 MsgRouting AwtChoice::WmNotify(UINT notifyCode)
 438 {
 439     if (notifyCode == CBN_SELCHANGE) {
 440         int selectedIndex = (int)SendMessage(CB_GETCURSEL);
 441 
 442         JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 443         jobject target = GetTarget(env);
 444         int previousIndex = env->GetIntField(target, selectedIndexID);
 445 
 446         if (selectedIndex != CB_ERR && selectedIndex != previousIndex){
 447             DoCallback("handleAction", "(I)V", selectedIndex);
 448         }
 449     } else if (notifyCode == CBN_DROPDOWN) {
 450 
 451         if (m_hList == NULL) {
 452             COMBOBOXINFO cbi;
 453             cbi.cbSize = sizeof(COMBOBOXINFO);
 454             ::GetComboBoxInfo(GetHWnd(), &cbi);
 455             m_hList = cbi.hwndList;
 456             m_listDefWindowProc = ComCtl32Util::GetInstance().SubclassHWND(m_hList, ListWindowProc);
 457             DASSERT(::GetWindowLongPtr(m_hList, GWLP_USERDATA) == NULL);
 458             ::SetWindowLongPtr(m_hList, GWLP_USERDATA, (LONG_PTR)this);
 459         }
 460         sm_isMouseMoveInList = FALSE;
 461 
 462         // Clicking in the dropdown list steals focus from the proxy.
 463         // So, set the focus-restore flag up.
 464         SetRestoreFocus(TRUE);
 465     } else if (notifyCode == CBN_CLOSEUP) {
 466         SetRestoreFocus(FALSE);
 467     }
 468     return mrDoDefault;
 469 }
 470 
 471 MsgRouting
 472 AwtChoice::OwnerDrawItem(UINT /*ctrlId*/, DRAWITEMSTRUCT& drawInfo)
 473 {
 474     DrawListItem((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), drawInfo);
 475     return mrConsume;
 476 }
 477 
 478 MsgRouting
 479 AwtChoice::OwnerMeasureItem(UINT /*ctrlId*/, MEASUREITEMSTRUCT& measureInfo)
 480 {
 481     MeasureListItem((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), measureInfo);
 482     return mrConsume;
 483 }
 484 
 485 /* Bug #4338368: when a choice loses focus, it triggers spurious MouseUp event,
 486  * even if the focus was lost due to TAB key pressing
 487  */
 488 
 489 MsgRouting
 490 AwtChoice::WmKillFocus(HWND hWndGotFocus)
 491 {
 492     skipNextMouseUp = TRUE;
 493     return AwtComponent::WmKillFocus(hWndGotFocus);
 494 }
 495 
 496 MsgRouting
 497 AwtChoice::WmMouseUp(UINT flags, int x, int y, int button)
 498 {
 499     if (skipNextMouseUp) {
 500         skipNextMouseUp = FALSE;
 501         return mrDoDefault;
 502     }
 503     return AwtComponent::WmMouseUp(flags, x, y, button);
 504 }
 505 
 506 MsgRouting AwtChoice::HandleEvent(MSG *msg, BOOL synthetic)
 507 {
 508     if (IsFocusingMouseMessage(msg)) {
 509         SendMessage(CB_SHOWDROPDOWN, ~SendMessage(CB_GETDROPPEDSTATE, 0, 0), 0);
 510         delete msg;
 511         return mrConsume;
 512     }
 513     // To simulate the native behavior, we close the list on WM_LBUTTONUP if
 514     // WM_MOUSEMOVE has been dedected on the list since it has been dropped down.
 515     if (msg->message == WM_LBUTTONUP && SendMessage(CB_GETDROPPEDSTATE, 0, 0) &&
 516         sm_isMouseMoveInList)
 517     {
 518         SendMessage(CB_SHOWDROPDOWN, FALSE, 0);
 519     }
 520     return AwtComponent::HandleEvent(msg, synthetic);
 521 }
 522 
 523 BOOL AwtChoice::InheritsNativeMouseWheelBehavior() {return true;}
 524 
 525 void AwtChoice::_Reshape(void *param)
 526 {
 527     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 528 
 529     ReshapeStruct *rs = (ReshapeStruct *)param;
 530     jobject choice = rs->choice;
 531     jint x = rs->x;
 532     jint y = rs->y;
 533     jint width = rs->width;
 534     jint height = rs->height;
 535 
 536     AwtChoice *c = NULL;
 537 
 538     PDATA pData;
 539     JNI_CHECK_PEER_GOTO(choice, done);
 540 
 541     c = (AwtChoice *)pData;
 542     if (::IsWindow(c->GetHWnd()))
 543     {
 544         c->Reshape(x, y, width, height);
 545         c->VerifyState();
 546     }
 547 
 548 done:
 549     env->DeleteGlobalRef(choice);
 550 
 551     delete rs;
 552 }
 553 
 554 void AwtChoice::_Select(void *param)
 555 {
 556     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 557 
 558     SelectStruct *ss = (SelectStruct *)param;
 559     jobject choice = ss->choice;
 560     jint index = ss->index;
 561 
 562     AwtChoice *c = NULL;
 563 
 564     PDATA pData;
 565     JNI_CHECK_PEER_GOTO(choice, done);
 566 
 567     c = (AwtChoice *)pData;
 568     if (::IsWindow(c->GetHWnd()))
 569     {
 570         c->SendMessage(CB_SETCURSEL, index);
 571 //        c->VerifyState();
 572     }
 573 
 574 done:
 575     env->DeleteGlobalRef(choice);
 576 
 577     delete ss;
 578 }
 579 
 580 void AwtChoice::_AddItems(void *param)
 581 {
 582     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 583 
 584     AddItemsStruct *ais = (AddItemsStruct *)param;
 585     jobject choice = ais->choice;
 586     jobjectArray items = ais->items;
 587     jint index = ais->index;
 588 
 589     AwtChoice *c = NULL;
 590 
 591     PDATA pData;
 592     JNI_CHECK_PEER_GOTO(choice, done);
 593     JNI_CHECK_NULL_GOTO(items, "null items", done);
 594 
 595     c = (AwtChoice *)pData;
 596     if (::IsWindow(c->GetHWnd()))
 597     {
 598         jsize i;
 599         int itemCount = env->GetArrayLength(items);
 600         if (itemCount > 0) {
 601            c->SendMessage(WM_SETREDRAW, (WPARAM)FALSE, 0);
 602            for (i = 0; i < itemCount; i++)
 603            {
 604                jstring item = (jstring)env->GetObjectArrayElement(items, i);
 605                if (env->ExceptionCheck()) goto done;
 606                if (item == NULL) goto next_elem;
 607                c->SendMessage(CB_INSERTSTRING, index + i, JavaStringBuffer(env, item));
 608                env->DeleteLocalRef(item);
 609 next_elem:
 610                ;
 611            }
 612            c->SendMessage(WM_SETREDRAW, (WPARAM)TRUE, 0);
 613            InvalidateRect(c->GetHWnd(), NULL, TRUE);
 614            c->ResetDropDownHeight();
 615            c->VerifyState();
 616         }
 617     }
 618 
 619 done:
 620     env->DeleteGlobalRef(choice);
 621     env->DeleteGlobalRef(items);
 622 
 623     delete ais;
 624 }
 625 
 626 void AwtChoice::_Remove(void *param)
 627 {
 628     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 629 
 630     RemoveStruct *rs = (RemoveStruct *)param;
 631     jobject choice = rs->choice;
 632     jint index = rs->index;
 633 
 634     AwtChoice *c = NULL;
 635 
 636     PDATA pData;
 637     JNI_CHECK_PEER_GOTO(choice, done);
 638 
 639     c = (AwtChoice *)pData;
 640     if (::IsWindow(c->GetHWnd()))
 641     {
 642         c->SendMessage(CB_DELETESTRING, index, 0);
 643         c->ResetDropDownHeight();
 644         c->VerifyState();
 645     }
 646 
 647 done:
 648     env->DeleteGlobalRef(choice);
 649 
 650     delete rs;
 651 }
 652 
 653 void AwtChoice::_RemoveAll(void *param)
 654 {
 655     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 656 
 657     jobject choice = (jobject)param;
 658 
 659     AwtChoice *c = NULL;
 660 
 661     PDATA pData;
 662     JNI_CHECK_PEER_GOTO(choice, done);
 663 
 664     c = (AwtChoice *)pData;
 665     if (::IsWindow(c->GetHWnd()))
 666     {
 667         c->SendMessage(CB_RESETCONTENT, 0, 0);
 668         c->ResetDropDownHeight();
 669         c->VerifyState();
 670     }
 671 
 672 done:
 673     env->DeleteGlobalRef(choice);
 674 }
 675 
 676 void AwtChoice::_CloseList(void *param)
 677 {
 678     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 679 
 680     jobject choice = (jobject)param;
 681 
 682     AwtChoice *c = NULL;
 683 
 684     PDATA pData;
 685     JNI_CHECK_PEER_GOTO(choice, done);
 686 
 687     c = (AwtChoice *)pData;
 688     if (::IsWindow(c->GetHWnd()) && c->SendMessage(CB_GETDROPPEDSTATE, 0, 0)) {
 689         c->SendMessage(CB_SHOWDROPDOWN, FALSE, 0);
 690     }
 691 
 692 done:
 693     env->DeleteGlobalRef(choice);
 694 }
 695 
 696 /************************************************************************
 697  * WChoicePeer native methods
 698  */
 699 
 700 extern "C" {
 701 
 702 JNIEXPORT void JNICALL
 703 Java_java_awt_Choice_initIDs(JNIEnv *env, jclass cls)
 704 {
 705     TRY;
 706     selectedIndexID = env->GetFieldID(cls, "selectedIndex", "I");
 707     DASSERT(selectedIndexID);
 708     CATCH_BAD_ALLOC;
 709 }
 710 
 711 /*
 712  * Class:     sun_awt_windows_WChoicePeer
 713  * Method:    select
 714  * Signature: (I)V
 715  */
 716 JNIEXPORT void JNICALL
 717 Java_sun_awt_windows_WChoicePeer_select(JNIEnv *env, jobject self,
 718                                         jint index)
 719 {
 720     TRY;
 721 
 722     SelectStruct *ss = new SelectStruct;
 723     ss->choice = env->NewGlobalRef(self);
 724     ss->index = index;
 725 
 726     AwtToolkit::GetInstance().SyncCall(AwtChoice::_Select, ss);
 727     // global refs and ss are removed in _Select
 728 
 729     CATCH_BAD_ALLOC;
 730 }
 731 
 732 /*
 733  * Class:     sun_awt_windows_WChoicePeer
 734  * Method:    remove
 735  * Signature: (I)V
 736  */
 737 JNIEXPORT void JNICALL
 738 Java_sun_awt_windows_WChoicePeer_remove(JNIEnv *env, jobject self,
 739                                         jint index)
 740 {
 741     TRY;
 742 
 743     RemoveStruct *rs = new RemoveStruct;
 744     rs->choice = env->NewGlobalRef(self);
 745     rs->index = index;
 746 
 747     AwtToolkit::GetInstance().SyncCall(AwtChoice::_Remove, rs);
 748     // global ref and rs are deleted in _Remove
 749 
 750     CATCH_BAD_ALLOC;
 751 }
 752 
 753 /*
 754  * Class:     sun_awt_windows_WChoicePeer
 755  * Method:    removeAll
 756  * Signature: ()V
 757  */
 758 JNIEXPORT void JNICALL
 759 Java_sun_awt_windows_WChoicePeer_removeAll(JNIEnv *env, jobject self)
 760 {
 761     TRY;
 762 
 763     jobject selfGlobalRef = env->NewGlobalRef(self);
 764 
 765     AwtToolkit::GetInstance().SyncCall(AwtChoice::_RemoveAll, (void *)selfGlobalRef);
 766     // selfGlobalRef is deleted in _RemoveAll
 767 
 768     CATCH_BAD_ALLOC;
 769 }
 770 
 771 /*
 772  * Class:     sun_awt_windows_WChoicePeer
 773  * Method:    addItems
 774  * Signature: ([Ljava/lang/String;I)V
 775  */
 776 JNIEXPORT void JNICALL
 777 Java_sun_awt_windows_WChoicePeer_addItems(JNIEnv *env, jobject self,
 778                                           jobjectArray items, jint index)
 779 {
 780     TRY;
 781 
 782     AddItemsStruct *ais = new AddItemsStruct;
 783     ais->choice = env->NewGlobalRef(self);
 784     ais->items = (jobjectArray)env->NewGlobalRef(items);
 785     ais->index = index;
 786 
 787     AwtToolkit::GetInstance().SyncCall(AwtChoice::_AddItems, ais);
 788     // global refs and ais are deleted in _AddItems
 789 
 790     CATCH_BAD_ALLOC;
 791 }
 792 
 793 /*
 794  * Class:     sun_awt_windows_WChoicePeer
 795  * Method:    reshape
 796  * Signature: (IIII)V
 797  */
 798 JNIEXPORT void JNICALL
 799 Java_sun_awt_windows_WChoicePeer_reshape(JNIEnv *env, jobject self,
 800                                          jint x, jint y,
 801                                          jint width, jint height)
 802 {
 803     TRY;
 804 
 805     ReshapeStruct *rs = new ReshapeStruct;
 806     rs->choice = env->NewGlobalRef(self);
 807     rs->x = x;
 808     rs->y = y;
 809     rs->width = width;
 810     rs->height = height;
 811 
 812     AwtToolkit::GetInstance().SyncCall(AwtChoice::_Reshape, rs);
 813     // global ref and rs are deleted in _Reshape
 814 
 815     CATCH_BAD_ALLOC;
 816 }
 817 
 818 /*
 819  * Class:     sun_awt_windows_WChoicePeer
 820  * Method:    create
 821  * Signature: (Lsun/awt/windows/WComponentPeer;)V
 822  */
 823 JNIEXPORT void JNICALL
 824 Java_sun_awt_windows_WChoicePeer_create(JNIEnv *env, jobject self,
 825                                         jobject parent)
 826 {
 827     TRY;
 828 
 829     AwtToolkit::CreateComponent(self, parent,
 830                                 (AwtToolkit::ComponentFactory)
 831                                 AwtChoice::Create);
 832 
 833     CATCH_BAD_ALLOC;
 834 }
 835 
 836 /*
 837  * Class:     sun_awt_windows_WChoicePeer
 838  * Method:    closeList
 839  * Signature: ()V
 840  */
 841 JNIEXPORT void JNICALL
 842 Java_sun_awt_windows_WChoicePeer_closeList(JNIEnv *env, jobject self)
 843 {
 844     TRY;
 845 
 846     jobject selfGlobalRef = env->NewGlobalRef(self);
 847 
 848     AwtToolkit::GetInstance().SyncCall(AwtChoice::_CloseList, (void *)selfGlobalRef);
 849     // global ref is deleted in _CloseList
 850 
 851     CATCH_BAD_ALLOC;
 852 }
 853 } /* extern "C" */
 854 
 855 
 856 /************************************************************************
 857  * Diagnostic routines
 858  */
 859 
 860 #ifdef DEBUG
 861 
 862 void AwtChoice::VerifyState()
 863 {
 864     if (AwtToolkit::GetInstance().VerifyComponents() == FALSE) {
 865         return;
 866     }
 867 
 868     if (m_callbacksEnabled == FALSE) {
 869         /* Component is not fully setup yet. */
 870         return;
 871     }
 872 
 873     AwtComponent::VerifyState();
 874     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 875     if (env->PushLocalFrame(1) < 0)
 876         return;
 877 
 878     jobject target = GetTarget(env);
 879 
 880     // To avoid possibly running client code on the toolkit thread, don't
 881     // do the following checks if we're running on the toolkit thread.
 882     if (AwtToolkit::MainThread() != ::GetCurrentThreadId()) {
 883         // Compare number of items.
 884         int nTargetItems = JNU_CallMethodByName(env, NULL, target,
 885                                                 "countItems", "()I").i;
 886         DASSERT(!safe_ExceptionOccurred(env));
 887         int nPeerItems = (int)::SendMessage(GetHWnd(), CB_GETCOUNT, 0, 0);
 888         DASSERT(nTargetItems == nPeerItems);
 889 
 890         // Compare selection
 891         int targetIndex = JNU_CallMethodByName(env, NULL, target,
 892                                                "getSelectedIndex", "()I").i;
 893         DASSERT(!safe_ExceptionOccurred(env));
 894         int peerCurSel = (int)::SendMessage(GetHWnd(), CB_GETCURSEL, 0, 0);
 895         DASSERT(targetIndex == peerCurSel);
 896     }
 897     env->PopLocalFrame(0);
 898 }
 899 #endif //DEBUG