|  | /* | 
|  | * Combo controls | 
|  | * | 
|  | * Copyright 1997 Alex Korobka | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | * | 
|  | * 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 "wine/unicode.h" | 
|  | #include "spy.h" | 
|  | #include "user.h" | 
|  | #include "win.h" | 
|  | #include "controls.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(combo); | 
|  |  | 
|  | /* bits in the dwKeyData */ | 
|  | #define KEYDATA_ALT             0x2000 | 
|  | #define KEYDATA_PREVSTATE       0x4000 | 
|  |  | 
|  | /* | 
|  | * Additional combo box definitions | 
|  | */ | 
|  |  | 
|  | #define CB_NOTIFY( lphc, code ) \ | 
|  | (SendMessageW((lphc)->owner, WM_COMMAND, \ | 
|  | MAKEWPARAM(GetWindowLongA((lphc)->self,GWL_ID), (code)), (LPARAM)(lphc)->self)) | 
|  |  | 
|  | #define CB_DISABLED( lphc )   (!IsWindowEnabled((lphc)->self)) | 
|  | #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) | 
|  | #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS) | 
|  | #define CB_HWND( lphc )       ((lphc)->self) | 
|  |  | 
|  | #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 ) | 
|  |  | 
|  | static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ); | 
|  | static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ); | 
|  |  | 
|  | /********************************************************************* | 
|  | * combo class descriptor | 
|  | */ | 
|  | const struct builtin_class_descr COMBO_builtin_class = | 
|  | { | 
|  | "ComboBox",           /* name */ | 
|  | CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS, /* style  */ | 
|  | ComboWndProcA,        /* procA */ | 
|  | ComboWndProcW,        /* procW */ | 
|  | sizeof(HEADCOMBO *),  /* extra */ | 
|  | IDC_ARROWA,           /* cursor */ | 
|  | 0                     /* brush */ | 
|  | }; | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           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 = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) ) | 
|  | { | 
|  | BITMAP      bm; | 
|  | HBITMAP     hPrevB; | 
|  | RECT        r; | 
|  |  | 
|  | GetObjectW( 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(HWND hwnd, LONG style) | 
|  | { | 
|  | LPHEADCOMBO lphc; | 
|  |  | 
|  | if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) ) | 
|  | { | 
|  | lphc->self = hwnd; | 
|  | SetWindowLongA( hwnd, 0, (LONG)lphc ); | 
|  |  | 
|  | /* some braindead apps do try to use scrollbar/border flags */ | 
|  |  | 
|  | lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL); | 
|  | SetWindowLongA( hwnd, GWL_STYLE, style & ~(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. | 
|  | */ | 
|  | SetWindowLongA( hwnd, GWL_EXSTYLE, | 
|  | GetWindowLongA( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE ); | 
|  |  | 
|  | if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) ) | 
|  | lphc->dwStyle |= CBS_HASSTRINGS; | 
|  | if( !(GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) ) | 
|  | lphc->wState |= CBF_NOTIFY; | 
|  |  | 
|  | TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle ); | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_NCDestroy | 
|  | */ | 
|  | static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc ) | 
|  | { | 
|  |  | 
|  | if( lphc ) | 
|  | { | 
|  | TRACE("[%04x]: freeing storage\n", lphc->self); | 
|  |  | 
|  | if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox ) | 
|  | DestroyWindow( lphc->hWndLBox ); | 
|  |  | 
|  | SetWindowLongA( lphc->self, 0, 0 ); | 
|  | HeapFree( GetProcessHeap(), 0, lphc ); | 
|  | } | 
|  | 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 explicitly 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 | 
|  | { | 
|  | TEXTMETRICW tm; | 
|  | HDC         hDC       = GetDC(hwnd); | 
|  | HFONT       hPrevFont = 0; | 
|  | INT         baseUnitY; | 
|  |  | 
|  | if (lphc->hFont) | 
|  | hPrevFont = SelectObject( hDC, lphc->hFont ); | 
|  |  | 
|  | GetTextMetricsW(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; | 
|  | UINT id = GetWindowLongA( lphc->self, GWL_ID ); | 
|  |  | 
|  | /* | 
|  | * 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      = id; | 
|  | measureItem.itemID     = -1; | 
|  | measureItem.itemWidth  = clientRect.right; | 
|  | measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */ | 
|  | measureItem.itemData   = 0; | 
|  | SendMessageW(lphc->owner, WM_MEASUREITEM, id, (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      = id; | 
|  | measureItem.itemID     = 0; | 
|  | measureItem.itemWidth  = clientRect.right; | 
|  | measureItem.itemHeight = originalItemHeight; | 
|  | measureItem.itemData   = 0; | 
|  | SendMessageW(lphc->owner, WM_MEASUREITEM, id, (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(lphc->self,lphc) + 2*COMBO_YBORDERSIZE(); | 
|  |  | 
|  | GetWindowRect(lphc->self, &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( lphc->self, | 
|  | (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, 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 from 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) | 
|  | { | 
|  | TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n", | 
|  | posChanging->cy, newComboHeight, lphc->droppedRect.bottom, | 
|  | lphc->droppedRect.top); | 
|  | lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight; | 
|  |  | 
|  | posChanging->cy = newComboHeight; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_Create | 
|  | */ | 
|  | static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style, | 
|  | BOOL unicode ) | 
|  | { | 
|  | static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0}; | 
|  | static const WCHAR editName[] = {'E','d','i','t',0}; | 
|  |  | 
|  | if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE; | 
|  | if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT; | 
|  |  | 
|  | lphc->owner = 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 || !(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( hwnd, &lphc->droppedRect ); | 
|  | CBCalcPlacement(hwnd, 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(hwnd, (LPPOINT)&lphc->droppedRect); | 
|  | ClientToScreen(hwnd, (LPPOINT)&lphc->droppedRect.right); | 
|  | } | 
|  |  | 
|  | /* create listbox popup */ | 
|  |  | 
|  | lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) | | 
|  | (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; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (unicode) | 
|  | lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle, | 
|  | lphc->droppedRect.left, | 
|  | lphc->droppedRect.top, | 
|  | lphc->droppedRect.right - lphc->droppedRect.left, | 
|  | lphc->droppedRect.bottom - lphc->droppedRect.top, | 
|  | hwnd, (HMENU)ID_CB_LISTBOX, | 
|  | GetWindowLongA( hwnd, GWL_HINSTANCE ), lphc ); | 
|  | else | 
|  | lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle, | 
|  | lphc->droppedRect.left, | 
|  | lphc->droppedRect.top, | 
|  | lphc->droppedRect.right - lphc->droppedRect.left, | 
|  | lphc->droppedRect.bottom - lphc->droppedRect.top, | 
|  | hwnd, (HMENU)ID_CB_LISTBOX, | 
|  | GetWindowLongA( hwnd, GWL_HINSTANCE ), 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; | 
|  |  | 
|  | if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED; | 
|  |  | 
|  | if (unicode) | 
|  | lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle, | 
|  | lphc->textRect.left, lphc->textRect.top, | 
|  | lphc->textRect.right - lphc->textRect.left, | 
|  | lphc->textRect.bottom - lphc->textRect.top, | 
|  | hwnd, (HMENU)ID_CB_EDIT, | 
|  | GetWindowLongA( hwnd, GWL_HINSTANCE ), NULL ); | 
|  | else | 
|  | lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle, | 
|  | lphc->textRect.left, lphc->textRect.top, | 
|  | lphc->textRect.right - lphc->textRect.left, | 
|  | lphc->textRect.bottom - lphc->textRect.top, | 
|  | hwnd, (HMENU)ID_CB_EDIT, | 
|  | GetWindowLongA( hwnd, GWL_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 0; | 
|  | } | 
|  | 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; | 
|  | LPWSTR pText = NULL; | 
|  |  | 
|  | if( lphc->wState & CBF_NOREDRAW ) return; | 
|  |  | 
|  | TRACE("\n"); | 
|  |  | 
|  | /* follow Windows combobox that sends a bunch of text | 
|  | * inquiries to its listbox while processing WM_PAINT. */ | 
|  |  | 
|  | if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR ) | 
|  | { | 
|  | size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0); | 
|  | if (size == LB_ERR) | 
|  | FIXME("LB_ERR probably not handled yet\n"); | 
|  | if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) ) | 
|  | { | 
|  | /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */ | 
|  | size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText); | 
|  | pText[size] = '\0';	/* just in case */ | 
|  | } else return; | 
|  | } | 
|  | else | 
|  | if( !CB_OWNERDRAWN(lphc) ) | 
|  | return; | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | { | 
|  | static const WCHAR empty_stringW[] = { 0 }; | 
|  | if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW ); | 
|  | if( lphc->wState & CBF_FOCUSED ) | 
|  | SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1)); | 
|  | } | 
|  | else /* paint text field ourselves */ | 
|  | { | 
|  | UINT	itemState = ODS_COMBOBOXEDIT; | 
|  | HFONT	hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0; | 
|  |  | 
|  | /* | 
|  | * Give ourselves some space. | 
|  | */ | 
|  | InflateRect( &rectEdit, -1, -1 ); | 
|  |  | 
|  | if( CB_OWNERDRAWN(lphc) ) | 
|  | { | 
|  | DRAWITEMSTRUCT dis; | 
|  | HRGN           clipRegion; | 
|  | UINT ctlid = GetWindowLongA( lphc->self, GWL_ID ); | 
|  |  | 
|  | /* setup state for DRAWITEM message. Owner will highlight */ | 
|  | if ( (lphc->wState & CBF_FOCUSED) && | 
|  | !(lphc->wState & CBF_DROPPED) ) | 
|  | itemState |= ODS_SELECTED | ODS_FOCUS; | 
|  |  | 
|  | /* | 
|  | * 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 (!IsWindowEnabled(lphc->self) & WS_DISABLED) itemState |= ODS_DISABLED; | 
|  |  | 
|  | dis.CtlType	= ODT_COMBOBOX; | 
|  | dis.CtlID	= ctlid; | 
|  | dis.hwndItem	= lphc->self; | 
|  | dis.itemAction	= ODA_DRAWENTIRE; | 
|  | dis.itemID	= id; | 
|  | dis.itemState	= itemState; | 
|  | dis.hDC		= hdc; | 
|  | dis.rcItem	= rectEdit; | 
|  | dis.itemData	= SendMessageW(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); | 
|  |  | 
|  | SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis ); | 
|  |  | 
|  | /* | 
|  | * Reset the clipping region. | 
|  | */ | 
|  | SelectClipRgn(hdc, clipRegion); | 
|  | } | 
|  | else | 
|  | { | 
|  | static const WCHAR empty_stringW[] = { 0 }; | 
|  |  | 
|  | 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 ) ); | 
|  | } | 
|  |  | 
|  | ExtTextOutW( hdc, | 
|  | rectEdit.left + 1, | 
|  | rectEdit.top + 1, | 
|  | ETO_OPAQUE | ETO_CLIPPED, | 
|  | &rectEdit, | 
|  | pText ? pText : empty_stringW , 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( | 
|  | LPHEADCOMBO lphc, | 
|  | HDC         hDC) | 
|  | { | 
|  | HBRUSH  hBkgBrush; | 
|  |  | 
|  | /* | 
|  | * Get the background brush for this control. | 
|  | */ | 
|  | if (CB_DISABLED(lphc)) | 
|  | { | 
|  | hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORSTATIC, hDC, (LPARAM)lphc->self ); | 
|  |  | 
|  | /* | 
|  | * 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 = SendMessageW(lphc->owner, WM_CTLCOLOREDIT, hDC, (LPARAM)lphc->self ); | 
|  | } | 
|  | else | 
|  | { | 
|  | hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX, hDC, (LPARAM)lphc->self ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Catch errors. | 
|  | */ | 
|  | if( !hBkgBrush ) | 
|  | hBkgBrush = GetSysColorBrush(COLOR_WINDOW); | 
|  |  | 
|  | return hBkgBrush; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_EraseBackground | 
|  | */ | 
|  | static LRESULT COMBO_EraseBackground( | 
|  | HWND        hwnd, | 
|  | LPHEADCOMBO lphc, | 
|  | HDC         hParamDC) | 
|  | { | 
|  | HBRUSH  hBkgBrush; | 
|  | HDC 	  hDC; | 
|  |  | 
|  | if(lphc->wState & CBF_EDIT) | 
|  | return TRUE; | 
|  |  | 
|  | hDC = (hParamDC) ? hParamDC | 
|  | : GetDC(hwnd); | 
|  | /* | 
|  | * Retrieve the background brush | 
|  | */ | 
|  | hBkgBrush = COMBO_PrepareColors(lphc, hDC); | 
|  |  | 
|  | FillRect(hDC, &lphc->textRect, 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, &ps); | 
|  |  | 
|  | TRACE("hdc=%04x\n", hDC); | 
|  |  | 
|  | if( hDC && !(lphc->wState & CBF_NOREDRAW) ) | 
|  | { | 
|  | HBRUSH	hPrevBrush, hBkgBrush; | 
|  |  | 
|  | /* | 
|  | * Retrieve the background brush and select it in the | 
|  | * DC. | 
|  | */ | 
|  | hBkgBrush = COMBO_PrepareColors(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(lphc->self, lphc, hDC); | 
|  | } | 
|  |  | 
|  | if( !IsRectEmpty(&lphc->buttonRect) ) | 
|  | { | 
|  | CBPaintButton(lphc, hDC, lphc->buttonRect); | 
|  | } | 
|  |  | 
|  | /* paint the edit control padding area */ | 
|  | if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST) | 
|  | { | 
|  | RECT rPadEdit = lphc->textRect; | 
|  |  | 
|  | InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING()); | 
|  |  | 
|  | FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) ); | 
|  | } | 
|  |  | 
|  | 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, SYSCOLOR_GetPen(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, &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; | 
|  | LPWSTR pText = NULL; | 
|  |  | 
|  | idx = LB_ERR; | 
|  | length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ); | 
|  |  | 
|  | if( length > 0 ) | 
|  | pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR)); | 
|  |  | 
|  | TRACE("\t edit text length %i\n", length ); | 
|  |  | 
|  | if( pText ) | 
|  | { | 
|  | if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1); | 
|  | else pText[0] = '\0'; | 
|  | idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, | 
|  | (WPARAM)(-1), (LPARAM)pText ); | 
|  | HeapFree( GetProcessHeap(), 0, pText ); | 
|  | } | 
|  |  | 
|  | SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0); | 
|  |  | 
|  | /* probably superfluous but Windows sends this too */ | 
|  | SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0); | 
|  | SendMessageW(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; | 
|  | LPWSTR pText = NULL; | 
|  | static const WCHAR empty_stringW[] = { 0 }; | 
|  |  | 
|  | TRACE("\t %i\n", index ); | 
|  |  | 
|  | if( index >= 0 ) /* got an entry */ | 
|  | { | 
|  | length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0); | 
|  | if( length != LB_ERR) | 
|  | { | 
|  | if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) ) | 
|  | { | 
|  | SendMessageW(lphc->hWndLBox, LB_GETTEXT, | 
|  | (WPARAM)index, (LPARAM)pText ); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT); | 
|  | SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW); | 
|  | lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT); | 
|  |  | 
|  | if( lphc->wState & CBF_FOCUSED ) | 
|  | SendMessageW(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", lphc->self); | 
|  |  | 
|  | CB_NOTIFY( lphc, CBN_DROPDOWN ); | 
|  |  | 
|  | /* set selection */ | 
|  |  | 
|  | lphc->wState |= CBF_DROPPED; | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | { | 
|  | lphc->droppedIndex = CBUpdateLBox( lphc, TRUE ); | 
|  |  | 
|  | /* Update edit only if item is in the list */ | 
|  | if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0) | 
|  | CBUpdateEdit( lphc, lphc->droppedIndex ); | 
|  | } | 
|  | else | 
|  | { | 
|  | lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); | 
|  |  | 
|  | SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, | 
|  | (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 ); | 
|  | SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0); | 
|  | } | 
|  |  | 
|  | /* now set popup position */ | 
|  | GetWindowRect( lphc->self, &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)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0); | 
|  |  | 
|  | if (nItems > 0) | 
|  | { | 
|  | int nHeight; | 
|  | int nIHeight; | 
|  |  | 
|  | nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0); | 
|  |  | 
|  | nHeight = nIHeight*nItems; | 
|  |  | 
|  | if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE()) | 
|  | nDroppedHeight = nHeight + COMBO_YBORDERSIZE(); | 
|  |  | 
|  | if (nDroppedHeight < nIHeight) | 
|  | { | 
|  | if (nItems < 5) | 
|  | nDroppedHeight = nHeight; | 
|  | else | 
|  | nDroppedHeight = 5*nIHeight; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*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, NULL, 0, RDW_INVALIDATE | | 
|  | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN ); | 
|  |  | 
|  | EnableWindow( lphc->hWndLBox, TRUE ); | 
|  | if (GetCapture() != lphc->self) | 
|  | SetCapture(lphc->hWndLBox); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CBRollUp | 
|  | * | 
|  | * Hide listbox popup. | 
|  | */ | 
|  | static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton ) | 
|  | { | 
|  | HWND	hWnd = lphc->self; | 
|  |  | 
|  | TRACE("[%04x]: sel ok? [%i] dropped? [%i]\n", | 
|  | lphc->self, (INT)ok, (INT)(lphc->wState & CBF_DROPPED)); | 
|  |  | 
|  | CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL ); | 
|  |  | 
|  | if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE ) | 
|  | { | 
|  |  | 
|  | 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(lphc->self, &lphc->buttonRect, TRUE); | 
|  | UpdateWindow(lphc->self); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_SetFocus | 
|  | */ | 
|  | static void COMBO_SetFocus( LPHEADCOMBO lphc ) | 
|  | { | 
|  | if( !(lphc->wState & CBF_FOCUSED) ) | 
|  | { | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) | 
|  | SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0); | 
|  |  | 
|  | /* This is wrong. Message sequences seem to indicate that this | 
|  | is set *after* the notify. */ | 
|  | /* lphc->wState |= CBF_FOCUSED;  */ | 
|  |  | 
|  | if( !(lphc->wState & CBF_EDIT) ) | 
|  | InvalidateRect(lphc->self, &lphc->textRect, TRUE); | 
|  |  | 
|  | CB_NOTIFY( lphc, CBN_SETFOCUS ); | 
|  | lphc->wState |= CBF_FOCUSED; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_KillFocus | 
|  | */ | 
|  | static void COMBO_KillFocus( LPHEADCOMBO lphc ) | 
|  | { | 
|  | HWND	hWnd = lphc->self; | 
|  |  | 
|  | if( lphc->wState & CBF_FOCUSED ) | 
|  | { | 
|  | CBRollUp( lphc, FALSE, TRUE ); | 
|  | if( IsWindow( hWnd ) ) | 
|  | { | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) | 
|  | SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0); | 
|  |  | 
|  | lphc->wState &= ~CBF_FOCUSED; | 
|  |  | 
|  | /* redraw text */ | 
|  | if( !(lphc->wState & CBF_EDIT) ) | 
|  | InvalidateRect(lphc->self, &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", | 
|  | lphc->self, lphc->hWndEdit ); | 
|  |  | 
|  | COMBO_SetFocus( lphc ); | 
|  | break; | 
|  |  | 
|  | case (EN_KILLFOCUS >> 8): | 
|  |  | 
|  | TRACE("[%04x]: edit [%04x] lost focus\n", | 
|  | lphc->self, 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_NOLBSELECT) | 
|  | { | 
|  | lphc->wState &= ~CBF_NOLBSELECT; | 
|  | } | 
|  | else | 
|  | { | 
|  | CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED ); | 
|  | } | 
|  |  | 
|  | if (!(lphc->wState & CBF_NOEDITNOTIFY)) | 
|  | CB_NOTIFY( lphc, CBN_EDITCHANGE ); | 
|  | 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", | 
|  | lphc->self, lphc->wState ); | 
|  |  | 
|  | if( HIWORD(wParam) == LBN_SELCHANGE) | 
|  | { | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | { | 
|  | INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); | 
|  | lphc->wState |= CBF_NOLBSELECT; | 
|  | CBUpdateEdit( lphc, index ); | 
|  | /* select text in edit, as Windows does */ | 
|  | SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1)); | 
|  | } | 
|  | else | 
|  | InvalidateRect(lphc->self, &lphc->textRect, TRUE); | 
|  | } | 
|  |  | 
|  | /* do not roll up if selection is being tracked | 
|  | * by arrowkeys in the dropdown listbox */ | 
|  | if( ((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, LPARAM lParam ) | 
|  | { | 
|  | HWND hWnd = lphc->self; | 
|  | UINT id = GetWindowLongA( hWnd, GWL_ID ); | 
|  |  | 
|  | TRACE("[%04x]: ownerdraw op %04x\n", lphc->self, msg ); | 
|  |  | 
|  | switch( msg ) | 
|  | { | 
|  | case WM_DELETEITEM: | 
|  | { | 
|  | DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam; | 
|  | lpIS->CtlType  = ODT_COMBOBOX; | 
|  | lpIS->CtlID    = id; | 
|  | lpIS->hwndItem = hWnd; | 
|  | break; | 
|  | } | 
|  | case WM_DRAWITEM: | 
|  | { | 
|  | DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam; | 
|  | lpIS->CtlType  = ODT_COMBOBOX; | 
|  | lpIS->CtlID    = id; | 
|  | lpIS->hwndItem = hWnd; | 
|  | break; | 
|  | } | 
|  | case WM_COMPAREITEM: | 
|  | { | 
|  | COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam; | 
|  | lpIS->CtlType  = ODT_COMBOBOX; | 
|  | lpIS->CtlID    = id; | 
|  | lpIS->hwndItem = hWnd; | 
|  | break; | 
|  | } | 
|  | case WM_MEASUREITEM: | 
|  | { | 
|  | MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam; | 
|  | lpIS->CtlType  = ODT_COMBOBOX; | 
|  | lpIS->CtlID    = id; | 
|  | break; | 
|  | } | 
|  | } | 
|  | return SendMessageW(lphc->owner, msg, id, lParam); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_GetTextW | 
|  | */ | 
|  | static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf ) | 
|  | { | 
|  | INT length; | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf ); | 
|  |  | 
|  | /* get it from the listbox */ | 
|  |  | 
|  | if (!count || !buf) return 0; | 
|  | if( lphc->hWndLBox ) | 
|  | { | 
|  | INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); | 
|  | if (idx == LB_ERR) goto error; | 
|  | length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 ); | 
|  | if (length == LB_ERR) goto error; | 
|  |  | 
|  | /* 'length' is without the terminating character */ | 
|  | if (length >= count) | 
|  | { | 
|  | LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR)); | 
|  | if (!lpBuffer) goto error; | 
|  | length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer); | 
|  |  | 
|  | /* truncate if buffer is too short */ | 
|  | if (length != LB_ERR) | 
|  | { | 
|  | lstrcpynW( buf, lpBuffer, count ); | 
|  | length = count; | 
|  | } | 
|  | HeapFree( GetProcessHeap(), 0, lpBuffer ); | 
|  | } | 
|  | else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf); | 
|  |  | 
|  | if (length == LB_ERR) return 0; | 
|  | return length; | 
|  | } | 
|  |  | 
|  | error:  /* error - truncate string, return zero */ | 
|  | buf[0] = 0; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_GetTextA | 
|  | * | 
|  | * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does. | 
|  | *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't. | 
|  | */ | 
|  | static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf ) | 
|  | { | 
|  | INT length; | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf ); | 
|  |  | 
|  | /* get it from the listbox */ | 
|  |  | 
|  | if (!count || !buf) return 0; | 
|  | if( lphc->hWndLBox ) | 
|  | { | 
|  | INT idx = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0); | 
|  | if (idx == LB_ERR) goto error; | 
|  | length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 ); | 
|  | if (length == LB_ERR) goto error; | 
|  |  | 
|  | /* 'length' is without the terminating character */ | 
|  | if (length >= count) | 
|  | { | 
|  | LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) ); | 
|  | if (!lpBuffer) goto error; | 
|  | length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer); | 
|  |  | 
|  | /* truncate if buffer is too short */ | 
|  | if (length != LB_ERR) | 
|  | { | 
|  | lstrcpynA( buf, lpBuffer, count ); | 
|  | length = count; | 
|  | } | 
|  | HeapFree( GetProcessHeap(), 0, lpBuffer ); | 
|  | } | 
|  | else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf); | 
|  |  | 
|  | if (length == LB_ERR) return 0; | 
|  | return length; | 
|  | } | 
|  |  | 
|  | error:  /* error - truncate string, return zero */ | 
|  | buf[0] = 0; | 
|  | 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, NULL, 0, | 
|  | RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW ); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_Size | 
|  | */ | 
|  | static void COMBO_Size( LPHEADCOMBO lphc ) | 
|  | { | 
|  | CBCalcPlacement(lphc->self, | 
|  | 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 ) | 
|  | SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw); | 
|  | SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw); | 
|  |  | 
|  | /* | 
|  | * Redo the layout of the control. | 
|  | */ | 
|  | if ( CB_GETTYPE(lphc) == CBS_SIMPLE) | 
|  | { | 
|  | CBCalcPlacement(lphc->self, | 
|  | 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, | 
|  | 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 = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, | 
|  | (WPARAM)index, (LPARAM)height ); | 
|  | return lRet; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_SelectString | 
|  | */ | 
|  | static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode ) | 
|  | { | 
|  | INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) : | 
|  | SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText); | 
|  | if( index >= 0 ) | 
|  | { | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | CBUpdateEdit( lphc, index ); | 
|  | else | 
|  | { | 
|  | InvalidateRect(lphc->self, &lphc->textRect, TRUE); | 
|  | } | 
|  | } | 
|  | return (LRESULT)index; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           COMBO_LButtonDown | 
|  | */ | 
|  | static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam ) | 
|  | { | 
|  | POINT     pt; | 
|  | BOOL      bButton; | 
|  | HWND      hWnd = lphc->self; | 
|  |  | 
|  | 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 ) | 
|  | { | 
|  | if( lphc->wState & CBF_CAPTURE ) | 
|  | { | 
|  | lphc->wState &= ~CBF_CAPTURE; | 
|  | if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | 
|  | { | 
|  | INT index = CBUpdateLBox( lphc, TRUE ); | 
|  | /* Update edit only if item is in the list */ | 
|  | if(index >= 0) | 
|  | { | 
|  | 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, 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 */ | 
|  | SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           ComboWndProc_common | 
|  | * | 
|  | * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm | 
|  | */ | 
|  | static LRESULT ComboWndProc_common( HWND hwnd, UINT message, | 
|  | WPARAM wParam, LPARAM lParam, BOOL unicode ) | 
|  | { | 
|  | LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 ); | 
|  |  | 
|  | TRACE("[%04x]: msg %s wp %08x lp %08lx\n", | 
|  | hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam ); | 
|  |  | 
|  | if( lphc || message == WM_NCCREATE ) | 
|  | switch(message) | 
|  | { | 
|  |  | 
|  | /* System messages */ | 
|  |  | 
|  | case WM_NCCREATE: | 
|  | { | 
|  | LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style : | 
|  | ((LPCREATESTRUCTA)lParam)->style; | 
|  | return COMBO_NCCreate(hwnd, style); | 
|  | } | 
|  | case WM_NCDESTROY: | 
|  | COMBO_NCDestroy(lphc); | 
|  | break;/* -> DefWindowProc */ | 
|  |  | 
|  | case WM_CREATE: | 
|  | { | 
|  | HWND hwndParent; | 
|  | LONG style; | 
|  | if(unicode) | 
|  | { | 
|  | hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent; | 
|  | style = ((LPCREATESTRUCTW)lParam)->style; | 
|  | } | 
|  | else | 
|  | { | 
|  | hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent; | 
|  | style = ((LPCREATESTRUCTA)lParam)->style; | 
|  | } | 
|  | return COMBO_Create(hwnd, lphc, hwndParent, style, unicode); | 
|  | } | 
|  |  | 
|  | 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: | 
|  | { | 
|  | HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam ); | 
|  | if( !hwndFocus || | 
|  | (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox )) | 
|  | COMBO_KillFocus( lphc ); | 
|  | return  TRUE; | 
|  | } | 
|  | case WM_COMMAND: | 
|  | return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) ); | 
|  | case WM_GETTEXT: | 
|  | return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam ) | 
|  | : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam ); | 
|  | case WM_SETTEXT: | 
|  | case WM_GETTEXTLENGTH: | 
|  | case WM_CLEAR: | 
|  | if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT)) | 
|  | { | 
|  | int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); | 
|  | if (j == -1) return 0; | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) : | 
|  | SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0); | 
|  | } | 
|  | else if( lphc->wState & CBF_EDIT ) | 
|  | { | 
|  | LRESULT ret; | 
|  | lphc->wState |= CBF_NOEDITNOTIFY; | 
|  | ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) : | 
|  | 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 unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) : | 
|  | 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, 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(lphc->self, NULL, TRUE); | 
|  | return  TRUE; | 
|  | case WM_SETREDRAW: | 
|  | if( wParam ) | 
|  | lphc->wState &= ~CBF_NOREDRAW; | 
|  | else | 
|  | lphc->wState |= CBF_NOREDRAW; | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | SendMessageW(lphc->hWndEdit, message, wParam, lParam); | 
|  | SendMessageW(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: | 
|  | { | 
|  | HWND hwndTarget; | 
|  |  | 
|  | if ((wParam == VK_RETURN || wParam == VK_ESCAPE) && | 
|  | (lphc->wState & CBF_DROPPED)) | 
|  | { | 
|  | CBRollUp( lphc, wParam == VK_RETURN, FALSE ); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | hwndTarget = lphc->hWndEdit; | 
|  | else | 
|  | hwndTarget = lphc->hWndLBox; | 
|  |  | 
|  | return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) : | 
|  | SendMessageA(hwndTarget, message, wParam, lParam); | 
|  | } | 
|  | case WM_LBUTTONDOWN: | 
|  | if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self ); | 
|  | if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam ); | 
|  | return  TRUE; | 
|  | case WM_LBUTTONUP: | 
|  | COMBO_LButtonUp( lphc ); | 
|  | 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 unicode ? DefWindowProcW(hwnd, message, wParam, lParam) : | 
|  | DefWindowProcA(hwnd, message, wParam, lParam); | 
|  | if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0); | 
|  | if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0); | 
|  | return TRUE; | 
|  |  | 
|  | /* Combo messages */ | 
|  |  | 
|  | case CB_ADDSTRING16: | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam); | 
|  | /* fall through */ | 
|  | case CB_ADDSTRING: | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) : | 
|  | SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam); | 
|  | case CB_INSERTSTRING16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam); | 
|  | /* fall through */ | 
|  | case CB_INSERTSTRING: | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) : | 
|  | SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam); | 
|  | case CB_DELETESTRING16: | 
|  | case CB_DELETESTRING: | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) : | 
|  | SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0); | 
|  | case CB_SELECTSTRING16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam); | 
|  | /* fall through */ | 
|  | case CB_SELECTSTRING: | 
|  | return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode); | 
|  | case CB_FINDSTRING16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam); | 
|  | /* fall through */ | 
|  | case CB_FINDSTRING: | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) : | 
|  | SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam); | 
|  | case CB_FINDSTRINGEXACT16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam); | 
|  | /* fall through */ | 
|  | case CB_FINDSTRINGEXACT: | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) : | 
|  | SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam); | 
|  | case CB_SETITEMHEIGHT16: | 
|  | wParam = (INT)(INT16)wParam;	/* signed integer */ | 
|  | /* fall through */ | 
|  | case CB_SETITEMHEIGHT: | 
|  | return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam); | 
|  | case CB_GETITEMHEIGHT16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | /* fall through */ | 
|  | case CB_GETITEMHEIGHT: | 
|  | if( (INT)wParam >= 0 )	/* listbox item */ | 
|  | return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0); | 
|  | return  CBGetTextAreaHeight(hwnd, lphc); | 
|  | case CB_RESETCONTENT16: | 
|  | case CB_RESETCONTENT: | 
|  | SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0); | 
|  | if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) ) | 
|  | { | 
|  | static const WCHAR empty_stringW[] = { 0 }; | 
|  | SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW); | 
|  | } | 
|  | else | 
|  | InvalidateRect(lphc->self, NULL, TRUE); | 
|  | return  TRUE; | 
|  | case CB_INITSTORAGE: | 
|  | return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam); | 
|  | case CB_GETHORIZONTALEXTENT: | 
|  | return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0); | 
|  | case CB_SETHORIZONTALEXTENT: | 
|  | return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0); | 
|  | case CB_GETTOPINDEX: | 
|  | return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0); | 
|  | case CB_GETLOCALE: | 
|  | return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0); | 
|  | case CB_SETLOCALE: | 
|  | return SendMessageW(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)MapSL(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)MapSL(lParam); | 
|  | message = LB_DIR16; | 
|  | /* fall through */ | 
|  | case CB_DIR: | 
|  | if(message == CB_DIR) message = LB_DIR; | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) : | 
|  | SendMessageA(lphc->hWndLBox, message, wParam, lParam); | 
|  |  | 
|  | 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 SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0); | 
|  | case CB_GETCURSEL16: | 
|  | case CB_GETCURSEL: | 
|  | return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); | 
|  | case CB_SETCURSEL16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | /* fall through */ | 
|  | case CB_SETCURSEL: | 
|  | lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0); | 
|  | if( lParam >= 0 ) | 
|  | SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0); | 
|  |  | 
|  | /* no LBN_SELCHANGE in this case, update manually */ | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | CBUpdateEdit( lphc, (INT)wParam ); | 
|  | else | 
|  | InvalidateRect(lphc->self, &lphc->textRect, TRUE); | 
|  | lphc->wState &= ~CBF_SELCHANGE; | 
|  | return  lParam; | 
|  | case CB_GETLBTEXT16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | lParam = (LPARAM)MapSL(lParam); | 
|  | /* fall through */ | 
|  | case CB_GETLBTEXT: | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) : | 
|  | SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam); | 
|  | case CB_GETLBTEXTLEN16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | /* fall through */ | 
|  | case CB_GETLBTEXTLEN: | 
|  | return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) : | 
|  | SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0); | 
|  | case CB_GETITEMDATA16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | /* fall through */ | 
|  | case CB_GETITEMDATA: | 
|  | return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0); | 
|  | case CB_SETITEMDATA16: | 
|  | wParam = (INT)(INT16)wParam; | 
|  | /* fall through */ | 
|  | case CB_SETITEMDATA: | 
|  | return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam); | 
|  | case CB_GETEDITSEL16: | 
|  | wParam = lParam = 0;   /* just in case */ | 
|  | /* fall through */ | 
|  | case CB_GETEDITSEL: | 
|  | /* Edit checks passed parameters itself */ | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam); | 
|  | return  CB_ERR; | 
|  | case CB_SETEDITSEL16: | 
|  | case CB_SETEDITSEL: | 
|  | if( lphc->wState & CBF_EDIT ) | 
|  | return SendMessageW(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 unicode ? DefWindowProcW(hwnd, message, wParam, lParam) : | 
|  | DefWindowProcA(hwnd, message, wParam, lParam); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           ComboWndProcA | 
|  | * | 
|  | * This is just a wrapper for the real ComboWndProc which locks/unlocks | 
|  | * window structs. | 
|  | */ | 
|  | static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) | 
|  | { | 
|  | if (!IsWindow(hwnd)) return 0; | 
|  | return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE ); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           ComboWndProcW | 
|  | */ | 
|  | static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) | 
|  | { | 
|  | if (!IsWindow(hwnd)) return 0; | 
|  | return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE ); | 
|  | } |