windows-nt/Source/XPSP1/NT/shell/comctl32/v6/combo.c
2020-09-26 16:20:57 +08:00

2910 lines
76 KiB
C

#include "ctlspriv.h"
#pragma hdrstop
#include "usrctl32.h"
#include "combo.h"
#include "listbox.h" // For LBIV struct
//---------------------------------------------------------------------------//
//
// InitComboboxClass() - Registers the control's window class
//
BOOL InitComboboxClass(HINSTANCE hInstance)
{
WNDCLASS wc;
wc.lpfnWndProc = ComboBox_WndProc;
wc.lpszClassName = WC_COMBOBOX;
wc.style = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(PCBOX);
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
if (!RegisterClass(&wc) && !GetClassInfo(hInstance, WC_COMBOBOX, &wc))
return FALSE;
return TRUE;
}
//---------------------------------------------------------------------------//
//
// InitComboLBoxClass() - Registers the control's dropdown window class
//
// The dropdown list is a specially registered version
// of the listbox control called ComboLBox. We need to
// register this dummy control since apps looking for a
// combobox's listbox look for the classname ComboLBox
//
BOOL FAR PASCAL InitComboLBoxClass(HINSTANCE hinst)
{
WNDCLASS wc;
wc.lpfnWndProc = ListBox_WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = NULL;
wc.lpszMenuName = NULL;
wc.hInstance = hinst;
wc.lpszClassName = WC_COMBOLBOX;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
wc.style = CS_GLOBALCLASS | CS_SAVEBITS | CS_DBLCLKS;
wc.cbWndExtra = sizeof(PLBIV);
wc.cbClsExtra = 0;
if (!RegisterClass(&wc) && !GetClassInfo(hinst, WC_COMBOLBOX, &wc))
return FALSE;
return TRUE;
}
//---------------------------------------------------------------------------//
//
// ComboBox_PressButton()
//
// Pops combobox button back up.
//
VOID ComboBox_PressButton(PCBOX pcbox, BOOL fPress)
{
//
// Publisher relies on getting a WM_PAINT message after the combo list
// pops back up. On a WM_PAINT they change the focus, which causes
// toolbar combos to send CBN_SELENDCANCEL notifications. On this
// notification they apply the font/pt size change you made to the
// selection.
//
// This happened in 3.1 because the dropdown list overlapped the button
// on the bottom or top by a pixel. Since we'd end up painting under
// the list SPB, when it went away USER would reinvalidate the dirty
// area. This would cause a paint message.
//
// In 4.0, this doesn't happen because the dropdown doesn't overlap. So
// we need to make sure Publisher gets a WM_PAINT anyway. We do this
// by changing where the dropdown shows up for 3.x apps
//
//
if ((pcbox->fButtonPressed != 0) != (fPress != 0))
{
HWND hwnd = pcbox->hwnd;
pcbox->fButtonPressed = (fPress != 0);
if (pcbox->f3DCombo)
{
InvalidateRect(hwnd, &pcbox->buttonrc, TRUE);
}
else
{
RECT rc;
CopyRect(&rc, &pcbox->buttonrc);
InflateRect(&rc, 0, GetSystemMetrics(SM_CYEDGE));
InvalidateRect(hwnd, &rc, TRUE);
}
UpdateWindow(hwnd);
NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON);
}
}
//---------------------------------------------------------------------------//
//
// ComboBox_HotTrack
//
// If we're not already hot-tracking and the mouse is over the combobox,
// turn on hot-tracking and invalidate the drop-down button.
//
VOID ComboBox_HotTrack(PCBOX pcbox, POINT pt)
{
if (!pcbox->fButtonHotTracked && !pcbox->fMouseDown)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = pcbox->hwnd;
tme.dwHoverTime = 0;
if (TrackMouseEvent(&tme))
{
if ((pcbox->CBoxStyle == SDROPDOWN &&
PtInRect(&pcbox->buttonrc, pt)) ||
pcbox->CBoxStyle == SDROPDOWNLIST)
{
pcbox->fButtonHotTracked = TRUE;
InvalidateRect(pcbox->hwnd, NULL, TRUE);
}
else
{
pcbox->fButtonHotTracked = FALSE;
}
}
}
}
//---------------------------------------------------------------------------//
//
// ComboBox_DBCharHandler
//
// Double Byte character handler for ANSI ComboBox
//
LRESULT ComboBox_DBCharHandler(PCBOX pcbox, HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WORD w;
HWND hwndSend;
w = DbcsCombine(hwnd, (BYTE)wParam);
if (w == 0)
{
return CB_ERR; // Failed to assemble DBCS
}
UserAssert(pcbox->hwndList);
if (pcbox->fNoEdit)
{
hwndSend = pcbox->hwndList;
}
else if (pcbox->hwndEdit)
{
TraceMsg(TF_STANDARD, "UxCombobox: ComboBoxWndProcWorker: WM_CHAR is posted to Combobox itself(%08x).",
hwnd);
hwndSend = pcbox->hwndEdit;
}
else
{
return CB_ERR;
}
TraceMsg(TF_STANDARD, "UxCombobox: ComboBoxWndProcWorker: sending WM_CHAR %04x", w);
if (!TestWF(hwndSend, WFANSIPROC))
{
//
// If receiver is not ANSI WndProc (may be subclassed?),
// send a UNICODE message.
//
WCHAR wChar;
LPWSTR lpwstr = &wChar;
if (MBToWCSEx(CP_ACP, (LPCSTR)&w, 2, &lpwstr, 1, FALSE) == 0)
{
TraceMsg(TF_STANDARD, "UxCombobox: ComboBoxWndProcWorker: cannot convert 0x%04x to UNICODE.", w);
return CB_ERR;
}
return SendMessage(hwndSend, message, wChar, lParam);
}
//
// Post the Trailing byte to the target
// so that they can peek the second WM_CHAR
// message later.
// Note: it's safe since sender is A and receiver is A,
// translation layer does not perform any DBCS combining and cracking.
//PostMessageA(hwndSend, message, CrackCombinedDbcsTB(w), lParam);
//
return SendMessage(hwndSend, message, wParam, lParam);
}
//---------------------------------------------------------------------------//
BOOL ComboBox_MsgOKInit(UINT message, LRESULT* plRet)
{
switch (message)
{
default:
break;
case WM_SIZE:
case CB_SETMINVISIBLE:
case CB_GETMINVISIBLE:
*plRet = 0;
return FALSE;
case WM_STYLECHANGED:
case WM_GETTEXT:
case WM_GETTEXTLENGTH:
case WM_PRINT:
case WM_COMMAND:
case CBEC_KILLCOMBOFOCUS:
case WM_PRINTCLIENT:
case WM_SETFONT:
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
case WM_CHAR:
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_MOUSEWHEEL:
case WM_CAPTURECHANGED:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
case WM_SETFOCUS:
case WM_KILLFOCUS:
case WM_SETREDRAW:
case WM_ENABLE:
case CB_SETDROPPEDWIDTH:
case CB_DIR:
case CB_ADDSTRING:
//
// Cannot handle those messages yet. Bail out.
//
*plRet = CB_ERR;
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------------//
//
// ComboBox_MessageItemHandler
//
// Handles WM_DRAWITEM,WM_MEASUREITEM,WM_DELETEITEM,WM_COMPAREITEM
// messages from the listbox.
//
LRESULT ComboBox_MessageItemHandler(PCBOX pcbox, UINT uMsg, LPVOID lpv)
{
LRESULT lRet;
//
// Send the <lpv>item message back to the application after changing some
// parameters to their combo box specific versions.
//
((LPMEASUREITEMSTRUCT)lpv)->CtlType = ODT_COMBOBOX;
((LPMEASUREITEMSTRUCT)lpv)->CtlID = GetWindowID(pcbox->hwnd);
switch (uMsg)
{
case WM_DRAWITEM:
((LPDRAWITEMSTRUCT)lpv)->hwndItem = pcbox->hwnd;
break;
case WM_DELETEITEM:
((LPDELETEITEMSTRUCT)lpv)->hwndItem = pcbox->hwnd;
break;
case WM_COMPAREITEM:
((LPCOMPAREITEMSTRUCT)lpv)->hwndItem = pcbox->hwnd;
break;
}
lRet = SendMessage(pcbox->hwndParent, uMsg, (WPARAM)GetWindowID(pcbox->hwnd), (LPARAM)lpv);
return lRet;
}
//---------------------------------------------------------------------------//
VOID ComboBox_Paint(PCBOX pcbox, HDC hdc)
{
RECT rc;
UINT msg;
HBRUSH hbr;
INT iStateId;
CCDBUFFER ccdb;
if (pcbox->fButtonPressed)
{
iStateId = CBXS_PRESSED;
}
else if ( !IsWindowEnabled(pcbox->hwnd))
{
iStateId = CBXS_DISABLED;
}
else if (pcbox->fButtonHotTracked)
{
iStateId = CBXS_HOT;
}
else
{
iStateId = CBXS_NORMAL;
}
rc.left = rc.top = 0;
rc.right = pcbox->cxCombo;
rc.bottom = pcbox->cyCombo;
hdc = CCBeginDoubleBuffer(hdc, &rc, &ccdb);
if ( !pcbox->hTheme )
{
DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST | (!pcbox->f3DCombo ? BF_FLAT | BF_MONO : 0));
}
else
{
DrawThemeBackground(pcbox->hTheme, hdc, 0, iStateId, &rc, 0);
}
if ( !IsRectEmpty(&pcbox->buttonrc) )
{
//
// Draw in the dropdown arrow button
//
if (!pcbox->hTheme)
{
DrawFrameControl(hdc, &pcbox->buttonrc, DFC_SCROLL,
DFCS_SCROLLCOMBOBOX |
(pcbox->fButtonPressed ? DFCS_PUSHED | DFCS_FLAT : 0) |
(TESTFLAG(GET_STYLE(pcbox), WS_DISABLED) ? DFCS_INACTIVE : 0) |
(pcbox->fButtonHotTracked ? DFCS_HOT: 0));
}
else
{
DrawThemeBackground(pcbox->hTheme, hdc, CP_DROPDOWNBUTTON, iStateId, &pcbox->buttonrc, 0);
}
if (pcbox->fRightAlign )
{
rc.left = pcbox->buttonrc.right;
}
else
{
rc.right = pcbox->buttonrc.left;
}
}
//
// Erase the background behind the edit/static item. Since a combo
// is an edit field/list box hybrid, we use the same coloring
// conventions.
//
msg = WM_CTLCOLOREDIT;
if (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
{
ULONG ulStyle = pcbox->hwndEdit ? GetWindowStyle(pcbox->hwndEdit) : 0;
if (TESTFLAG(GET_STYLE(pcbox), WS_DISABLED) ||
(!pcbox->fNoEdit && pcbox->hwndEdit && (ulStyle & ES_READONLY)))
{
msg = WM_CTLCOLORSTATIC;
}
}
else
{
msg = WM_CTLCOLORLISTBOX;
}
//
// GetControlBrush
//
hbr = (HBRUSH)SendMessage(GetParent(pcbox->hwnd), msg, (WPARAM)hdc, (LPARAM)pcbox->hwnd);
if (pcbox->fNoEdit)
{
ComboBox_InternalUpdateEditWindow(pcbox, hdc);
}
else if (!pcbox->hTheme)
{
FillRect(hdc, &rc, hbr);
}
CCEndDoubleBuffer(&ccdb);
}
//---------------------------------------------------------------------------//
//
// ComboBox_NotifyParent
//
// Sends the notification code to the parent of the combo box control
//
VOID ComboBox_NotifyParent(PCBOX pcbox, short notificationCode)
{
HWND hwndSend = (pcbox->hwndParent != 0) ? pcbox->hwndParent : pcbox->hwnd;
//
// wParam contains Control ID and notification code.
// lParam contains Handle to window
//
SendMessage(hwndSend, WM_COMMAND,
MAKELONG(GetWindowID(pcbox->hwnd), notificationCode),
(LPARAM)pcbox->hwnd);
}
//---------------------------------------------------------------------------//
//
// ComboBox_UpdateListBoxWindow
//
// matches the text in the editcontrol. If fSelectionAlso is false, then we
// unselect the current listbox selection and just move the caret to the item
// which is the closest match to the text in the editcontrol.
//
VOID ComboBox_UpdateListBoxWindow(PCBOX pcbox, BOOL fSelectionAlso)
{
if (pcbox->hwndEdit)
{
INT cchText;
INT sItem, sSel;
LPWSTR pText = NULL;
sItem = CB_ERR;
cchText = (int)SendMessage(pcbox->hwndEdit, WM_GETTEXTLENGTH, 0, 0);
if (cchText)
{
cchText++;
pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR));
if (pText != NULL)
{
try
{
SendMessage(pcbox->hwndEdit, WM_GETTEXT, cchText, (LPARAM)pText);
sItem = (int)SendMessage(pcbox->hwndList, LB_FINDSTRING,
(WPARAM)-1L, (LPARAM)pText);
}
finally
{
UserLocalFree((HANDLE)pText);
}
}
}
if (fSelectionAlso)
{
sSel = sItem;
}
else
{
sSel = CB_ERR;
}
if (sItem == CB_ERR)
{
sItem = 0;
//
// Old apps: w/ editable combos, selected 1st item in list even if
// it didn't match text in edit field. This is not desirable
// behavior for 4.0 dudes esp. with cancel allowed. Reason:
// (1) User types in text that doesn't match list choices
// (2) User drops combo
// (3) User pops combo back up
// (4) User presses OK in dialog that does stuff w/ combo
// contents.
// In 3.1, when the combo dropped, we'd select the 1st item anyway.
// So the last CBN_SELCHANGE the owner got would be 0--which is
// bogus because it really should be -1. In fact if you type anything
// into the combo afterwards it will reset itself to -1.
//
// 4.0 dudes won't get this bogus 0 selection.
//
if (fSelectionAlso && !TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
{
sSel = 0;
}
}
SendMessage(pcbox->hwndList, LB_SETCURSEL, (DWORD)sSel, 0);
SendMessage(pcbox->hwndList, LB_SETCARETINDEX, (DWORD)sItem, 0);
SendMessage(pcbox->hwndList, LB_SETTOPINDEX, (DWORD)sItem, 0);
}
}
//---------------------------------------------------------------------------//
//
// ComboBox_InvertStaticWindow
//
// Inverts the static text/picture window associated with the combo
// box. Gets its own hdc, if the one given is null.
//
VOID ComboBox_InvertStaticWindow(PCBOX pcbox, BOOL fNewSelectionState, HDC hdc)
{
BOOL focusSave = pcbox->fFocus;
pcbox->fFocus = (UINT)fNewSelectionState;
ComboBox_InternalUpdateEditWindow(pcbox, hdc);
pcbox->fFocus = (UINT)focusSave;
}
//---------------------------------------------------------------------------//
//
// ComboBox_GetFocusHandler
//
// Handles getting the focus for the combo box
//
VOID ComboBox_GetFocusHandler(PCBOX pcbox)
{
if (pcbox->fFocus)
{
return;
}
//
// The combo box has gotten the focus for the first time.
//
//
// First turn on the listbox caret
//
if (pcbox->CBoxStyle == SDROPDOWNLIST)
{
SendMessage(pcbox->hwndList, LBCB_CARETON, 0, 0);
}
//
// and select all the text in the editcontrol or static text rectangle.
//
if (pcbox->fNoEdit)
{
//
// Invert the static text rectangle
//
ComboBox_InvertStaticWindow(pcbox, TRUE, (HDC)NULL);
}
else if (pcbox->hwndEdit)
{
UserAssert(pcbox->hwnd);
SendMessage(pcbox->hwndEdit, EM_SETSEL, 0, MAXLONG);
}
pcbox->fFocus = TRUE;
//
// Notify the parent we have the focus
//
ComboBox_NotifyParent(pcbox, CBN_SETFOCUS);
}
//---------------------------------------------------------------------------//
//
// ComboBox_KillFocusHandler
//
// Handles losing the focus for the combo box.
//
VOID ComboBox_KillFocusHandler(PCBOX pcbox)
{
if (!pcbox->fFocus || pcbox->hwndList == NULL)
{
return;
}
//
// The combo box is losing the focus. Send buttonup clicks so that
// things release the mouse capture if they have it... If the
// pwndListBox is null, don't do anything. This occurs if the combo box
// is destroyed while it has the focus.
//
SendMessage(pcbox->hwnd, WM_LBUTTONUP, 0L, 0xFFFFFFFFL);
if (!ComboBox_HideListBoxWindow(pcbox, TRUE, FALSE))
{
return;
}
//
// Turn off the listbox caret
//
if (pcbox->CBoxStyle == SDROPDOWNLIST)
{
SendMessage(pcbox->hwndList, LBCB_CARETOFF, 0, 0);
}
if (pcbox->fNoEdit)
{
//
// Invert the static text rectangle
//
ComboBox_InvertStaticWindow(pcbox, FALSE, (HDC)NULL);
}
else if (pcbox->hwndEdit)
{
SendMessage(pcbox->hwndEdit, EM_SETSEL, 0, 0);
}
pcbox->fFocus = FALSE;
ComboBox_NotifyParent(pcbox, CBN_KILLFOCUS);
}
//---------------------------------------------------------------------------//
//
// ComboBox_CommandHandler
//
// Check the various notification codes from the controls and do the
// proper thing.
// always returns 0L
//
LONG ComboBox_CommandHandler(PCBOX pcbox, DWORD wParam, HWND hwndControl)
{
//
// Check the edit control notification codes. Note that currently, edit
// controls don't send EN_KILLFOCUS messages to the parent.
//
if (!pcbox->fNoEdit && (hwndControl == pcbox->hwndEdit))
{
//
// Edit control notification codes
//
switch (HIWORD(wParam))
{
case EN_SETFOCUS:
if (!pcbox->fFocus)
{
//
// The edit control has the focus for the first time which means
// this is the first time the combo box has received the focus
// and the parent must be notified that we have the focus.
//
ComboBox_GetFocusHandler(pcbox);
}
break;
case EN_CHANGE:
ComboBox_NotifyParent(pcbox, CBN_EDITCHANGE);
ComboBox_UpdateListBoxWindow(pcbox, FALSE);
break;
case EN_UPDATE:
ComboBox_NotifyParent(pcbox, CBN_EDITUPDATE);
break;
case EN_ERRSPACE:
ComboBox_NotifyParent(pcbox, CBN_ERRSPACE);
break;
}
}
//
// Check listbox control notification codes
//
if (hwndControl == pcbox->hwndList)
{
//
// Listbox control notification codes
//
switch ((int)HIWORD(wParam))
{
case LBN_DBLCLK:
ComboBox_NotifyParent(pcbox, CBN_DBLCLK);
break;
case LBN_ERRSPACE:
ComboBox_NotifyParent(pcbox, CBN_ERRSPACE);
break;
case LBN_SELCHANGE:
case LBN_SELCANCEL:
if (!pcbox->fKeyboardSelInListBox)
{
//
// If the selchange is caused by the user keyboarding through,
// we don't want to hide the listbox.
//
if (!ComboBox_HideListBoxWindow(pcbox, TRUE, TRUE))
{
return 0;
}
}
else
{
pcbox->fKeyboardSelInListBox = FALSE;
}
ComboBox_NotifyParent(pcbox, CBN_SELCHANGE);
ComboBox_InternalUpdateEditWindow(pcbox, NULL);
if (pcbox->fNoEdit)
{
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, pcbox->hwnd, OBJID_CLIENT, INDEX_COMBOBOX);
}
break;
}
}
return 0;
}
//---------------------------------------------------------------------------//
//
// ComboBox_CompleteEditWindow
//
//
// Completes the text in the edit box with the closest match from the
// listbox. If a prefix match can't be found, the edit control text isn't
// updated. Assume a DROPDOWN style combo box.
//
VOID ComboBox_CompleteEditWindow(PCBOX pcbox)
{
int cchText;
int cchItemText;
int itemNumber;
LPWSTR pText;
//
// Firstly check the edit control.
//
if (pcbox->hwndEdit == NULL)
{
return;
}
//
// +1 for null terminator
//
cchText = (int)SendMessage(pcbox->hwndEdit, WM_GETTEXTLENGTH, 0, 0);
if (cchText)
{
cchText++;
pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR));
if (!pText)
{
goto Unlock;
}
//
// We want to be sure to free the above allocated memory even if
// the client dies during callback (xxx) or some of the following
// window revalidation fails.
//
try
{
SendMessage(pcbox->hwndEdit, WM_GETTEXT, cchText, (LPARAM)pText);
itemNumber = (int)SendMessage(pcbox->hwndList,
LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pText);
if (itemNumber == -1)
{
itemNumber = (int)SendMessage(pcbox->hwndList,
LB_FINDSTRING, (WPARAM)-1, (LPARAM)pText);
}
}
finally
{
UserLocalFree((HANDLE)pText);
}
if (itemNumber == -1)
{
//
// No close match. Blow off.
//
goto Unlock;
}
cchItemText = (int)SendMessage(pcbox->hwndList, LB_GETTEXTLEN,
itemNumber, 0);
if (cchItemText)
{
cchItemText++;
pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchItemText*sizeof(WCHAR));
if (!pText)
{
goto Unlock;
}
//
// We want to be sure to free the above allocated memory even if
// the client dies during callback (xxx) or some of the following
// window revalidation fails.
//
try
{
SendMessage(pcbox->hwndList, LB_GETTEXT, itemNumber, (LPARAM)pText);
SendMessage(pcbox->hwndEdit, WM_SETTEXT, 0, (LPARAM)pText);
}
finally
{
UserLocalFree((HANDLE)pText);
}
SendMessage(pcbox->hwndEdit, EM_SETSEL, 0, MAXLONG);
}
}
Unlock:
return;
}
//---------------------------------------------------------------------------//
//
// ComboBox_HideListBoxWindow
//
// Hides the dropdown listbox window if it is a dropdown style.
//
BOOL ComboBox_HideListBoxWindow(PCBOX pcbox, BOOL fNotifyParent, BOOL fSelEndOK)
{
HWND hwnd = pcbox->hwnd;
HWND hwndList = pcbox->hwndList;
//
// For 3.1+ apps, send CBN_SELENDOK to all types of comboboxes but only
// allow CBN_SELENDCANCEL to be sent for droppable comboboxes
//
if (fNotifyParent && TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN31COMPAT) &&
((pcbox->CBoxStyle & SDROPPABLE) || fSelEndOK))
{
if (fSelEndOK)
{
ComboBox_NotifyParent(pcbox, CBN_SELENDOK);
}
else
{
ComboBox_NotifyParent(pcbox, CBN_SELENDCANCEL);
}
if (!IsWindow(hwnd))
{
return FALSE;
}
}
//
// return, we don't hide simple combo boxes.
//
if (!(pcbox->CBoxStyle & SDROPPABLE))
{
return TRUE;
}
//
// Send a faked buttonup message to the listbox so that it can release
// the capture and all.
//
SendMessage(pcbox->hwndList, LBCB_ENDTRACK, fSelEndOK, 0);
if (pcbox->fLBoxVisible)
{
WORD swpFlags = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE;
if (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN31COMPAT))
{
swpFlags |= SWP_FRAMECHANGED;
}
pcbox->fLBoxVisible = FALSE;
//
// Hide the listbox window
//
ShowWindow(hwndList, SW_HIDE);
//
// Invalidate the item area now since SWP() might update stuff.
// Since the combo is CS_VREDRAW/CS_HREDRAW, a size change will
// redraw the whole thing, including the item rect. But if it
// isn't changing size, we still want to redraw the item anyway
// to show focus/selection.
//
if (!(pcbox->CBoxStyle & SEDITABLE))
{
InvalidateRect(hwnd, &pcbox->editrc, TRUE);
}
SetWindowPos(hwnd, HWND_TOP, 0, 0,
pcbox->cxCombo, pcbox->cyCombo, swpFlags);
//
// In case size didn't change
//
UpdateWindow(hwnd);
if (pcbox->CBoxStyle & SEDITABLE)
{
ComboBox_CompleteEditWindow(pcbox);
}
if (fNotifyParent)
{
//
// Notify parent we will be popping up the combo box.
//
ComboBox_NotifyParent(pcbox, CBN_CLOSEUP);
if (!IsWindow(hwnd))
{
return FALSE;
}
}
}
return TRUE;
}
//---------------------------------------------------------------------------//
//
// ComboBox_ShowListBoxWindow
//
// Lowers the dropdown listbox window.
//
VOID ComboBox_ShowListBoxWindow(PCBOX pcbox, BOOL fTrack)
{
RECT editrc;
RECT rcWindow;
RECT rcList;
int itemNumber;
int iHeight;
int yTop;
DWORD dwMult;
int cyItem;
HWND hwnd = pcbox->hwnd;
HWND hwndList = pcbox->hwndList;
BOOL fAnimPos;
HMONITOR hMonitor;
MONITORINFO mi = {0};
BOOL bCBAnim = FALSE;
//
// This function is only called for droppable list comboboxes
//
UserAssert(pcbox->CBoxStyle & SDROPPABLE);
//
// Notify parent we will be dropping down the combo box.
//
ComboBox_NotifyParent(pcbox, CBN_DROPDOWN);
//
// Invalidate the button rect so that the depressed arrow is drawn.
//
InvalidateRect(hwnd, &pcbox->buttonrc, TRUE);
pcbox->fLBoxVisible = TRUE;
if (pcbox->CBoxStyle == SDROPDOWN)
{
//
// If an item in the listbox matches the text in the edit control,
// scroll it to the top of the listbox. Select the item only if the
// mouse button isn't down otherwise we will select the item when the
// mouse button goes up.
//
ComboBox_UpdateListBoxWindow(pcbox, !pcbox->fMouseDown);
if (!pcbox->fMouseDown)
{
ComboBox_CompleteEditWindow(pcbox);
}
}
else
{
//
// Scroll the currently selected item to the top of the listbox.
//
itemNumber = (int)SendMessage(pcbox->hwndList, LB_GETCURSEL, 0, 0);
if (itemNumber == -1)
{
itemNumber = 0;
}
SendMessage(pcbox->hwndList, LB_SETTOPINDEX, itemNumber, 0);
SendMessage(pcbox->hwndList, LBCB_CARETON, 0, 0);
//
// We need to invalidate the edit rect so that the focus frame/invert
// will be turned off when the listbox is visible. Tandy wants this for
// his typical reasons...
//
InvalidateRect(hwnd, &pcbox->editrc, TRUE);
}
//
// Figure out where to position the dropdown listbox. We want it just
// touching the edge around the edit rectangle. Note that since the
// listbox is a popup, we need the position in screen coordinates.
//
//
// We want the dropdown to pop below or above the combo
//
//
// Get screen coords
//
GetWindowRect(pcbox->hwnd, &rcWindow);
editrc.left = rcWindow.left;
editrc.top = rcWindow.top;
editrc.right = rcWindow.left + pcbox->cxCombo;
editrc.bottom = rcWindow.top + pcbox->cyCombo;
//
// List area
//
cyItem = (int)SendMessage(pcbox->hwndList, LB_GETITEMHEIGHT, 0, 0);
if (cyItem == 0)
{
//
// Make sure that it's not 0
//
TraceMsg(TF_STANDARD, "UxCombobox: LB_GETITEMHEIGHT is returning 0" );
cyItem = SYSFONT_CYCHAR;
}
//
// we shoulda' just been able to use cyDrop here, but thanks to VB's need
// to do things their OWN SPECIAL WAY, we have to keep monitoring the size
// of the listbox 'cause VB changes it directly (jeffbog 03/21/94)
//
GetWindowRect(pcbox->hwndList, &rcList);
iHeight = max(pcbox->cyDrop, rcList.bottom - rcList.top);
if (dwMult = (DWORD)SendMessage(pcbox->hwndList, LB_GETCOUNT, 0, 0))
{
dwMult = (DWORD)(LOWORD(dwMult) * cyItem);
dwMult += GetSystemMetrics(SM_CYEDGE);
if (dwMult < 0x7FFF)
{
iHeight = min(LOWORD(dwMult), iHeight);
}
}
if (!GET_STYLE(pcbox) & CBS_NOINTEGRALHEIGHT)
{
UserAssert(cyItem);
iHeight = ((iHeight - GetSystemMetrics(SM_CYEDGE)) / cyItem) * cyItem + GetSystemMetrics(SM_CYEDGE);
}
//
// Other 1/2 of old app combo fix. Make dropdown overlap combo window
// a little. That way we can have a chance of invalidating the overlap
// and causing a repaint to help out Publisher 2.0's toolbar combos.
// See comments for PressButton() above.
//
hMonitor = MonitorFromWindow(pcbox->hwnd, MONITOR_DEFAULTTOPRIMARY);
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
if (editrc.bottom + iHeight <= mi.rcMonitor.bottom)
{
yTop = editrc.bottom;
if (!pcbox->f3DCombo)
{
yTop -= GetSystemMetrics(SM_CYBORDER);
}
fAnimPos = TRUE;
}
else
{
yTop = max(editrc.top - iHeight, mi.rcMonitor.top);
if (!pcbox->f3DCombo)
{
yTop += GetSystemMetrics(SM_CYBORDER);
}
fAnimPos = FALSE;
}
if (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
{
//
// fix for Winword B#7504, Combo-ListBox text gets
// truncated by a small width, this is do to us
// now setting size here in SetWindowPos, rather than
// earlier where we did this in Win3.1
//
GetWindowRect(pcbox->hwndList, &rcList);
if ((rcList.right - rcList.left ) > pcbox->cxDrop)
{
pcbox->cxDrop = rcList.right - rcList.left;
}
}
if (!TESTFLAG(GET_EXSTYLE(pcbox), WS_EX_LAYOUTRTL))
{
SetWindowPos(hwndList, HWND_TOPMOST, editrc.left,
yTop, max(pcbox->cxDrop, pcbox->cxCombo), iHeight, SWP_NOACTIVATE);
}
else
{
int cx = max(pcbox->cxDrop, pcbox->cxCombo);
SetWindowPos(hwndList, HWND_TOPMOST, editrc.right - cx,
yTop, cx, iHeight, SWP_NOACTIVATE);
}
//
// Get any drawing in the combo box window out of the way so it doesn't
// invalidate any of the SPB underneath the list window.
//
UpdateWindow(hwnd);
SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, (LPVOID)&bCBAnim, 0);
if (!bCBAnim)
{
ShowWindow(hwndList, SW_SHOWNA);
}
else
{
AnimateWindow(hwndList, CMS_QANIMATION, (fAnimPos ? AW_VER_POSITIVE :
AW_VER_NEGATIVE) | AW_SLIDE);
}
//
// Restart search buffer from first char
//
{
PLBIV plb = ListBox_GetPtr(pcbox->hwndList);
if ((plb != NULL) && (plb != (PLBIV)-1))
{
plb->iTypeSearch = 0;
}
}
if (fTrack && TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
{
SendMessage(pcbox->hwndList, LBCB_STARTTRACK, FALSE, 0);
}
}
//---------------------------------------------------------------------------//
//
// ComboBox_InternalUpdateEditWindow
//
// Updates the editcontrol/statictext window so that it contains the text
// given by the current selection in the listbox. If the listbox has no
// selection (ie. -1), then we erase all the text in the editcontrol.
//
// hdcPaint is from WM_PAINT messages Begin/End Paint hdc. If null, we should
// get our own dc.
//
VOID ComboBox_InternalUpdateEditWindow(PCBOX pcbox, HDC hdcPaint)
{
int cchText = 0;
LPWSTR pText = NULL;
int sItem;
HDC hdc;
UINT msg;
HBRUSH hbrSave;
HBRUSH hbrControl;
HANDLE hOldFont;
DRAWITEMSTRUCT dis;
RECT rc;
HWND hwnd = pcbox->hwnd;
sItem = (int)SendMessage(pcbox->hwndList, LB_GETCURSEL, 0, 0);
//
// This 'try-finally' block ensures that the allocated 'pText' will
// be freed no matter how this routine is exited.
//
try
{
if (sItem != -1)
{
cchText = (int)SendMessage(pcbox->hwndList, LB_GETTEXTLEN, (DWORD)sItem, 0);
pText = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, (cchText+1) * sizeof(WCHAR));
if (pText)
{
cchText = (int)SendMessage(pcbox->hwndList, LB_GETTEXT,
(DWORD)sItem, (LPARAM)pText);
}
else
{
cchText = 0;
}
}
if (!pcbox->fNoEdit)
{
if (pcbox->hwndEdit)
{
if (GET_STYLE(pcbox) & CBS_HASSTRINGS)
{
SetWindowText(pcbox->hwndEdit, pText ? pText : TEXT(""));
}
if (pcbox->fFocus)
{
//
// Only hilite the text if we have the focus.
//
SendMessage(pcbox->hwndEdit, EM_SETSEL, 0, MAXLONG);
}
}
}
else if (IsComboVisible(pcbox))
{
if (hdcPaint)
{
hdc = hdcPaint;
}
else
{
hdc = GetDC(hwnd);
}
SetBkMode(hdc, OPAQUE);
if (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
{
if (TESTFLAG(GET_STYLE(pcbox), WS_DISABLED))
{
msg = WM_CTLCOLORSTATIC;
}
else
{
msg = WM_CTLCOLOREDIT;
}
}
else
{
msg = WM_CTLCOLORLISTBOX;
}
hbrControl = (HBRUSH)SendMessage(GetParent(hwnd), msg, (WPARAM)hdc, (LPARAM)hwnd);
hbrSave = SelectObject(hdc, hbrControl);
CopyRect(&rc, &pcbox->editrc);
InflateRect(&rc, GetSystemMetrics(SM_CXBORDER), GetSystemMetrics(SM_CYBORDER));
PatBlt(hdc, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, PATCOPY);
InflateRect(&rc, -GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_CYBORDER));
if (pcbox->fFocus && !pcbox->fLBoxVisible)
{
//
// Fill in the selected area
//
//
// only do the FillRect if we know its not
// ownerdraw item, otherwise we mess up people up
// BUT: for Compat's sake we still do this for Win 3.1 guys
//
if (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT) || !pcbox->OwnerDraw)
{
FillRect(hdc, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
}
SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else if (TESTFLAG(GET_STYLE(pcbox), WS_DISABLED) && !pcbox->OwnerDraw)
{
if ((COLORREF)GetSysColor(COLOR_GRAYTEXT) != GetBkColor(hdc))
{
SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
}
}
if (pcbox->hFont != NULL)
{
hOldFont = SelectObject(hdc, pcbox->hFont);
}
if (pcbox->OwnerDraw)
{
//
// Let the app draw the stuff in the static text box.
//
dis.CtlType = ODT_COMBOBOX;
dis.CtlID = GetWindowID(pcbox->hwnd);
dis.itemID = sItem;
dis.itemAction = ODA_DRAWENTIRE;
dis.itemState = (UINT)
((pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_SELECTED : 0) |
(TESTFLAG(GET_STYLE(pcbox), WS_DISABLED) ? ODS_DISABLED : 0) |
(pcbox->fFocus && !pcbox->fLBoxVisible ? ODS_FOCUS : 0) |
(TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT) ? ODS_COMBOBOXEDIT : 0) |
(TESTFLAG(GET_EXSTYLE(pcbox), WS_EXP_UIFOCUSHIDDEN) ? ODS_NOFOCUSRECT : 0) |
(TESTFLAG(GET_EXSTYLE(pcbox), WS_EXP_UIACCELHIDDEN) ? ODS_NOACCEL : 0));
dis.hwndItem = hwnd;
dis.hDC = hdc;
CopyRect(&dis.rcItem, &rc);
//
// Don't let ownerdraw dudes draw outside of the combo client
// bounds.
//
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
dis.itemData = (ULONG_PTR)SendMessage(pcbox->hwndList,
LB_GETITEMDATA, (UINT)sItem, 0);
SendMessage(pcbox->hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
}
else
{
//
// Start the text one pixel within the rect so that we leave a
// nice hilite border around the text.
//
int x;
UINT align;
if (pcbox->fRightAlign )
{
align = TA_RIGHT;
x = rc.right - GetSystemMetrics(SM_CXBORDER);
}
else
{
x = rc.left + GetSystemMetrics(SM_CXBORDER);
align = 0;
}
if (pcbox->fRtoLReading)
{
align |= TA_RTLREADING;
}
if (align)
{
SetTextAlign(hdc, GetTextAlign(hdc) | align);
}
//
// Draw the text, leaving a gap on the left & top for selection.
//
ExtTextOut(hdc, x, rc.top + GetSystemMetrics(SM_CYBORDER), ETO_CLIPPED | ETO_OPAQUE,
&rc, pText ? pText : TEXT(""), cchText, NULL);
if (pcbox->fFocus && !pcbox->fLBoxVisible)
{
if (!TESTFLAG(GET_EXSTYLE(pcbox), WS_EXP_UIFOCUSHIDDEN))
{
DrawFocusRect(hdc, &rc);
}
}
}
if (pcbox->hFont && hOldFont)
{
SelectObject(hdc, hOldFont);
}
if (hbrSave)
{
SelectObject(hdc, hbrSave);
}
if (!hdcPaint)
{
ReleaseDC(hwnd, hdc);
}
}
}
finally
{
if (pText != NULL)
{
UserLocalFree((HANDLE)pText);
}
}
}
//---------------------------------------------------------------------------//
//
// ComboBox_GetTextLengthHandler
//
// For the combo box without an edit control, returns size of current selected
// item
//
LONG ComboBox_GetTextLengthHandler(PCBOX pcbox)
{
int item;
int cchText;
item = (int)SendMessage(pcbox->hwndList, LB_GETCURSEL, 0, 0);
if (item == LB_ERR)
{
//
// No selection so no text.
//
cchText = 0;
}
else
{
cchText = (int)SendMessage(pcbox->hwndList, LB_GETTEXTLEN, item, 0);
}
return cchText;
}
//---------------------------------------------------------------------------//
//
// ComboBox_GetTextHandler
//
// For the combo box without an edit control, copies cbString bytes of the
// string in the static text box to the buffer given by lpszString.
//
LONG ComboBox_GetTextHandler(PCBOX pcbox, int cchString, LPWSTR lpszString)
{
int item;
int cchText;
LPWSTR lpszBuffer;
DWORD dw;
if (!cchString || !lpszString)
{
return 0;
}
//
// Null the buffer to be nice.
//
*lpszString = 0;
item = (int)SendMessage(pcbox->hwndList, LB_GETCURSEL, 0, 0);
if (item == LB_ERR)
{
//
// No selection so no text.
//
return 0;
}
cchText = (int)SendMessage(pcbox->hwndList, LB_GETTEXTLEN, item, 0);
cchText++;
if ((cchText <= cchString) ||
(!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN31COMPAT) && cchString == 2))
{
//
// Just do the copy if the given buffer size is large enough to hold
// everything. Or if old 3.0 app. (Norton used to pass 2 & expect 3
// chars including the \0 in 3.0; Bug #7018 win31: vatsanp)
//
dw = (int)SendMessage(pcbox->hwndList, LB_GETTEXT, item,
(LPARAM)lpszString);
return dw;
}
lpszBuffer = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, cchText*sizeof(WCHAR));
if (!lpszBuffer)
{
//
// Bail. Not enough memory to chop up the text.
//
return 0;
}
try
{
SendMessage(pcbox->hwndList, LB_GETTEXT, item, (LPARAM)lpszBuffer);
RtlCopyMemory((PBYTE)lpszString, (PBYTE)lpszBuffer, cchString * sizeof(WCHAR));
lpszString[cchString - 1] = 0;
}
finally
{
UserLocalFree((HANDLE)lpszBuffer);
}
return cchString;
}
//---------------------------------------------------------------------------//
// ComboBox_GetInfo
//
// return information about this combobox to the caller
// in the ComboBoxInfo struct
//
BOOL ComboBox_GetInfo(PCBOX pcbox, PCOMBOBOXINFO pcbi)
{
BOOL bRet = FALSE;
if (!pcbi || pcbi->cbSize != sizeof(COMBOBOXINFO))
{
SetLastError(ERROR_INVALID_PARAMETER);
}
else
{
//
// populate the structure
//
pcbi->hwndCombo = pcbox->hwnd;
pcbi->hwndItem = pcbox->hwndEdit;
pcbi->hwndList = pcbox->hwndList;
pcbi->rcItem = pcbox->editrc;
pcbi->rcButton = pcbox->buttonrc;
pcbi->stateButton = 0;
if (pcbox->CBoxStyle == CBS_SIMPLE)
{
pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
}
if (pcbox->fButtonPressed)
{
pcbi->stateButton |= STATE_SYSTEM_PRESSED;
}
bRet = TRUE;
}
return bRet;
}
//---------------------------------------------------------------------------//
//
// ComboBox_WndProc
//
// WndProc for comboboxes.
//
LRESULT WINAPI ComboBox_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PCBOX pcbox;
POINT pt;
LPWSTR lpwsz = NULL;
LRESULT lReturn = TRUE;
static BOOL fInit = TRUE;
INT i;
RECT rcCombo;
RECT rcList;
RECT rcWindow;
//
// Get the instance data for this combobox control
//
pcbox = ComboBox_GetPtr(hwnd);
if (!pcbox && uMsg != WM_NCCREATE)
{
goto CallDWP;
}
//
// Protect the combobox during the initialization.
//
if (!pcbox || pcbox->hwndList == NULL)
{
if (!ComboBox_MsgOKInit(uMsg, &lReturn))
{
TraceMsg(TF_STANDARD, "UxCombobox: ComboBoxWndProcWorker: msg=%04x is sent to hwnd=%08x in the middle of initialization.",
uMsg, hwnd);
return lReturn;
}
}
//
// Dispatch the various messages we can receive
//
switch (uMsg)
{
case CBEC_KILLCOMBOFOCUS:
//
// Private message coming from editcontrol informing us that the combo
// box is losing the focus to a window which isn't in this combo box.
//
ComboBox_KillFocusHandler(pcbox);
break;
case WM_COMMAND:
//
// So that we can handle notification messages from the listbox and
// edit control.
//
return ComboBox_CommandHandler(pcbox, (DWORD)wParam, (HWND)lParam);
case WM_STYLECHANGED:
{
LONG OldStyle;
LONG NewStyle = 0;
UserAssert(pcbox->hwndList != NULL);
pcbox->fRtoLReading = TESTFLAG(GET_EXSTYLE(pcbox), WS_EX_RTLREADING);
pcbox->fRightAlign = TESTFLAG(GET_EXSTYLE(pcbox), WS_EX_RIGHT);
if (pcbox->fRtoLReading)
{
NewStyle |= (WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
}
if (pcbox->fRightAlign)
{
NewStyle |= WS_EX_RIGHT;
}
OldStyle = GetWindowExStyle(pcbox->hwndList) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR);
SetWindowLong(pcbox->hwndList, GWL_EXSTYLE, OldStyle|NewStyle);
if (!pcbox->fNoEdit && pcbox->hwndEdit)
{
OldStyle = GetWindowExStyle(pcbox->hwndEdit) & ~(WS_EX_RIGHT|WS_EX_RTLREADING|WS_EX_LEFTSCROLLBAR);
SetWindowLong(pcbox->hwndEdit, GWL_EXSTYLE, OldStyle|NewStyle);
}
ComboBox_Position(pcbox);
InvalidateRect(hwnd, NULL, FALSE);
break;
}
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
case WM_CTLCOLOR:
//
// Causes compatibility problems for 3.X apps. Forward only
// for 4.0
//
if (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
{
LRESULT ret;
ret = SendMessage(pcbox->hwndParent, uMsg, wParam, lParam);
return ret;
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
break;
case WM_GETTEXT:
if (pcbox->fNoEdit)
{
return ComboBox_GetTextHandler(pcbox, (int)wParam, (LPWSTR)lParam);
}
goto CallEditSendMessage;
break;
case WM_GETTEXTLENGTH:
//
// If the is not edit control, CBS_DROPDOWNLIST, then we have to
// ask the list box for the size
//
if (pcbox->fNoEdit)
{
return ComboBox_GetTextLengthHandler(pcbox);
}
// FALL THROUGH
case WM_CLEAR:
case WM_CUT:
case WM_PASTE:
case WM_COPY:
case WM_SETTEXT:
goto CallEditSendMessage;
break;
case WM_CREATE:
//
// wParam - not used
// lParam - Points to the CREATESTRUCT data structure for the window.
//
return ComboBox_CreateHandler(pcbox, hwnd);
case WM_ERASEBKGND:
//
// Just return 1L so that the background isn't erased
//
return 1L;
case WM_GETFONT:
return (LRESULT)pcbox->hFont;
case WM_PRINT:
if (!DefWindowProc(hwnd, uMsg, wParam, lParam))
return FALSE;
if ( (lParam & PRF_OWNED) &&
(pcbox->CBoxStyle & SDROPPABLE) &&
IsWindowVisible(pcbox->hwndList) )
{
INT iDC = SaveDC((HDC) wParam);
GetWindowRect(hwnd, &rcCombo);
GetWindowRect(pcbox->hwndList, &rcList);
OffsetWindowOrgEx((HDC) wParam, 0, rcCombo.top - rcList.top, NULL);
lParam &= ~PRF_CHECKVISIBLE;
SendMessage(pcbox->hwndList, WM_PRINT, wParam, lParam);
RestoreDC((HDC) wParam, iDC);
}
return TRUE;
case WM_PRINTCLIENT:
ComboBox_Paint(pcbox, (HDC) wParam);
break;
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
//
// wParam - perhaps a hdc
//
hdc = (wParam) ? (HDC) wParam : BeginPaint(hwnd, &ps);
if (IsComboVisible(pcbox))
{
ComboBox_Paint(pcbox, hdc);
}
if (!wParam)
{
EndPaint(hwnd, &ps);
}
break;
}
case WM_GETDLGCODE:
//
// wParam - not used
// lParam - not used
//
{
LRESULT code = DLGC_WANTCHARS | DLGC_WANTARROWS;
//
// If the listbox is dropped and the ENTER key is pressed,
// we want this message so we can close up the listbox
//
if ((lParam != 0) &&
(((LPMSG)lParam)->message == WM_KEYDOWN) &&
pcbox->fLBoxVisible &&
((wParam == VK_RETURN) || (wParam == VK_ESCAPE)))
{
code |= DLGC_WANTMESSAGE;
}
return code;
}
case WM_SETFONT:
ComboBox_SetFontHandler(pcbox, (HANDLE)wParam, LOWORD(lParam));
break;
case WM_SYSKEYDOWN:
//
// Check if the alt key is down
//
if (lParam & 0x20000000L)
{
//
// Handle Combobox support. We want alt up or down arrow to behave
// like F4 key which completes the combo box selection
//
if (lParam & 0x1000000)
{
//
// This is an extended key such as the arrow keys not on the
// numeric keypad so just drop the combobox.
//
if (wParam == VK_DOWN || wParam == VK_UP)
{
goto DropCombo;
}
goto CallDWP;
}
if (GetKeyState(VK_NUMLOCK) & 0x1)
{
//
// If numlock down, just send all system keys to dwp
//
goto CallDWP;
}
else
{
//
// We just want to ignore keys on the number pad...
//
if (!(wParam == VK_DOWN || wParam == VK_UP))
{
goto CallDWP;
}
}
DropCombo:
if (!pcbox->fLBoxVisible)
{
//
// If the listbox isn't visible, just show it
//
ComboBox_ShowListBoxWindow(pcbox, TRUE);
}
else
{
//
// Ok, the listbox is visible. So hide the listbox window.
//
if (!ComboBox_HideListBoxWindow(pcbox, TRUE, TRUE))
{
return 0L;
}
}
}
goto CallDWP;
break;
case WM_KEYDOWN:
//
// If the listbox is dropped and the ENTER key is pressed,
// close up the listbox successfully. If ESCAPE is pressed,
// close it up like cancel.
//
if (pcbox->fLBoxVisible)
{
if ((wParam == VK_RETURN) || (wParam == VK_ESCAPE))
{
ComboBox_HideListBoxWindow(pcbox, TRUE, (wParam != VK_ESCAPE));
break;
}
}
//
// FALL THROUGH
//
case WM_CHAR:
if (g_fDBCSEnabled && IsDBCSLeadByte((BYTE)wParam))
{
return ComboBox_DBCharHandler(pcbox, hwnd, uMsg, wParam, lParam);
}
if (pcbox->fNoEdit)
{
goto CallListSendMessage;
}
else
{
goto CallEditSendMessage;
}
break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
pcbox->fButtonHotTracked = FALSE;
//
// Set the focus to the combo box if we get a mouse click on it.
//
if (!pcbox->fFocus)
{
SetFocus(hwnd);
if (!pcbox->fFocus)
{
//
// Don't do anything if we still don't have the focus.
//
break;
}
}
//
// If user clicked in button rect and we are a combobox with edit, then
// drop the listbox. (The button rect is 0 if there is no button so the
// ptinrect will return false.) If a drop down list (no edit), clicking
// anywhere on the face causes the list to drop.
//
POINTSTOPOINT(pt, lParam);
if ((pcbox->CBoxStyle == SDROPDOWN &&
PtInRect(&pcbox->buttonrc, pt)) ||
pcbox->CBoxStyle == SDROPDOWNLIST)
{
//
// Set the fMouseDown flag so that we can handle clicking on
// the popdown button and dragging into the listbox (when it just
// dropped down) to make a selection.
//
pcbox->fButtonPressed = TRUE;
if (pcbox->fLBoxVisible)
{
if (pcbox->fMouseDown)
{
pcbox->fMouseDown = FALSE;
ReleaseCapture();
}
ComboBox_PressButton(pcbox, FALSE);
if (!ComboBox_HideListBoxWindow(pcbox, TRUE, TRUE))
{
return 0L;
}
}
else
{
ComboBox_ShowListBoxWindow(pcbox, FALSE);
// Setting and resetting this flag must always be followed
// imediately by SetCapture or ReleaseCapture
//
pcbox->fMouseDown = TRUE;
SetCapture(hwnd);
NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEX_COMBOBOX_BUTTON);
}
}
break;
case WM_MOUSEWHEEL:
//
// Handle only scrolling.
//
if (wParam & (MK_CONTROL | MK_SHIFT))
{
goto CallDWP;
}
//
// If the listbox is visible, send it the message to scroll.
//
if (pcbox->fLBoxVisible)
{
goto CallListSendMessage;
}
//
// If we're in extended UI mode or the edit control isn't yet created,
// bail.
//
if (pcbox->fExtendedUI || pcbox->hwndEdit == NULL)
{
return TRUE;
}
//
// Emulate arrow up/down messages to the edit control.
//
i = abs(((short)HIWORD(wParam))/WHEEL_DELTA);
wParam = ((short)HIWORD(wParam) > 0) ? VK_UP : VK_DOWN;
while (i-- > 0)
{
SendMessage(pcbox->hwndEdit, WM_KEYDOWN, wParam, 0);
}
return TRUE;
case WM_CAPTURECHANGED:
if (!TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
{
return 0;
}
if ((pcbox->fMouseDown))
{
pcbox->fMouseDown = FALSE;
ComboBox_PressButton(pcbox, FALSE);
//
// Pop combo listbox back up, canceling.
//
if (pcbox->fLBoxVisible)
{
ComboBox_HideListBoxWindow(pcbox, TRUE, FALSE);
}
}
break;
case WM_LBUTTONUP:
ComboBox_PressButton(pcbox, FALSE);
//
// Clear this flag so that mouse moves aren't sent to the listbox
//
if (pcbox->fMouseDown || ((pcbox->CBoxStyle & SDROPPABLE) && pcbox->fLBoxVisible))
{
if (pcbox->fMouseDown)
{
pcbox->fMouseDown = FALSE;
if (pcbox->CBoxStyle == SDROPDOWN)
{
//
// If an item in the listbox matches the text in the edit
// control, scroll it to the top of the listbox. Select the
// item only if the mouse button isn't down otherwise we
// will select the item when the mouse button goes up.
//
ComboBox_UpdateListBoxWindow(pcbox, TRUE);
ComboBox_CompleteEditWindow(pcbox);
}
ReleaseCapture();
}
//
// Now, we want listbox to track mouse moves while mouse up
// until mouse down, and select items as though they were
// clicked on.
//
if (TESTFLAG(GET_STATE2(pcbox), WS_S2_WIN40COMPAT))
{
SendMessage(pcbox->hwndList, LBCB_STARTTRACK, FALSE, 0);
}
}
if (pcbox->hTheme)
{
POINTSTOPOINT(pt, lParam);
ComboBox_HotTrack(pcbox, pt);
}
break;
case WM_MOUSELEAVE:
pcbox->fButtonHotTracked = FALSE;
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_MOUSEMOVE:
if (pcbox->fMouseDown)
{
POINTSTOPOINT(pt, lParam);
ClientToScreen(hwnd, &pt);
GetWindowRect(pcbox->hwndList, &rcList);
if (PtInRect(&rcList, pt))
{
//
// This handles dropdown comboboxes/listboxes so that clicking
// on the dropdown button and dragging into the listbox window
// will let the user make a listbox selection.
//
pcbox->fMouseDown = FALSE;
ReleaseCapture();
if (pcbox->CBoxStyle & SEDITABLE)
{
// If an item in the listbox matches the text in the edit
// control, scroll it to the top of the listbox. Select the
// item only if the mouse button isn't down otherwise we
// will select the item when the mouse button goes up.
//
// We need to select the item which matches the editcontrol
// so that if the user drags out of the listbox, we don't
// cancel back to his origonal selection
//
ComboBox_UpdateListBoxWindow(pcbox, TRUE);
}
//
// Convert point to listbox coordinates and send a buttondown
// message to the listbox window.
//
ScreenToClient(pcbox->hwndList, &pt);
lParam = POINTTOPOINTS(pt);
uMsg = WM_LBUTTONDOWN;
goto CallListSendMessage;
}
}
if (pcbox->hTheme)
{
POINTSTOPOINT(pt, lParam);
ComboBox_HotTrack(pcbox, pt);
}
break;
case WM_NCDESTROY:
case WM_FINALDESTROY:
ComboBox_NcDestroyHandler(hwnd, pcbox);
break;
case WM_SETFOCUS:
if (pcbox->fNoEdit)
{
//
// There is no editcontrol so set the focus to the combo box itself.
//
ComboBox_GetFocusHandler(pcbox);
}
else if (pcbox->hwndEdit)
{
//
// Set the focus to the edit control window if there is one
//
SetFocus(pcbox->hwndEdit);
}
break;
case WM_KILLFOCUS:
//
// wParam has the new focus hwnd
//
if ((wParam == 0) || !IsChild(hwnd, (HWND)wParam))
{
//
// We only give up the focus if the new window getting the focus
// doesn't belong to the combo box.
//
ComboBox_KillFocusHandler(pcbox);
}
if ( IsWindow(hwnd) )
{
PLBIV plb = ListBox_GetPtr(pcbox->hwndList);
if ((plb != NULL) && (plb != (PLBIV)-1))
{
plb->iTypeSearch = 0;
if (plb->pszTypeSearch)
{
UserLocalFree(plb->pszTypeSearch);
plb->pszTypeSearch = NULL;
}
}
}
break;
case WM_SETREDRAW:
//
// wParam - specifies state of the redraw flag. nonzero = redraw
// lParam - not used
//
//
// effects: Sets the state of the redraw flag for this combo box
// and its children.
//
pcbox->fNoRedraw = (UINT)!((BOOL)wParam);
//
// Must check pcbox->spwnEdit in case we get this message before
// WM_CREATE - PCBOX won't be initialized yet. (Eudora does this)
//
if (!pcbox->fNoEdit && pcbox->hwndEdit)
{
SendMessage(pcbox->hwndEdit, uMsg, wParam, lParam);
}
goto CallListSendMessage;
break;
case WM_ENABLE:
//
// Invalidate the rect to cause it to be drawn in grey for its
// disabled view or ungreyed for non-disabled view.
//
InvalidateRect(hwnd, NULL, FALSE);
if ((pcbox->CBoxStyle & SEDITABLE) && pcbox->hwndEdit)
{
//
// Enable/disable the edit control window
//
EnableWindow(pcbox->hwndEdit, !TESTFLAG(GET_STYLE(pcbox), WS_DISABLED));
}
//
// Enable/disable the listbox window
//
UserAssert(pcbox->hwndList);
EnableWindow(pcbox->hwndList, !TESTFLAG(GET_STYLE(pcbox), WS_DISABLED));
break;
case WM_SIZE:
//
// wParam - defines the type of resizing fullscreen, sizeiconic,
// sizenormal etc.
// lParam - new width in LOWORD, new height in HIGHUINT of client area
//
UserAssert(pcbox->hwndList);
if (LOWORD(lParam) == 0 || HIWORD(lParam) == 0)
{
//
// If being sized to a zero width or to a zero height or we aren't
// fully initialized, just return.
//
return 0;
}
//
// OPTIMIZATIONS -- first check if old and new widths are the same
//
GetWindowRect(hwnd, &rcWindow);
if (pcbox->cxCombo == rcWindow.right - rcWindow.left)
{
int iNewHeight = rcWindow.bottom - rcWindow.top;
//
// now check if new height is the dropped down height
//
if (pcbox->fLBoxVisible)
{
//
// Check if new height is the full size height
//
if (pcbox->cyDrop + pcbox->cyCombo == iNewHeight)
{
return 0;
}
}
else
{
//
// Check if new height is the closed up height
//
if (pcbox->cyCombo == iNewHeight)
{
return 0;
}
}
}
ComboBox_SizeHandler(pcbox);
break;
case WM_WINDOWPOSCHANGING:
if (lParam)
{
((LPWINDOWPOS)lParam)->flags |= SWP_NOCOPYBITS;
}
break;
case WM_WININICHANGE:
InitGlobalMetrics(wParam);
break;
case CB_GETDROPPEDSTATE:
//
// returns 1 if combo is dropped down else 0
// wParam - not used
// lParam - not used
//
return pcbox->fLBoxVisible;
case CB_GETDROPPEDCONTROLRECT:
//
// wParam - not used
// lParam - lpRect which will get the dropped down window rect in
// screen coordinates.
//
if ( lParam )
{
GetWindowRect(hwnd, &rcWindow);
((LPRECT)lParam)->left = rcWindow.left;
((LPRECT)lParam)->top = rcWindow.top;
((LPRECT)lParam)->right = rcWindow.left + max(pcbox->cxDrop, pcbox->cxCombo);
((LPRECT)lParam)->bottom = rcWindow.top + pcbox->cyCombo + pcbox->cyDrop;
}
else
{
lReturn = 0;
}
break;
case CB_SETDROPPEDWIDTH:
if (pcbox->CBoxStyle & SDROPPABLE)
{
if (wParam)
{
wParam = max(wParam, (UINT)pcbox->cxCombo);
if (wParam != (UINT) pcbox->cxDrop)
{
pcbox->cxDrop = (int)wParam;
ComboBox_Position(pcbox);
}
}
}
//
// fall thru
//
case CB_GETDROPPEDWIDTH:
if (pcbox->CBoxStyle & SDROPPABLE)
{
return (LRESULT)max(pcbox->cxDrop, pcbox->cxCombo);
}
else
{
return CB_ERR;
}
break;
case CB_DIR:
//
// wParam - Dos attribute value.
// lParam - Points to a file specification string
//
return lParam ? CBDir(pcbox, LOWORD(wParam), (LPWSTR)lParam) : CB_ERR;
case CB_SETEXTENDEDUI:
//
// wParam - specifies state to set extendui flag to.
// Currently only 1 is allowed. Return CB_ERR (-1) if
// failure else 0 if success.
//
if (pcbox->CBoxStyle & SDROPPABLE)
{
if (!wParam)
{
pcbox->fExtendedUI = 0;
return 0;
}
if (wParam == 1)
{
pcbox->fExtendedUI = 1;
return 0;
}
TraceMsg(TF_STANDARD,
"UxCombobox: Invalid parameter \"wParam\" (%ld) to ComboBoxWndProcWorker",
wParam);
}
else
{
TraceMsg(TF_STANDARD,
"UxCombobox: Invalid message (%ld) sent to ComboBoxWndProcWorker",
uMsg);
}
return CB_ERR;
case CB_GETEXTENDEDUI:
if (pcbox->CBoxStyle & SDROPPABLE)
{
if (pcbox->fExtendedUI)
{
return TRUE;
}
}
return FALSE;
case CB_GETEDITSEL:
//
// wParam - not used
// lParam - not used
// effects: Gets the selection range for the given edit control. The
// starting BYTE-position is in the low order word. It contains the
// the BYTE-position of the first nonselected character after the end
// of the selection in the high order word. Returns CB_ERR if no
// editcontrol.
//
uMsg = EM_GETSEL;
goto CallEditSendMessage;
break;
case CB_LIMITTEXT:
//
// wParam - max number of bytes that can be entered
// lParam - not used
// effects: Specifies the maximum number of bytes of text the user may
// enter. If maxLength is 0, we may enter MAXINT number of BYTES.
//
uMsg = EM_LIMITTEXT;
goto CallEditSendMessage;
break;
case CB_SETEDITSEL:
//
// wParam - ichStart
// lParam - ichEnd
//
uMsg = EM_SETSEL;
wParam = (int)(SHORT)LOWORD(lParam);
lParam = (int)(SHORT)HIWORD(lParam);
goto CallEditSendMessage;
break;
case CB_ADDSTRING:
//
// wParam - not used
// lParam - Points to null terminated string to be added to listbox
//
if (!pcbox->fCase)
{
uMsg = LB_ADDSTRING;
}
else
{
uMsg = (pcbox->fCase & UPPERCASE) ? LB_ADDSTRINGUPPER : LB_ADDSTRINGLOWER;
}
goto CallListSendMessage;
break;
case CB_DELETESTRING:
//
// wParam - index to string to be deleted
// lParam - not used
//
uMsg = LB_DELETESTRING;
goto CallListSendMessage;
break;
case CB_INITSTORAGE:
//
// wParamLo - number of items
// lParam - number of bytes of string space
//
uMsg = LB_INITSTORAGE;
goto CallListSendMessage;
case CB_SETTOPINDEX:
//
// wParamLo - index to make top
// lParam - not used
//
uMsg = LB_SETTOPINDEX;
goto CallListSendMessage;
case CB_GETTOPINDEX:
//
// wParamLo / lParam - not used
//
uMsg = LB_GETTOPINDEX;
goto CallListSendMessage;
case CB_GETCOUNT:
//
// wParam - not used
// lParam - not used
//
uMsg = LB_GETCOUNT;
goto CallListSendMessage;
break;
case CB_GETCURSEL:
//
// wParam - not used
// lParam - not used
//
uMsg = LB_GETCURSEL;
goto CallListSendMessage;
break;
case CB_GETLBTEXT:
//
// wParam - index of string to be copied
// lParam - buffer that is to receive the string
//
uMsg = LB_GETTEXT;
goto CallListSendMessage;
break;
case CB_GETLBTEXTLEN:
//
// wParam - index to string
// lParam - now used for cbANSI
//
uMsg = LB_GETTEXTLEN;
goto CallListSendMessage;
break;
case CB_INSERTSTRING:
//
// wParam - position to receive the string
// lParam - points to the string
//
if (!pcbox->fCase)
{
uMsg = LB_INSERTSTRING;
}
else
{
uMsg = (pcbox->fCase & UPPERCASE) ? LB_INSERTSTRINGUPPER : LB_INSERTSTRINGLOWER;
}
goto CallListSendMessage;
break;
case CB_RESETCONTENT:
//
// wParam - not used
// lParam - not used
// If we come here before WM_CREATE has been processed,
// pcbox->spwndList will be NULL.
//
UserAssert(pcbox->hwndList);
SendMessage(pcbox->hwndList, LB_RESETCONTENT, 0, 0);
ComboBox_InternalUpdateEditWindow(pcbox, NULL);
break;
case CB_GETHORIZONTALEXTENT:
uMsg = LB_GETHORIZONTALEXTENT;
goto CallListSendMessage;
case CB_SETHORIZONTALEXTENT:
uMsg = LB_SETHORIZONTALEXTENT;
goto CallListSendMessage;
case CB_FINDSTRING:
//
// wParam - index of starting point for search
// lParam - points to prefix string
//
uMsg = LB_FINDSTRING;
goto CallListSendMessage;
break;
case CB_FINDSTRINGEXACT:
//
// wParam - index of starting point for search
// lParam - points to a exact string
//
uMsg = LB_FINDSTRINGEXACT;
goto CallListSendMessage;
break;
case CB_SELECTSTRING:
//
// wParam - index of starting point for search
// lParam - points to prefix string
//
UserAssert(pcbox->hwndList);
lParam = SendMessage(pcbox->hwndList, LB_SELECTSTRING, wParam, lParam);
ComboBox_InternalUpdateEditWindow(pcbox, NULL);
return lParam;
case CB_SETCURSEL:
//
// wParam - Contains index to be selected
// lParam - not used
// If we come here before WM_CREATE has been processed,
// pcbox->spwndList will be NULL.
//
UserAssert(pcbox->hwndList);
lParam = SendMessage(pcbox->hwndList, LB_SETCURSEL, wParam, lParam);
if (lParam != -1)
{
SendMessage(pcbox->hwndList, LB_SETTOPINDEX, wParam, 0);
}
ComboBox_InternalUpdateEditWindow(pcbox, NULL);
return lParam;
case CB_GETITEMDATA:
uMsg = LB_GETITEMDATA;
goto CallListSendMessage;
break;
case CB_SETITEMDATA:
uMsg = LB_SETITEMDATA;
goto CallListSendMessage;
break;
case CB_SETITEMHEIGHT:
if (wParam == -1)
{
if (HIWORD(lParam) != 0)
{
return CB_ERR;
}
return ComboBox_SetEditItemHeight(pcbox, LOWORD(lParam));
}
uMsg = LB_SETITEMHEIGHT;
goto CallListSendMessage;
break;
case CB_GETITEMHEIGHT:
if (wParam == -1)
{
return pcbox->editrc.bottom - pcbox->editrc.top;
}
uMsg = LB_GETITEMHEIGHT;
goto CallListSendMessage;
break;
case CB_SHOWDROPDOWN:
//
// wParam - True then drop down the listbox if possible else hide it
// lParam - not used
//
if (wParam && !pcbox->fLBoxVisible)
{
ComboBox_ShowListBoxWindow(pcbox, TRUE);
}
else
{
if (!wParam && pcbox->fLBoxVisible)
{
ComboBox_HideListBoxWindow(pcbox, TRUE, FALSE);
}
}
break;
case CB_SETLOCALE:
//
// wParam - locale id
// lParam - not used
//
uMsg = LB_SETLOCALE;
goto CallListSendMessage;
break;
case CB_GETLOCALE:
//
// wParam - not used
// lParam - not used
//
uMsg = LB_GETLOCALE;
goto CallListSendMessage;
break;
case CB_GETCOMBOBOXINFO:
//
// wParam - not used
// lParam - pointer to COMBOBOXINFO struct
//
lReturn = ComboBox_GetInfo(pcbox, (PCOMBOBOXINFO)lParam);
break;
case CB_SETMINVISIBLE:
if (wParam > 0)
{
PLBIV plb = ListBox_GetPtr(pcbox->hwndList);
pcbox->iMinVisible = (int)wParam;
if (plb && !plb->fNoIntegralHeight)
{
// forward through to the listbox to let him adjust
// his size if necessary
SendMessage(pcbox->hwndList, uMsg, wParam, 0L);
}
lReturn = TRUE;
}
else
{
lReturn = FALSE;
}
break;
case CB_GETMINVISIBLE:
return pcbox->iMinVisible;
case WM_MEASUREITEM:
case WM_DELETEITEM:
case WM_DRAWITEM:
case WM_COMPAREITEM:
return ComboBox_MessageItemHandler(pcbox, uMsg, (LPVOID)lParam);
case WM_NCCREATE:
//
// wParam - Contains a handle to the window being created
// lParam - Points to the CREATESTRUCT data structure for the window.
//
//
// Allocate the combobox instance stucture
//
pcbox = (PCBOX)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CBOX));
if (pcbox)
{
//
// Success... store the instance pointer.
//
TraceMsg(TF_STANDARD, "COMBOBOX: Setting combobox instance pointer.");
ComboBox_SetPtr(hwnd, pcbox);
return ComboBox_NcCreateHandler(pcbox, hwnd);
}
else
{
//
// Failed... return FALSE.
//
// From a WM_NCCREATE msg, this will cause the
// CreateWindow call to fail.
//
TraceMsg(TF_STANDARD, "COMBOBOX: Unable to allocate combobox instance structure.");
lReturn = FALSE;
}
break;
case WM_PARENTNOTIFY:
if (LOWORD(wParam) == WM_DESTROY)
{
if ((HWND)lParam == pcbox->hwndEdit)
{
pcbox->CBoxStyle &= ~SEDITABLE;
pcbox->fNoEdit = TRUE;
pcbox->hwndEdit = hwnd;
}
else if ((HWND)lParam == pcbox->hwndList)
{
pcbox->CBoxStyle &= ~SDROPPABLE;
pcbox->hwndList = NULL;
}
}
break;
case WM_UPDATEUISTATE:
//
// Propagate the change to the list control, if any
//
UserAssert(pcbox->hwndList);
SendMessage(pcbox->hwndList, WM_UPDATEUISTATE, wParam, lParam);
goto CallDWP;
case WM_GETOBJECT:
if(lParam == OBJID_QUERYCLASSNAMEIDX)
{
lReturn = MSAA_CLASSNAMEIDX_COMBOBOX;
}
else
{
lReturn = FALSE;
}
break;
case WM_THEMECHANGED:
if ( pcbox->hTheme )
{
CloseThemeData(pcbox->hTheme);
}
pcbox->hTheme = OpenThemeData(pcbox->hwnd, L"Combobox");
ComboBox_Position(pcbox);
InvalidateRect(pcbox->hwnd, NULL, TRUE);
lReturn = TRUE;
break;
case WM_HELP:
{
LPHELPINFO lpHelpInfo;
//
// Check if this message is from a child of this combo
//
if ((lpHelpInfo = (LPHELPINFO)lParam) != NULL &&
((pcbox->hwndEdit && lpHelpInfo->iCtrlId == (SHORT)GetWindowID(pcbox->hwndEdit)) ||
lpHelpInfo->iCtrlId == (SHORT)GetWindowID(pcbox->hwndList) ))
{
//
// Make it look like the WM_HELP is coming form this combo.
// Then DefWindowProcWorker will pass it up to our parent,
// who can do whatever he wants with it.
//
lpHelpInfo->iCtrlId = (SHORT)GetWindowID(hwnd);
lpHelpInfo->hItemHandle = hwnd;
#if 0 // PORTPORT: GetContextHelpId
lpHelpInfo->dwContextId = GetContextHelpId(hwnd);
#endif
}
//
// Fall through to DefWindowProc
//
}
default:
if ( (GetSystemMetrics(SM_PENWINDOWS)) &&
(uMsg >= WM_PENWINFIRST && uMsg <= WM_PENWINLAST))
{
goto CallEditSendMessage;
}
else
{
CallDWP:
lReturn = DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
return lReturn;
//
// The following forward messages off to the child controls.
//
CallEditSendMessage:
if (!pcbox->fNoEdit && pcbox->hwndEdit)
{
lReturn = SendMessage(pcbox->hwndEdit, uMsg, wParam, lParam);
}
else
{
TraceMsg(TF_STANDARD, "COMBOBOX: Invalid combobox message %#.4x", uMsg);
lReturn = CB_ERR;
}
return lReturn;
CallListSendMessage:
UserAssert(pcbox->hwndList);
lReturn = SendMessage(pcbox->hwndList, uMsg, wParam, lParam);
return lReturn;
}