📄 comboex.c
字号:
/*
* ComboBoxEx control
*
* Copyright 1998, 1999 Eric Kohl
* Copyright 2000, 2001, 2002 Guy Albertelli <galberte@neo.lrun.com>
* Copyright 2002 Dimitrie O. Paun
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTE
*
* This code was audited for completeness against the documented features
* of Comctl32.dll version 6.0 on Sep. 9, 2002, by Dimitrie O. Paun.
*
* Unless otherwise noted, we believe this code to be complete, as per
* the specification mentioned above.
* If you discover missing features, or bugs, please note them below.
*
*/
#include <stdarg.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(comboex);
/* Item structure */
typedef struct _CBE_ITEMDATA
{
struct _CBE_ITEMDATA *next;
UINT mask;
LPWSTR pszText;
LPWSTR pszTemp;
int cchTextMax;
int iImage;
int iSelectedImage;
int iOverlay;
int iIndent;
LPARAM lParam;
} CBE_ITEMDATA;
/* ComboBoxEx structure */
typedef struct
{
HIMAGELIST himl;
HWND hwndSelf; /* my own hwnd */
HWND hwndNotify; /* my parent hwnd */
HWND hwndCombo;
HWND hwndEdit;
WNDPROC prevEditWndProc; /* previous Edit WNDPROC value */
WNDPROC prevComboWndProc; /* previous Combo WNDPROC value */
DWORD dwExtStyle;
INT selected; /* index of selected item */
DWORD flags; /* WINE internal flags */
HFONT defaultFont;
HFONT font;
INT nb_items; /* Number of items */
BOOL unicode; /* TRUE if this window is Unicode */
BOOL NtfUnicode; /* TRUE if parent wants notify in Unicode */
CBE_ITEMDATA *edit; /* item data for edit item */
CBE_ITEMDATA *items; /* Array of items */
} COMBOEX_INFO;
/* internal flags in the COMBOEX_INFO structure */
#define WCBE_ACTEDIT 0x00000001 /* Edit active i.e.
* CBEN_BEGINEDIT issued
* but CBEN_ENDEDIT{A|W}
* not yet issued. */
#define WCBE_EDITCHG 0x00000002 /* Edit issued EN_CHANGE */
#define WCBE_EDITHASCHANGED (WCBE_ACTEDIT | WCBE_EDITCHG)
#define WCBE_EDITFOCUSED 0x00000004 /* Edit control has focus */
#define WCBE_MOUSECAPTURED 0x00000008 /* Combo has captured mouse */
#define WCBE_MOUSEDRAGGED 0x00000010 /* User has dragged in combo */
#define ID_CB_EDIT 1001
/*
* Special flag set in DRAWITEMSTRUCT itemState field. It is set by
* the ComboEx version of the Combo Window Proc so that when the
* WM_DRAWITEM message is then passed to ComboEx, we know that this
* particular WM_DRAWITEM message is for listbox only items. Any messasges
* without this flag is then for the Edit control field.
*
* We really cannot use the ODS_COMBOBOXEDIT flag because MSDN states that
* only version 4.0 applications will have ODS_COMBOBOXEDIT set.
*/
#define ODS_COMBOEXLBOX 0x4000
/* Height in pixels of control over the amount of the selected font */
#define CBE_EXTRA 3
/* Indent amount per MS documentation */
#define CBE_INDENT 10
/* Offset in pixels from left side for start of image or text */
#define CBE_STARTOFFSET 6
/* Offset between image and text */
#define CBE_SEP 4
static const WCHAR COMBOEX_SUBCLASS_PROP[] = {
'C','C','C','o','m','b','o','E','x','3','2',
'S','u','b','c','l','a','s','s','I','n','f','o',0
};
#define COMBOEX_GetInfoPtr(hwnd) ((COMBOEX_INFO *)GetWindowLongPtrW (hwnd, 0))
/* Things common to the entire DLL */
static LRESULT WINAPI COMBOEX_EditWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT WINAPI COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT COMBOEX_Destroy (COMBOEX_INFO *infoPtr);
typedef INT (WINAPI *cmp_func_t)(LPCWSTR, LPCWSTR);
inline static BOOL is_textW(LPCWSTR str)
{
return str && str != LPSTR_TEXTCALLBACKW;
}
inline static BOOL is_textA(LPCSTR str)
{
return str && str != LPSTR_TEXTCALLBACKA;
}
inline static LPCSTR debugstr_txt(LPCWSTR str)
{
if (str == LPSTR_TEXTCALLBACKW) return "(callback)";
return debugstr_w(str);
}
static void COMBOEX_DumpItem (CBE_ITEMDATA *item)
{
TRACE("item %p - mask=%08x, pszText=%p, cchTM=%d, iImage=%d\n",
item, item->mask, item->pszText, item->cchTextMax, item->iImage);
TRACE("item %p - iSelectedImage=%d, iOverlay=%d, iIndent=%d, lParam=%08lx\n",
item, item->iSelectedImage, item->iOverlay, item->iIndent, item->lParam);
if (item->mask & CBEIF_TEXT)
TRACE("item %p - pszText=%s\n", item, debugstr_txt(item->pszText));
}
static void COMBOEX_DumpInput (COMBOBOXEXITEMW *input)
{
TRACE("input - mask=%08x, iItem=%d, pszText=%p, cchTM=%d, iImage=%d\n",
input->mask, input->iItem, input->pszText, input->cchTextMax,
input->iImage);
if (input->mask & CBEIF_TEXT)
TRACE("input - pszText=<%s>\n", debugstr_txt(input->pszText));
TRACE("input - iSelectedImage=%d, iOverlay=%d, iIndent=%d, lParam=%08lx\n",
input->iSelectedImage, input->iOverlay, input->iIndent, input->lParam);
}
inline static CBE_ITEMDATA *get_item_data(COMBOEX_INFO *infoPtr, INT index)
{
return (CBE_ITEMDATA *)SendMessageW (infoPtr->hwndCombo, CB_GETITEMDATA,
(WPARAM)index, 0);
}
inline static cmp_func_t get_cmp_func(COMBOEX_INFO *infoPtr)
{
return infoPtr->dwExtStyle & CBES_EX_CASESENSITIVE ? lstrcmpW : lstrcmpiW;
}
static INT COMBOEX_Notify (COMBOEX_INFO *infoPtr, INT code, NMHDR *hdr)
{
hdr->idFrom = GetDlgCtrlID (infoPtr->hwndSelf);
hdr->hwndFrom = infoPtr->hwndSelf;
hdr->code = code;
if (infoPtr->NtfUnicode)
return SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, 0, (LPARAM)hdr);
else
return SendMessageA (infoPtr->hwndNotify, WM_NOTIFY, 0, (LPARAM)hdr);
}
static INT
COMBOEX_NotifyItem (COMBOEX_INFO *infoPtr, INT code, NMCOMBOBOXEXW *hdr)
{
/* Change the Text item from Unicode to ANSI if necessary for NOTIFY */
if (infoPtr->NtfUnicode)
return COMBOEX_Notify (infoPtr, code, &hdr->hdr);
else {
LPWSTR wstr = hdr->ceItem.pszText;
LPSTR astr = 0;
INT ret, len = 0;
if ((hdr->ceItem.mask & CBEIF_TEXT) && is_textW(wstr)) {
len = WideCharToMultiByte (CP_ACP, 0, wstr, -1, 0, 0, NULL, NULL);
if (len > 0) {
astr = (LPSTR)Alloc ((len + 1)*sizeof(CHAR));
if (!astr) return 0;
WideCharToMultiByte (CP_ACP, 0, wstr, -1, astr, len, 0, 0);
hdr->ceItem.pszText = (LPWSTR)astr;
}
}
if (code == CBEN_ENDEDITW) code = CBEN_ENDEDITA;
else if (code == CBEN_GETDISPINFOW) code = CBEN_GETDISPINFOA;
else if (code == CBEN_DRAGBEGINW) code = CBEN_DRAGBEGINA;
ret = COMBOEX_Notify (infoPtr, code, (NMHDR *)hdr);
if (astr && hdr->ceItem.pszText == (LPWSTR)astr)
hdr->ceItem.pszText = wstr;
if (astr) Free(astr);
return ret;
}
}
static INT COMBOEX_NotifyEndEdit (COMBOEX_INFO *infoPtr, NMCBEENDEDITW *neew, LPCWSTR wstr)
{
/* Change the Text item from Unicode to ANSI if necessary for NOTIFY */
if (infoPtr->NtfUnicode) {
lstrcpynW(neew->szText, wstr, CBEMAXSTRLEN);
return COMBOEX_Notify (infoPtr, CBEN_ENDEDITW, &neew->hdr);
} else {
NMCBEENDEDITA neea;
memcpy (&neea.hdr, &neew->hdr, sizeof(NMHDR));
neea.fChanged = neew->fChanged;
neea.iNewSelection = neew->iNewSelection;
WideCharToMultiByte (CP_ACP, 0, wstr, -1, neea.szText, CBEMAXSTRLEN, 0, 0);
neea.iWhy = neew->iWhy;
return COMBOEX_Notify (infoPtr, CBEN_ENDEDITA, &neea.hdr);
}
}
static void COMBOEX_NotifyDragBegin(COMBOEX_INFO *infoPtr, LPCWSTR wstr)
{
/* Change the Text item from Unicode to ANSI if necessary for NOTIFY */
if (infoPtr->NtfUnicode) {
NMCBEDRAGBEGINW ndbw;
ndbw.iItemid = -1;
lstrcpynW(ndbw.szText, wstr, CBEMAXSTRLEN);
COMBOEX_Notify (infoPtr, CBEN_DRAGBEGINW, &ndbw.hdr);
} else {
NMCBEDRAGBEGINA ndba;
ndba.iItemid = -1;
WideCharToMultiByte (CP_ACP, 0, wstr, -1, ndba.szText, CBEMAXSTRLEN, 0, 0);
COMBOEX_Notify (infoPtr, CBEN_DRAGBEGINA, &ndba.hdr);
}
}
static void COMBOEX_FreeText (CBE_ITEMDATA *item)
{
if (is_textW(item->pszText)) Free(item->pszText);
item->pszText = 0;
if (item->pszTemp) Free(item->pszTemp);
item->pszTemp = 0;
}
static INT COMBOEX_GetIndex(COMBOEX_INFO *infoPtr, CBE_ITEMDATA *item)
{
CBE_ITEMDATA *moving;
INT index;
moving = infoPtr->items;
index = infoPtr->nb_items - 1;
while (moving && (moving != item)) {
moving = moving->next;
index--;
}
if (!moving || (index < 0)) {
ERR("COMBOBOXEX item structures broken. Please report!\n");
return -1;
}
return index;
}
static LPCWSTR COMBOEX_GetText(COMBOEX_INFO *infoPtr, CBE_ITEMDATA *item)
{
NMCOMBOBOXEXW nmce;
LPWSTR text, buf;
INT len;
if (item->pszText != LPSTR_TEXTCALLBACKW)
return item->pszText;
ZeroMemory(&nmce, sizeof(nmce));
nmce.ceItem.mask = CBEIF_TEXT;
nmce.ceItem.lParam = item->lParam;
nmce.ceItem.iItem = COMBOEX_GetIndex(infoPtr, item);
COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce);
if (is_textW(nmce.ceItem.pszText)) {
len = MultiByteToWideChar (CP_ACP, 0, (LPSTR)nmce.ceItem.pszText, -1, NULL, 0);
buf = (LPWSTR)Alloc ((len + 1)*sizeof(WCHAR));
if (buf)
MultiByteToWideChar (CP_ACP, 0, (LPSTR)nmce.ceItem.pszText, -1, buf, len);
if (nmce.ceItem.mask & CBEIF_DI_SETITEM) {
COMBOEX_FreeText(item);
item->pszText = buf;
} else {
if (item->pszTemp) Free(item->pszTemp);
item->pszTemp = buf;
}
text = buf;
} else
text = nmce.ceItem.pszText;
if (nmce.ceItem.mask & CBEIF_DI_SETITEM)
item->pszText = text;
return text;
}
static void COMBOEX_GetComboFontSize (COMBOEX_INFO *infoPtr, SIZE *size)
{
static const WCHAR strA[] = { 'A', 0 };
HFONT nfont, ofont;
HDC mydc;
mydc = GetDC (0); /* why the entire screen???? */
nfont = (HFONT)SendMessageW (infoPtr->hwndCombo, WM_GETFONT, 0, 0);
ofont = (HFONT) SelectObject (mydc, nfont);
GetTextExtentPointW (mydc, strA, 1, size);
SelectObject (mydc, ofont);
ReleaseDC (0, mydc);
TRACE("selected font hwnd=%p, height=%d\n", nfont, size->cy);
}
static void COMBOEX_CopyItem (CBE_ITEMDATA *item, COMBOBOXEXITEMW *cit)
{
if (cit->mask & CBEIF_TEXT) {
/*
* when given a text buffer actually use that buffer
*/
if (cit->pszText) {
if (is_textW(item->pszText))
lstrcpynW(cit->pszText, item->pszText, cit->cchTextMax);
else
cit->pszText[0] = 0;
} else {
cit->pszText = item->pszText;
cit->cchTextMax = item->cchTextMax;
}
}
if (cit->mask & CBEIF_IMAGE)
cit->iImage = item->iImage;
if (cit->mask & CBEIF_SELECTEDIMAGE)
cit->iSelectedImage = item->iSelectedImage;
if (cit->mask & CBEIF_OVERLAY)
cit->iOverlay = item->iOverlay;
if (cit->mask & CBEIF_INDENT)
cit->iIndent = item->iIndent;
if (cit->mask & CBEIF_LPARAM)
cit->lParam = item->lParam;
}
static void COMBOEX_AdjustEditPos (COMBOEX_INFO *infoPtr)
{
SIZE mysize;
INT x, y, w, h, xioff;
RECT rect;
if (!infoPtr->hwndEdit) return;
if (infoPtr->himl && !(infoPtr->dwExtStyle & CBES_EX_NOEDITIMAGEINDENT)) {
IMAGEINFO iinfo;
iinfo.rcImage.left = iinfo.rcImage.right = 0;
ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo);
xioff = iinfo.rcImage.right - iinfo.rcImage.left + CBE_SEP;
} else xioff = 0;
GetClientRect (infoPtr->hwndCombo, &rect);
InflateRect (&rect, -2, -2);
InvalidateRect (infoPtr->hwndCombo, &rect, TRUE);
/* reposition the Edit control based on whether icon exists */
COMBOEX_GetComboFontSize (infoPtr, &mysize);
TRACE("Combo font x=%d, y=%d\n", mysize.cx, mysize.cy);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -