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