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 
AwtChoice()90 AwtChoice::AwtChoice() {
91     m_hList = NULL;
92     m_listDefWindowProc = NULL;
93 }
94 
GetClassName()95 LPCTSTR AwtChoice::GetClassName() {
96     return TEXT("COMBOBOX");  /* System provided combobox class */
97 }
98 
Dispose()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 
Create(jobject peer,jobject parent)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
GetDropDownHeight()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
GetFieldHeight()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
GetTotalHeight()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.
ResetDropDownHeight()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 */
SetDragCapture(UINT flags)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 */
ReleaseDragCapture(UINT flags)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 
Reshape(int x,int y,int w,int h)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 
PreferredItemSize(JNIEnv * env)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 
SetFont(AwtFont * font)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 
ListWindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)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 
WmNotify(UINT notifyCode)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
OwnerDrawItem(UINT,DRAWITEMSTRUCT & drawInfo)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
OwnerMeasureItem(UINT,MEASUREITEMSTRUCT & measureInfo)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
WmKillFocus(HWND hWndGotFocus)490 AwtChoice::WmKillFocus(HWND hWndGotFocus)
491 {
492     skipNextMouseUp = TRUE;
493     return AwtComponent::WmKillFocus(hWndGotFocus);
494 }
495 
496 MsgRouting
WmMouseUp(UINT flags,int x,int y,int button)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 
HandleEvent(MSG * msg,BOOL synthetic)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 
InheritsNativeMouseWheelBehavior()523 BOOL AwtChoice::InheritsNativeMouseWheelBehavior() {return true;}
524 
_Reshape(void * param)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 
_Select(void * param)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 
_AddItems(void * param)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 
_Remove(void * param)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 
_RemoveAll(void * param)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 
_CloseList(void * param)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
Java_java_awt_Choice_initIDs(JNIEnv * env,jclass cls)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
Java_sun_awt_windows_WChoicePeer_select(JNIEnv * env,jobject self,jint index)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
Java_sun_awt_windows_WChoicePeer_remove(JNIEnv * env,jobject self,jint index)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
Java_sun_awt_windows_WChoicePeer_removeAll(JNIEnv * env,jobject self)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
Java_sun_awt_windows_WChoicePeer_addItems(JNIEnv * env,jobject self,jobjectArray items,jint index)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
Java_sun_awt_windows_WChoicePeer_reshape(JNIEnv * env,jobject self,jint x,jint y,jint width,jint height)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
Java_sun_awt_windows_WChoicePeer_create(JNIEnv * env,jobject self,jobject parent)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
Java_sun_awt_windows_WChoicePeer_closeList(JNIEnv * env,jobject self)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 
VerifyState()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
900