/*
* 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 <windows.h>
-#include <user32/sysmetr.h>
-#include <user32/win.h>
+#include <user32.h>
+#define NDEBUG
+#include <debug.h>
-#include <user32/heapdup.h>
-#include <user32/combo.h>
-#include <user32/debug.h>
+WINE_DEFAULT_DEBUG_CHANNEL(combo);
/* bits in the dwKeyData */
#define KEYDATA_ALT 0x2000
* 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)), (LPARAM)(lphc)->self->hwndSelf))
-#define CB_GETEDITTEXTLENGTH( lphc ) \
- (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
+ (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;
-static UINT CBitOffset = 8;
+static UINT CBitHeight, CBitWidth;
+
+/*
+ * Look and feel dependent "constants"
+ */
+
+#define COMBO_YBORDERGAP 5
+#define COMBO_XBORDERSIZE() 2
+#define COMBO_YBORDERSIZE() 2
+#define COMBO_EDITBUTTONSPACE() 0
+#define EDIT_CONTROL_PADDING() 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 =
+{
+#ifdef __REACTOS__
+ L"ComboBox", /* name */
+ CS_PARENTDC | CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS, /* style */
+ (WNDPROC) ComboWndProcW, /* procW */
+ (WNDPROC) ComboWndProcA, /* procA */
+ sizeof(HEADCOMBO *), /* extra */
+ (LPCWSTR) IDC_ARROW, /* cursor */
+ 0 /* brush */
+#else
+ "ComboBox", /* name */
+ CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style */
+ ComboWndProcA, /* procA */
+ ComboWndProcW, /* procW */
+ sizeof(HEADCOMBO *), /* extra */
+ IDC_ARROW, /* cursor */
+ 0 /* brush */
+#endif
+};
+
/***********************************************************************
* COMBO_Init
*
* Load combo button bitmap.
*/
-WINBOOL COMBO_Init(void)
+static BOOL COMBO_Init()
{
HDC hDC;
-
+
if( hComboBmp ) return TRUE;
if( (hDC = CreateCompatibleDC(0)) )
{
- WINBOOL bRet = FALSE;
- if( (hComboBmp = LoadBitmap(0, MAKEINTRESOURCE(OBM_COMBO))) )
+ BOOL bRet = FALSE;
+ if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
{
BITMAP bm;
HBITMAP hPrevB;
RECT r;
- GetObjectA( hComboBmp, sizeof(bm), &bm );
+ GetObjectW( hComboBmp, sizeof(bm), &bm );
CBitHeight = bm.bmHeight;
CBitWidth = bm.bmWidth;
- DPRINT( "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
+ TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
hPrevB = SelectObject( hDC, hComboBmp);
SetRect( &r, 0, 0, CBitWidth, CBitHeight );
/***********************************************************************
* COMBO_NCCreate
*/
-static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
+static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
{
- LPHEADCOMBO lphc;
+ LPHEADCOMBO lphc;
- if ( wnd && COMBO_Init() &&
- (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
- {
- LPCREATESTRUCT lpcs = (CREATESTRUCT*)lParam;
-
- memset( lphc, 0, sizeof(HEADCOMBO) );
- *(LPHEADCOMBO*)wnd->wExtra = 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 = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
- wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
+ 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( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
+ if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
lphc->dwStyle |= CBS_HASSTRINGS;
- if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
+ if( !(GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
lphc->wState |= CBF_NOTIFY;
- DPRINT( "[0x%08x], style = %08x\n",
- (UINT)lphc, lphc->dwStyle );
-
- return (LRESULT)(UINT)wnd->hwndSelf;
+ TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
+ return TRUE;
}
- return (LRESULT)FALSE;
+ return FALSE;
}
/***********************************************************************
if( lphc )
{
- WND* wnd = lphc->self;
-
- DPRINT("[%04x]: freeing storage\n", CB_HWND(lphc));
+ TRACE("[%p]: freeing storage\n", lphc->self);
- if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
+ if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
DestroyWindow( lphc->hWndLBox );
+ SetWindowLongA( lphc->self, 0, 0 );
HeapFree( GetProcessHeap(), 0, lphc );
- wnd->wExtra[0] = 0;
}
return 0;
}
/***********************************************************************
- * CBGetDefaultTextHeight
- */
-static void CBGetDefaultTextHeight( LPHEADCOMBO lphc, LPSIZE lpSize )
-{
- if( lphc->editHeight ) /* explicitly set height */
- lpSize->cy = lphc->editHeight;
- else
- {
- HDC hDC = GetDC( lphc->self->hwndSelf );
- HFONT hPrevFont = 0;
-
- if( lphc->hFont ) hPrevFont = SelectObject( hDC, lphc->hFont );
-
- GetTextExtentPointA( hDC, "0", 1, lpSize);
-
- lpSize->cy += lpSize->cy / 4 + 4 * SYSMETRICS_CYBORDER;
-
- if( hPrevFont ) SelectObject( hDC, hPrevFont );
- ReleaseDC( lphc->self->hwndSelf, hDC );
- }
- lpSize->cx = lphc->RectCombo.right - lphc->RectCombo.left;
-}
-
-
-/***********************************************************************
- * CBCalcPlacement
+ * CBGetTextAreaHeight
*
- * Set up component coordinates given valid lphc->RectCombo.
+ * 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 void CBCalcPlacement( LPHEADCOMBO lphc, LPRECT lprEdit,
- LPRECT lprButton, LPRECT lprLB )
+static INT CBGetTextAreaHeight(
+ HWND hwnd,
+ LPHEADCOMBO lphc)
{
- RECT rect = lphc->RectCombo;
- SIZE size;
+ INT iTextItemHeight;
- /* get combo height and width */
+ if( lphc->editHeight ) /* explicitly set height */
+ {
+ iTextItemHeight = lphc->editHeight;
+ }
+ else
+ {
+ TEXTMETRICW tm;
+ HDC hDC = GetDC(hwnd);
+ HFONT hPrevFont = 0;
+ INT baseUnitY;
- if( CB_OWNERDRAWN(lphc) )
- {
- UINT u = lphc->RectEdit.bottom - lphc->RectEdit.top;
+ if (lphc->hFont)
+ hPrevFont = SelectObject( hDC, lphc->hFont );
- if( lphc->wState & CBF_MEASUREITEM ) /* first initialization */
- {
- MEASUREITEMSTRUCT mi;
+ GetTextMetricsW(hDC, &tm);
- /* calculate defaults before sending WM_MEASUREITEM */
+ baseUnitY = tm.tmHeight;
- CBGetDefaultTextHeight( lphc, &size );
+ if( hPrevFont )
+ SelectObject( hDC, hPrevFont );
- lphc->wState &= ~CBF_MEASUREITEM;
+ ReleaseDC(hwnd, hDC);
- mi.CtlType = ODT_COMBOBOX;
- mi.CtlID = lphc->self->wIDmenu;
- mi.itemID = -1;
- mi.itemWidth = size.cx;
- mi.itemHeight = size.cy - 6; /* ownerdrawn cb is taller */
- mi.itemData = 0;
- SendMessageA(lphc->owner, WM_MEASUREITEM,
- (WPARAM)mi.CtlID, (LPARAM)&mi);
- u = 6 + (UINT)mi.itemHeight;
- }
- else
- size.cx = rect.right - rect.left;
- size.cy = u;
- }
- else
- CBGetDefaultTextHeight( lphc, &size );
+ iTextItemHeight = ((13 * baseUnitY) / 8);
- /* calculate text and button placement */
+ /*
+ * 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();
+ }
- lprEdit->left = lprEdit->top = lprButton->top = 0;
- if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* no button */
- lprButton->left = lprButton->right = lprButton->bottom = 0;
- else
- {
- INT i = size.cx - CBitWidth - 10; /* seems ok */
+ /*
+ * 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;
+ }
- lprButton->right = size.cx;
- lprButton->left = (INT)i;
- lprButton->bottom = lprButton->top + size.cy;
+ /*
+ * Keep the size for the next time
+ */
+ lphc->editHeight = iTextItemHeight;
+ }
- if( i < 0 ) size.cx = 0;
- else size.cx = i;
- }
+ return iTextItemHeight;
+}
- if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
- {
- size.cx -= CBitOffset;
- if( size.cx < 0 ) size.cx = 0;
- }
+/***********************************************************************
+ * 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,
+ NULL,
+ 0, 0,
+ windowRect.right - windowRect.left,
+ newComboHeight,
+ SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
+}
- lprEdit->right = size.cx; lprEdit->bottom = size.cy;
+/***********************************************************************
+ * 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;
+ }
- /* listbox placement */
+ /*
+ * 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();
+ }
- lprLB->left = ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
- lprLB->top = lprEdit->bottom - SYSMETRICS_CYBORDER;
- lprLB->right = rect.right - rect.left;
- lprLB->bottom = rect.bottom - rect.top;
+ /*
+ * 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());
+ }
- if( lphc->droppedWidth > (lprLB->right - lprLB->left) )
+ /*
+ * 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;
+ }
- DPRINT("[%04x]: (%i,%i-%i,%i) placement\n",
- CB_HWND(lphc), lphc->RectCombo.left, lphc->RectCombo.top,
- lphc->RectCombo.right, lphc->RectCombo.bottom);
-
- DPRINT("\ttext\t= (%i,%i-%i,%i)\n",
- lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
+ TRACE("\ttext\t= (%ld,%ld-%ld,%ld)\n",
+ lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
- DPRINT("\tbutton\t= (%i,%i-%i,%i)\n",
- lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
+ TRACE("\tbutton\t= (%ld,%ld-%ld,%ld)\n",
+ lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
- DPRINT("\tlbox\t= (%i,%i-%i,%i)\n",
- lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
+ TRACE("\tlbox\t= (%ld,%ld-%ld,%ld)\n",
+ lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
}
/***********************************************************************
*/
static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
{
- lpRect->left = lphc->RectCombo.left +
- (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
- lpRect->top = lphc->RectCombo.top + lphc->RectEdit.bottom -
- SYSMETRICS_CYBORDER;
- lpRect->right = lphc->RectCombo.right;
- lpRect->bottom = lphc->RectCombo.bottom - SYSMETRICS_CYBORDER;
+ /* 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=%ld, oldtop=%ld\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
*/
-LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
+static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
+ BOOL unicode )
{
- static char clbName[] = "ComboLBox";
- static char editName[] = "Edit";
+ static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
+ static const WCHAR editName[] = {'E','d','i','t',0};
- LPCREATESTRUCT lpcs = (CREATESTRUCT*)lParam;
-
if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
- else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
+ if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
+
+ lphc->owner = hwndParent;
- 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) )
+ if( lphc->owner || !(style & WS_VISIBLE) )
{
- UINT lbeStyle;
- RECT editRect, btnRect, lbRect;
+ 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();
- GetWindowRect( wnd->hwndSelf, &lphc->RectCombo );
+ /*
+ * If it's a dropdown, the listbox is offset
+ */
+ if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
+ lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
- lphc->wState |= CBF_MEASUREITEM;
- CBCalcPlacement( lphc, &editRect, &btnRect, &lbRect );
- lphc->RectButton = btnRect;
- lphc->droppedWidth = lphc->editHeight = 0;
+ ClientToScreen(hwnd, (LPPOINT)&lphc->droppedRect);
+ ClientToScreen(hwnd, (LPPOINT)&lphc->droppedRect.right);
+ }
/* create listbox popup */
- lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
- (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
+ lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
+ (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
if( lphc->dwStyle & CBS_SORT )
lbeStyle |= LBS_SORT;
lbeStyle |= LBS_NOINTEGRALHEIGHT;
if( lphc->dwStyle & CBS_DISABLENOSCROLL )
lbeStyle |= LBS_DISABLENOSCROLL;
-
+
if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
- lbeStyle |= WS_CHILD | WS_VISIBLE;
- else /* popup listbox */
{
- lbeStyle |= WS_POPUP;
- OffsetRect( &lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
+ 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.
+ */
+ lbeStyle &= ~WS_BORDER;
+ lbeExStyle |= WS_EX_CLIENTEDGE;
+ }
+ else
+ {
+ lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
}
- /* Dropdown ComboLBox is not a child window and we cannot pass
- * ID_CB_LISTBOX directly because it will be treated as a menu handle.
- */
+ 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,
+ (HINSTANCE)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,
+ (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE ), lphc );
- lphc->hWndLBox = CreateWindowExA( 0, clbName, NULL, lbeStyle,
- lbRect.left + SYSMETRICS_CXBORDER,
- lbRect.top + SYSMETRICS_CYBORDER,
- lbRect.right - lbRect.left - 2 * SYSMETRICS_CXBORDER,
- lbRect.bottom - lbRect.top - 2 * SYSMETRICS_CYBORDER,
- lphc->self->hwndSelf,
- (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
- lphc->self->hInstance, (LPVOID)lphc );
if( lphc->hWndLBox )
{
- WINBOOL bEdit = TRUE;
- lbeStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NOHIDESEL | ES_LEFT;
- if( lphc->wState & CBF_EDIT )
+ BOOL bEdit = TRUE;
+ lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
+
+ if( lphc->wState & CBF_EDIT )
{
if( lphc->dwStyle & CBS_OEMCONVERT )
lbeStyle |= ES_OEMCONVERT;
lbeStyle |= ES_LOWERCASE;
else if( lphc->dwStyle & CBS_UPPERCASE )
lbeStyle |= ES_UPPERCASE;
- lphc->hWndEdit = CreateWindowExA( 0, editName, NULL, lbeStyle,
- editRect.left, editRect.top, editRect.right - editRect.left,
- editRect.bottom - editRect.top, lphc->self->hwndSelf,
- (HMENU)ID_CB_EDIT, lphc->self->hInstance, NULL );
- if( !lphc->hWndEdit ) bEdit = FALSE;
- }
+
+ 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,
+ (HINSTANCE)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,
+ (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE ), NULL );
+
+ if( !lphc->hWndEdit )
+ bEdit = FALSE;
+ }
if( bEdit )
{
- lphc->RectEdit = editRect;
- if( CB_GETTYPE(lphc) != CBS_SIMPLE )
- {
- lphc->wState |= CBF_NORESIZE;
- SetWindowPos( wnd->hwndSelf, 0, 0, 0,
- lphc->RectCombo.right - lphc->RectCombo.left,
- lphc->RectEdit.bottom - lphc->RectEdit.top,
- SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
- lphc->wState &= ~CBF_NORESIZE;
- }
- DPRINT("init done\n");
- return (LRESULT)wnd->hwndSelf;
+ 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;
}
- DPRINT("edit control failure.\n");
- } else DPRINT("listbox failure.\n");
- } else DPRINT("no owner for visible combo.\n");
+ 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 */
*
* Paint combo button (normal, pressed, and disabled states).
*/
-static void CBPaintButton(LPHEADCOMBO lphc, HDC hdc)
+static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
{
- RECT r;
- UINT x, y;
- WINBOOL bWINBOOL;
- HDC hMemDC;
- HBRUSH hPrevBrush;
- COLORREF oldTextColor, oldBkColor;
-
- if( lphc->wState & CBF_NOREDRAW ) return;
+ UINT buttonState = DFCS_SCROLLCOMBOBOX;
- hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
- r = lphc->RectButton;
-
- Rectangle(hdc, r.left, r.top, r.right, r.bottom );
- if( (bWINBOOL = lphc->wState & CBF_BUTTONDOWN) )
- {
- DrawEdge( hdc, &r, EDGE_SUNKEN, BF_RECT );
- OffsetRect( &r, 1, 1 );
- } else {
- r.top++, r.left++;
- DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
- r.top--, r.left--;
- }
+ if( lphc->wState & CBF_NOREDRAW )
+ return;
- InflateRect( &r, -1, -1 );
- x = (r.left + r.right - CBitWidth) >> 1;
- y = (r.top + r.bottom - CBitHeight) >> 1;
+ if (lphc->wState & CBF_BUTTONDOWN)
+ buttonState |= DFCS_PUSHED;
- InflateRect( &r, -3, -3 );
+ if (CB_DISABLED(lphc))
+ buttonState |= DFCS_INACTIVE;
- 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, 8, 8, hMemDC, 0, 0, SRCCOPY );
- SetBkColor( hdc, oldBkColor );
- SetTextColor( hdc, oldTextColor );
- DeleteDC( hMemDC );
- SelectObject( hdc, hPrevBrush );
+ DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
}
/***********************************************************************
*
* Paint CBS_DROPDOWNLIST text field / update edit control contents.
*/
-static void CBPaintText(LPHEADCOMBO lphc, HDC hdc)
+static void CBPaintText(
+ LPHEADCOMBO lphc,
+ HDC hdc,
+ RECT rectEdit)
{
INT id, size = 0;
- LPSTR pText = NULL;
+ LPWSTR pText = NULL;
if( lphc->wState & CBF_NOREDRAW ) return;
- /* follow Windows combobox that sends a bunch of text
+ TRACE("\n");
+
+ /* 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 )
+ if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
{
- size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
- if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
+ 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))) )
{
- SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
+ /* 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 )
{
- if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
- if( lphc->wState & CBF_FOCUSED )
- SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
+ 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 */
{
- HBRUSH hPrevBrush = 0;
- HDC hDC = hdc;
+ UINT itemState = ODS_COMBOBOXEDIT;
+ HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
- if( !hDC )
- {
- if ((hDC = GetDC(lphc->self->hwndSelf)))
- {
- HBRUSH hBrush = (HBRUSH)SendMessageA( lphc->owner,
- WM_CTLCOLORLISTBOX,
- (WPARAM)hDC, (LPARAM)lphc->self->hwndSelf );
- hPrevBrush = SelectObject( hDC,
- (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
- }
- }
- if( hDC )
- {
- RECT rect;
- UINT itemState;
- HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
-
- PatBlt( hDC, (rect.left = lphc->RectEdit.left + SYSMETRICS_CXBORDER),
- (rect.top = lphc->RectEdit.top + SYSMETRICS_CYBORDER),
- (rect.right = lphc->RectEdit.right - SYSMETRICS_CXBORDER),
- (rect.bottom = lphc->RectEdit.bottom - SYSMETRICS_CYBORDER) - 1, PATCOPY );
- InflateRect( &rect, -1, -1 );
-
- if( lphc->wState & CBF_FOCUSED &&
- !(lphc->wState & CBF_DROPPED) )
- {
- /* highlight */
+ /*
+ * 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=NULL;
+ }
- FillRect( hDC, &rect, GetSysColorBrush(COLOR_HIGHLIGHT) );
- SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
- SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
- itemState = ODS_SELECTED | ODS_FOCUS;
- } else itemState = 0;
+ 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 ) );
+ }
- if( CB_OWNERDRAWN(lphc) )
- {
- DRAWITEMSTRUCT dis;
-
- 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 = rect;
- dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
- (WPARAM)id, 0 );
- SendMessageA( lphc->owner, WM_DRAWITEM,
- lphc->self->wIDmenu, (LPARAM)&dis );
- }
- else
- {
- ExtTextOutA( hDC, rect.left + 1, rect.top + 1,
- ETO_OPAQUE | ETO_CLIPPED, &rect,
- pText ? pText : "" , size, NULL );
- if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
- DrawFocusRect( hDC, &rect );
- }
+ ExtTextOutW( hdc,
+ rectEdit.left + 1,
+ rectEdit.top + 1,
+ ETO_OPAQUE | ETO_CLIPPED,
+ &rectEdit,
+ pText ? pText : empty_stringW , size, NULL );
- if( hPrevFont ) SelectObject(hDC, hPrevFont );
- if( !hdc )
- {
- if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
- ReleaseDC( lphc->self->hwndSelf, hDC );
- }
- }
+ 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 = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
+ (WPARAM)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 = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
+ (WPARAM)hDC, (LPARAM)lphc->self );
+ }
+ else
+ {
+ hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX,
+ (WPARAM)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
*/
{
PAINTSTRUCT ps;
HDC hDC;
-
+
hDC = (hParamDC) ? hParamDC
- : BeginPaint( lphc->self->hwndSelf, &ps);
+ : BeginPaint( lphc->self, &ps);
+
+ TRACE("hdc=%p\n", hDC);
+
if( hDC && !(lphc->wState & CBF_NOREDRAW) )
{
HBRUSH hPrevBrush, hBkgBrush;
- hBkgBrush = (HBRUSH)SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
- (WPARAM)hDC, (LPARAM)lphc->self->hwndSelf );
- if( !hBkgBrush ) hBkgBrush = GetStockObject(WHITE_BRUSH);
+ /*
+ * Retrieve the background brush and select it in the
+ * DC.
+ */
+ hBkgBrush = COMBO_PrepareColors(lphc, hDC);
hPrevBrush = SelectObject( hDC, hBkgBrush );
- if( !IsRectEmpty(&lphc->RectButton) )
- {
- /* paint everything to the right of the text field */
- PatBlt( hDC, lphc->RectEdit.right, lphc->RectEdit.top,
- lphc->RectButton.right - lphc->RectEdit.right,
- lphc->RectEdit.bottom - lphc->RectEdit.top, PATCOPY );
- CBPaintButton( lphc, hDC );
+ /*
+ * In non 3.1 look, there is a sunken border on the combobox
+ */
+ CBPaintBorder(lphc->self, lphc, hDC);
+
+ if( !IsRectEmpty(&lphc->buttonRect) )
+ {
+ CBPaintButton(lphc, hDC, lphc->buttonRect);
}
- if( !(lphc->wState & CBF_EDIT) )
+ /* paint the edit control padding area */
+ if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
{
- /* paint text field */
-
- HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(
- COLOR_WINDOWFRAME) );
-
- Rectangle( hDC, lphc->RectEdit.left, lphc->RectEdit.top,
- lphc->RectEdit.right, lphc->RectButton.bottom );
- SelectObject( hDC, hPrevPen );
- CBPaintText( lphc, hDC );
+ RECT rPadEdit = lphc->textRect;
+
+ InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
+
+ FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
}
- if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
+
+ if( !(lphc->wState & CBF_EDIT) )
+ CBPaintText( lphc, hDC, lphc->textRect);
+
+ if( hPrevBrush )
+ SelectObject( hDC, hPrevBrush );
}
- if( !hParamDC ) EndPaint(lphc->self->hwndSelf, &ps);
+
+ if( !hParamDC )
+ EndPaint(lphc->self, &ps);
+
return 0;
}
*
* Select listbox entry according to the contents of the edit control.
*/
-static INT CBUpdateLBox( LPHEADCOMBO lphc )
+static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
{
- INT length, idx, ret;
- LPSTR pText = NULL;
-
- idx = ret = LB_ERR;
- length = CB_GETEDITTEXTLENGTH( lphc );
-
- if( length > 0 )
- pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
+ INT length, idx;
+ LPWSTR pText = NULL;
+
+ idx = LB_ERR;
+ length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
- DPRINT("\t edit text length %i\n", length );
+ if( length > 0 )
+ pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
+
+ TRACE("\t edit text length %i\n", length );
if( pText )
{
- if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
+ if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
else pText[0] = '\0';
- idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
+ idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
(WPARAM)(-1), (LPARAM)pText );
- if( idx == LB_ERR ) idx = 0; /* select first item */
- else ret = idx;
HeapFree( GetProcessHeap(), 0, pText );
}
- /* select entry */
+ SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
- SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
-
- if( idx >= 0 )
- {
- SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
- /* probably superfluous but Windows sends this too */
- SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
- }
- return ret;
+ /* 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;
}
/***********************************************************************
static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
{
INT length;
- LPSTR pText = NULL;
-
- DPRINT("\t %i\n", index );
+ LPWSTR pText = NULL;
+ static const WCHAR empty_stringW[] = { 0 };
- if( index == -1 )
- {
- length = CB_GETEDITTEXTLENGTH( lphc );
- if( length )
- {
- if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
- {
- GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
- index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
- (WPARAM)(-1), (LPARAM)pText );
- HeapFree( GetProcessHeap(), 0, pText );
- }
- }
- }
+ TRACE("\t %i\n", index );
if( index >= 0 ) /* got an entry */
{
- length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
- if( length )
+ length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
+ if( length != LB_ERR)
{
- if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
+ if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
{
- SendMessageA( lphc->hWndLBox, LB_GETTEXT,
+ SendMessageW(lphc->hWndLBox, LB_GETTEXT,
(WPARAM)index, (LPARAM)pText );
- SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
- SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
- HeapFree( GetProcessHeap(), 0, 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 )
{
- INT index;
- RECT rect;
- LPRECT pRect = NULL;
+ RECT rect,r;
+ int nItems = 0;
+ int nDroppedHeight;
- DPRINT("[%04x]: drop down\n", CB_HWND(lphc));
+ TRACE("[%p]: drop down\n", lphc->self);
CB_NOTIFY( lphc, CBN_DROPDOWN );
lphc->wState |= CBF_DROPPED;
if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
{
- index = CBUpdateLBox( lphc );
- if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index );
+ 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
{
- index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
- if( index == LB_ERR ) index = 0;
- SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)index, 0 );
- SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
- pRect = &lphc->RectEdit;
+ 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);
- GetWindowRect( lphc->self->hwndSelf, &rect );
-
- rect.top += lphc->RectEdit.bottom - lphc->RectEdit.top - SYSMETRICS_CYBORDER;
- rect.bottom = rect.top + lphc->RectCombo.bottom -
- lphc->RectCombo.top - SYSMETRICS_CYBORDER;
- rect.right = rect.left + lphc->RectCombo.right - lphc->RectCombo.left;
- rect.left += ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
+ nHeight = nIHeight*nItems;
+
+ if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
+ nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
+
+ if (nDroppedHeight < nIHeight)
+ {
+ if (nItems < 5)
+ nDroppedHeight = (nItems+1)*nIHeight;
+ else
+ nDroppedHeight = 6*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);
- SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.top,
- rect.right - rect.left, rect.bottom - rect.top,
- SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW);
if( !(lphc->wState & CBF_NOREDRAW) )
- if( pRect )
- RedrawWindow( lphc->self->hwndSelf, pRect, 0, RDW_INVALIDATE |
+ RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
- ShowWindow( lphc->hWndLBox, SW_SHOWNA );
+
+ EnableWindow( lphc->hWndLBox, TRUE );
+ if (GetCapture() != lphc->self)
+ SetCapture(lphc->hWndLBox);
}
/***********************************************************************
*
* Hide listbox popup.
*/
-static void CBRollUp( LPHEADCOMBO lphc, WINBOOL ok, WINBOOL bButton )
+static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
{
- HWND hWnd = lphc->self->hwndSelf;
+ HWND hWnd = lphc->self;
+
+ TRACE("[%p]: 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 )
{
- DPRINT("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
-
- /* always send WM_LBUTTONUP? */
- SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
-
- if( lphc->wState & CBF_DROPPED )
+ 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 )
{
- INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
- CBUpdateEdit( lphc, index );
- rect = lphc->RectButton;
+ rect = lphc->buttonRect;
}
- else
+ else
{
if( bButton )
- UnionRect( &rect, &lphc->RectButton,
- &lphc->RectEdit );
+ {
+ UnionRect( &rect,
+ &lphc->buttonRect,
+ &lphc->textRect);
+ }
else
- rect = lphc->RectEdit;
+ rect = lphc->textRect;
+
bButton = TRUE;
}
if( bButton && !(lphc->wState & CBF_NOREDRAW) )
- RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
+ RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
CB_NOTIFY( lphc, CBN_CLOSEUP );
}
*
* Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
*/
-WINBOOL COMBO_FlipListbox( LPHEADCOMBO lphc, WINBOOL bRedrawButton )
+BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
{
if( lphc->wState & CBF_DROPPED )
{
- CBRollUp( lphc, TRUE, bRedrawButton );
+ CBRollUp( lphc, ok, bRedrawButton );
return FALSE;
}
return TRUE;
}
-/***********************************************************************
- * COMBO_GetLBWindow
- *
- * Edit control helper.
- */
-HWND COMBO_GetLBWindow( WND* pWnd )
-{
- LPHEADCOMBO lphc = CB_GETPTR(pWnd);
- if( lphc ) return lphc->hWndLBox;
- return 0;
-}
-
-
/***********************************************************************
* CBRepaintButton
*/
static void CBRepaintButton( LPHEADCOMBO lphc )
-{
- HDC hDC = GetDC( lphc->self->hwndSelf );
-
- if( hDC )
{
- CBPaintButton( lphc, hDC );
- ReleaseDC( lphc->self->hwndSelf, hDC );
- }
+ InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
+ UpdateWindow(lphc->self);
}
/***********************************************************************
if( !(lphc->wState & CBF_FOCUSED) )
{
if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
- SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
+ SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
- if( lphc->wState & CBF_EDIT )
- SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
- lphc->wState |= CBF_FOCUSED;
- if( !(lphc->wState & CBF_EDIT) ) CBPaintText( lphc, 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;
}
}
*/
static void COMBO_KillFocus( LPHEADCOMBO lphc )
{
- HWND hWnd = lphc->self->hwndSelf;
+ HWND hWnd = lphc->self;
if( lphc->wState & CBF_FOCUSED )
{
- SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
-
CBRollUp( lphc, FALSE, TRUE );
if( IsWindow( hWnd ) )
{
if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
- SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
+ SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
lphc->wState &= ~CBF_FOCUSED;
/* redraw text */
- if( lphc->wState & CBF_EDIT )
- SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
- else CBPaintText( lphc, 0 );
+ if( !(lphc->wState & CBF_EDIT) )
+ InvalidateRect(lphc->self, &lphc->textRect, TRUE);
CB_NOTIFY( lphc, CBN_KILLFOCUS );
}
*/
static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
{
- if( lphc->wState & CBF_EDIT && lphc->hWndEdit == 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):
- DPRINT("[%04x]: edit [%04x] got focus\n",
- CB_HWND(lphc), lphc->hWndEdit );
+ TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
- if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
+ COMBO_SetFocus( lphc );
break;
case (EN_KILLFOCUS >> 8):
- DPRINT("[%04x]: edit [%04x] lost focus\n",
- CB_HWND(lphc), lphc->hWndEdit );
+ TRACE("[%p]: edit [%p] 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
+ * notification (only when it happens to be a part of
* the combo). ?? - AK.
*/
case (EN_CHANGE >> 8):
- CB_NOTIFY( lphc, CBN_EDITCHANGE );
- CBUpdateLBox( lphc );
+ /*
+ * 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):
- CB_NOTIFY( lphc, CBN_EDITUPDATE );
+ if (!(lphc->wState & CBF_NOEDITNOTIFY))
+ CB_NOTIFY( lphc, CBN_EDITUPDATE );
break;
case (EN_ERRSPACE >> 8):
}
else if( lphc->hWndLBox == hWnd )
{
- switch( HIWORD(wParam) )
+ switch( (short)HIWORD(wParam) )
{
case LBN_ERRSPACE:
CB_NOTIFY( lphc, CBN_ERRSPACE );
case LBN_SELCHANGE:
case LBN_SELCANCEL:
- DPRINT("[%04x]: lbox selection change [%04x]\n",
- CB_HWND(lphc), lphc->wState );
+ TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
- /* do not roll up if selection is being tracked
- * by arrowkeys in the dropdown listbox */
+ 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);
+ }
- if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
- CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), 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 );
- CBPaintText( lphc, 0 );
+
/* fall through */
case LBN_SETFOCUS:
*
* 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 )
+static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
{
- HWND hWnd = lphc->self->hwndSelf;
-
- DPRINT("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
+ HWND hWnd = lphc->self;
+ UINT id = GetWindowLongA( hWnd, GWL_ID );
-#define lpIS ((LPDELETEITEMSTRUCT)lParam)
+ TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
- /* two first items are the same in all 4 structs */
- lpIS->CtlType = ODT_COMBOBOX;
- lpIS->CtlID = lphc->self->wIDmenu;
-
- switch( msg ) /* patch window handle */
+ switch( msg )
{
- 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;
+ 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 SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
+ return SendMessageW(lphc->owner, msg, id, lParam);
}
+
/***********************************************************************
- * COMBO_GetText
+ * COMBO_GetTextW
*/
-static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
+static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
{
- if( lphc->wState & CBF_EDIT )
- return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
- (WPARAM)N, (LPARAM)lpText );
+ INT length;
- /* get it from the listbox */
+ if( lphc->wState & CBF_EDIT )
+ return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
- 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 );
+ /* get it from the listbox */
- /* 'length' is without the terminating character */
- if( length >= N )
- lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
- else
- lpBuffer = lpText;
+ 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( lpBuffer )
- {
- INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
- (WPARAM)idx, (LPARAM)lpBuffer );
+ if (length == LB_ERR) return 0;
+ return length;
+ }
- /* truncate if buffer is too short */
+ error: /* error - truncate string, return zero */
+ buf[0] = 0;
+ return 0;
+}
- 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;
+
+/***********************************************************************
+ * 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
+ * This function sets window positions according to the updated
* component placement struct.
*/
-static void CBResetPos( LPHEADCOMBO lphc, LPRECT lbRect, WINBOOL bRedraw )
+static void CBResetPos(
+ LPHEADCOMBO lphc,
+ LPRECT rectEdit,
+ LPRECT rectLB,
+ BOOL bRedraw)
{
- WINBOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
+ 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, lphc->RectEdit.left, lphc->RectEdit.top,
- lphc->RectEdit.right - lphc->RectEdit.left,
- lphc->RectEdit.bottom - lphc->RectEdit.top,
+ SetWindowPos( lphc->hWndEdit, 0,
+ rectEdit->left, rectEdit->top,
+ rectEdit->right - rectEdit->left,
+ rectEdit->bottom - rectEdit->top,
SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
- if( bDrop )
- OffsetRect( lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
-
- lbRect->right -= lbRect->left; /* convert to width */
- lbRect->bottom -= lbRect->top;
- SetWindowPos( lphc->hWndLBox, 0, lbRect->left, lbRect->top,
- lbRect->right, lbRect->bottom,
+ 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 )
ShowWindow( lphc->hWndLBox, SW_HIDE );
}
- lphc->wState |= CBF_NORESIZE;
- SetWindowPos( lphc->self->hwndSelf, 0, 0, 0,
- lphc->RectCombo.right - lphc->RectCombo.left,
- lphc->RectEdit.bottom - lphc->RectEdit.top,
- SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW );
- lphc->wState &= ~CBF_NORESIZE;
-
if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
- RedrawWindow( lphc->self->hwndSelf, NULL, 0,
+ RedrawWindow( lphc->self, NULL, 0,
RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
}
}
* COMBO_Size
*/
static void COMBO_Size( LPHEADCOMBO lphc )
-{
- RECT rect;
- INT w, h;
-
- GetWindowRect( lphc->self->hwndSelf, &rect );
- w = rect.right - rect.left; h = rect.bottom - rect.top;
-
- DPRINT("w = %i, h = %i\n", w, h );
-
- /* CreateWindow() may send a bogus WM_SIZE, ignore it */
-
- if( w == (lphc->RectCombo.right - lphc->RectCombo.left) )
{
- if( (CB_GETTYPE(lphc) == CBS_SIMPLE) &&
- (h == (lphc->RectCombo.bottom - lphc->RectCombo.top)) )
- return;
- else if( (lphc->dwStyle & CBS_DROPDOWN) &&
- (h == (lphc->RectEdit.bottom - lphc->RectEdit.top)) )
- return;
- }
- lphc->RectCombo = rect;
- CBCalcPlacement( lphc, &lphc->RectEdit, &lphc->RectButton, &rect );
- CBResetPos( lphc, &rect, TRUE );
+ 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, WINBOOL bRedraw )
+static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
{
- RECT rect;
-
+ /*
+ * 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 );
-
- GetWindowRect( lphc->self->hwndSelf, &rect );
- OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
- rect.top - lphc->RectCombo.top );
- CBCalcPlacement( lphc, &lphc->RectEdit,
- &lphc->RectButton, &rect );
- CBResetPos( lphc, &rect, bRedraw );
+ 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);
+ }
}
if( index == -1 ) /* set text field height */
{
- if( height < 768 )
+ if( height < 32768 )
{
- RECT rect;
-
lphc->editHeight = height;
- GetWindowRect( lphc->self->hwndSelf, &rect );
- OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
- rect.top - lphc->RectCombo.top );
- CBCalcPlacement( lphc, &lphc->RectEdit,
- &lphc->RectButton, &rect );
- CBResetPos( lphc, &rect, TRUE );
+
+ /*
+ * 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 = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
+ lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
(WPARAM)index, (LPARAM)height );
return lRet;
}
/***********************************************************************
* COMBO_SelectString
*/
-static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
+static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
{
- INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
- (WPARAM)start, (LPARAM)pText );
+ 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
- CBPaintText( lphc, 0 );
+ if( lphc->wState & CBF_EDIT )
+ CBUpdateEdit( lphc, index );
+ else
+ {
+ InvalidateRect(lphc->self, &lphc->textRect, TRUE);
+ }
}
return (LRESULT)index;
}
*/
static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
{
- POINT pt = { LOWORD(lParam), HIWORD(lParam) };
- WINBOOL bButton = PtInRect(&lphc->RectButton, pt);
- HWND hWnd = lphc->self->hwndSelf;
+ 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)) )
{
/* got a click to cancel selection */
+ lphc->wState &= ~CBF_BUTTONDOWN;
CBRollUp( lphc, TRUE, FALSE );
if( !IsWindow( hWnd ) ) return;
lphc->wState &= ~CBF_CAPTURE;
ReleaseCapture();
}
- lphc->wState &= ~CBF_BUTTONDOWN;
}
else
{
/* drop down the listbox and start tracking */
lphc->wState |= CBF_CAPTURE;
- CBDropDown( lphc );
SetCapture( hWnd );
+ CBDropDown( lphc );
}
if( bButton ) CBRepaintButton( lphc );
}
*
* Release capture and stop tracking if needed.
*/
-static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
+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 );
- CBUpdateEdit( lphc, index );
+ 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 )
*/
static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
{
- POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ POINT pt;
RECT lbRect;
+ pt.x = LOWORD(lParam);
+ pt.y = HIWORD(lParam);
+
if( lphc->wState & CBF_BUTTONDOWN )
{
- WINBOOL bButton = PtInRect(&lphc->RectButton, pt);
+ BOOL bButton;
- if( !bButton )
- {
- lphc->wState &= ~CBF_BUTTONDOWN;
- CBRepaintButton( lphc );
- }
+ 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 );
+ MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
if( PtInRect(&lbRect, pt) )
{
lphc->wState &= ~CBF_CAPTURE;
ReleaseCapture();
- if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
+ if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
/* hand over pointer tracking */
- SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
+ SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
}
}
/***********************************************************************
- * ComboWndProc
+ * ComboWndProc_common
*
- * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win/ctrl/src/combobox_15.htm
+ * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
*/
-LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
- WPARAM wParam, LPARAM lParam )
+static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam, BOOL unicode )
{
- WND* pWnd = WIN_FindWndPtr(hwnd);
-
- if( pWnd )
- {
- LPHEADCOMBO lphc = CB_GETPTR(pWnd);
+ LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 );
- DPRINT( "[%04x]: msg %s wp %08x lp %08lx\n",
- pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
+ //TRACE("[%p]: msg %s wp %08x lp %08lx\n",
+ // hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
if( lphc || message == WM_NCCREATE )
- switch(message)
- {
+ switch(message)
+ {
/* System messages */
- case WM_NCCREATE:
- return COMBO_NCCreate(pWnd, lParam);
-
- case WM_NCDESTROY:
+ case WM_NCCREATE:
+ {
+ LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
+ ((LPCREATESTRUCTA)lParam)->style;
+ return COMBO_NCCreate(hwnd, style);
+ }
+ case WM_NCDESTROY:
COMBO_NCDestroy(lphc);
- break;
+ 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_CREATE:
- return COMBO_Create(lphc, pWnd, lParam);
+ case WM_PRINTCLIENT:
+ if (lParam & PRF_ERASEBKGND)
+ COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
+ /* Fallthrough */
case WM_PAINT:
/* wParam may contain a valid HDC! */
- return COMBO_Paint(lphc, wParam);
-
+ return COMBO_Paint(lphc, (HDC)wParam);
case WM_ERASEBKGND:
- return TRUE;
-
- case WM_GETDLGCODE:
- return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
+ return COMBO_EraseBackground(hwnd, lphc, (HDC)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 &&
+ if( lphc->hWndLBox &&
!(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
- return TRUE;
-
+ return TRUE;
case WM_SETFONT:
- COMBO_Font( lphc, (HFONT)wParam, (WINBOOL)lParam );
- return TRUE;
-
+ COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
+ return TRUE;
case WM_GETFONT:
- return (LRESULT)lphc->hFont;
-
+ return (LRESULT)lphc->hFont;
case WM_SETFOCUS:
if( lphc->wState & CBF_EDIT )
SetFocus( lphc->hWndEdit );
else
COMBO_SetFocus( lphc );
- return TRUE;
-
+ return TRUE;
case WM_KILLFOCUS:
-#define hwndFocus ((HWND)wParam)
+ {
+#ifdef __REACTOS__
+ HWND hwndFocus = (HWND)wParam;
+#else
+ HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
+#endif
if( !hwndFocus ||
(hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
COMBO_KillFocus( lphc );
-#undef hwndFocus
- return TRUE;
-
+ return TRUE;
+ }
case WM_COMMAND:
- return COMBO_Command( lphc, wParam, (HWND)lParam );
-
+#ifdef __REACTOS__
+ return COMBO_Command( lphc, wParam, (HWND)lParam);
+#else
+ return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
+#endif
case WM_GETTEXT:
- return COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
-
+ 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 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
- return CB_ERR;
+ {
+ 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, wParam, lParam );
-
+ return COMBO_ItemOp(lphc, message, lParam);
case WM_ENABLE:
if( lphc->wState & CBF_EDIT )
- EnableWindow( lphc->hWndEdit, (WINBOOL)wParam );
- EnableWindow( lphc->hWndLBox, (WINBOOL)wParam );
- return TRUE;
+ 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;
lphc->wState |= CBF_NOREDRAW;
if( lphc->wState & CBF_EDIT )
- SendMessageA( lphc->hWndEdit, message, wParam, lParam );
- SendMessageA( lphc->hWndLBox, message, wParam, lParam );
- return 0;
-
+ 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, TRUE );
- break;
+ COMBO_FlipListbox( lphc, FALSE, FALSE );
+ return 0;
case WM_CHAR:
+ case WM_IME_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;
+ }
+ else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
+ {
+ COMBO_FlipListbox( lphc, FALSE, FALSE );
+ return TRUE;
+ }
+
if( lphc->wState & CBF_EDIT )
- return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
+ hwndTarget = lphc->hWndEdit;
else
- return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
+ hwndTarget = lphc->hWndLBox;
- case WM_LBUTTONDOWN:
- if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
+ 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;
-
+ return TRUE;
case WM_LBUTTONUP:
- COMBO_LButtonUp( lphc, lParam );
- return TRUE;
-
- case WM_MOUSEMOVE:
- if( lphc->wState & CBF_CAPTURE )
+ COMBO_LButtonUp( lphc );
+ return TRUE;
+ case WM_MOUSEMOVE:
+ if( lphc->wState & CBF_CAPTURE )
COMBO_MouseMove( lphc, wParam, lParam );
- return TRUE;
+ return TRUE;
+
+ case WM_MOUSEWHEEL:
+ if (wParam & (MK_SHIFT | MK_CONTROL))
+ return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
+ DefWindowProcA(hwnd, message, wParam, lParam);
+
+ if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
+ if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
+ return TRUE;
/* Combo messages */
+#ifndef __REACTOS__
+ case CB_ADDSTRING16:
+ if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
+ /* fall through */
+#endif
case CB_ADDSTRING:
- return SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
-
+ if( unicode )
+ {
+ if( lphc->dwStyle & CBS_LOWERCASE )
+ strlwrW((LPWSTR)lParam);
+ else if( lphc->dwStyle & CBS_UPPERCASE )
+ struprW((LPWSTR)lParam);
+ return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
+ }
+ else
+ {
+ if( lphc->dwStyle & CBS_LOWERCASE )
+ _strlwr((LPSTR)lParam);
+ else if( lphc->dwStyle & CBS_UPPERCASE )
+ _strupr((LPSTR)lParam);
+ return SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
+ }
+#ifndef __REACTOS__
+ case CB_INSERTSTRING16:
+ wParam = (INT)(INT16)wParam;
+ if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
+ /* fall through */
+#endif
case CB_INSERTSTRING:
- return SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
-
+ if( unicode )
+ {
+ if( lphc->dwStyle & CBS_LOWERCASE )
+ strlwrW((LPWSTR)lParam);
+ else if( lphc->dwStyle & CBS_UPPERCASE )
+ struprW((LPWSTR)lParam);
+ return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
+ }
+ else
+ {
+ if( lphc->dwStyle & CBS_LOWERCASE )
+ _strlwr((LPSTR)lParam);
+ else if( lphc->dwStyle & CBS_UPPERCASE )
+ _strupr((LPSTR)lParam);
+ return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
+ }
+#ifndef __REACTOS__
+ case CB_DELETESTRING16:
+#endif
case CB_DELETESTRING:
- return SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
-
+ return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
+ SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
+#ifndef __REACTOS__
+ case CB_SELECTSTRING16:
+ wParam = (INT)(INT16)wParam;
+ if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
+ /* fall through */
+#endif
case CB_SELECTSTRING:
- return COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
-
+ return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
+#ifndef __REACTOS__
+ case CB_FINDSTRING16:
+ wParam = (INT)(INT16)wParam;
+ if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
+ /* fall through */
+#endif
case CB_FINDSTRING:
- return SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
-
+ return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
+ SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
+#ifndef __REACTOS__
+ case CB_FINDSTRINGEXACT16:
+ wParam = (INT)(INT16)wParam;
+ if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
+ /* fall through */
+#endif
case CB_FINDSTRINGEXACT:
- return SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
- wParam, lParam );
-
+ return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
+ SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
+#ifndef __REACTOS__
+ case CB_SETITEMHEIGHT16:
+ wParam = (INT)(INT16)wParam; /* signed integer */
+ /* fall through */
+#endif
case CB_SETITEMHEIGHT:
- return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
-
+ return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
+#ifndef __REACTOS__
+ case CB_GETITEMHEIGHT16:
+ wParam = (INT)(INT16)wParam;
+ /* fall through */
+#endif
case CB_GETITEMHEIGHT:
if( (INT)wParam >= 0 ) /* listbox item */
- return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
- return (lphc->RectEdit.bottom - lphc->RectEdit.top);
-
+ return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
+ return CBGetTextAreaHeight(hwnd, lphc);
+#ifndef __REACTOS__
+ case CB_RESETCONTENT16:
+#endif
case CB_RESETCONTENT:
- SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
- CBPaintText( lphc, 0 );
- return TRUE;
-
+ 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 SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
-
+ return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
case CB_GETHORIZONTALEXTENT:
- return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
-
+ return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
case CB_SETHORIZONTALEXTENT:
- return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
-
+ return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
case CB_GETTOPINDEX:
- return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
-
+ return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
case CB_GETLOCALE:
- return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
-
+ return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
case CB_SETLOCALE:
- return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
-
+ return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
case CB_GETDROPPEDWIDTH:
if( lphc->droppedWidth )
- return lphc->droppedWidth;
- return lphc->RectCombo.right - lphc->RectCombo.left -
- (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
-
+ return lphc->droppedWidth;
+ return lphc->droppedRect.right - lphc->droppedRect.left;
case CB_SETDROPPEDWIDTH:
if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
- (INT)wParam < 768 ) lphc->droppedWidth = (INT)wParam;
- return CB_ERR;
-
+ (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
+ return CB_ERR;
+#ifndef __REACTOS__
+ case CB_GETDROPPEDCONTROLRECT16:
+ lParam = (LPARAM)MapSL(lParam);
+ if( lParam )
+ {
+ RECT r;
+ CBGetDroppedControlRect( lphc, &r );
+ CONV_RECT32TO16( &r, (LPRECT16)lParam );
+ }
+ return CB_OKAY;
+#endif
case CB_GETDROPPEDCONTROLRECT:
if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
- return CB_OKAY;
-
+ return CB_OKAY;
+#ifndef __REACTOS__
+ case CB_GETDROPPEDSTATE16:
+#endif
case CB_GETDROPPEDSTATE:
- return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
-
-
+ return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
+#ifndef __REACTOS__
+ case CB_DIR16:
+ return SendMessageA(lphc->hWndLBox, LB_DIR16, wParam, lParam);
+#endif
case CB_DIR:
- return COMBO_Directory( lphc, (UINT)wParam,
- (LPSTR)lParam, (message == CB_DIR));
+ return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
+ SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
+
+#ifndef __REACTOS__
+ case CB_SHOWDROPDOWN16:
+#endif
case CB_SHOWDROPDOWN:
if( CB_GETTYPE(lphc) != CBS_SIMPLE )
{
if( !(lphc->wState & CBF_DROPPED) )
CBDropDown( lphc );
}
- else
- if( lphc->wState & CBF_DROPPED )
+ else
+ if( lphc->wState & CBF_DROPPED )
CBRollUp( lphc, FALSE, TRUE );
}
- return TRUE;
-
+ return TRUE;
+#ifndef __REACTOS__
+ case CB_GETCOUNT16:
+#endif
case CB_GETCOUNT:
- return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
-
+ return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
+#ifndef __REACTOS__
+ case CB_GETCURSEL16:
+#endif
case CB_GETCURSEL:
- return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
-
+ return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
+#ifndef __REACTOS__
+ case CB_SETCURSEL16:
+ wParam = (INT)(INT16)wParam;
+ /* fall through */
+#endif
case CB_SETCURSEL:
- lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
- if( lphc->wState & CBF_SELCHANGE )
- {
- /* no LBN_SELCHANGE in this case, update manually */
-
- CBPaintText( lphc, 0 );
- lphc->wState &= ~CBF_SELCHANGE;
- }
- return lParam;
-
+ 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;
+#ifndef __REACTOS__
+ case CB_GETLBTEXT16:
+ wParam = (INT)(INT16)wParam;
+ lParam = (LPARAM)MapSL(lParam);
+ /* fall through */
+#endif
case CB_GETLBTEXT:
- return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
-
+ return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
+ SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
+#ifndef __REACTOS__
+ case CB_GETLBTEXTLEN16:
+ wParam = (INT)(INT16)wParam;
+ /* fall through */
+#endif
case CB_GETLBTEXTLEN:
- return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
-
+ return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
+ SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
+#ifndef __REACTOS__
+ case CB_GETITEMDATA16:
+ wParam = (INT)(INT16)wParam;
+ /* fall through */
+#endif
case CB_GETITEMDATA:
- return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
-
+ return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
+#ifndef __REACTOS__
+ case CB_SETITEMDATA16:
+ wParam = (INT)(INT16)wParam;
+ /* fall through */
+#endif
case CB_SETITEMDATA:
- return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
-
+ return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
+#ifndef __REACTOS__
+ case CB_GETEDITSEL16:
+ wParam = lParam = 0; /* just in case */
+ /* fall through */
+#endif
case CB_GETEDITSEL:
+ /* Edit checks passed parameters itself */
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;
-
-
+ return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
+ return CB_ERR;
+#ifndef __REACTOS__
+ case CB_SETEDITSEL16:
+#endif
case CB_SETEDITSEL:
- if( lphc->wState & CBF_EDIT )
- return SendMessageA( lphc->hWndEdit, EM_SETSEL,
- (INT)(INT)LOWORD(lParam), (INT)(INT)HIWORD(lParam) );
- return CB_ERR;
-
-
+ if( lphc->wState & CBF_EDIT )
+ return SendMessageW(lphc->hWndEdit, EM_SETSEL,
+ (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
+ return CB_ERR;
+#ifndef __REACTOS__
+ case CB_SETEXTENDEDUI16:
+#endif
case CB_SETEXTENDEDUI:
- if( CB_GETTYPE(lphc) == CBS_SIMPLE ) return CB_ERR;
-
+ if( CB_GETTYPE(lphc) == CBS_SIMPLE )
+ return CB_ERR;
if( wParam )
lphc->wState |= CBF_EUI;
else lphc->wState &= ~CBF_EUI;
- return CB_OKAY;
+ return CB_OKAY;
+#ifndef __REACTOS__
+ case CB_GETEXTENDEDUI16:
+#endif
+ 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);
+}
- case CB_GETEXTENDEDUI:
- return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
+/***********************************************************************
+ * 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 );
+}
- case (WM_USER + 0x1B):
- DPRINT( "[%04x]: undocumented msg!\n", hwnd );
- }
- return DefWindowProcA(hwnd, message, wParam, lParam);
- }
- return CB_ERR;
+/***********************************************************************
+ * 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 );
}
+/*************************************************************************
+ * GetComboBoxInfo (USER32.@)
+ */
+BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
+ PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
+{
+ FIXME("\n");
+ return FALSE;
+
+}