2910 lines
76 KiB
C
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;
|
|
}
|