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