📄 comboex.c
字号:
if ((cit->mask & CBEIF_TEXT) && is_textA(cit->pszText)) {
INT len = MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, NULL, 0);
wstr = (LPWSTR)Alloc ((len + 1)*sizeof(WCHAR));
if (!wstr) return FALSE;
MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, wstr, len);
citW.pszText = wstr;
}
ret = COMBOEX_SetItemW(infoPtr, &citW);
if (wstr) Free(wstr);
return ret;
}
static BOOL COMBOEX_SetUnicodeFormat (COMBOEX_INFO *infoPtr, BOOL value)
{
BOOL bTemp = infoPtr->unicode;
TRACE("to %s, was %s\n", value ? "TRUE":"FALSE", bTemp ? "TRUE":"FALSE");
infoPtr->unicode = value;
return bTemp;
}
/* *** CB_xxx message support *** */
static INT
COMBOEX_FindStringExact (COMBOEX_INFO *infoPtr, INT start, LPCWSTR str)
{
INT i;
cmp_func_t cmptext = get_cmp_func(infoPtr);
INT count = SendMessageW (infoPtr->hwndCombo, CB_GETCOUNT, 0, 0);
/* now search from after starting loc and wrapping back to start */
for(i=start+1; i<count; i++) {
CBE_ITEMDATA *item = get_item_data(infoPtr, i);
if (cmptext(COMBOEX_GetText(infoPtr, item), str) == 0) return i;
}
for(i=0; i<=start; i++) {
CBE_ITEMDATA *item = get_item_data(infoPtr, i);
if (cmptext(COMBOEX_GetText(infoPtr, item), str) == 0) return i;
}
return CB_ERR;
}
static DWORD_PTR COMBOEX_GetItemData (COMBOEX_INFO *infoPtr, INT index)
{
CBE_ITEMDATA *item1, *item2;
DWORD_PTR ret = 0;
item1 = get_item_data(infoPtr, index);
if ((item1 != NULL) && ((LRESULT)item1 != CB_ERR)) {
item2 = COMBOEX_FindItem (infoPtr, index);
if (item2 != item1) {
ERR("data structures damaged!\n");
return CB_ERR;
}
if (item1->mask & CBEIF_LPARAM) ret = item1->lParam;
TRACE("returning 0x%08lx\n", ret);
} else {
ret = (DWORD_PTR)item1;
TRACE("non-valid result from combo, returning 0x%08lx\n", ret);
}
return ret;
}
static INT COMBOEX_SetCursel (COMBOEX_INFO *infoPtr, INT index)
{
CBE_ITEMDATA *item;
INT sel;
if (!(item = COMBOEX_FindItem(infoPtr, index)))
return SendMessageW (infoPtr->hwndCombo, CB_SETCURSEL, index, 0);
TRACE("selecting item %d text=%s\n", index, debugstr_txt(item->pszText));
infoPtr->selected = index;
sel = (INT)SendMessageW (infoPtr->hwndCombo, CB_SETCURSEL, index, 0);
COMBOEX_SetEditText (infoPtr, item);
return sel;
}
static DWORD_PTR COMBOEX_SetItemData (COMBOEX_INFO *infoPtr, INT index, DWORD_PTR data)
{
CBE_ITEMDATA *item1, *item2;
item1 = get_item_data(infoPtr, index);
if ((item1 != NULL) && ((LRESULT)item1 != CB_ERR)) {
item2 = COMBOEX_FindItem (infoPtr, index);
if (item2 != item1) {
ERR("data structures damaged!\n");
return CB_ERR;
}
item1->mask |= CBEIF_LPARAM;
item1->lParam = data;
TRACE("setting lparam to 0x%08lx\n", data);
return 0;
}
TRACE("non-valid result from combo %p\n", item1);
return (DWORD_PTR)item1;
}
static INT COMBOEX_SetItemHeight (COMBOEX_INFO *infoPtr, INT index, UINT height)
{
RECT cb_wrect, cbx_wrect, cbx_crect;
/* First, lets forward the message to the normal combo control
just like Windows. */
if (infoPtr->hwndCombo)
if (SendMessageW (infoPtr->hwndCombo, CB_SETITEMHEIGHT,
index, height) == CB_ERR) return CB_ERR;
GetWindowRect (infoPtr->hwndCombo, &cb_wrect);
GetWindowRect (infoPtr->hwndSelf, &cbx_wrect);
GetClientRect (infoPtr->hwndSelf, &cbx_crect);
/* the height of comboex as height of the combo + comboex border */
height = cb_wrect.bottom-cb_wrect.top
+ cbx_wrect.bottom-cbx_wrect.top
- (cbx_crect.bottom-cbx_crect.top);
TRACE("EX window=(%d,%d)-(%d,%d), client=(%d,%d)-(%d,%d)\n",
cbx_wrect.left, cbx_wrect.top, cbx_wrect.right, cbx_wrect.bottom,
cbx_crect.left, cbx_crect.top, cbx_crect.right, cbx_crect.bottom);
TRACE("CB window=(%d,%d)-(%d,%d), EX setting=(0,0)-(%d,%d)\n",
cb_wrect.left, cb_wrect.top, cb_wrect.right, cb_wrect.bottom,
cbx_wrect.right-cbx_wrect.left, height);
SetWindowPos (infoPtr->hwndSelf, HWND_TOP, 0, 0,
cbx_wrect.right-cbx_wrect.left, height,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
return 0;
}
/* *** WM_xxx message support *** */
static LRESULT COMBOEX_Create (HWND hwnd, LPCREATESTRUCTA cs)
{
static const WCHAR COMBOBOX[] = { 'C', 'o', 'm', 'b', 'o', 'B', 'o', 'x', 0 };
static const WCHAR EDIT[] = { 'E', 'D', 'I', 'T', 0 };
static const WCHAR NIL[] = { 0 };
COMBOEX_INFO *infoPtr;
LOGFONTW mylogfont;
RECT wnrc1, clrc1, cmbwrc;
INT i;
/* allocate memory for info structure */
infoPtr = (COMBOEX_INFO *)Alloc (sizeof(COMBOEX_INFO));
if (!infoPtr) return -1;
/* initialize info structure */
/* note that infoPtr is allocated zero-filled */
infoPtr->hwndSelf = hwnd;
infoPtr->selected = -1;
infoPtr->unicode = IsWindowUnicode (hwnd);
infoPtr->hwndNotify = cs->hwndParent;
i = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_QUERY);
if ((i != NFR_ANSI) && (i != NFR_UNICODE)) {
WARN("wrong response to WM_NOTIFYFORMAT (%d), assuming ANSI\n", i);
i = NFR_ANSI;
}
infoPtr->NtfUnicode = (i == NFR_UNICODE);
SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
/* create combo box */
GetWindowRect(hwnd, &wnrc1);
GetClientRect(hwnd, &clrc1);
TRACE("EX window=(%d,%d)-(%d,%d) client=(%d,%d)-(%d,%d)\n",
wnrc1.left, wnrc1.top, wnrc1.right, wnrc1.bottom,
clrc1.left, clrc1.top, clrc1.right, clrc1.bottom);
/* Native version of ComboEx creates the ComboBox with DROPDOWNLIST */
/* specified. It then creates it's own version of the EDIT control */
/* and makes the ComboBox the parent. This is because a normal */
/* DROPDOWNLIST does not have an EDIT control, but we need one. */
/* We also need to place the edit control at the proper location */
/* (allow space for the icons). */
infoPtr->hwndCombo = CreateWindowW (COMBOBOX, NIL,
/* following line added to match native */
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL |
CBS_NOINTEGRALHEIGHT | CBS_DROPDOWNLIST |
/* was base and is necessary */
WS_CHILD | WS_VISIBLE | CBS_OWNERDRAWFIXED |
GetWindowLongW (hwnd, GWL_STYLE),
cs->y, cs->x, cs->cx, cs->cy, hwnd,
(HMENU) GetWindowLongPtrW (hwnd, GWLP_ID),
(HINSTANCE)GetWindowLongPtrW (hwnd, GWLP_HINSTANCE), NULL);
/*
* native does the following at this point according to trace:
* GetWindowThreadProcessId(hwndCombo,0)
* GetCurrentThreadId()
* GetWindowThreadProcessId(hwndCombo, &???)
* GetCurrentProcessId()
*/
/*
* Setup a property to hold the pointer to the COMBOBOXEX
* data structure.
*/
SetPropW(infoPtr->hwndCombo, COMBOEX_SUBCLASS_PROP, hwnd);
infoPtr->prevComboWndProc = (WNDPROC)SetWindowLongPtrW(infoPtr->hwndCombo,
GWLP_WNDPROC, (DWORD_PTR)COMBOEX_ComboWndProc);
infoPtr->font = (HFONT)SendMessageW (infoPtr->hwndCombo, WM_GETFONT, 0, 0);
/*
* Now create our own EDIT control so we can position it.
* It is created only for CBS_DROPDOWN style
*/
if ((cs->style & CBS_DROPDOWNLIST) == CBS_DROPDOWN) {
infoPtr->hwndEdit = CreateWindowExW (0, EDIT, NIL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | ES_AUTOHSCROLL,
0, 0, 0, 0, /* will set later */
infoPtr->hwndCombo,
(HMENU) GetWindowLongPtrW (hwnd, GWLP_ID),
(HINSTANCE)GetWindowLongPtrW (hwnd, GWLP_HINSTANCE), NULL);
/* native does the following at this point according to trace:
* GetWindowThreadProcessId(hwndEdit,0)
* GetCurrentThreadId()
* GetWindowThreadProcessId(hwndEdit, &???)
* GetCurrentProcessId()
*/
/*
* Setup a property to hold the pointer to the COMBOBOXEX
* data structure.
*/
SetPropW(infoPtr->hwndEdit, COMBOEX_SUBCLASS_PROP, hwnd);
infoPtr->prevEditWndProc = (WNDPROC)SetWindowLongPtrW(infoPtr->hwndEdit,
GWLP_WNDPROC, (DWORD_PTR)COMBOEX_EditWndProc);
infoPtr->font = (HFONT)SendMessageW(infoPtr->hwndCombo, WM_GETFONT, 0, 0);
}
/*
* Locate the default font if necessary and then set it in
* all associated controls
*/
if (!infoPtr->font) {
SystemParametersInfoW (SPI_GETICONTITLELOGFONT, sizeof(mylogfont),
&mylogfont, 0);
infoPtr->font = infoPtr->defaultFont = CreateFontIndirectW (&mylogfont);
}
SendMessageW (infoPtr->hwndCombo, WM_SETFONT, (WPARAM)infoPtr->font, 0);
if (infoPtr->hwndEdit) {
SendMessageW (infoPtr->hwndEdit, WM_SETFONT, (WPARAM)infoPtr->font, 0);
SendMessageW (infoPtr->hwndEdit, EM_SETMARGINS, (WPARAM)EC_USEFONTINFO, 0);
}
COMBOEX_ReSize (infoPtr);
/* Above is fairly certain, below is much less certain. */
GetWindowRect(hwnd, &wnrc1);
GetClientRect(hwnd, &clrc1);
GetWindowRect(infoPtr->hwndCombo, &cmbwrc);
TRACE("EX window=(%d,%d)-(%d,%d) client=(%d,%d)-(%d,%d) CB wnd=(%d,%d)-(%d,%d)\n",
wnrc1.left, wnrc1.top, wnrc1.right, wnrc1.bottom,
clrc1.left, clrc1.top, clrc1.right, clrc1.bottom,
cmbwrc.left, cmbwrc.top, cmbwrc.right, cmbwrc.bottom);
SetWindowPos(infoPtr->hwndCombo, HWND_TOP,
0, 0, wnrc1.right-wnrc1.left, wnrc1.bottom-wnrc1.top,
SWP_NOACTIVATE | SWP_NOREDRAW);
GetWindowRect(infoPtr->hwndCombo, &cmbwrc);
TRACE("CB window=(%d,%d)-(%d,%d)\n",
cmbwrc.left, cmbwrc.top, cmbwrc.right, cmbwrc.bottom);
SetWindowPos(hwnd, HWND_TOP,
0, 0, cmbwrc.right-cmbwrc.left, cmbwrc.bottom-cmbwrc.top,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
COMBOEX_AdjustEditPos (infoPtr);
/*
* Create an item structure to represent the data in the
* EDIT control. It is allocated zero-filled.
*/
infoPtr->edit = (CBE_ITEMDATA *)Alloc (sizeof (CBE_ITEMDATA));
if (!infoPtr->edit) {
COMBOEX_Destroy(infoPtr);
return -1;
}
return 0;
}
static LRESULT COMBOEX_Command (COMBOEX_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
LRESULT lret;
INT command = HIWORD(wParam);
CBE_ITEMDATA *item = 0;
WCHAR wintext[520];
INT cursel, n, oldItem;
NMCBEENDEDITW cbeend;
DWORD oldflags;
HWND parent = infoPtr->hwndNotify;
TRACE("for command %d\n", command);
switch (command)
{
case CBN_DROPDOWN:
SetFocus (infoPtr->hwndCombo);
ShowWindow (infoPtr->hwndEdit, SW_HIDE);
return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
case CBN_CLOSEUP:
SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
/*
* from native trace of first dropdown after typing in URL in IE4
* CB_GETCURSEL(Combo)
* GetWindowText(Edit)
* CB_GETCURSEL(Combo)
* CB_GETCOUNT(Combo)
* CB_GETITEMDATA(Combo, n)
* WM_NOTIFY(parent, CBEN_ENDEDITA|W)
* CB_GETCURSEL(Combo)
* CB_SETCURSEL(COMBOEX, n)
* SetFocus(Combo)
* the rest is supposition
*/
ShowWindow (infoPtr->hwndEdit, SW_SHOW);
InvalidateRect (infoPtr->hwndCombo, 0, TRUE);
InvalidateRect (infoPtr->hwndEdit, 0, TRUE);
cursel = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0);
if (cursel == -1) {
cmp_func_t cmptext = get_cmp_func(infoPtr);
/* find match from edit against those in Combobox */
GetWindowTextW (infoPtr->hwndEdit, wintext, 520);
n = SendMessageW (infoPtr->hwndCombo, CB_GETCOUNT, 0, 0);
for (cursel = 0; cursel < n; cursel++){
item = get_item_data(infoPtr, cursel);
if ((INT_PTR)item == CB_ERR) break;
if (!cmptext(COMBOEX_GetText(infoPtr, item), wintext)) break;
}
if ((cursel == n) || ((INT_PTR)item == CB_ERR)) {
TRACE("failed to find match??? item=%p cursel=%d\n",
item, cursel);
if (infoPtr->hwndEdit)
SetFocus(infoPtr->hwndEdit);
return 0;
}
}
else {
item = get_item_data(infoPtr, cursel);
if ((INT_PTR)item == CB_ERR) {
TRACE("failed to find match??? item=%p cursel=%d\n",
item, cursel);
if (infoPtr->hwndEdit)
SetFocus(infoPtr->hwndEdit);
return 0;
}
}
/* Save flags for testing and reset them */
oldflags = infoPtr->flags;
infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG);
if (oldflags & WCBE_ACTEDIT) {
cbeend.fChanged = (oldflags & WCBE_EDITCHG);
cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo,
CB_GETCURSEL, 0, 0);
cbeend.iWhy = CBENF_DROPDOWN;
if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, COMBOEX_GetText(infoPtr, item))) return 0;
}
/* if selection has changed the set the new current selection */
cursel = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0);
if ((oldflags & WCBE_EDITCHG) || (cursel != infoPtr->selected)) {
infoPtr->selected = cursel;
SendMessageW (infoPtr->hwndSelf, CB_SETCURSEL, cursel, 0);
SetFocus(infoPtr->hwndCombo);
}
return 0;
case CBN_SELCHANGE:
/*
* CB_GETCURSEL(Combo)
* CB_GETITEMDATA(Combo) < simulated by COMBOEX_FindItem
* lstrlenA
* WM_SETTEXT(Edit)
* WM_GETTEXTLENGTH(Edit)
* WM_GETTEXT(Edit)
* EM_SETSEL(Edit, 0,0)
* WM_GETTEXTLENGTH(Edit)
* WM_GETTEXT(Edit)
* EM_SETSEL(Edit, 0,len)
* return WM_COMMAND to parent
*/
oldItem = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0);
if (!(item = COMBOEX_FindItem(infoPtr, oldItem))) {
ERR("item %d not found. Problem!\n", oldItem);
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -