|  | /* | 
|  | * Combo controls | 
|  | * | 
|  | * Copyright 1997 Alex Korobka | 
|  | * | 
|  | * FIXME: roll up in Netscape 3.01. | 
|  | */ | 
|  |  | 
|  | #include <string.h> | 
|  |  | 
|  | #include "winbase.h" | 
|  | #include "windef.h" | 
|  | #include "wingdi.h" | 
|  | #include "winuser.h" | 
|  | #include "wine/winuser16.h" | 
|  | #include "win.h" | 
|  | #include "spy.h" | 
|  | #include "user.h" | 
|  | #include "heap.h" | 
|  | #include "combo.h" | 
|  | #include "drive.h" | 
|  | #include "debugtools.h" | 
|  | #include "tweak.h" | 
|  |  | 
|  | DEFAULT_DEBUG_CHANNEL(combo); | 
|  |  | 
|  | /* bits in the dwKeyData */ | 
|  | #define KEYDATA_ALT             0x2000 | 
|  | #define KEYDATA_PREVSTATE       0x4000 | 
|  |  | 
|  | /* | 
|  | * Additional combo box definitions | 
|  | */ | 
|  |  | 
|  | #define CB_GETPTR( wnd )      (*(LPHEADCOMBO*)((wnd)->wExtra)) | 
|  | #define CB_NOTIFY( lphc, code ) \ | 
|  | (SendMessageA( (lphc)->owner, WM_COMMAND, \ | 
|  | MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf)) | 
|  | #define CB_GETEDITTEXTLENGTH( lphc ) \ | 
|  | (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 )) | 
|  |  | 
|  | #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03) | 
|  |  | 
|  | /* | 
|  | * Drawing globals | 
|  | */ | 
|  | static HBITMAP 	hComboBmp = 0; | 
|  | static UINT	CBitHeight, CBitWidth; | 
|  |  | 
|  | /* | 
|  | * Look and feel dependant "constants" | 
|  | */ | 
|  |  | 
|  | #define COMBO_YBORDERGAP         5 | 
|  | #define COMBO_XBORDERSIZE()      ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 ) | 
|  | #define COMBO_YBORDERSIZE()      ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 ) | 
|  | #define COMBO_EDITBUTTONSPACE()  ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 ) | 
|  | #define EDIT_CONTROL_PADDING()   ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 ) | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_Init | 
|  | * | 
|  | * Load combo button bitmap. | 
|  | */ | 
|  | static BOOL COMBO_Init() | 
|  | { | 
|  | HDC		hDC; | 
|  |  | 
|  | if( hComboBmp ) return TRUE; | 
|  | if( (hDC = CreateCompatibleDC(0)) ) | 
|  | { | 
|  | BOOL	bRet = FALSE; | 
|  | if( (hComboBmp = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) ) | 
|  | { | 
|  | BITMAP      bm; | 
|  | HBITMAP     hPrevB; | 
|  | RECT        r; | 
|  |  | 
|  | GetObjectA( hComboBmp, sizeof(bm), &bm ); | 
|  | CBitHeight = bm.bmHeight; | 
|  | CBitWidth  = bm.bmWidth; | 
|  |  | 
|  | TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight ); | 
|  |  | 
|  | hPrevB = SelectObject( hDC, hComboBmp); | 
|  | SetRect( &r, 0, 0, CBitWidth, CBitHeight ); | 
|  | InvertRect( hDC, &r ); | 
|  | SelectObject( hDC, hPrevB ); | 
|  | bRet = TRUE; | 
|  | } | 
|  | DeleteDC( hDC ); | 
|  | return bRet; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_NCCreate | 
|  | */ | 
|  | static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam) | 
|  | { | 
|  | LPHEADCOMBO 		lphc; | 
|  |  | 
|  | if ( wnd && COMBO_Init() && | 
|  | (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) ) | 
|  | { | 
|  | LPCREATESTRUCTA     lpcs = (CREATESTRUCTA*)lParam; | 
|  |  | 
|  | memset( lphc, 0, sizeof(HEADCOMBO) ); | 
|  | *(LPHEADCOMBO*)wnd->wExtra = lphc; | 
|  |  | 
|  | /* some braindead apps do try to use scrollbar/border flags */ | 
|  |  | 
|  | lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL)); | 
|  | wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL); | 
|  |  | 
|  | /* | 
|  | * We also have to remove the client edge style to make sure | 
|  | * we don't end-up with a non client area. | 
|  | */ | 
|  | wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE); | 
|  |  | 
|  | if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) ) | 
|  | lphc->dwStyle |= CBS_HASSTRINGS; | 
|  | if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) ) | 
|  | lphc->wState |= CBF_NOTIFY; | 
|  |  | 
|  | TRACE("[0x%08x], style = %08x\n", | 
|  | (UINT)lphc, lphc->dwStyle ); | 
|  |  | 
|  | return (LRESULT)(UINT)wnd->hwndSelf; | 
|  | } | 
|  | return (LRESULT)FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_NCDestroy | 
|  | */ | 
|  | static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc ) | 
|  | { | 
|  |  | 
|  | if( lphc ) | 
|  | { | 
|  | WND*		wnd = lphc->self; | 
|  |  | 
|  | TRACE("[%04x]: freeing storage\n", CB_HWND(lphc)); | 
|  |  | 
|  | if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox ) | 
|  | DestroyWindow( lphc->hWndLBox ); | 
|  |  | 
|  | HeapFree( GetProcessHeap(), 0, lphc ); | 
|  | wnd->wExtra[0] = 0; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBGetTextAreaHeight | 
|  | * | 
|  | * This method will calculate the height of the text area of the | 
|  | * combobox. | 
|  | * The height of the text area is set in two ways. | 
|  | * It can be set explicitely through a combobox message or through a | 
|  | * WM_MEASUREITEM callback. | 
|  | * If this is not the case, the height is set to 13 dialog units. | 
|  | * This height was determined through experimentation. | 
|  | */ | 
|  | static INT CBGetTextAreaHeight( | 
|  | HWND        hwnd, | 
|  | LPHEADCOMBO lphc) | 
|  | { | 
|  | INT iTextItemHeight; | 
|  |  | 
|  | if( lphc->editHeight ) /* explicitly set height */ | 
|  | { | 
|  | iTextItemHeight = lphc->editHeight; | 
|  | } | 
|  | else | 
|  | { | 
|  | TEXTMETRICA tm; | 
|  | HDC         hDC       = GetDC(hwnd); | 
|  | HFONT       hPrevFont = 0; | 
|  | INT         baseUnitY; | 
|  |  | 
|  | if (lphc->hFont) | 
|  | hPrevFont = SelectObject( hDC, lphc->hFont ); | 
|  |  | 
|  | GetTextMetricsA(hDC, &tm); | 
|  |  | 
|  | baseUnitY = tm.tmHeight; | 
|  |  | 
|  | if( hPrevFont ) | 
|  | SelectObject( hDC, hPrevFont ); | 
|  |  | 
|  | ReleaseDC(hwnd, hDC); | 
|  |  | 
|  | iTextItemHeight = ((13 * baseUnitY) / 8); | 
|  |  | 
|  | /* | 
|  | * This "formula" calculates the height of the complete control. | 
|  | * To calculate the height of the text area, we have to remove the | 
|  | * borders. | 
|  | */ | 
|  | iTextItemHeight -= 2*COMBO_YBORDERSIZE(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Check the ownerdraw case if we haven't asked the parent the size | 
|  | * of the item yet. | 
|  | */ | 
|  | if ( CB_OWNERDRAWN(lphc) && | 
|  | (lphc->wState & CBF_MEASUREITEM) ) | 
|  | { | 
|  | MEASUREITEMSTRUCT measureItem; | 
|  | RECT              clientRect; | 
|  | INT               originalItemHeight = iTextItemHeight; | 
|  |  | 
|  | /* | 
|  | * We use the client rect for the width of the item. | 
|  | */ | 
|  | GetClientRect(hwnd, &clientRect); | 
|  |  | 
|  | lphc->wState &= ~CBF_MEASUREITEM; | 
|  |  | 
|  | /* | 
|  | * Send a first one to measure the size of the text area | 
|  | */ | 
|  | measureItem.CtlType    = ODT_COMBOBOX; | 
|  | measureItem.CtlID      = lphc->self->wIDmenu; | 
|  | measureItem.itemID     = -1; | 
|  | measureItem.itemWidth  = clientRect.right; | 
|  | measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */ | 
|  | measureItem.itemData   = 0; | 
|  | SendMessageA(lphc->owner, WM_MEASUREITEM, | 
|  | (WPARAM)measureItem.CtlID, (LPARAM)&measureItem); | 
|  | iTextItemHeight = 6 + measureItem.itemHeight; | 
|  |  | 
|  | /* | 
|  | * Send a second one in the case of a fixed ownerdraw list to calculate the | 
|  | * size of the list items. (we basically do this on behalf of the listbox) | 
|  | */ | 
|  | if (lphc->dwStyle & CBS_OWNERDRAWFIXED) | 
|  | { | 
|  | measureItem.CtlType    = ODT_COMBOBOX; | 
|  | measureItem.CtlID      = lphc->self->wIDmenu; | 
|  | measureItem.itemID     = 0; | 
|  | measureItem.itemWidth  = clientRect.right; | 
|  | measureItem.itemHeight = originalItemHeight; | 
|  | measureItem.itemData   = 0; | 
|  | SendMessageA(lphc->owner, WM_MEASUREITEM, | 
|  | (WPARAM)measureItem.CtlID, (LPARAM)&measureItem); | 
|  | lphc->fixedOwnerDrawHeight = measureItem.itemHeight; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Keep the size for the next time | 
|  | */ | 
|  | lphc->editHeight = iTextItemHeight; | 
|  | } | 
|  |  | 
|  | return iTextItemHeight; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBForceDummyResize | 
|  | * | 
|  | * The dummy resize is used for listboxes that have a popup to trigger | 
|  | * a re-arranging of the contents of the combobox and the recalculation | 
|  | * of the size of the "real" control window. | 
|  | */ | 
|  | static void CBForceDummyResize( | 
|  | LPHEADCOMBO lphc) | 
|  | { | 
|  | RECT windowRect; | 
|  | int newComboHeight; | 
|  |  | 
|  | newComboHeight = CBGetTextAreaHeight(CB_HWND(lphc),lphc) + 2*COMBO_YBORDERSIZE(); | 
|  |  | 
|  | GetWindowRect(CB_HWND(lphc), &windowRect); | 
|  |  | 
|  | /* | 
|  | * We have to be careful, resizing a combobox also has the meaning that the | 
|  | * dropped rect will be resized. In this case, we want to trigger a resize | 
|  | * to recalculate layout but we don't want to change the dropped rectangle | 
|  | * So, we pass the height of text area of control as the height. | 
|  | * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING | 
|  | * message. | 
|  | */ | 
|  | SetWindowPos( CB_HWND(lphc), | 
|  | (HWND)NULL, | 
|  | 0, 0, | 
|  | windowRect.right  - windowRect.left, | 
|  | newComboHeight, | 
|  | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE ); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBCalcPlacement | 
|  | * | 
|  | * Set up component coordinates given valid lphc->RectCombo. | 
|  | */ | 
|  | static void CBCalcPlacement( | 
|  | HWND        hwnd, | 
|  | LPHEADCOMBO lphc, | 
|  | LPRECT      lprEdit, | 
|  | LPRECT      lprButton, | 
|  | LPRECT      lprLB) | 
|  | { | 
|  | /* | 
|  | * Again, start with the client rectangle. | 
|  | */ | 
|  | GetClientRect(hwnd, lprEdit); | 
|  |  | 
|  | /* | 
|  | * Remove the borders | 
|  | */ | 
|  | InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE()); | 
|  |  | 
|  | /* | 
|  | * Chop off the bottom part to fit with the height of the text area. | 
|  | */ | 
|  | lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc); | 
|  |  | 
|  | /* | 
|  | * The button starts the same vertical position as the text area. | 
|  | */ | 
|  | CopyRect(lprButton, lprEdit); | 
|  |  | 
|  | /* | 
|  | * If the combobox is "simple" there is no button. | 
|  | */ | 
|  | if( CB_GETTYPE(lphc) == CBS_SIMPLE ) | 
|  | lprButton->left = lprButton->right = lprButton->bottom = 0; | 
|  | else | 
|  | { | 
|  | /* | 
|  | * Let's assume the combobox button is the same width as the | 
|  | * scrollbar button. | 
|  | * size the button horizontally and cut-off the text area. | 
|  | */ | 
|  | lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL); | 
|  | lprEdit->right  = lprButton->left; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * In the case of a dropdown, there is an additional spacing between the | 
|  | * text area and the button. | 
|  | */ | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | { | 
|  | lprEdit->right -= COMBO_EDITBUTTONSPACE(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If we have an edit control, we space it away from the borders slightly. | 
|  | */ | 
|  | if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST) | 
|  | { | 
|  | InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING()); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Adjust the size of the listbox popup. | 
|  | */ | 
|  | if( CB_GETTYPE(lphc) == CBS_SIMPLE ) | 
|  | { | 
|  | /* | 
|  | * Use the client rectangle to initialize the listbox rectangle | 
|  | */ | 
|  | GetClientRect(hwnd, lprLB); | 
|  |  | 
|  | /* | 
|  | * Then, chop-off the top part. | 
|  | */ | 
|  | lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE(); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* | 
|  | * Make sure the dropped width is as large as the combobox itself. | 
|  | */ | 
|  | if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE())) | 
|  | { | 
|  | lprLB->right  = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE()); | 
|  |  | 
|  | /* | 
|  | * In the case of a dropdown, the popup listbox is offset to the right. | 
|  | * so, we want to make sure it's flush with the right side of the | 
|  | * combobox | 
|  | */ | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | lprLB->right -= COMBO_EDITBUTTONSPACE(); | 
|  | } | 
|  | else | 
|  | lprLB->right = lprLB->left + lphc->droppedWidth; | 
|  | } | 
|  |  | 
|  | TRACE("\ttext\t= (%i,%i-%i,%i)\n", | 
|  | lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom); | 
|  |  | 
|  | TRACE("\tbutton\t= (%i,%i-%i,%i)\n", | 
|  | lprButton->left, lprButton->top, lprButton->right, lprButton->bottom); | 
|  |  | 
|  | TRACE("\tlbox\t= (%i,%i-%i,%i)\n", | 
|  | lprLB->left, lprLB->top, lprLB->right, lprLB->bottom ); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBGetDroppedControlRect | 
|  | */ | 
|  | static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect) | 
|  | { | 
|  | /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner | 
|  | of the combo box and the lower right corner of the listbox */ | 
|  |  | 
|  | GetWindowRect(lphc->self->hwndSelf, lpRect); | 
|  |  | 
|  | lpRect->right =  lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left; | 
|  | lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top; | 
|  |  | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_WindowPosChanging | 
|  | */ | 
|  | static LRESULT COMBO_WindowPosChanging( | 
|  | HWND        hwnd, | 
|  | LPHEADCOMBO lphc, | 
|  | WINDOWPOS*  posChanging) | 
|  | { | 
|  | /* | 
|  | * We need to override the WM_WINDOWPOSCHANGING method to handle all | 
|  | * the non-simple comboboxes. The problem is that those controls are | 
|  | * always the same height. We have to make sure they are not resized | 
|  | * to another value. | 
|  | */ | 
|  | if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) && | 
|  | ((posChanging->flags & SWP_NOSIZE) == 0) ) | 
|  | { | 
|  | int newComboHeight; | 
|  |  | 
|  | newComboHeight = CBGetTextAreaHeight(hwnd,lphc) + | 
|  | 2*COMBO_YBORDERSIZE(); | 
|  |  | 
|  | /* | 
|  | * Resizing a combobox has another side effect, it resizes the dropped | 
|  | * rectangle as well. However, it does it only if the new height for the | 
|  | * combobox is different than the height it should have. In other words, | 
|  | * if the application resizing the combobox only had the intention to resize | 
|  | * the actual control, for example, to do the layout of a dialog that is | 
|  | * resized, the height of the dropdown is not changed. | 
|  | */ | 
|  | if (posChanging->cy != newComboHeight) | 
|  | { | 
|  | lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight; | 
|  |  | 
|  | posChanging->cy = newComboHeight; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_Create | 
|  | */ | 
|  | static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam) | 
|  | { | 
|  | static char clbName[] = "ComboLBox"; | 
|  | static char editName[] = "Edit"; | 
|  |  | 
|  | LPCREATESTRUCTA  lpcs = (CREATESTRUCTA*)lParam; | 
|  |  | 
|  | if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE; | 
|  | if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT; | 
|  |  | 
|  | lphc->self  = wnd; | 
|  | lphc->owner = lpcs->hwndParent; | 
|  |  | 
|  | /* | 
|  | * The item height and dropped width are not set when the control | 
|  | * is created. | 
|  | */ | 
|  | lphc->droppedWidth = lphc->editHeight = 0; | 
|  |  | 
|  | /* | 
|  | * The first time we go through, we want to measure the ownerdraw item | 
|  | */ | 
|  | lphc->wState |= CBF_MEASUREITEM; | 
|  |  | 
|  | /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */ | 
|  |  | 
|  | if( lphc->owner || !(lpcs->style & WS_VISIBLE) ) | 
|  | { | 
|  | UINT lbeStyle   = 0; | 
|  | UINT lbeExStyle = 0; | 
|  |  | 
|  | /* | 
|  | * Initialize the dropped rect to the size of the client area of the | 
|  | * control and then, force all the areas of the combobox to be | 
|  | * recalculated. | 
|  | */ | 
|  | GetClientRect( wnd->hwndSelf, &lphc->droppedRect ); | 
|  |  | 
|  | CBCalcPlacement(wnd->hwndSelf, | 
|  | lphc, | 
|  | &lphc->textRect, | 
|  | &lphc->buttonRect, | 
|  | &lphc->droppedRect ); | 
|  |  | 
|  | /* | 
|  | * Adjust the position of the popup listbox if it's necessary | 
|  | */ | 
|  | if ( CB_GETTYPE(lphc) != CBS_SIMPLE ) | 
|  | { | 
|  | lphc->droppedRect.top   = lphc->textRect.bottom + COMBO_YBORDERSIZE(); | 
|  |  | 
|  | /* | 
|  | * If it's a dropdown, the listbox is offset | 
|  | */ | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | lphc->droppedRect.left += COMBO_EDITBUTTONSPACE(); | 
|  |  | 
|  | ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect); | 
|  | ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right); | 
|  | } | 
|  |  | 
|  | /* create listbox popup */ | 
|  |  | 
|  | lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) | | 
|  | (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)); | 
|  |  | 
|  | if( lphc->dwStyle & CBS_SORT ) | 
|  | lbeStyle |= LBS_SORT; | 
|  | if( lphc->dwStyle & CBS_HASSTRINGS ) | 
|  | lbeStyle |= LBS_HASSTRINGS; | 
|  | if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT ) | 
|  | lbeStyle |= LBS_NOINTEGRALHEIGHT; | 
|  | if( lphc->dwStyle & CBS_DISABLENOSCROLL ) | 
|  | lbeStyle |= LBS_DISABLENOSCROLL; | 
|  |  | 
|  | if( CB_GETTYPE(lphc) == CBS_SIMPLE ) 	/* child listbox */ | 
|  | { | 
|  | lbeStyle |= WS_VISIBLE; | 
|  |  | 
|  | /* | 
|  | * In win 95 look n feel, the listbox in the simple combobox has | 
|  | * the WS_EXCLIENTEDGE style instead of the WS_BORDER style. | 
|  | */ | 
|  | if (TWEAK_WineLook > WIN31_LOOK) | 
|  | { | 
|  | lbeStyle   &= ~WS_BORDER; | 
|  | lbeExStyle |= WS_EX_CLIENTEDGE; | 
|  | } | 
|  | } | 
|  |  | 
|  | lphc->hWndLBox = CreateWindowExA(lbeExStyle, | 
|  | clbName, | 
|  | NULL, | 
|  | lbeStyle, | 
|  | lphc->droppedRect.left, | 
|  | lphc->droppedRect.top, | 
|  | lphc->droppedRect.right - lphc->droppedRect.left, | 
|  | lphc->droppedRect.bottom - lphc->droppedRect.top, | 
|  | lphc->self->hwndSelf, | 
|  | (HMENU)ID_CB_LISTBOX, | 
|  | lphc->self->hInstance, | 
|  | (LPVOID)lphc ); | 
|  |  | 
|  | if( lphc->hWndLBox ) | 
|  | { | 
|  | BOOL	bEdit = TRUE; | 
|  | lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO; | 
|  |  | 
|  | /* | 
|  | * In Win95 look, the border fo the edit control is | 
|  | * provided by the combobox | 
|  | */ | 
|  | if (TWEAK_WineLook == WIN31_LOOK) | 
|  | lbeStyle |= WS_BORDER; | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | { | 
|  | if( lphc->dwStyle & CBS_OEMCONVERT ) | 
|  | lbeStyle |= ES_OEMCONVERT; | 
|  | if( lphc->dwStyle & CBS_AUTOHSCROLL ) | 
|  | lbeStyle |= ES_AUTOHSCROLL; | 
|  | if( lphc->dwStyle & CBS_LOWERCASE ) | 
|  | lbeStyle |= ES_LOWERCASE; | 
|  | else if( lphc->dwStyle & CBS_UPPERCASE ) | 
|  | lbeStyle |= ES_UPPERCASE; | 
|  |  | 
|  | lphc->hWndEdit = CreateWindowExA(0, | 
|  | editName, | 
|  | NULL, | 
|  | lbeStyle, | 
|  | lphc->textRect.left, lphc->textRect.top, | 
|  | lphc->textRect.right - lphc->textRect.left, | 
|  | lphc->textRect.bottom - lphc->textRect.top, | 
|  | lphc->self->hwndSelf, | 
|  | (HMENU)ID_CB_EDIT, | 
|  | lphc->self->hInstance, | 
|  | NULL ); | 
|  |  | 
|  | if( !lphc->hWndEdit ) | 
|  | bEdit = FALSE; | 
|  | } | 
|  |  | 
|  | if( bEdit ) | 
|  | { | 
|  | if( CB_GETTYPE(lphc) != CBS_SIMPLE ) | 
|  | { | 
|  | /* Now do the trick with parent */ | 
|  | SetParent(lphc->hWndLBox, HWND_DESKTOP); | 
|  | /* | 
|  | * If the combo is a dropdown, we must resize the control | 
|  | * to fit only the text area and button. To do this, | 
|  | * we send a dummy resize and the WM_WINDOWPOSCHANGING message | 
|  | * will take care of setting the height for us. | 
|  | */ | 
|  | CBForceDummyResize(lphc); | 
|  | } | 
|  |  | 
|  | TRACE("init done\n"); | 
|  | return wnd->hwndSelf; | 
|  | } | 
|  | ERR("edit control failure.\n"); | 
|  | } else ERR("listbox failure.\n"); | 
|  | } else ERR("no owner for visible combo.\n"); | 
|  |  | 
|  | /* CreateWindow() will send WM_NCDESTROY to cleanup */ | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBPaintButton | 
|  | * | 
|  | * Paint combo button (normal, pressed, and disabled states). | 
|  | */ | 
|  | static void CBPaintButton( | 
|  | LPHEADCOMBO lphc, | 
|  | HDC         hdc, | 
|  | RECT        rectButton) | 
|  | { | 
|  | if( lphc->wState & CBF_NOREDRAW ) | 
|  | return; | 
|  |  | 
|  | if (TWEAK_WineLook == WIN31_LOOK) | 
|  | { | 
|  | UINT 	  x, y; | 
|  | BOOL 	  bBool; | 
|  | HDC       hMemDC; | 
|  | HBRUSH    hPrevBrush; | 
|  | COLORREF  oldTextColor, oldBkColor; | 
|  |  | 
|  |  | 
|  | hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE)); | 
|  |  | 
|  | /* | 
|  | * Draw the button background | 
|  | */ | 
|  | PatBlt( hdc, | 
|  | rectButton.left, | 
|  | rectButton.top, | 
|  | rectButton.right-rectButton.left, | 
|  | rectButton.bottom-rectButton.top, | 
|  | PATCOPY ); | 
|  |  | 
|  | if( (bBool = lphc->wState & CBF_BUTTONDOWN) ) | 
|  | { | 
|  | DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT ); | 
|  | } | 
|  | else | 
|  | { | 
|  | DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT ); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Remove the edge of the button from the rectangle | 
|  | * and calculate the position of the bitmap. | 
|  | */ | 
|  | InflateRect( &rectButton, -2, -2); | 
|  |  | 
|  | x = (rectButton.left + rectButton.right - CBitWidth) >> 1; | 
|  | y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1; | 
|  |  | 
|  |  | 
|  | hMemDC = CreateCompatibleDC( hdc ); | 
|  | SelectObject( hMemDC, hComboBmp ); | 
|  | oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) ); | 
|  | oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) : | 
|  | RGB(0,0,0) ); | 
|  | BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY ); | 
|  | SetBkColor( hdc, oldBkColor ); | 
|  | SetTextColor( hdc, oldTextColor ); | 
|  | DeleteDC( hMemDC ); | 
|  | SelectObject( hdc, hPrevBrush ); | 
|  | } | 
|  | else | 
|  | { | 
|  | UINT buttonState = DFCS_SCROLLCOMBOBOX; | 
|  |  | 
|  | if (lphc->wState & CBF_BUTTONDOWN) | 
|  | { | 
|  | buttonState |= DFCS_PUSHED; | 
|  | } | 
|  |  | 
|  | if (CB_DISABLED(lphc)) | 
|  | { | 
|  | buttonState |= DFCS_INACTIVE; | 
|  | } | 
|  |  | 
|  | DrawFrameControl(hdc, | 
|  | &rectButton, | 
|  | DFC_SCROLL, | 
|  | buttonState); | 
|  | } | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBPaintText | 
|  | * | 
|  | * Paint CBS_DROPDOWNLIST text field / update edit control contents. | 
|  | */ | 
|  | static void CBPaintText( | 
|  | LPHEADCOMBO lphc, | 
|  | HDC         hdc, | 
|  | RECT        rectEdit) | 
|  | { | 
|  | INT	id, size = 0; | 
|  | LPSTR	pText = NULL; | 
|  |  | 
|  | if( lphc->wState & CBF_NOREDRAW ) return; | 
|  |  | 
|  | /* follow Windows combobox that sends a bunch of text | 
|  | * inquiries to its listbox while processing WM_PAINT. */ | 
|  |  | 
|  | if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR ) | 
|  | { | 
|  | size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0); | 
|  | if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) ) | 
|  | { | 
|  | SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText ); | 
|  | pText[size] = '\0';	/* just in case */ | 
|  | } else return; | 
|  | } | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | { | 
|  | if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" ); | 
|  | if( lphc->wState & CBF_FOCUSED ) | 
|  | SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1)); | 
|  | } | 
|  | else /* paint text field ourselves */ | 
|  | { | 
|  | UINT	itemState; | 
|  | HFONT	hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0; | 
|  |  | 
|  | /* | 
|  | * Give ourselves some space. | 
|  | */ | 
|  | InflateRect( &rectEdit, -1, -1 ); | 
|  |  | 
|  | if ( (lphc->wState & CBF_FOCUSED) && | 
|  | !(lphc->wState & CBF_DROPPED) ) | 
|  | { | 
|  | /* highlight */ | 
|  |  | 
|  | FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) ); | 
|  | SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) ); | 
|  | SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) ); | 
|  | itemState = ODS_SELECTED | ODS_FOCUS; | 
|  | } | 
|  | else | 
|  | itemState = 0; | 
|  |  | 
|  | if( CB_OWNERDRAWN(lphc) ) | 
|  | { | 
|  | DRAWITEMSTRUCT dis; | 
|  | HRGN           clipRegion; | 
|  |  | 
|  | /* | 
|  | * Save the current clip region. | 
|  | * To retrieve the clip region, we need to create one "dummy" | 
|  | * clip region. | 
|  | */ | 
|  | clipRegion = CreateRectRgnIndirect(&rectEdit); | 
|  |  | 
|  | if (GetClipRgn(hdc, clipRegion)!=1) | 
|  | { | 
|  | DeleteObject(clipRegion); | 
|  | clipRegion=(HRGN)NULL; | 
|  | } | 
|  |  | 
|  | if ( lphc->self->dwStyle & WS_DISABLED ) | 
|  | itemState |= ODS_DISABLED; | 
|  |  | 
|  | dis.CtlType	= ODT_COMBOBOX; | 
|  | dis.CtlID	= lphc->self->wIDmenu; | 
|  | dis.hwndItem	= lphc->self->hwndSelf; | 
|  | dis.itemAction	= ODA_DRAWENTIRE; | 
|  | dis.itemID	= id; | 
|  | dis.itemState	= itemState; | 
|  | dis.hDC		= hdc; | 
|  | dis.rcItem	= rectEdit; | 
|  | dis.itemData	= SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, | 
|  | (WPARAM)id, 0 ); | 
|  |  | 
|  | /* | 
|  | * Clip the DC and have the parent draw the item. | 
|  | */ | 
|  | IntersectClipRect(hdc, | 
|  | rectEdit.left,  rectEdit.top, | 
|  | rectEdit.right, rectEdit.bottom); | 
|  |  | 
|  | SendMessageA(lphc->owner, WM_DRAWITEM, | 
|  | lphc->self->wIDmenu, (LPARAM)&dis ); | 
|  |  | 
|  | /* | 
|  | * Reset the clipping region. | 
|  | */ | 
|  | SelectClipRgn(hdc, clipRegion); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExtTextOutA( hdc, | 
|  | rectEdit.left + 1, | 
|  | rectEdit.top + 1, | 
|  | ETO_OPAQUE | ETO_CLIPPED, | 
|  | &rectEdit, | 
|  | pText ? pText : "" , size, NULL ); | 
|  |  | 
|  | if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED)) | 
|  | DrawFocusRect( hdc, &rectEdit ); | 
|  | } | 
|  |  | 
|  | if( hPrevFont ) | 
|  | SelectObject(hdc, hPrevFont ); | 
|  | } | 
|  | if (pText) | 
|  | HeapFree( GetProcessHeap(), 0, pText ); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBPaintBorder | 
|  | */ | 
|  | static void CBPaintBorder( | 
|  | HWND        hwnd, | 
|  | LPHEADCOMBO lphc, | 
|  | HDC         hdc) | 
|  | { | 
|  | RECT clientRect; | 
|  |  | 
|  | if (CB_GETTYPE(lphc) != CBS_SIMPLE) | 
|  | { | 
|  | GetClientRect(hwnd, &clientRect); | 
|  | } | 
|  | else | 
|  | { | 
|  | CopyRect(&clientRect, &lphc->textRect); | 
|  |  | 
|  | InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING()); | 
|  | InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE()); | 
|  | } | 
|  |  | 
|  | DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_PrepareColors | 
|  | * | 
|  | * This method will sent the appropriate WM_CTLCOLOR message to | 
|  | * prepare and setup the colors for the combo's DC. | 
|  | * | 
|  | * It also returns the brush to use for the background. | 
|  | */ | 
|  | static HBRUSH COMBO_PrepareColors( | 
|  | HWND        hwnd, | 
|  | LPHEADCOMBO lphc, | 
|  | HDC         hDC) | 
|  | { | 
|  | HBRUSH  hBkgBrush; | 
|  |  | 
|  | /* | 
|  | * Get the background brush for this control. | 
|  | */ | 
|  | if (CB_DISABLED(lphc)) | 
|  | { | 
|  | hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORSTATIC, | 
|  | hDC, lphc->self->hwndSelf ); | 
|  |  | 
|  | /* | 
|  | * We have to change the text color since WM_CTLCOLORSTATIC will | 
|  | * set it to the "enabled" color. This is the same behavior as the | 
|  | * edit control | 
|  | */ | 
|  | SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT)); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (lphc->wState & CBF_EDIT) | 
|  | { | 
|  | hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLOREDIT, | 
|  | hDC, lphc->self->hwndSelf ); | 
|  | } | 
|  | else | 
|  | { | 
|  | hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX, | 
|  | hDC, lphc->self->hwndSelf ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Catch errors. | 
|  | */ | 
|  | if( !hBkgBrush ) | 
|  | hBkgBrush = GetSysColorBrush(COLOR_WINDOW); | 
|  |  | 
|  | return hBkgBrush; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_EraseBackground | 
|  | */ | 
|  | static LRESULT COMBO_EraseBackground( | 
|  | HWND        hwnd, | 
|  | LPHEADCOMBO lphc, | 
|  | HDC         hParamDC) | 
|  | { | 
|  | HBRUSH  hBkgBrush; | 
|  | RECT    clientRect; | 
|  | HDC 	  hDC; | 
|  |  | 
|  | hDC = (hParamDC) ? hParamDC | 
|  | : GetDC(hwnd); | 
|  |  | 
|  | /* | 
|  | * Calculate the area that we want to erase. | 
|  | */ | 
|  | if (CB_GETTYPE(lphc) != CBS_SIMPLE) | 
|  | { | 
|  | GetClientRect(hwnd, &clientRect); | 
|  | } | 
|  | else | 
|  | { | 
|  | CopyRect(&clientRect, &lphc->textRect); | 
|  |  | 
|  | InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE()); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Retrieve the background brush | 
|  | */ | 
|  | hBkgBrush = COMBO_PrepareColors(hwnd, lphc, hDC); | 
|  |  | 
|  | FillRect(hDC, &clientRect, hBkgBrush); | 
|  |  | 
|  | if (!hParamDC) | 
|  | ReleaseDC(hwnd, hDC); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_Paint | 
|  | */ | 
|  | static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC) | 
|  | { | 
|  | PAINTSTRUCT ps; | 
|  | HDC 	hDC; | 
|  |  | 
|  | hDC = (hParamDC) ? hParamDC | 
|  | : BeginPaint( lphc->self->hwndSelf, &ps); | 
|  |  | 
|  |  | 
|  | if( hDC && !(lphc->wState & CBF_NOREDRAW) ) | 
|  | { | 
|  | HBRUSH	hPrevBrush, hBkgBrush; | 
|  |  | 
|  | /* | 
|  | * Retrieve the background brush and select it in the | 
|  | * DC. | 
|  | */ | 
|  | hBkgBrush = COMBO_PrepareColors(lphc->self->hwndSelf, lphc, hDC); | 
|  |  | 
|  | hPrevBrush = SelectObject( hDC, hBkgBrush ); | 
|  |  | 
|  | /* | 
|  | * In non 3.1 look, there is a sunken border on the combobox | 
|  | */ | 
|  | if (TWEAK_WineLook != WIN31_LOOK) | 
|  | { | 
|  | CBPaintBorder(CB_HWND(lphc), lphc, hDC); | 
|  | } | 
|  |  | 
|  | if( !IsRectEmpty(&lphc->buttonRect) ) | 
|  | { | 
|  | CBPaintButton(lphc, hDC, lphc->buttonRect); | 
|  | } | 
|  |  | 
|  | if( !(lphc->wState & CBF_EDIT) ) | 
|  | { | 
|  | /* | 
|  | * The text area has a border only in Win 3.1 look. | 
|  | */ | 
|  | if (TWEAK_WineLook == WIN31_LOOK) | 
|  | { | 
|  | HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) ); | 
|  |  | 
|  | Rectangle( hDC, | 
|  | lphc->textRect.left, lphc->textRect.top, | 
|  | lphc->textRect.right - 1, lphc->textRect.bottom - 1); | 
|  |  | 
|  | SelectObject( hDC, hPrevPen ); | 
|  | } | 
|  |  | 
|  | CBPaintText( lphc, hDC, lphc->textRect); | 
|  | } | 
|  |  | 
|  | if( hPrevBrush ) | 
|  | SelectObject( hDC, hPrevBrush ); | 
|  | } | 
|  |  | 
|  | if( !hParamDC ) | 
|  | EndPaint(lphc->self->hwndSelf, &ps); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBUpdateLBox | 
|  | * | 
|  | * Select listbox entry according to the contents of the edit control. | 
|  | */ | 
|  | static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect ) | 
|  | { | 
|  | INT	length, idx; | 
|  | LPSTR	pText = NULL; | 
|  |  | 
|  | idx = LB_ERR; | 
|  | length = CB_GETEDITTEXTLENGTH( lphc ); | 
|  |  | 
|  | if( length > 0 ) | 
|  | pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1); | 
|  |  | 
|  | TRACE("\t edit text length %i\n", length ); | 
|  |  | 
|  | if( pText ) | 
|  | { | 
|  | if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1); | 
|  | else pText[0] = '\0'; | 
|  | idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING, | 
|  | (WPARAM)(-1), (LPARAM)pText ); | 
|  | HeapFree( GetProcessHeap(), 0, pText ); | 
|  | } | 
|  |  | 
|  | if (idx >= 0) | 
|  | { | 
|  | SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0 ); | 
|  |  | 
|  | /* probably superfluous but Windows sends this too */ | 
|  | SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0 ); | 
|  | SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0 ); | 
|  | } | 
|  | return idx; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBUpdateEdit | 
|  | * | 
|  | * Copy a listbox entry to the edit control. | 
|  | */ | 
|  | static void CBUpdateEdit( LPHEADCOMBO lphc , INT index ) | 
|  | { | 
|  | INT	length; | 
|  | LPSTR	pText = NULL; | 
|  |  | 
|  | TRACE("\t %i\n", index ); | 
|  |  | 
|  | if( index >= 0 ) /* got an entry */ | 
|  | { | 
|  | length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0); | 
|  | if( length ) | 
|  | { | 
|  | if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) ) | 
|  | { | 
|  | SendMessageA( lphc->hWndLBox, LB_GETTEXT, | 
|  | (WPARAM)index, (LPARAM)pText ); | 
|  | } | 
|  | } | 
|  |  | 
|  | lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT); | 
|  | SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)"" ); | 
|  | lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT); | 
|  | } | 
|  | else | 
|  | InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE); | 
|  |  | 
|  | if( lphc->wState & CBF_FOCUSED ) | 
|  | SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) ); | 
|  |  | 
|  | if( pText ) | 
|  | HeapFree( GetProcessHeap(), 0, pText ); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBDropDown | 
|  | * | 
|  | * Show listbox popup. | 
|  | */ | 
|  | static void CBDropDown( LPHEADCOMBO lphc ) | 
|  | { | 
|  | RECT rect,r; | 
|  | int nItems = 0; | 
|  | int nDroppedHeight; | 
|  |  | 
|  | TRACE("[%04x]: drop down\n", CB_HWND(lphc)); | 
|  |  | 
|  | CB_NOTIFY( lphc, CBN_DROPDOWN ); | 
|  |  | 
|  | /* set selection */ | 
|  |  | 
|  | lphc->wState |= CBF_DROPPED; | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | { | 
|  | lphc->droppedIndex = CBUpdateLBox( lphc, TRUE ); | 
|  |  | 
|  | if( !(lphc->wState & CBF_CAPTURE) ) | 
|  | CBUpdateEdit( lphc, lphc->droppedIndex ); | 
|  | } | 
|  | else | 
|  | { | 
|  | lphc->droppedIndex = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 ); | 
|  |  | 
|  | SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, | 
|  | (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 ); | 
|  | SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 ); | 
|  | } | 
|  |  | 
|  | /* now set popup position */ | 
|  | GetWindowRect( lphc->self->hwndSelf, &rect ); | 
|  |  | 
|  | /* | 
|  | * If it's a dropdown, the listbox is offset | 
|  | */ | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | rect.left += COMBO_EDITBUTTONSPACE(); | 
|  |  | 
|  | /* if the dropped height is greater than the total height of the dropped | 
|  | items list, then force the drop down list height to be the total height | 
|  | of the items in the dropped list */ | 
|  |  | 
|  | /* And Remove any extra space (Best Fit) */ | 
|  | nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top; | 
|  | /* if listbox length has been set directly by its handle */ | 
|  | GetWindowRect(lphc->hWndLBox, &r); | 
|  | if (nDroppedHeight < r.bottom - r.top) | 
|  | nDroppedHeight = r.bottom - r.top; | 
|  | nItems = (int)SendMessageA (lphc->hWndLBox, LB_GETCOUNT, 0, 0); | 
|  |  | 
|  | if (nItems > 0) | 
|  | { | 
|  | int nHeight; | 
|  |  | 
|  | nHeight = (int)SendMessageA (lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0); | 
|  | nHeight *= nItems; | 
|  |  | 
|  | if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE()) | 
|  | nDroppedHeight = nHeight + COMBO_YBORDERSIZE(); | 
|  | } | 
|  |  | 
|  | /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/ | 
|  | if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) ) | 
|  | rect.bottom = rect.top - nDroppedHeight; | 
|  |  | 
|  | SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom, | 
|  | lphc->droppedRect.right - lphc->droppedRect.left, | 
|  | nDroppedHeight, | 
|  | SWP_NOACTIVATE | SWP_SHOWWINDOW); | 
|  |  | 
|  |  | 
|  | if( !(lphc->wState & CBF_NOREDRAW) ) | 
|  | RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE | | 
|  | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN ); | 
|  |  | 
|  | EnableWindow( lphc->hWndLBox, TRUE ); | 
|  | if (GetCapture() != lphc->self->hwndSelf) | 
|  | SetCapture(lphc->hWndLBox); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBRollUp | 
|  | * | 
|  | * Hide listbox popup. | 
|  | */ | 
|  | static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton ) | 
|  | { | 
|  | HWND	hWnd = lphc->self->hwndSelf; | 
|  |  | 
|  | CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL ); | 
|  |  | 
|  | if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE ) | 
|  | { | 
|  |  | 
|  | TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok ); | 
|  |  | 
|  | if( lphc->wState & CBF_DROPPED ) | 
|  | { | 
|  | RECT	rect; | 
|  |  | 
|  | lphc->wState &= ~CBF_DROPPED; | 
|  | ShowWindow( lphc->hWndLBox, SW_HIDE ); | 
|  | if(GetCapture() == lphc->hWndLBox) | 
|  | { | 
|  | ReleaseCapture(); | 
|  | } | 
|  |  | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | { | 
|  | rect = lphc->buttonRect; | 
|  | } | 
|  | else | 
|  | { | 
|  | if( bButton ) | 
|  | { | 
|  | UnionRect( &rect, | 
|  | &lphc->buttonRect, | 
|  | &lphc->textRect); | 
|  | } | 
|  | else | 
|  | rect = lphc->textRect; | 
|  |  | 
|  | bButton = TRUE; | 
|  | } | 
|  |  | 
|  | if( bButton && !(lphc->wState & CBF_NOREDRAW) ) | 
|  | RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | | 
|  | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN ); | 
|  | CB_NOTIFY( lphc, CBN_CLOSEUP ); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_FlipListbox | 
|  | * | 
|  | * Used by the ComboLBox to show/hide itself in response to VK_F4, etc... | 
|  | */ | 
|  | BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton ) | 
|  | { | 
|  | if( lphc->wState & CBF_DROPPED ) | 
|  | { | 
|  | CBRollUp( lphc, ok, bRedrawButton ); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | CBDropDown( lphc ); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBRepaintButton | 
|  | */ | 
|  | static void CBRepaintButton( LPHEADCOMBO lphc ) | 
|  | { | 
|  | InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE); | 
|  | UpdateWindow(CB_HWND(lphc)); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_SetFocus | 
|  | */ | 
|  | static void COMBO_SetFocus( LPHEADCOMBO lphc ) | 
|  | { | 
|  | if( !(lphc->wState & CBF_FOCUSED) ) | 
|  | { | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) | 
|  | SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 ); | 
|  |  | 
|  | lphc->wState |= CBF_FOCUSED; | 
|  |  | 
|  | if( !(lphc->wState & CBF_EDIT) ) | 
|  | InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE); | 
|  |  | 
|  | CB_NOTIFY( lphc, CBN_SETFOCUS ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_KillFocus | 
|  | */ | 
|  | static void COMBO_KillFocus( LPHEADCOMBO lphc ) | 
|  | { | 
|  | HWND	hWnd = lphc->self->hwndSelf; | 
|  |  | 
|  | if( lphc->wState & CBF_FOCUSED ) | 
|  | { | 
|  | CBRollUp( lphc, FALSE, TRUE ); | 
|  | if( IsWindow( hWnd ) ) | 
|  | { | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) | 
|  | SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 ); | 
|  |  | 
|  | lphc->wState &= ~CBF_FOCUSED; | 
|  |  | 
|  | /* redraw text */ | 
|  | if( !(lphc->wState & CBF_EDIT) ) | 
|  | InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE); | 
|  |  | 
|  | CB_NOTIFY( lphc, CBN_KILLFOCUS ); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_Command | 
|  | */ | 
|  | static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd ) | 
|  | { | 
|  | if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd ) | 
|  | { | 
|  | /* ">> 8" makes gcc generate jump-table instead of cmp ladder */ | 
|  |  | 
|  | switch( HIWORD(wParam) >> 8 ) | 
|  | { | 
|  | case (EN_SETFOCUS >> 8): | 
|  |  | 
|  | TRACE("[%04x]: edit [%04x] got focus\n", | 
|  | CB_HWND(lphc), lphc->hWndEdit ); | 
|  |  | 
|  | COMBO_SetFocus( lphc ); | 
|  | break; | 
|  |  | 
|  | case (EN_KILLFOCUS >> 8): | 
|  |  | 
|  | TRACE("[%04x]: edit [%04x] lost focus\n", | 
|  | CB_HWND(lphc), lphc->hWndEdit ); | 
|  |  | 
|  | /* NOTE: it seems that Windows' edit control sends an | 
|  | * undocumented message WM_USER + 0x1B instead of this | 
|  | * notification (only when it happens to be a part of | 
|  | * the combo). ?? - AK. | 
|  | */ | 
|  |  | 
|  | COMBO_KillFocus( lphc ); | 
|  | break; | 
|  |  | 
|  |  | 
|  | case (EN_CHANGE >> 8): | 
|  | /* | 
|  | * In some circumstances (when the selection of the combobox | 
|  | * is changed for example) we don't wans the EN_CHANGE notification | 
|  | * to be forwarded to the parent of the combobox. This code | 
|  | * checks a flag that is set in these occasions and ignores the | 
|  | * notification. | 
|  | */ | 
|  | if (!(lphc->wState & CBF_NOEDITNOTIFY)) | 
|  | CB_NOTIFY( lphc, CBN_EDITCHANGE ); | 
|  |  | 
|  | if (lphc->wState & CBF_NOLBSELECT) | 
|  | { | 
|  | lphc->wState &= ~CBF_NOLBSELECT; | 
|  | } | 
|  | else | 
|  | { | 
|  | CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED ); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case (EN_UPDATE >> 8): | 
|  | if (!(lphc->wState & CBF_NOEDITNOTIFY)) | 
|  | CB_NOTIFY( lphc, CBN_EDITUPDATE ); | 
|  | break; | 
|  |  | 
|  | case (EN_ERRSPACE >> 8): | 
|  | CB_NOTIFY( lphc, CBN_ERRSPACE ); | 
|  | } | 
|  | } | 
|  | else if( lphc->hWndLBox == hWnd ) | 
|  | { | 
|  | switch( HIWORD(wParam) ) | 
|  | { | 
|  | case LBN_ERRSPACE: | 
|  | CB_NOTIFY( lphc, CBN_ERRSPACE ); | 
|  | break; | 
|  |  | 
|  | case LBN_DBLCLK: | 
|  | CB_NOTIFY( lphc, CBN_DBLCLK ); | 
|  | break; | 
|  |  | 
|  | case LBN_SELCHANGE: | 
|  | case LBN_SELCANCEL: | 
|  |  | 
|  | TRACE("[%04x]: lbox selection change [%04x]\n", | 
|  | CB_HWND(lphc), lphc->wState ); | 
|  |  | 
|  | if( HIWORD(wParam) == LBN_SELCHANGE) | 
|  | { | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | { | 
|  | INT index = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0); | 
|  | lphc->wState |= CBF_NOLBSELECT; | 
|  | CBUpdateEdit( lphc, index ); | 
|  | /* select text in edit, as Windows does */ | 
|  | SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) ); | 
|  | } | 
|  | else | 
|  | InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE); | 
|  | } | 
|  |  | 
|  | /* do not roll up if selection is being tracked | 
|  | * by arrowkeys in the dropdown listbox */ | 
|  |  | 
|  | if( (lphc->dwStyle & CBS_SIMPLE) || | 
|  | ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) ) | 
|  | { | 
|  | CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE ); | 
|  | } | 
|  | else lphc->wState &= ~CBF_NOROLLUP; | 
|  |  | 
|  | CB_NOTIFY( lphc, CBN_SELCHANGE ); | 
|  |  | 
|  | /* fall through */ | 
|  |  | 
|  | case LBN_SETFOCUS: | 
|  | case LBN_KILLFOCUS: | 
|  | /* nothing to do here since ComboLBox always resets the focus to its | 
|  | * combo/edit counterpart */ | 
|  | break; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_ItemOp | 
|  | * | 
|  | * Fixup an ownerdrawn item operation and pass it up to the combobox owner. | 
|  | */ | 
|  | static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, | 
|  | WPARAM wParam, LPARAM lParam ) | 
|  | { | 
|  | HWND	hWnd = lphc->self->hwndSelf; | 
|  |  | 
|  | TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg ); | 
|  |  | 
|  | #define lpIS    ((LPDELETEITEMSTRUCT)lParam) | 
|  |  | 
|  | /* two first items are the same in all 4 structs */ | 
|  | lpIS->CtlType = ODT_COMBOBOX; | 
|  | lpIS->CtlID   = lphc->self->wIDmenu; | 
|  |  | 
|  | switch( msg )	/* patch window handle */ | 
|  | { | 
|  | case WM_DELETEITEM: | 
|  | lpIS->hwndItem = hWnd; | 
|  | #undef  lpIS | 
|  | break; | 
|  | case WM_DRAWITEM: | 
|  | #define lpIS    ((LPDRAWITEMSTRUCT)lParam) | 
|  | lpIS->hwndItem = hWnd; | 
|  | #undef  lpIS | 
|  | break; | 
|  | case WM_COMPAREITEM: | 
|  | #define lpIS    ((LPCOMPAREITEMSTRUCT)lParam) | 
|  | lpIS->hwndItem = hWnd; | 
|  | #undef  lpIS | 
|  | break; | 
|  | } | 
|  |  | 
|  | return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam ); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_GetText | 
|  | */ | 
|  | static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText) | 
|  | { | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | return SendMessageA( lphc->hWndEdit, WM_GETTEXT, | 
|  | (WPARAM)N, (LPARAM)lpText ); | 
|  |  | 
|  | /* get it from the listbox */ | 
|  |  | 
|  | if( lphc->hWndLBox ) | 
|  | { | 
|  | INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 ); | 
|  | if( idx != LB_ERR ) | 
|  | { | 
|  | LPSTR	lpBuffer; | 
|  | INT	length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, | 
|  | (WPARAM)idx, 0 ); | 
|  |  | 
|  | /* 'length' is without the terminating character */ | 
|  | if( length >= N ) | 
|  | lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 ); | 
|  | else | 
|  | lpBuffer = lpText; | 
|  |  | 
|  | if( lpBuffer ) | 
|  | { | 
|  | INT    n = SendMessageA( lphc->hWndLBox, LB_GETTEXT, | 
|  | (WPARAM)idx, (LPARAM)lpBuffer ); | 
|  |  | 
|  | /* truncate if buffer is too short */ | 
|  |  | 
|  | if( length >= N ) | 
|  | { | 
|  | if (N && lpText) { | 
|  | if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 ); | 
|  | lpText[N - 1] = '\0'; | 
|  | } | 
|  | HeapFree( GetProcessHeap(), 0, lpBuffer ); | 
|  | } | 
|  | return (LRESULT)n; | 
|  | } | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBResetPos | 
|  | * | 
|  | * This function sets window positions according to the updated | 
|  | * component placement struct. | 
|  | */ | 
|  | static void CBResetPos( | 
|  | LPHEADCOMBO lphc, | 
|  | LPRECT      rectEdit, | 
|  | LPRECT      rectLB, | 
|  | BOOL        bRedraw) | 
|  | { | 
|  | BOOL	bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE); | 
|  |  | 
|  | /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of | 
|  | * sizing messages */ | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | SetWindowPos( lphc->hWndEdit, 0, | 
|  | rectEdit->left, rectEdit->top, | 
|  | rectEdit->right - rectEdit->left, | 
|  | rectEdit->bottom - rectEdit->top, | 
|  | SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) ); | 
|  |  | 
|  | SetWindowPos( lphc->hWndLBox, 0, | 
|  | rectLB->left, rectLB->top, | 
|  | rectLB->right - rectLB->left, | 
|  | rectLB->bottom - rectLB->top, | 
|  | SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) ); | 
|  |  | 
|  | if( bDrop ) | 
|  | { | 
|  | if( lphc->wState & CBF_DROPPED ) | 
|  | { | 
|  | lphc->wState &= ~CBF_DROPPED; | 
|  | ShowWindow( lphc->hWndLBox, SW_HIDE ); | 
|  | } | 
|  |  | 
|  | if( bRedraw && !(lphc->wState & CBF_NOREDRAW) ) | 
|  | RedrawWindow( lphc->self->hwndSelf, NULL, 0, | 
|  | RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW ); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_Size | 
|  | */ | 
|  | static void COMBO_Size( LPHEADCOMBO lphc ) | 
|  | { | 
|  | CBCalcPlacement(lphc->self->hwndSelf, | 
|  | lphc, | 
|  | &lphc->textRect, | 
|  | &lphc->buttonRect, | 
|  | &lphc->droppedRect); | 
|  |  | 
|  | CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_Font | 
|  | */ | 
|  | static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw ) | 
|  | { | 
|  | /* | 
|  | * Set the font | 
|  | */ | 
|  | lphc->hFont = hFont; | 
|  |  | 
|  | /* | 
|  | * Propagate to owned windows. | 
|  | */ | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw ); | 
|  | SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw ); | 
|  |  | 
|  | /* | 
|  | * Redo the layout of the control. | 
|  | */ | 
|  | if ( CB_GETTYPE(lphc) == CBS_SIMPLE) | 
|  | { | 
|  | CBCalcPlacement(lphc->self->hwndSelf, | 
|  | lphc, | 
|  | &lphc->textRect, | 
|  | &lphc->buttonRect, | 
|  | &lphc->droppedRect); | 
|  |  | 
|  | CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); | 
|  | } | 
|  | else | 
|  | { | 
|  | CBForceDummyResize(lphc); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_SetItemHeight | 
|  | */ | 
|  | static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height ) | 
|  | { | 
|  | LRESULT	lRet = CB_ERR; | 
|  |  | 
|  | if( index == -1 ) /* set text field height */ | 
|  | { | 
|  | if( height < 32768 ) | 
|  | { | 
|  | lphc->editHeight = height; | 
|  |  | 
|  | /* | 
|  | * Redo the layout of the control. | 
|  | */ | 
|  | if ( CB_GETTYPE(lphc) == CBS_SIMPLE) | 
|  | { | 
|  | CBCalcPlacement(lphc->self->hwndSelf, | 
|  | lphc, | 
|  | &lphc->textRect, | 
|  | &lphc->buttonRect, | 
|  | &lphc->droppedRect); | 
|  |  | 
|  | CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); | 
|  | } | 
|  | else | 
|  | { | 
|  | CBForceDummyResize(lphc); | 
|  | } | 
|  |  | 
|  | lRet = height; | 
|  | } | 
|  | } | 
|  | else if ( CB_OWNERDRAWN(lphc) )	/* set listbox item height */ | 
|  | lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT, | 
|  | (WPARAM)index, (LPARAM)height ); | 
|  | return lRet; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_SelectString | 
|  | */ | 
|  | static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText ) | 
|  | { | 
|  | INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING, | 
|  | (WPARAM)start, (LPARAM)pText ); | 
|  | if( index >= 0 ) | 
|  | { | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | CBUpdateEdit( lphc, index ); | 
|  | else | 
|  | { | 
|  | InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE); | 
|  | } | 
|  | } | 
|  | return (LRESULT)index; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_LButtonDown | 
|  | */ | 
|  | static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam ) | 
|  | { | 
|  | POINT     pt; | 
|  | BOOL      bButton; | 
|  | HWND      hWnd = lphc->self->hwndSelf; | 
|  |  | 
|  | pt.x = LOWORD(lParam); | 
|  | pt.y = HIWORD(lParam); | 
|  | bButton = PtInRect(&lphc->buttonRect, pt); | 
|  |  | 
|  | if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) || | 
|  | (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) ) | 
|  | { | 
|  | lphc->wState |= CBF_BUTTONDOWN; | 
|  | if( lphc->wState & CBF_DROPPED ) | 
|  | { | 
|  | /* got a click to cancel selection */ | 
|  |  | 
|  | lphc->wState &= ~CBF_BUTTONDOWN; | 
|  | CBRollUp( lphc, TRUE, FALSE ); | 
|  | if( !IsWindow( hWnd ) ) return; | 
|  |  | 
|  | if( lphc->wState & CBF_CAPTURE ) | 
|  | { | 
|  | lphc->wState &= ~CBF_CAPTURE; | 
|  | ReleaseCapture(); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | /* drop down the listbox and start tracking */ | 
|  |  | 
|  | lphc->wState |= CBF_CAPTURE; | 
|  | SetCapture( hWnd ); | 
|  | CBDropDown( lphc ); | 
|  | } | 
|  | if( bButton ) CBRepaintButton( lphc ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_LButtonUp | 
|  | * | 
|  | * Release capture and stop tracking if needed. | 
|  | */ | 
|  | static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam ) | 
|  | { | 
|  | if( lphc->wState & CBF_CAPTURE ) | 
|  | { | 
|  | lphc->wState &= ~CBF_CAPTURE; | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | { | 
|  | INT index = CBUpdateLBox( lphc, TRUE ); | 
|  | lphc->wState |= CBF_NOLBSELECT; | 
|  | CBUpdateEdit( lphc, index ); | 
|  | lphc->wState &= ~CBF_NOLBSELECT; | 
|  | } | 
|  | ReleaseCapture(); | 
|  | SetCapture(lphc->hWndLBox); | 
|  |  | 
|  | } | 
|  |  | 
|  | if( lphc->wState & CBF_BUTTONDOWN ) | 
|  | { | 
|  | lphc->wState &= ~CBF_BUTTONDOWN; | 
|  | CBRepaintButton( lphc ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_MouseMove | 
|  | * | 
|  | * Two things to do - track combo button and release capture when | 
|  | * pointer goes into the listbox. | 
|  | */ | 
|  | static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam ) | 
|  | { | 
|  | POINT  pt; | 
|  | RECT   lbRect; | 
|  |  | 
|  | pt.x = LOWORD(lParam); | 
|  | pt.y = HIWORD(lParam); | 
|  |  | 
|  | if( lphc->wState & CBF_BUTTONDOWN ) | 
|  | { | 
|  | BOOL bButton; | 
|  |  | 
|  | bButton = PtInRect(&lphc->buttonRect, pt); | 
|  |  | 
|  | if( !bButton ) | 
|  | { | 
|  | lphc->wState &= ~CBF_BUTTONDOWN; | 
|  | CBRepaintButton( lphc ); | 
|  | } | 
|  | } | 
|  |  | 
|  | GetClientRect( lphc->hWndLBox, &lbRect ); | 
|  | MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 ); | 
|  | if( PtInRect(&lbRect, pt) ) | 
|  | { | 
|  | lphc->wState &= ~CBF_CAPTURE; | 
|  | ReleaseCapture(); | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE ); | 
|  |  | 
|  | /* hand over pointer tracking */ | 
|  | SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam ); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           ComboWndProc_locked | 
|  | * | 
|  | * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm | 
|  | */ | 
|  | static inline LRESULT WINAPI ComboWndProc_locked( WND* pWnd, UINT message, | 
|  | WPARAM wParam, LPARAM lParam ) | 
|  | { | 
|  | if( pWnd ) { | 
|  | LPHEADCOMBO	lphc = CB_GETPTR(pWnd); | 
|  | HWND		hwnd = pWnd->hwndSelf; | 
|  |  | 
|  | TRACE("[%04x]: msg %s wp %08x lp %08lx\n", | 
|  | pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam ); | 
|  |  | 
|  | if( lphc || message == WM_NCCREATE ) | 
|  | switch(message) | 
|  | { | 
|  |  | 
|  | /* System messages */ | 
|  |  | 
|  | case WM_NCCREATE: | 
|  | return COMBO_NCCreate(pWnd, lParam); | 
|  | case WM_NCDESTROY: | 
|  | COMBO_NCDestroy(lphc); | 
|  | break;/* -> DefWindowProc */ | 
|  |  | 
|  | case WM_CREATE: | 
|  | return COMBO_Create(lphc, pWnd, lParam); | 
|  |  | 
|  | case WM_PRINTCLIENT: | 
|  | if (lParam & PRF_ERASEBKGND) | 
|  | COMBO_EraseBackground(hwnd, lphc, wParam); | 
|  |  | 
|  | /* Fallthrough */ | 
|  | case WM_PAINT: | 
|  | /* wParam may contain a valid HDC! */ | 
|  | return  COMBO_Paint(lphc, wParam); | 
|  | case WM_ERASEBKGND: | 
|  | return  COMBO_EraseBackground(hwnd, lphc, wParam); | 
|  | case WM_GETDLGCODE: | 
|  | { | 
|  | LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS; | 
|  | if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN)) | 
|  | { | 
|  | int vk = (int)((LPMSG)lParam)->wParam; | 
|  |  | 
|  | if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED)) | 
|  | result |= DLGC_WANTMESSAGE; | 
|  | } | 
|  | return  result; | 
|  | } | 
|  | case WM_WINDOWPOSCHANGING: | 
|  | return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam); | 
|  | case WM_WINDOWPOSCHANGED: | 
|  | /* SetWindowPos can be called on a Combobox to resize its Listbox. | 
|  | * In that case, the Combobox itself will not be resized, so we won't | 
|  | * get a WM_SIZE. Since we still want to update the Listbox, we have to | 
|  | * do it here. | 
|  | */ | 
|  | /* fall through */ | 
|  | case WM_SIZE: | 
|  | if( lphc->hWndLBox && | 
|  | !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc ); | 
|  | return  TRUE; | 
|  | case WM_SETFONT: | 
|  | COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam ); | 
|  | return  TRUE; | 
|  | case WM_GETFONT: | 
|  | return  (LRESULT)lphc->hFont; | 
|  | case WM_SETFOCUS: | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | SetFocus( lphc->hWndEdit ); | 
|  | else | 
|  | COMBO_SetFocus( lphc ); | 
|  | return  TRUE; | 
|  | case WM_KILLFOCUS: | 
|  | #define hwndFocus ((HWND16)wParam) | 
|  | if( !hwndFocus || | 
|  | (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox )) | 
|  | COMBO_KillFocus( lphc ); | 
|  | #undef hwndFocus | 
|  | return  TRUE; | 
|  | case WM_COMMAND: | 
|  | return  COMBO_Command( lphc, wParam, (HWND)lParam ); | 
|  | case WM_GETTEXT: | 
|  | return  COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam ); | 
|  | case WM_SETTEXT: | 
|  | case WM_GETTEXTLENGTH: | 
|  | case WM_CLEAR: | 
|  | if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT)) | 
|  | { | 
|  | int j = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 ); | 
|  | if (j == -1) return 0; | 
|  | return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, j, 0); | 
|  | } | 
|  | else if( lphc->wState & CBF_EDIT ) | 
|  | { | 
|  | LRESULT ret; | 
|  | lphc->wState |= CBF_NOEDITNOTIFY; | 
|  | ret = SendMessageA( lphc->hWndEdit, message, wParam, lParam ); | 
|  | lphc->wState &= ~CBF_NOEDITNOTIFY; | 
|  | return ret; | 
|  | } | 
|  | else return CB_ERR; | 
|  | case WM_CUT: | 
|  | case WM_PASTE: | 
|  | case WM_COPY: | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | return SendMessageA( lphc->hWndEdit, message, wParam, lParam ); | 
|  | else return  CB_ERR; | 
|  |  | 
|  | case WM_DRAWITEM: | 
|  | case WM_DELETEITEM: | 
|  | case WM_COMPAREITEM: | 
|  | case WM_MEASUREITEM: | 
|  | return  COMBO_ItemOp( lphc, message, wParam, lParam ); | 
|  | case WM_ENABLE: | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | EnableWindow( lphc->hWndEdit, (BOOL)wParam ); | 
|  | EnableWindow( lphc->hWndLBox, (BOOL)wParam ); | 
|  |  | 
|  | /* Force the control to repaint when the enabled state changes. */ | 
|  | InvalidateRect(CB_HWND(lphc), NULL, TRUE); | 
|  | return  TRUE; | 
|  | case WM_SETREDRAW: | 
|  | if( wParam ) | 
|  | lphc->wState &= ~CBF_NOREDRAW; | 
|  | else | 
|  | lphc->wState |= CBF_NOREDRAW; | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | SendMessageA( lphc->hWndEdit, message, wParam, lParam ); | 
|  | SendMessageA( lphc->hWndLBox, message, wParam, lParam ); | 
|  | return  0; | 
|  | case WM_SYSKEYDOWN: | 
|  | if( KEYDATA_ALT & HIWORD(lParam) ) | 
|  | if( wParam == VK_UP || wParam == VK_DOWN ) | 
|  | COMBO_FlipListbox( lphc, FALSE, FALSE ); | 
|  | return  0; | 
|  |  | 
|  | case WM_CHAR: | 
|  | case WM_KEYDOWN: | 
|  | if (((CHAR)wParam == VK_RETURN || (CHAR)wParam == VK_ESCAPE) && | 
|  | (lphc->wState & CBF_DROPPED)) | 
|  | { | 
|  | CBRollUp( lphc, (CHAR)wParam == VK_RETURN, FALSE ); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | return  SendMessageA( lphc->hWndEdit, message, wParam, lParam ); | 
|  | else | 
|  | return  SendMessageA( lphc->hWndLBox, message, wParam, lParam ); | 
|  | case WM_LBUTTONDOWN: | 
|  | if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf ); | 
|  | if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam ); | 
|  | return  TRUE; | 
|  | case WM_LBUTTONUP: | 
|  | COMBO_LButtonUp( lphc, lParam ); | 
|  | return  TRUE; | 
|  | case WM_MOUSEMOVE: | 
|  | if( lphc->wState & CBF_CAPTURE ) | 
|  | COMBO_MouseMove( lphc, wParam, lParam ); | 
|  | return  TRUE; | 
|  |  | 
|  | case WM_MOUSEWHEEL: | 
|  | if (wParam & (MK_SHIFT | MK_CONTROL)) | 
|  | return DefWindowProcA( hwnd, message, wParam, lParam ); | 
|  | if ((short) HIWORD(wParam) > 0) return SendMessageA(hwnd,WM_KEYDOWN,VK_UP,0); | 
|  | if ((short) HIWORD(wParam) < 0) return SendMessageA(hwnd,WM_KEYDOWN,VK_DOWN,0); | 
|  | return TRUE; | 
|  |  | 
|  | /* Combo messages */ | 
|  |  | 
|  | case CB_ADDSTRING16: | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | 
|  | case CB_ADDSTRING: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam); | 
|  | case CB_INSERTSTRING16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | 
|  | case CB_INSERTSTRING: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam); | 
|  | case CB_DELETESTRING16: | 
|  | case CB_DELETESTRING: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0); | 
|  | case CB_SELECTSTRING16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | 
|  | case CB_SELECTSTRING: | 
|  | return  COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam ); | 
|  | case CB_FINDSTRING16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | 
|  | case CB_FINDSTRING: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam); | 
|  | case CB_FINDSTRINGEXACT16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | 
|  | case CB_FINDSTRINGEXACT: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT, | 
|  | wParam, lParam ); | 
|  | case CB_SETITEMHEIGHT16: | 
|  | wParam = (INT)(INT16)wParam;	/* signed integer */ | 
|  | case CB_SETITEMHEIGHT: | 
|  | return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam); | 
|  | case CB_GETITEMHEIGHT16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | case CB_GETITEMHEIGHT: | 
|  | if( (INT)wParam >= 0 )	/* listbox item */ | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0); | 
|  | return  CBGetTextAreaHeight(hwnd, lphc); | 
|  | case CB_RESETCONTENT16: | 
|  | case CB_RESETCONTENT: | 
|  | SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 ); | 
|  | if( CB_HASSTRINGS(lphc) ) | 
|  | SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)"" ); | 
|  | else | 
|  | InvalidateRect(CB_HWND(lphc), NULL, TRUE); | 
|  | return  TRUE; | 
|  | case CB_INITSTORAGE: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam); | 
|  | case CB_GETHORIZONTALEXTENT: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0); | 
|  | case CB_SETHORIZONTALEXTENT: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0); | 
|  | case CB_GETTOPINDEX: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0); | 
|  | case CB_GETLOCALE: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0); | 
|  | case CB_SETLOCALE: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0); | 
|  | case CB_GETDROPPEDWIDTH: | 
|  | if( lphc->droppedWidth ) | 
|  | return  lphc->droppedWidth; | 
|  | return  lphc->droppedRect.right - lphc->droppedRect.left; | 
|  | case CB_SETDROPPEDWIDTH: | 
|  | if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && | 
|  | (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam; | 
|  | return  CB_ERR; | 
|  | case CB_GETDROPPEDCONTROLRECT16: | 
|  | lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | 
|  | if( lParam ) | 
|  | { | 
|  | RECT	r; | 
|  | CBGetDroppedControlRect( lphc, &r ); | 
|  | CONV_RECT32TO16( &r, (LPRECT16)lParam ); | 
|  | } | 
|  | return  CB_OKAY; | 
|  | case CB_GETDROPPEDCONTROLRECT: | 
|  | if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam ); | 
|  | return  CB_OKAY; | 
|  | case CB_GETDROPPEDSTATE16: | 
|  | case CB_GETDROPPEDSTATE: | 
|  | return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE; | 
|  | case CB_DIR16: | 
|  | lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | 
|  | /* fall through */ | 
|  | case CB_DIR: | 
|  | return  COMBO_Directory( lphc, (UINT)wParam, | 
|  | (LPSTR)lParam, (message == CB_DIR)); | 
|  | case CB_SHOWDROPDOWN16: | 
|  | case CB_SHOWDROPDOWN: | 
|  | if( CB_GETTYPE(lphc) != CBS_SIMPLE ) | 
|  | { | 
|  | if( wParam ) | 
|  | { | 
|  | if( !(lphc->wState & CBF_DROPPED) ) | 
|  | CBDropDown( lphc ); | 
|  | } | 
|  | else | 
|  | if( lphc->wState & CBF_DROPPED ) | 
|  | CBRollUp( lphc, FALSE, TRUE ); | 
|  | } | 
|  | return  TRUE; | 
|  | case CB_GETCOUNT16: | 
|  | case CB_GETCOUNT: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0); | 
|  | case CB_GETCURSEL16: | 
|  | case CB_GETCURSEL: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0); | 
|  | case CB_SETCURSEL16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | case CB_SETCURSEL: | 
|  | lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0); | 
|  | if( lParam >= 0 ) | 
|  | SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0); | 
|  | if( lphc->wState & CBF_SELCHANGE ) | 
|  | { | 
|  | /* no LBN_SELCHANGE in this case, update manually */ | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | CBUpdateEdit( lphc, (INT)wParam ); | 
|  | else | 
|  | InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE); | 
|  | lphc->wState &= ~CBF_SELCHANGE; | 
|  | } | 
|  | return  lParam; | 
|  | case CB_GETLBTEXT16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | 
|  | case CB_GETLBTEXT: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam); | 
|  | case CB_GETLBTEXTLEN16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | case CB_GETLBTEXTLEN: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0); | 
|  | case CB_GETITEMDATA16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | case CB_GETITEMDATA: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0); | 
|  | case CB_SETITEMDATA16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | case CB_SETITEMDATA: | 
|  | return  SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam); | 
|  | case CB_GETEDITSEL16: | 
|  | wParam = lParam = 0;   /* just in case */ | 
|  | case CB_GETEDITSEL: | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | { | 
|  | INT	a, b; | 
|  |  | 
|  | return  SendMessageA( lphc->hWndEdit, EM_GETSEL, | 
|  | (wParam) ? wParam : (WPARAM)&a, | 
|  | (lParam) ? lParam : (LPARAM)&b ); | 
|  | } | 
|  | return  CB_ERR; | 
|  | case CB_SETEDITSEL16: | 
|  | case CB_SETEDITSEL: | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | return  SendMessageA( lphc->hWndEdit, EM_SETSEL, | 
|  | (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) ); | 
|  | return  CB_ERR; | 
|  | case CB_SETEXTENDEDUI16: | 
|  | case CB_SETEXTENDEDUI: | 
|  | if( CB_GETTYPE(lphc) == CBS_SIMPLE ) | 
|  | return  CB_ERR; | 
|  | if( wParam ) | 
|  | lphc->wState |= CBF_EUI; | 
|  | else lphc->wState &= ~CBF_EUI; | 
|  | return  CB_OKAY; | 
|  | case CB_GETEXTENDEDUI16: | 
|  | case CB_GETEXTENDEDUI: | 
|  | return  (lphc->wState & CBF_EUI) ? TRUE : FALSE; | 
|  |  | 
|  | default: | 
|  | if (message >= WM_USER) | 
|  | WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n", | 
|  | message - WM_USER, wParam, lParam ); | 
|  | break; | 
|  | } | 
|  | return DefWindowProcA(hwnd, message, wParam, lParam); | 
|  | } | 
|  | return CB_ERR; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           ComboWndProc | 
|  | * | 
|  | * This is just a wrapper for the real ComboWndProc which locks/unlocks | 
|  | * window structs. | 
|  | */ | 
|  | LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message, | 
|  | WPARAM wParam, LPARAM lParam ) | 
|  | { | 
|  | WND*	pWnd = WIN_FindWndPtr(hwnd); | 
|  | LRESULT retvalue = ComboWndProc_locked(pWnd,message,wParam,lParam); | 
|  |  | 
|  |  | 
|  | WIN_ReleaseWndPtr(pWnd); | 
|  | return retvalue; | 
|  | } | 
|  |  |