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