📄 combo.c
字号:
if (lprEdit->right < lprEdit->left)
lprEdit->right = lprEdit->left;
TRACE("\ttext\t= (%ld,%ld-%ld,%ld)\n",
lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
TRACE("\tbutton\t= (%ld,%ld-%ld,%ld)\n",
lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
TRACE("\tlbox\t= (%ld,%ld-%ld,%ld)\n",
lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
}
/***********************************************************************
* CBGetDroppedControlRect
*/
static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
{
/* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
of the combo box and the lower right corner of the listbox */
GetWindowRect(lphc->self, lpRect);
lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
}
/***********************************************************************
* COMBO_WindowPosChanging
*/
static LRESULT COMBO_WindowPosChanging(
HWND hwnd,
LPHEADCOMBO lphc,
WINDOWPOS* posChanging)
{
/*
* We need to override the WM_WINDOWPOSCHANGING method to handle all
* the non-simple comboboxes. The problem is that those controls are
* always the same height. We have to make sure they are not resized
* to another value.
*/
if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
((posChanging->flags & SWP_NOSIZE) == 0) )
{
int newComboHeight;
newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
2*COMBO_YBORDERSIZE();
/*
* Resizing a combobox has another side effect, it resizes the dropped
* rectangle as well. However, it does it only if the new height for the
* combobox is different from the height it should have. In other words,
* if the application resizing the combobox only had the intention to resize
* the actual control, for example, to do the layout of a dialog that is
* resized, the height of the dropdown is not changed.
*/
if (posChanging->cy != newComboHeight)
{
TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%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
*/
static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
BOOL unicode )
{
static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
static const WCHAR editName[] = {'E','d','i','t',0};
if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
lphc->owner = hwndParent;
/*
* The item height and dropped width are not set when the control
* is created.
*/
lphc->droppedWidth = lphc->editHeight = 0;
/*
* The first time we go through, we want to measure the ownerdraw item
*/
lphc->wState |= CBF_MEASUREITEM;
/* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
if( lphc->owner || !(style & WS_VISIBLE) )
{
UINT lbeStyle = 0;
UINT lbeExStyle = 0;
/*
* Initialize the dropped rect to the size of the client area of the
* control and then, force all the areas of the combobox to be
* recalculated.
*/
GetClientRect( hwnd, &lphc->droppedRect );
CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
/*
* Adjust the position of the popup listbox if it's necessary
*/
if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
{
lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
/*
* If it's a dropdown, the listbox is offset
*/
if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
if (lphc->droppedRect.bottom < lphc->droppedRect.top)
lphc->droppedRect.bottom = lphc->droppedRect.top;
if (lphc->droppedRect.right < lphc->droppedRect.left)
lphc->droppedRect.right = lphc->droppedRect.left;
MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
}
/* create listbox popup */
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;
if( lphc->dwStyle & CBS_HASSTRINGS )
lbeStyle |= LBS_HASSTRINGS;
if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
lbeStyle |= LBS_NOINTEGRALHEIGHT;
if( lphc->dwStyle & CBS_DISABLENOSCROLL )
lbeStyle |= LBS_DISABLENOSCROLL;
if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
{
lbeStyle |= WS_VISIBLE;
/*
* In win 95 look n feel, the listbox in the simple combobox has
* the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
*/
lbeStyle &= ~WS_BORDER;
lbeExStyle |= WS_EX_CLIENTEDGE;
}
else
{
lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
}
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)GetWindowLongPtrW( hwnd, GWLP_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)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
if( lphc->hWndLBox )
{
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;
if( lphc->dwStyle & CBS_AUTOHSCROLL )
lbeStyle |= ES_AUTOHSCROLL;
if( lphc->dwStyle & CBS_LOWERCASE )
lbeStyle |= ES_LOWERCASE;
else if( lphc->dwStyle & CBS_UPPERCASE )
lbeStyle |= ES_UPPERCASE;
if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
if (unicode)
lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
lphc->textRect.left, lphc->textRect.top,
lphc->textRect.right - lphc->textRect.left,
lphc->textRect.bottom - lphc->textRect.top,
hwnd, (HMENU)ID_CB_EDIT,
(HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_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)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
if( !lphc->hWndEdit )
bEdit = FALSE;
}
if( bEdit )
{
if( CB_GETTYPE(lphc) != CBS_SIMPLE )
{
/* Now do the trick with parent */
SetParent(lphc->hWndLBox, HWND_DESKTOP);
/*
* If the combo is a dropdown, we must resize the control
* to fit only the text area and button. To do this,
* we send a dummy resize and the WM_WINDOWPOSCHANGING message
* will take care of setting the height for us.
*/
CBForceDummyResize(lphc);
}
TRACE("init done\n");
return 0;
}
ERR("edit control failure.\n");
} else ERR("listbox failure.\n");
} else ERR("no owner for visible combo.\n");
/* CreateWindow() will send WM_NCDESTROY to cleanup */
return -1;
}
/***********************************************************************
* CBPaintButton
*
* Paint combo button (normal, pressed, and disabled states).
*/
static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
{
UINT buttonState = DFCS_SCROLLCOMBOBOX;
if( lphc->wState & CBF_NOREDRAW )
return;
if (lphc->wState & CBF_BUTTONDOWN)
buttonState |= DFCS_PUSHED;
if (CB_DISABLED(lphc))
buttonState |= DFCS_INACTIVE;
DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
}
/***********************************************************************
* CBPaintText
*
* Paint CBS_DROPDOWNLIST text field / update edit control contents.
*/
static void CBPaintText(
LPHEADCOMBO lphc,
HDC hdc,
RECT rectEdit)
{
INT id, size = 0;
LPWSTR pText = NULL;
if( lphc->wState & CBF_NOREDRAW ) return;
TRACE("\n");
/* follow Windows combobox that sends a bunch of text
* inquiries to its listbox while processing WM_PAINT. */
if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
{
size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
if (size == LB_ERR)
FIXME("LB_ERR probably not handled yet\n");
if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
{
/* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
pText[size] = '\0'; /* just in case */
} else return;
}
else
if( !CB_OWNERDRAWN(lphc) )
return;
if( lphc->wState & CBF_EDIT )
{
static const WCHAR empty_stringW[] = { 0 };
if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
if( lphc->wState & CBF_FOCUSED )
SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
}
else /* paint text field ourselves */
{
UINT itemState = ODS_COMBOBOXEDIT;
HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
/*
* Give ourselves some space.
*/
InflateRect( &rectEdit, -1, -1 );
if( CB_OWNERDRAWN(lphc) )
{
DRAWITEMSTRUCT dis;
HRGN clipRegion;
UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_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;
}
if (!IsWindowEnabled(lphc->self) & WS_DISABLED) itemState |= ODS_DISABLED;
dis.CtlType = ODT_COMBOBOX;
dis.CtlID = ctlid;
dis.hwndItem = lphc->self;
dis.itemAction = ODA_DRAWENTIRE;
dis.itemID = id;
dis.itemState = itemState;
dis.hDC = hdc;
dis.rcItem = rectEdit;
dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
(WPARAM)id, 0 );
/*
* Clip the DC and have the parent draw the item.
*/
IntersectClipRect(hdc,
rectEdit.left, rectEdit.top,
rectEdit.right, rectEdit.bottom);
SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
/*
* Reset the clipping region.
*/
SelectClipRgn(hdc, clipRegion);
}
else
{
static const WCHAR empty_stringW[] = { 0 };
if ( (lphc->wState & CBF_FOCUSED) &&
!(lphc->wState & CBF_DROPPED) ) {
/* highlight */
FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
}
ExtTextOutW( hdc,
rectEdit.left + 1,
rectEdit.top + 1,
ETO_OPAQUE | ETO_CLIPPED,
&rectEdit,
pText ? pText : empty_stringW , size, NULL );
if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
DrawFocusRect( hdc, &rectEdit );
}
if( hPrevFont )
SelectObject(hdc, hPrevFont );
}
if (pText)
HeapFree( GetProcessHeap(), 0, pText );
}
/***********************************************************************
* CBPaintBorder
*/
static void CBPaintBorder(
HWND hwnd,
LPHEADCOMBO lphc,
HDC hdc)
{
RECT clientRect;
if (CB_GETTYPE(lphc) != CBS_SIMPLE)
{
GetClientRect(hwnd, &clientRect);
}
else
{
CopyRect(&clientRect, &lphc->textRect);
InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
}
DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
}
/***********************************************************************
* COMBO_PrepareColors
*
* This method will sent the appropriate WM_CTLCOLOR message to
* prepare and setup the colors for the combo's DC.
*
* It also returns the brush to use for the background.
*/
static HBRUSH COMBO_PrepareColors(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -