2310 lines
56 KiB
C
2310 lines
56 KiB
C
#include "ctlspriv.h"
|
|
#pragma hdrstop
|
|
#include "usrctl32.h"
|
|
#include "listbox.h"
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Forwards
|
|
//
|
|
VOID ListBox_CalcItemRowsAndColumns(PLBIV);
|
|
LONG ListBox_Create(PLBIV, HWND, LPCREATESTRUCT);
|
|
VOID ListBox_Destroy(PLBIV, HWND);
|
|
VOID ListBox_SetFont(PLBIV, HANDLE, BOOL);
|
|
VOID ListBox_Size(PLBIV, INT, INT, BOOL);
|
|
BOOL ListBox_SetTabStopsHandler(PLBIV, INT, LPINT);
|
|
VOID ListBox_DropObjectHandler(PLBIV, PDROPSTRUCT);
|
|
int ListBox_GetSetItemHeightHandler(PLBIV, UINT, int, UINT);
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// InitListBoxClass() - Registers the control's window class
|
|
//
|
|
BOOL InitListBoxClass(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_LISTBOX;
|
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
|
|
wc.style = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS;
|
|
wc.cbWndExtra = sizeof(PLBIV);
|
|
wc.cbClsExtra = 0;
|
|
|
|
if (!RegisterClass(&wc) && !GetClassInfo(hinst, WC_LISTBOX, &wc))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_WndProc
|
|
//
|
|
// Window Procedure for ListBox AND ComboLBox controls.
|
|
//
|
|
LRESULT APIENTRY ListBox_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PLBIV plb;
|
|
UINT wFlags;
|
|
LRESULT lReturn = FALSE;
|
|
|
|
//
|
|
// Get the instance data for this listbox control
|
|
//
|
|
plb = ListBox_GetPtr(hwnd);
|
|
if (!plb && uMsg != WM_NCCREATE)
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
case LB_GETTOPINDEX:
|
|
//
|
|
// Return index of top item displayed.
|
|
//
|
|
return plb->iTop;
|
|
|
|
case LB_SETTOPINDEX:
|
|
if (wParam && ((INT)wParam < 0 || (INT)wParam >= plb->cMac))
|
|
{
|
|
TraceMsg(TF_STANDARD, "Invalid index");
|
|
return LB_ERR;
|
|
}
|
|
|
|
if (plb->cMac)
|
|
{
|
|
ListBox_NewITop(plb, (INT)wParam);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_STYLECHANGED:
|
|
plb->fRtoLReading = ((GET_EXSTYLE(plb) & WS_EX_RTLREADING) != 0);
|
|
plb->fRightAlign = ((GET_EXSTYLE(plb) & WS_EX_RIGHT) != 0);
|
|
ListBox_CheckRedraw(plb, FALSE, 0);
|
|
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGED:
|
|
//
|
|
// If we are in the middle of creation, ignore this
|
|
// message because it will generate a WM_SIZE message.
|
|
// See ListBox_Create().
|
|
//
|
|
if (!plb->fIgnoreSizeMsg)
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
//
|
|
// If we are in the middle of creation, ignore size
|
|
// messages. See ListBox_Create().
|
|
//
|
|
if (!plb->fIgnoreSizeMsg)
|
|
{
|
|
ListBox_Size(plb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), FALSE);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
{
|
|
HDC hdcSave = plb->hdc;
|
|
HBRUSH hbr;
|
|
|
|
plb->hdc = (HDC)wParam;
|
|
hbr = ListBox_GetBrush(plb, NULL);
|
|
if (hbr)
|
|
{
|
|
RECT rcClient;
|
|
|
|
GetClientRect(hwnd, &rcClient);
|
|
FillRect(plb->hdc, &rcClient, hbr);
|
|
|
|
lReturn = TRUE;
|
|
}
|
|
|
|
plb->hdc = hdcSave;
|
|
break;
|
|
}
|
|
case LB_RESETCONTENT:
|
|
ListBox_ResetContentHandler(plb);
|
|
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if (wParam == IDSYS_LBSEARCH)
|
|
{
|
|
plb->iTypeSearch = 0;
|
|
KillTimer(hwnd, IDSYS_LBSEARCH);
|
|
ListBox_InvertItem(plb, plb->iSel, TRUE);
|
|
|
|
break;
|
|
}
|
|
|
|
uMsg = WM_MOUSEMOVE;
|
|
ListBox_TrackMouse(plb, uMsg, plb->ptPrev);
|
|
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
//
|
|
// 295135: if the combobox dropdown button is pressed and the listbox
|
|
// covers the combobox, the ensuing buttonup message gets sent to
|
|
// list instead of the combobox, which causes the dropdown to be
|
|
// closed immediately.
|
|
//
|
|
|
|
//
|
|
// send this to the combo if it hasn't processed buttonup yet after
|
|
// dropping the list.
|
|
//
|
|
if (plb->pcbox && plb->pcbox->hwnd && plb->pcbox->fButtonPressed)
|
|
{
|
|
return SendMessage(plb->pcbox->hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
// fall through
|
|
|
|
case WM_MOUSEMOVE:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONDBLCLK:
|
|
{
|
|
POINT pt;
|
|
|
|
POINTSTOPOINT(pt, lParam);
|
|
ListBox_TrackMouse(plb, uMsg, pt);
|
|
|
|
break;
|
|
}
|
|
case WM_MBUTTONDOWN:
|
|
EnterReaderMode(hwnd);
|
|
|
|
break;
|
|
|
|
case WM_CAPTURECHANGED:
|
|
//
|
|
// Note that this message should be handled only on unexpected
|
|
// capture changes currently.
|
|
//
|
|
ASSERT(TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT));
|
|
|
|
if (plb->fCaptured)
|
|
{
|
|
ListBox_ButtonUp(plb, LBUP_NOTIFY);
|
|
}
|
|
|
|
break;
|
|
|
|
case LBCB_STARTTRACK:
|
|
//
|
|
// Start tracking mouse moves in the listbox, setting capture
|
|
//
|
|
if (!plb->pcbox)
|
|
{
|
|
break;
|
|
}
|
|
|
|
plb->fCaptured = FALSE;
|
|
if (wParam)
|
|
{
|
|
POINT pt;
|
|
|
|
POINTSTOPOINT(pt, lParam);
|
|
|
|
ScreenToClient(hwnd, &pt);
|
|
ListBox_TrackMouse(plb, WM_LBUTTONDOWN, pt);
|
|
}
|
|
else
|
|
{
|
|
SetCapture(hwnd);
|
|
plb->fCaptured = TRUE;
|
|
plb->iLastSelection = plb->iSel;
|
|
}
|
|
|
|
break;
|
|
|
|
case LBCB_ENDTRACK:
|
|
//
|
|
// Kill capture, tracking, etc.
|
|
//
|
|
if ( plb->fCaptured || (GetCapture() == plb->hwndParent) )
|
|
{
|
|
ListBox_ButtonUp(plb, LBUP_RELEASECAPTURE | (wParam ? LBUP_SELCHANGE :
|
|
LBUP_RESETSELECTION));
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_PRINTCLIENT:
|
|
ListBox_Paint(plb, (HDC)wParam, NULL);
|
|
|
|
break;
|
|
|
|
case WM_NCPAINT:
|
|
if (plb->hTheme && (GET_EXSTYLE(plb) & WS_EX_CLIENTEDGE))
|
|
{
|
|
HRGN hrgn = (wParam != 1) ? (HRGN)wParam : NULL;
|
|
HBRUSH hbr = (HBRUSH)GetClassLongPtr(hwnd, GCLP_HBRBACKGROUND);
|
|
|
|
if (CCDrawNonClientTheme(plb->hTheme, hwnd, hrgn, hbr, 0, 0))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
goto CallDWP;
|
|
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
LPRECT lprc;
|
|
|
|
if (wParam)
|
|
{
|
|
hdc = (HDC) wParam;
|
|
lprc = NULL;
|
|
}
|
|
else
|
|
{
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
lprc = &(ps.rcPaint);
|
|
}
|
|
|
|
if (IsLBoxVisible(plb))
|
|
{
|
|
ListBox_Paint(plb, hdc, lprc);
|
|
}
|
|
|
|
if (!wParam)
|
|
{
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
case WM_NCDESTROY:
|
|
case WM_FINALDESTROY:
|
|
ListBox_Destroy(plb, hwnd);
|
|
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
CaretCreate(plb);
|
|
ListBox_SetCaret(plb, TRUE);
|
|
ListBox_NotifyOwner(plb, LBN_SETFOCUS);
|
|
ListBox_Event(plb, EVENT_OBJECT_FOCUS, plb->iSelBase);
|
|
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
//
|
|
// Reset the wheel delta count.
|
|
//
|
|
gcWheelDelta = 0;
|
|
|
|
ListBox_SetCaret(plb, FALSE);
|
|
ListBox_CaretDestroy(plb);
|
|
ListBox_NotifyOwner(plb, LBN_KILLFOCUS);
|
|
|
|
if (plb->iTypeSearch)
|
|
{
|
|
plb->iTypeSearch = 0;
|
|
KillTimer(hwnd, IDSYS_LBSEARCH);
|
|
}
|
|
|
|
if (plb->pszTypeSearch)
|
|
{
|
|
ControlFree(GetProcessHeap(), plb->pszTypeSearch);
|
|
plb->pszTypeSearch = NULL;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
int cDetants;
|
|
int cPage;
|
|
int cLines;
|
|
RECT rc;
|
|
int windowWidth;
|
|
int cPos;
|
|
UINT ucWheelScrollLines;
|
|
|
|
//
|
|
// Don't handle zoom and datazoom.
|
|
//
|
|
if (wParam & (MK_SHIFT | MK_CONTROL))
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
lReturn = 1;
|
|
gcWheelDelta -= (short) HIWORD(wParam);
|
|
cDetants = gcWheelDelta / WHEEL_DELTA;
|
|
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ucWheelScrollLines, 0);
|
|
if ( cDetants != 0 &&
|
|
ucWheelScrollLines > 0 &&
|
|
(GET_STYLE(plb) & (WS_VSCROLL | WS_HSCROLL)))
|
|
{
|
|
gcWheelDelta = gcWheelDelta % WHEEL_DELTA;
|
|
|
|
if (GET_STYLE(plb) & WS_VSCROLL)
|
|
{
|
|
cPage = max(1, (plb->cItemFullMax - 1));
|
|
cLines = cDetants *
|
|
(int) min((UINT) cPage, ucWheelScrollLines);
|
|
|
|
cPos = max(0, min(plb->iTop + cLines, plb->cMac - 1));
|
|
if (cPos != plb->iTop)
|
|
{
|
|
ListBox_VScroll(plb, SB_THUMBPOSITION, cPos);
|
|
ListBox_VScroll(plb, SB_ENDSCROLL, 0);
|
|
}
|
|
}
|
|
else if (plb->fMultiColumn)
|
|
{
|
|
cPage = max(1, plb->numberOfColumns);
|
|
cLines = cDetants * (int) min((UINT) cPage, ucWheelScrollLines);
|
|
cPos = max(
|
|
0,
|
|
min((plb->iTop / plb->itemsPerColumn) + cLines,
|
|
plb->cMac - 1 - ((plb->cMac - 1) % plb->itemsPerColumn)));
|
|
|
|
if (cPos != plb->iTop)
|
|
{
|
|
ListBox_HSrollMultiColumn(plb, SB_THUMBPOSITION, cPos);
|
|
ListBox_HSrollMultiColumn(plb, SB_ENDSCROLL, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GetClientRect(plb->hwnd, &rc);
|
|
windowWidth = rc.right;
|
|
cPage = max(plb->cxChar, (windowWidth / 3) * 2) /
|
|
plb->cxChar;
|
|
|
|
cLines = cDetants *
|
|
(int) min((UINT) cPage, ucWheelScrollLines);
|
|
|
|
cPos = max(
|
|
0,
|
|
min(plb->xOrigin + (cLines * plb->cxChar),
|
|
plb->maxWidth));
|
|
|
|
if (cPos != plb->xOrigin) {
|
|
ListBox_HScroll(plb, SB_THUMBPOSITION, cPos);
|
|
ListBox_HScroll(plb, SB_ENDSCROLL, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case WM_VSCROLL:
|
|
ListBox_VScroll(plb, LOWORD(wParam), HIWORD(wParam));
|
|
|
|
break;
|
|
|
|
case WM_HSCROLL:
|
|
ListBox_HScroll(plb, LOWORD(wParam), HIWORD(wParam));
|
|
|
|
break;
|
|
|
|
case WM_GETDLGCODE:
|
|
return DLGC_WANTARROWS | DLGC_WANTCHARS;
|
|
|
|
case WM_CREATE:
|
|
return ListBox_Create(plb, hwnd, (LPCREATESTRUCT)lParam);
|
|
|
|
case WM_SETREDRAW:
|
|
//
|
|
// If wParam is nonzero, the redraw flag is set
|
|
// If wParam is zero, the flag is cleared
|
|
//
|
|
ListBox_SetRedraw(plb, (wParam != 0));
|
|
|
|
break;
|
|
|
|
case WM_ENABLE:
|
|
ListBox_InvalidateRect(plb, NULL, !plb->OwnerDraw);
|
|
|
|
break;
|
|
|
|
case WM_SETFONT:
|
|
ListBox_SetFont(plb, (HANDLE)wParam, LOWORD(lParam));
|
|
|
|
break;
|
|
|
|
case WM_GETFONT:
|
|
return (LRESULT)plb->hFont;
|
|
|
|
case WM_DRAGSELECT:
|
|
case WM_DRAGLOOP:
|
|
case WM_DRAGMOVE:
|
|
case WM_DROPFILES:
|
|
return SendMessage(plb->hwndParent, uMsg, wParam, lParam);
|
|
|
|
case WM_QUERYDROPOBJECT:
|
|
case WM_DROPOBJECT:
|
|
|
|
//
|
|
// fix up control data, then pass message to parent
|
|
//
|
|
ListBox_DropObjectHandler(plb, (PDROPSTRUCT)lParam);
|
|
return SendMessage(plb->hwndParent, uMsg, wParam, lParam);
|
|
|
|
case LB_GETITEMRECT:
|
|
return ListBox_GetItemRectHandler(plb, (INT)wParam, (LPRECT)lParam);
|
|
|
|
case LB_GETITEMDATA:
|
|
//
|
|
// wParam = item index
|
|
//
|
|
return ListBox_GetItemDataHandler(plb, (INT)wParam);
|
|
|
|
case LB_SETITEMDATA:
|
|
|
|
//
|
|
// wParam is item index
|
|
//
|
|
return ListBox_SetItemDataHandler(plb, (INT)wParam, lParam);
|
|
|
|
case LB_ADDSTRINGUPPER:
|
|
wFlags = UPPERCASE | LBI_ADD;
|
|
goto CallInsertItem;
|
|
|
|
case LB_ADDSTRINGLOWER:
|
|
wFlags = LOWERCASE | LBI_ADD;
|
|
goto CallInsertItem;
|
|
|
|
case LB_ADDSTRING:
|
|
wFlags = LBI_ADD;
|
|
goto CallInsertItem;
|
|
|
|
case LB_INSERTSTRINGUPPER:
|
|
wFlags = UPPERCASE;
|
|
goto CallInsertItem;
|
|
|
|
case LB_INSERTSTRINGLOWER:
|
|
wFlags = LOWERCASE;
|
|
goto CallInsertItem;
|
|
|
|
case LB_INSERTSTRING:
|
|
wFlags = 0;
|
|
|
|
CallInsertItem:
|
|
// Validate the lParam. If the listbox does not have HASSTRINGS,
|
|
// the lParam is a data value. Otherwise, it is a string
|
|
// pointer, fail if NULL.
|
|
if ( !TESTFLAG(GET_STYLE(plb), LBS_HASSTRINGS) || lParam )
|
|
{
|
|
lReturn = (LRESULT)ListBox_InsertItem(plb, (LPWSTR) lParam, (int) wParam, wFlags);
|
|
if (!plb->fNoIntegralHeight)
|
|
{
|
|
ListBox_Size(plb, 0, 0, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lReturn = LB_ERR;
|
|
}
|
|
|
|
break;
|
|
|
|
case LB_INITSTORAGE:
|
|
return ListBox_InitStorage(plb, FALSE, (INT)wParam, (INT)lParam);
|
|
|
|
case LB_DELETESTRING:
|
|
return ListBox_DeleteStringHandler(plb, (INT)wParam);
|
|
|
|
case LB_DIR:
|
|
//
|
|
// wParam - Dos attribute value.
|
|
// lParam - Points to a file specification string
|
|
//
|
|
lReturn = ListBox_DirHandler(plb, (INT)wParam, (LPWSTR)lParam);
|
|
|
|
break;
|
|
|
|
case LB_ADDFILE:
|
|
lReturn = ListBox_InsertFile(plb, (LPWSTR)lParam);
|
|
|
|
break;
|
|
|
|
case LB_SETSEL:
|
|
return ListBox_SetSelHandler(plb, (wParam != 0), (INT)lParam);
|
|
|
|
case LB_SETCURSEL:
|
|
//
|
|
// If window obscured, update so invert will work correctly
|
|
//
|
|
return ListBox_SetCurSelHandler(plb, (INT)wParam);
|
|
|
|
case LB_GETSEL:
|
|
if (wParam >= (UINT)plb->cMac)
|
|
{
|
|
return (LRESULT)LB_ERR;
|
|
}
|
|
|
|
return ListBox_IsSelected(plb, (INT)wParam, SELONLY);
|
|
|
|
case LB_GETCURSEL:
|
|
if (plb->wMultiple == SINGLESEL)
|
|
{
|
|
return plb->iSel;
|
|
}
|
|
|
|
return plb->iSelBase;
|
|
|
|
case LB_SELITEMRANGE:
|
|
if (plb->wMultiple == SINGLESEL)
|
|
{
|
|
//
|
|
// Can't select a range if only single selections are enabled
|
|
//
|
|
TraceMsg(TF_STANDARD, "Invalid index passed to LB_SELITEMRANGE");
|
|
return LB_ERR;
|
|
}
|
|
|
|
ListBox_SetRange(plb, LOWORD(lParam), HIWORD(lParam), (wParam != 0));
|
|
|
|
break;
|
|
|
|
case LB_SELITEMRANGEEX:
|
|
if (plb->wMultiple == SINGLESEL)
|
|
{
|
|
//
|
|
// Can't select a range if only single selections are enabled
|
|
//
|
|
TraceMsg(TF_STANDARD, "LB_SELITEMRANGEEX:Can't select a range if only single selections are enabled");
|
|
return LB_ERR;
|
|
}
|
|
else
|
|
{
|
|
BOOL fHighlight = ((DWORD)lParam > (DWORD)wParam);
|
|
if (fHighlight == FALSE)
|
|
{
|
|
ULONG_PTR temp = lParam;
|
|
lParam = wParam;
|
|
wParam = temp;
|
|
}
|
|
|
|
ListBox_SetRange(plb, (INT)wParam, (INT)lParam, fHighlight);
|
|
}
|
|
|
|
break;
|
|
|
|
case LB_GETTEXTLEN:
|
|
if (lParam != 0)
|
|
{
|
|
TraceMsg(TF_WARNING, "LB_GETTEXTLEN with lParam = %lx\n", lParam);
|
|
}
|
|
|
|
lReturn = ListBox_GetTextHandler(plb, TRUE, FALSE, (INT)wParam, NULL);
|
|
|
|
break;
|
|
|
|
case LB_GETTEXT:
|
|
lReturn = ListBox_GetTextHandler(plb, FALSE, FALSE, (INT)wParam, (LPWSTR)lParam);
|
|
|
|
break;
|
|
|
|
case LB_GETCOUNT:
|
|
return (LRESULT)plb->cMac;
|
|
|
|
case LB_SETCOUNT:
|
|
return ListBox_SetCount(plb, (INT)wParam);
|
|
|
|
case LB_SELECTSTRING:
|
|
case LB_FINDSTRING:
|
|
{
|
|
int iSel = Listbox_FindStringHandler(plb, (LPWSTR)lParam, (INT)wParam, PREFIX, TRUE);
|
|
|
|
if (uMsg == LB_FINDSTRING || iSel == LB_ERR)
|
|
{
|
|
lReturn = iSel;
|
|
}
|
|
else
|
|
{
|
|
lReturn = ListBox_SetCurSelHandler(plb, iSel);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case LB_GETLOCALE:
|
|
return plb->dwLocaleId;
|
|
|
|
case LB_SETLOCALE:
|
|
{
|
|
DWORD dwRet;
|
|
|
|
//
|
|
// Validate locale
|
|
//
|
|
wParam = ConvertDefaultLocale((LCID)wParam);
|
|
if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
|
|
{
|
|
return LB_ERR;
|
|
}
|
|
|
|
dwRet = plb->dwLocaleId;
|
|
plb->dwLocaleId = (DWORD)wParam;
|
|
|
|
return dwRet;
|
|
|
|
}
|
|
case LB_GETLISTBOXINFO:
|
|
|
|
//
|
|
// wParam - not used
|
|
// lParam - not used
|
|
//
|
|
if (plb->fMultiColumn)
|
|
{
|
|
lReturn = (LRESULT)plb->itemsPerColumn;
|
|
}
|
|
else
|
|
{
|
|
lReturn = (LRESULT)plb->cMac;
|
|
}
|
|
|
|
break;
|
|
|
|
case CB_GETCOMBOBOXINFO:
|
|
//
|
|
// wParam - not used
|
|
// lParam - pointer to COMBOBOXINFO struct
|
|
//
|
|
if (plb->pcbox && plb->pcbox->hwnd && IsWindow(plb->pcbox->hwnd))
|
|
{
|
|
lReturn = SendMessage(plb->pcbox->hwnd, uMsg, wParam, lParam);
|
|
}
|
|
break;
|
|
|
|
case CB_SETMINVISIBLE:
|
|
if (!plb->fNoIntegralHeight)
|
|
{
|
|
ListBox_Size(plb, 0, 0, TRUE);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
//
|
|
// IanJa: Use LOWORD() to get low 16-bits of wParam - this should
|
|
// work for Win16 & Win32. The value obtained is the virtual key
|
|
//
|
|
ListBox_KeyInput(plb, uMsg, LOWORD(wParam));
|
|
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
ListBox_CharHandler(plb, LOWORD(wParam), FALSE);
|
|
|
|
break;
|
|
|
|
case LB_GETSELITEMS:
|
|
case LB_GETSELCOUNT:
|
|
//
|
|
// IanJa/Win32 should this be LPWORD now?
|
|
//
|
|
return ListBox_GetSelItemsHandler(plb, (uMsg == LB_GETSELCOUNT), (INT)wParam, (LPINT)lParam);
|
|
|
|
case LB_SETTABSTOPS:
|
|
|
|
//
|
|
// IanJa/Win32: Tabs given by array of INT for backwards compatability
|
|
//
|
|
return ListBox_SetTabStopsHandler(plb, (INT)wParam, (LPINT)lParam);
|
|
|
|
case LB_GETHORIZONTALEXTENT:
|
|
//
|
|
// Return the max width of the listbox used for horizontal scrolling
|
|
//
|
|
return plb->maxWidth;
|
|
|
|
case LB_SETHORIZONTALEXTENT:
|
|
//
|
|
// Set the max width of the listbox used for horizontal scrolling
|
|
//
|
|
if (plb->maxWidth != (INT)wParam)
|
|
{
|
|
plb->maxWidth = (INT)wParam;
|
|
|
|
//
|
|
// When horizontal extent is set, Show/hide the scroll bars.
|
|
// NOTE: ListBox_ShowHideScrollBars() takes care if Redraw is OFF.
|
|
// Fix for Bug #2477 -- 01/14/91 -- SANKAR --
|
|
//
|
|
|
|
//
|
|
// Try to show or hide scroll bars
|
|
//
|
|
ListBox_ShowHideScrollBars(plb);
|
|
if (plb->fHorzBar && plb->fRightAlign && !(plb->fMultiColumn || plb->OwnerDraw))
|
|
{
|
|
//
|
|
// origin to right
|
|
//
|
|
ListBox_HScroll(plb, SB_BOTTOM, 0);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case LB_SETCOLUMNWIDTH:
|
|
|
|
//
|
|
// Set the width of a column in a multicolumn listbox
|
|
//
|
|
plb->cxColumn = (INT)wParam;
|
|
ListBox_CalcItemRowsAndColumns(plb);
|
|
|
|
if (IsLBoxVisible(plb))
|
|
{
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
|
|
ListBox_ShowHideScrollBars(plb);
|
|
|
|
break;
|
|
|
|
case LB_SETANCHORINDEX:
|
|
if ((INT)wParam >= plb->cMac)
|
|
{
|
|
TraceMsg(TF_ERROR, "Invalid index passed to LB_SETANCHORINDEX");
|
|
return LB_ERR;
|
|
}
|
|
|
|
plb->iMouseDown = (INT)wParam;
|
|
plb->iLastMouseMove = (INT)wParam;
|
|
|
|
ListBox_InsureVisible(plb, (int) wParam, (BOOL)(lParam != 0));
|
|
|
|
break;
|
|
|
|
case LB_GETANCHORINDEX:
|
|
return plb->iMouseDown;
|
|
|
|
case LB_SETCARETINDEX:
|
|
if ( (plb->iSel == -1) || ((plb->wMultiple != SINGLESEL) &&
|
|
(plb->cMac > (INT)wParam)))
|
|
{
|
|
//
|
|
// Set's the iSelBase to the wParam
|
|
// if lParam, then don't scroll if partially visible
|
|
// else scroll into view if not fully visible
|
|
//
|
|
ListBox_InsureVisible(plb, (INT)wParam, (BOOL)LOWORD(lParam));
|
|
ListBox_SetISelBase(plb, (INT)wParam);
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ((INT)wParam >= plb->cMac)
|
|
{
|
|
TraceMsg(TF_ERROR, "Invalid index passed to LB_SETCARETINDEX");
|
|
}
|
|
|
|
return LB_ERR;
|
|
}
|
|
|
|
break;
|
|
|
|
case LB_GETCARETINDEX:
|
|
return plb->iSelBase;
|
|
|
|
case LB_SETITEMHEIGHT:
|
|
case LB_GETITEMHEIGHT:
|
|
return ListBox_GetSetItemHeightHandler(plb, uMsg, (INT)wParam, LOWORD(lParam));
|
|
|
|
case LB_FINDSTRINGEXACT:
|
|
return Listbox_FindStringHandler(plb, (LPWSTR)lParam, (INT)wParam, EQ, TRUE);
|
|
|
|
case LB_ITEMFROMPOINT:
|
|
{
|
|
POINT pt;
|
|
BOOL bOutside;
|
|
DWORD dwItem;
|
|
|
|
POINTSTOPOINT(pt, lParam);
|
|
bOutside = ListBox_ISelFromPt(plb, pt, &dwItem);
|
|
ASSERT(bOutside == 1 || bOutside == 0);
|
|
|
|
return (LRESULT)MAKELONG(dwItem, bOutside);
|
|
}
|
|
|
|
case LBCB_CARETON:
|
|
|
|
//
|
|
// Internal message for combo box support
|
|
//
|
|
|
|
CaretCreate(plb);
|
|
|
|
//
|
|
// Set up the caret in the proper location for drop downs.
|
|
//
|
|
plb->iSelBase = plb->iSel;
|
|
ListBox_SetCaret(plb, TRUE);
|
|
|
|
if (IsWindowVisible(hwnd) || (GetFocus() == hwnd))
|
|
{
|
|
ListBox_Event(plb, EVENT_OBJECT_FOCUS, plb->iSelBase);
|
|
}
|
|
|
|
return plb->iSel;
|
|
|
|
case LBCB_CARETOFF:
|
|
|
|
//
|
|
// Internal message for combo box support
|
|
//
|
|
ListBox_SetCaret(plb, FALSE);
|
|
ListBox_CaretDestroy(plb);
|
|
|
|
break;
|
|
|
|
case WM_NCCREATE:
|
|
|
|
//
|
|
// Allocate the listbox instance stucture
|
|
//
|
|
plb = (PLBIV)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(LBIV));
|
|
if(plb)
|
|
{
|
|
ULONG ulStyle;
|
|
|
|
//
|
|
// Success... store the instance pointer.
|
|
//
|
|
TraceMsg(TF_STANDARD, "LISTBOX: Setting listbox instance pointer.");
|
|
ListBox_SetPtr(hwnd, plb);
|
|
|
|
plb->hwnd = hwnd;
|
|
plb->pww = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS);
|
|
|
|
ulStyle = GET_STYLE(plb);
|
|
if ( (ulStyle & LBS_MULTICOLUMN) &&
|
|
(ulStyle & WS_VSCROLL))
|
|
{
|
|
DWORD dwMask = WS_VSCROLL;
|
|
DWORD dwFlags = 0;
|
|
|
|
if (!TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT))
|
|
{
|
|
dwMask |= WS_HSCROLL;
|
|
dwFlags = WS_HSCROLL;
|
|
}
|
|
|
|
AlterWindowStyle(hwnd, dwMask, dwFlags);
|
|
}
|
|
|
|
goto CallDWP;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Failed... return FALSE.
|
|
//
|
|
// From a WM_NCCREATE msg, this will cause the
|
|
// CreateWindow call to fail.
|
|
//
|
|
TraceMsg(TF_STANDARD, "LISTBOX: Unable to allocate listbox instance structure.");
|
|
lReturn = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_GETOBJECT:
|
|
|
|
if(lParam == OBJID_QUERYCLASSNAMEIDX)
|
|
{
|
|
lReturn = MSAA_CLASSNAMEIDX_LISTBOX;
|
|
}
|
|
else
|
|
{
|
|
lReturn = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_THEMECHANGED:
|
|
|
|
if ( plb->hTheme )
|
|
{
|
|
CloseThemeData(plb->hTheme);
|
|
}
|
|
|
|
plb->hTheme = OpenThemeData(plb->hwnd, L"Listbox");
|
|
|
|
InvalidateRect(plb->hwnd, NULL, TRUE);
|
|
|
|
lReturn = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CallDWP:
|
|
lReturn = DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Function: GetWindowBorders
|
|
//
|
|
// Synopsis: Calculates # of borders around window
|
|
//
|
|
// Algorithm: Calculate # of window borders and # of client borders
|
|
//
|
|
int GetWindowBorders(LONG lStyle, DWORD dwExStyle, BOOL fWindow, BOOL fClient)
|
|
{
|
|
int cBorders = 0;
|
|
DWORD dwTemp;
|
|
|
|
if (fWindow)
|
|
{
|
|
//
|
|
// Is there a 3D border around the window?
|
|
//
|
|
if (dwExStyle & WS_EX_WINDOWEDGE)
|
|
{
|
|
cBorders += 2;
|
|
}
|
|
else if (dwExStyle & WS_EX_STATICEDGE)
|
|
{
|
|
++cBorders;
|
|
}
|
|
|
|
//
|
|
// Is there a single flat border around the window? This is true for
|
|
// WS_BORDER, WS_DLGFRAME, and WS_EX_DLGMODALFRAME windows.
|
|
//
|
|
if ( (lStyle & WS_CAPTION) || (dwExStyle & WS_EX_DLGMODALFRAME) )
|
|
{
|
|
++cBorders;
|
|
}
|
|
|
|
//
|
|
// Is there a sizing flat border around the window?
|
|
//
|
|
if (lStyle & WS_SIZEBOX)
|
|
{
|
|
if(SystemParametersInfo(SPI_GETBORDER, 0, &dwTemp, 0))
|
|
{
|
|
cBorders += dwTemp;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (fClient)
|
|
{
|
|
//
|
|
// Is there a 3D border around the client?
|
|
//
|
|
if (dwExStyle & WS_EX_CLIENTEDGE)
|
|
{
|
|
cBorders += 2;
|
|
}
|
|
}
|
|
|
|
return cBorders;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// GetLpszItem
|
|
//
|
|
// Returns a far pointer to the string belonging to item sItem
|
|
// ONLY for Listboxes maintaining their own strings (pLBIV->fHasStrings == TRUE)
|
|
//
|
|
LPWSTR GetLpszItem(PLBIV pLBIV, INT sItem)
|
|
{
|
|
LONG offsz;
|
|
lpLBItem plbi;
|
|
|
|
if (sItem < 0 || sItem >= pLBIV->cMac)
|
|
{
|
|
TraceMsg(TF_ERROR, "Invalid parameter \"sItem\" (%ld) to GetLpszItem", sItem);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// get pointer to item index array
|
|
// NOTE: NOT OWNERDRAW
|
|
//
|
|
plbi = (lpLBItem)(pLBIV->rgpch);
|
|
offsz = plbi[sItem].offsz;
|
|
|
|
return (LPWSTR)((PBYTE)(pLBIV->hStrings) + offsz);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Multi column Listbox functions
|
|
//
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_CalcItemRowsAndColumns
|
|
//
|
|
// Calculates the number of columns (including partially visible)
|
|
// in the listbox and calculates the number of items per column
|
|
//
|
|
void ListBox_CalcItemRowsAndColumns(PLBIV plb)
|
|
{
|
|
RECT rc;
|
|
|
|
GetClientRect(plb->hwnd, &rc);
|
|
|
|
//
|
|
// B#4155
|
|
// We need to check if plb->cyChar has been initialized. This is because
|
|
// we remove WS_BORDER from old listboxes and add on WS_EX_CLIENTEDGE.
|
|
// Since listboxes are always inflated by CXBORDER and CYBORDER, a
|
|
// listbox that was created empty always ends up 2 x 2. Since this isn't
|
|
// big enough to fit the entire client border, we don't mark it as
|
|
// present. Thus the client isn't empty in VER40, although it was in
|
|
// VER31 and before. It is possible to get to this spot without
|
|
// plb->cyChar having been initialized yet if the listbox is
|
|
// multicolumn && ownerdraw variable.
|
|
//
|
|
|
|
if (rc.bottom && rc.right && plb->cyChar)
|
|
{
|
|
//
|
|
// Only make these calculations if the width & height are positive
|
|
//
|
|
plb->itemsPerColumn = (INT)max(rc.bottom / plb->cyChar, 1);
|
|
plb->numberOfColumns = (INT)max(rc.right / plb->cxColumn, 1);
|
|
|
|
plb->cItemFullMax = plb->itemsPerColumn * plb->numberOfColumns;
|
|
|
|
//
|
|
// Adjust iTop so it's at the top of a column
|
|
//
|
|
ListBox_NewITop(plb, plb->iTop);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_HSrollMultiColumn
|
|
//
|
|
// Supports horizontal scrolling of multicolumn listboxes
|
|
//
|
|
void ListBox_HSrollMultiColumn(PLBIV plb, INT cmd, INT xAmt)
|
|
{
|
|
INT iTop = plb->iTop;
|
|
|
|
if (!plb->cMac)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (cmd)
|
|
{
|
|
case SB_LINEUP:
|
|
if (plb->fRightAlign)
|
|
{
|
|
goto ReallyLineDown;
|
|
}
|
|
|
|
ReallyLineUp:
|
|
iTop -= plb->itemsPerColumn;
|
|
|
|
break;
|
|
|
|
case SB_LINEDOWN:
|
|
if (plb->fRightAlign)
|
|
{
|
|
goto ReallyLineUp;
|
|
}
|
|
|
|
ReallyLineDown:
|
|
iTop += plb->itemsPerColumn;
|
|
|
|
break;
|
|
|
|
case SB_PAGEUP:
|
|
if (plb->fRightAlign)
|
|
{
|
|
goto ReallyPageDown;
|
|
}
|
|
|
|
ReallyPageUp:
|
|
iTop -= plb->itemsPerColumn * plb->numberOfColumns;
|
|
|
|
break;
|
|
|
|
case SB_PAGEDOWN:
|
|
if (plb->fRightAlign)
|
|
{
|
|
goto ReallyPageUp;
|
|
}
|
|
|
|
ReallyPageDown:
|
|
iTop += plb->itemsPerColumn * plb->numberOfColumns;
|
|
|
|
break;
|
|
|
|
case SB_THUMBTRACK:
|
|
case SB_THUMBPOSITION:
|
|
if (plb->fRightAlign)
|
|
{
|
|
int iCols = plb->cMac ? ((plb->cMac-1) / plb->itemsPerColumn) + 1 : 0;
|
|
|
|
xAmt = iCols - (xAmt + plb->numberOfColumns);
|
|
if (xAmt < 0)
|
|
{
|
|
xAmt=0;
|
|
}
|
|
}
|
|
|
|
iTop = xAmt * plb->itemsPerColumn;
|
|
|
|
break;
|
|
|
|
case SB_TOP:
|
|
if (plb->fRightAlign)
|
|
{
|
|
goto ReallyBottom;
|
|
}
|
|
|
|
ReallyTop:
|
|
iTop = 0;
|
|
|
|
break;
|
|
|
|
case SB_BOTTOM:
|
|
if (plb->fRightAlign)
|
|
{
|
|
goto ReallyTop;
|
|
}
|
|
ReallyBottom:
|
|
iTop = plb->cMac - 1 - ((plb->cMac - 1) % plb->itemsPerColumn);
|
|
|
|
break;
|
|
|
|
case SB_ENDSCROLL:
|
|
plb->fSmoothScroll = TRUE;
|
|
ListBox_ShowHideScrollBars(plb);
|
|
|
|
break;
|
|
}
|
|
|
|
ListBox_NewITop(plb, iTop);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox variable height owner draw functions
|
|
//
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_GetVarHeightItemHeight
|
|
//
|
|
// Returns the height of the given item number. Assumes variable
|
|
// height owner draw.
|
|
//
|
|
INT ListBox_GetVarHeightItemHeight(PLBIV plb, INT itemNumber)
|
|
{
|
|
BYTE itemHeight;
|
|
UINT offsetHeight;
|
|
|
|
if (plb->cMac)
|
|
{
|
|
if (plb->fHasStrings)
|
|
{
|
|
offsetHeight = plb->cMac * sizeof(LBItem);
|
|
}
|
|
else
|
|
{
|
|
offsetHeight = plb->cMac * sizeof(LBODItem);
|
|
}
|
|
|
|
if (plb->wMultiple)
|
|
{
|
|
offsetHeight += plb->cMac;
|
|
}
|
|
|
|
offsetHeight += itemNumber;
|
|
|
|
itemHeight = *(plb->rgpch+(UINT)offsetHeight);
|
|
|
|
return (INT)itemHeight;
|
|
|
|
}
|
|
|
|
//
|
|
// Default, we return the height of the system font. This is so we can draw
|
|
// the focus rect even though there are no items in the listbox.
|
|
//
|
|
return SYSFONT_CYCHAR;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_SetVarHeightItemHeight
|
|
//
|
|
// Sets the height of the given item number. Assumes variable height
|
|
// owner draw, a valid item number and valid height.
|
|
//
|
|
void ListBox_SetVarHeightItemHeight(PLBIV plb, INT itemNumber, INT itemHeight)
|
|
{
|
|
int offsetHeight;
|
|
|
|
if (plb->fHasStrings)
|
|
offsetHeight = plb->cMac * sizeof(LBItem);
|
|
else
|
|
offsetHeight = plb->cMac * sizeof(LBODItem);
|
|
|
|
if (plb->wMultiple)
|
|
offsetHeight += plb->cMac;
|
|
|
|
offsetHeight += itemNumber;
|
|
|
|
*(plb->rgpch + (UINT)offsetHeight) = (BYTE)itemHeight;
|
|
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_VisibleItemsVarOwnerDraw
|
|
//
|
|
// Returns the number of items which can fit in a variable height OWNERDRAW
|
|
// list box. If fDirection, then we return the number of items which
|
|
// fit starting at sTop and going forward (for page down), otherwise, we are
|
|
// going backwards (for page up). (Assumes var height ownerdraw) If fPartial,
|
|
// then include the partially visible item at the bottom of the listbox.
|
|
//
|
|
INT ListBox_VisibleItemsVarOwnerDraw(PLBIV plb, BOOL fPartial)
|
|
{
|
|
RECT rect;
|
|
INT sItem;
|
|
INT clientbottom;
|
|
|
|
GetClientRect(plb->hwnd, (LPRECT)&rect);
|
|
clientbottom = rect.bottom;
|
|
|
|
//
|
|
// Find the number of var height ownerdraw items which are visible starting
|
|
// from plb->iTop.
|
|
//
|
|
for (sItem = plb->iTop; sItem < plb->cMac; sItem++)
|
|
{
|
|
//
|
|
// Find out if the item is visible or not
|
|
//
|
|
if (!ListBox_GetItemRectHandler(plb, sItem, (LPRECT)&rect))
|
|
{
|
|
//
|
|
// This is the first item which is completely invisible, so return
|
|
// how many items are visible.
|
|
//
|
|
return (sItem - plb->iTop);
|
|
}
|
|
|
|
if (!fPartial && rect.bottom > clientbottom)
|
|
{
|
|
//
|
|
// If we only want fully visible items, then if this item is
|
|
// visible, we check if the bottom of the item is below the client
|
|
// rect, so we return how many are fully visible.
|
|
//
|
|
return (sItem - plb->iTop - 1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// All the items are visible
|
|
//
|
|
return (plb->cMac - plb->iTop);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_Page
|
|
//
|
|
// For variable height ownerdraw listboxes, calaculates the new iTop we must
|
|
// move to when paging (page up/down) through variable height listboxes.
|
|
//
|
|
INT ListBox_Page(PLBIV plb, INT startItem, BOOL fPageForwardDirection)
|
|
{
|
|
INT i;
|
|
INT height;
|
|
RECT rc;
|
|
|
|
if (plb->cMac == 1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
GetClientRect(plb->hwnd, &rc);
|
|
height = rc.bottom;
|
|
i = startItem;
|
|
|
|
if (fPageForwardDirection)
|
|
{
|
|
while ((height >= 0) && (i < plb->cMac))
|
|
{
|
|
height -= ListBox_GetVarHeightItemHeight(plb, i++);
|
|
}
|
|
|
|
return (height >= 0) ? (plb->cMac - 1) : max(i - 2, startItem + 1);
|
|
|
|
}
|
|
else
|
|
{
|
|
while ((height >= 0) && (i >= 0))
|
|
{
|
|
height -= ListBox_GetVarHeightItemHeight(plb, i--);
|
|
}
|
|
|
|
return (height >= 0) ? 0 : min(i + 2, startItem - 1);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_CalcVarITopScrollAmt
|
|
//
|
|
// Changing the top most item in the listbox from iTopOld to iTopNew we
|
|
// want to calculate the number of pixels to scroll so that we minimize the
|
|
// number of items we will redraw.
|
|
//
|
|
INT ListBox_CalcVarITopScrollAmt(PLBIV plb, INT iTopOld, INT iTopNew)
|
|
{
|
|
RECT rc;
|
|
RECT rcClient;
|
|
|
|
GetClientRect(plb->hwnd, (LPRECT)&rcClient);
|
|
|
|
//
|
|
// Just optimize redrawing when move +/- 1 item. We will redraw all items
|
|
// if moving more than 1 item ahead or back. This is good enough for now.
|
|
//
|
|
if (iTopOld + 1 == iTopNew)
|
|
{
|
|
//
|
|
// We are scrolling the current iTop up off the top off the listbox so
|
|
// return a negative number.
|
|
//
|
|
ListBox_GetItemRectHandler(plb, iTopOld, (LPRECT)&rc);
|
|
|
|
return (rcClient.top - rc.bottom);
|
|
}
|
|
|
|
if (iTopOld - 1 == iTopNew)
|
|
{
|
|
//
|
|
// We are scrolling the current iTop down and the previous item is
|
|
// becoming the new iTop so return a positive number.
|
|
//
|
|
ListBox_GetItemRectHandler(plb, iTopNew, (LPRECT)&rc);
|
|
|
|
return -rc.top;
|
|
}
|
|
|
|
return rcClient.bottom - rcClient.top;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// (supposedly) Rarely called Listbox functions
|
|
//
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
void ListBox_SetCItemFullMax(PLBIV plb)
|
|
{
|
|
if (plb->OwnerDraw != OWNERDRAWVAR)
|
|
{
|
|
plb->cItemFullMax = ListBox_CItemInWindow(plb, FALSE);
|
|
}
|
|
else if (plb->cMac < 2)
|
|
{
|
|
plb->cItemFullMax = 1;
|
|
}
|
|
else
|
|
{
|
|
int height;
|
|
RECT rect;
|
|
int i;
|
|
int j = 0;
|
|
|
|
GetClientRect(plb->hwnd, &rect);
|
|
height = rect.bottom;
|
|
|
|
plb->cItemFullMax = 0;
|
|
for (i = plb->cMac - 1; i >= 0; i--, j++)
|
|
{
|
|
height -= ListBox_GetVarHeightItemHeight(plb, i);
|
|
|
|
if (height < 0)
|
|
{
|
|
plb->cItemFullMax = j;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!plb->cItemFullMax)
|
|
{
|
|
plb->cItemFullMax = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
LONG ListBox_Create(PLBIV plb, HWND hwnd, LPCREATESTRUCT lpcs)
|
|
{
|
|
UINT style;
|
|
DWORD ExStyle;
|
|
MEASUREITEMSTRUCT measureItemStruct;
|
|
HDC hdc;
|
|
HWND hwndParent;
|
|
SIZE size;
|
|
|
|
//
|
|
// Once we make it here, nobody can change the ownerdraw style bits
|
|
// by calling SetWindowLong. The window style must match the flags in plb
|
|
//
|
|
plb->fInitialized = TRUE;
|
|
|
|
style = lpcs->style;
|
|
ExStyle = lpcs->dwExStyle;
|
|
hwndParent = lpcs->hwndParent;
|
|
|
|
plb->hwndParent = hwndParent;
|
|
plb->hTheme = OpenThemeData(plb->hwnd, L"Listbox");
|
|
|
|
//
|
|
// Break out the style bits
|
|
//
|
|
plb->fRedraw = ((style & LBS_NOREDRAW) == 0);
|
|
plb->fDeferUpdate = FALSE;
|
|
plb->fNotify = (UINT)((style & LBS_NOTIFY) != 0);
|
|
plb->fVertBar = ((style & WS_VSCROLL) != 0);
|
|
plb->fHorzBar = ((style & WS_HSCROLL) != 0);
|
|
|
|
if (!TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT))
|
|
{
|
|
//
|
|
// for 3.x apps, if either scroll bar was specified, the app got BOTH
|
|
//
|
|
if (plb->fVertBar || plb->fHorzBar)
|
|
{
|
|
plb->fVertBar = plb->fHorzBar = TRUE;
|
|
}
|
|
}
|
|
|
|
plb->fRtoLReading = (ExStyle & WS_EX_RTLREADING)!= 0;
|
|
plb->fRightAlign = (ExStyle & WS_EX_RIGHT) != 0;
|
|
plb->fDisableNoScroll = ((style & LBS_DISABLENOSCROLL) != 0);
|
|
|
|
plb->fSmoothScroll = TRUE;
|
|
|
|
//
|
|
// LBS_NOSEL gets priority over any other selection style. Next highest
|
|
// priority goes to LBS_EXTENDEDSEL. Then LBS_MULTIPLESEL.
|
|
//
|
|
if (TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT) && (style & LBS_NOSEL))
|
|
{
|
|
plb->wMultiple = SINGLESEL;
|
|
plb->fNoSel = TRUE;
|
|
}
|
|
else if (style & LBS_EXTENDEDSEL)
|
|
{
|
|
plb->wMultiple = EXTENDEDSEL;
|
|
}
|
|
else
|
|
{
|
|
plb->wMultiple = (UINT)((style & LBS_MULTIPLESEL) ? MULTIPLESEL : SINGLESEL);
|
|
}
|
|
|
|
plb->fNoIntegralHeight = ((style & LBS_NOINTEGRALHEIGHT) != 0);
|
|
plb->fWantKeyboardInput = ((style & LBS_WANTKEYBOARDINPUT) != 0);
|
|
plb->fUseTabStops = ((style & LBS_USETABSTOPS) != 0);
|
|
|
|
if (plb->fUseTabStops)
|
|
{
|
|
//
|
|
// Set tab stops every <default> dialog units.
|
|
//
|
|
ListBox_SetTabStopsHandler(plb, 0, NULL);
|
|
}
|
|
|
|
plb->fMultiColumn = ((style & LBS_MULTICOLUMN) != 0);
|
|
plb->fHasStrings = TRUE;
|
|
plb->iLastSelection = -1;
|
|
|
|
//
|
|
// Anchor point for multi selection
|
|
//
|
|
plb->iMouseDown = -1;
|
|
plb->iLastMouseMove = -1;
|
|
|
|
//
|
|
// Get ownerdraw style bits
|
|
//
|
|
if ((style & LBS_OWNERDRAWFIXED))
|
|
{
|
|
plb->OwnerDraw = OWNERDRAWFIXED;
|
|
}
|
|
else if ((style & LBS_OWNERDRAWVARIABLE) && !plb->fMultiColumn)
|
|
{
|
|
plb->OwnerDraw = OWNERDRAWVAR;
|
|
|
|
//
|
|
// Integral height makes no sense with var height owner draw
|
|
//
|
|
plb->fNoIntegralHeight = TRUE;
|
|
}
|
|
|
|
if (plb->OwnerDraw && !(style & LBS_HASSTRINGS))
|
|
{
|
|
//
|
|
// If owner draw, do they want the listbox to maintain strings?
|
|
//
|
|
plb->fHasStrings = FALSE;
|
|
}
|
|
|
|
//
|
|
// If user specifies sort and not hasstrings, then we will send
|
|
// WM_COMPAREITEM messages to the parent.
|
|
//
|
|
plb->fSort = ((style & LBS_SORT) != 0);
|
|
|
|
//
|
|
// "No data" lazy-eval listbox mandates certain other style settings
|
|
//
|
|
plb->fHasData = TRUE;
|
|
|
|
if (style & LBS_NODATA)
|
|
{
|
|
if (plb->OwnerDraw != OWNERDRAWFIXED || plb->fSort || plb->fHasStrings)
|
|
{
|
|
TraceMsg(TF_STANDARD, "NODATA listbox must be OWNERDRAWFIXED, w/o SORT or HASSTRINGS");
|
|
}
|
|
else
|
|
{
|
|
plb->fHasData = FALSE;
|
|
}
|
|
}
|
|
|
|
plb->dwLocaleId = GetThreadLocale();
|
|
|
|
//
|
|
// Check if this is part of a combo box
|
|
//
|
|
if ((style & LBS_COMBOBOX) != 0)
|
|
{
|
|
//
|
|
// Get the pcbox structure contained in the parent window's extra data
|
|
// pointer. Check cbwndExtra to ensure compatibility with SQL windows.
|
|
//
|
|
plb->pcbox = ComboBox_GetPtr(hwndParent);
|
|
}
|
|
|
|
plb->iSel = -1;
|
|
plb->hdc = NULL;
|
|
|
|
//
|
|
// Set the keyboard state so that when the user keyboard clicks he selects
|
|
// an item.
|
|
//
|
|
plb->fNewItemState = TRUE;
|
|
|
|
ListBox_InitHStrings(plb);
|
|
|
|
if (plb->fHasStrings && plb->hStrings == NULL)
|
|
{
|
|
return -1L;
|
|
}
|
|
|
|
hdc = GetDC(hwnd);
|
|
GetCharDimensions(hdc, &size);
|
|
plb->cxChar = size.cx;
|
|
plb->cyChar = size.cy;
|
|
ReleaseDC(hwnd, hdc);
|
|
|
|
if ((plb->cxChar == 0) || (plb->cyChar == 0))
|
|
{
|
|
TraceMsg(TF_STANDARD, "LISTBOX: GetCharDimensions failed.");
|
|
plb->cxChar = SYSFONT_CXCHAR;
|
|
plb->cyChar = SYSFONT_CYCHAR;
|
|
}
|
|
|
|
if (plb->OwnerDraw == OWNERDRAWFIXED)
|
|
{
|
|
//
|
|
// Query for item height only if we are fixed height owner draw. Note
|
|
// that we don't care about an item's width for listboxes.
|
|
//
|
|
measureItemStruct.CtlType = ODT_LISTBOX;
|
|
measureItemStruct.CtlID = GetDlgCtrlID(hwnd);
|
|
|
|
//
|
|
// System font height is default height
|
|
//
|
|
measureItemStruct.itemHeight = plb->cyChar;
|
|
measureItemStruct.itemWidth = 0;
|
|
measureItemStruct.itemData = 0;
|
|
|
|
//
|
|
// IanJa: #ifndef WIN16 (32-bit Windows), plb->id gets extended
|
|
// to LONG wParam automatically by the compiler
|
|
//
|
|
SendMessage(plb->hwndParent, WM_MEASUREITEM,
|
|
measureItemStruct.CtlID,
|
|
(LPARAM)&measureItemStruct);
|
|
|
|
//
|
|
// Use default height if given 0. This prevents any possible future
|
|
// div-by-zero errors.
|
|
//
|
|
if (measureItemStruct.itemHeight)
|
|
{
|
|
plb->cyChar = measureItemStruct.itemHeight;
|
|
}
|
|
|
|
if (plb->fMultiColumn)
|
|
{
|
|
//
|
|
// Get default column width from measure items struct if we are a
|
|
// multicolumn listbox.
|
|
//
|
|
plb->cxColumn = measureItemStruct.itemWidth;
|
|
}
|
|
}
|
|
else if (plb->OwnerDraw == OWNERDRAWVAR)
|
|
{
|
|
plb->cyChar = 0;
|
|
}
|
|
|
|
|
|
if (plb->fMultiColumn)
|
|
{
|
|
//
|
|
// Set these default values till we get the WM_SIZE message and we
|
|
// calculate them properly. This is because some people create a
|
|
// 0 width/height listbox and size it later. We don't want to have
|
|
// problems with invalid values in these fields
|
|
//
|
|
if (plb->cxColumn <= 0)
|
|
{
|
|
plb->cxColumn = 15 * plb->cxChar;
|
|
}
|
|
|
|
plb->numberOfColumns = plb->itemsPerColumn = 1;
|
|
}
|
|
|
|
ListBox_SetCItemFullMax(plb);
|
|
|
|
//
|
|
// Don't do this for 4.0 apps. It'll make everyone's lives easier and
|
|
// fix the anomaly that a combo & list created the same width end up
|
|
// different when all is done.
|
|
// B#1520
|
|
//
|
|
if (!TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT))
|
|
{
|
|
plb->fIgnoreSizeMsg = TRUE;
|
|
MoveWindow(hwnd,
|
|
lpcs->x - SYSMET(CXBORDER),
|
|
lpcs->y - SYSMET(CYBORDER),
|
|
lpcs->cx + SYSMET(CXEDGE),
|
|
lpcs->cy + SYSMET(CYEDGE),
|
|
FALSE);
|
|
plb->fIgnoreSizeMsg = FALSE;
|
|
}
|
|
|
|
if (!plb->fNoIntegralHeight)
|
|
{
|
|
//
|
|
// Send a message to ourselves to resize the listbox to an integral
|
|
// height. We need to do it this way because at create time we are all
|
|
// mucked up with window rects etc...
|
|
// IanJa: #ifndef WIN16 (32-bit Windows), wParam 0 gets extended
|
|
// to wParam 0L automatically by the compiler.
|
|
//
|
|
PostMessage(hwnd, WM_SIZE, 0, 0L);
|
|
}
|
|
|
|
return 1L;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_DoDeleteItems
|
|
//
|
|
// Send DELETEITEM message for all the items in the ownerdraw listbox.
|
|
//
|
|
void ListBox_DoDeleteItems(PLBIV plb)
|
|
{
|
|
INT sItem;
|
|
|
|
//
|
|
// Send WM_DELETEITEM message for ownerdraw listboxes which are
|
|
// being deleted. (NODATA listboxes don't send such, though.)
|
|
//
|
|
if (plb->OwnerDraw && plb->cMac && plb->fHasData)
|
|
{
|
|
for (sItem = plb->cMac - 1; sItem >= 0; sItem--)
|
|
{
|
|
ListBox_DeleteItem(plb, sItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
VOID ListBox_Destroy(PLBIV plv, HWND hwnd)
|
|
{
|
|
|
|
if (plv != NULL)
|
|
{
|
|
//
|
|
// If ownerdraw, send deleteitem messages to parent
|
|
//
|
|
ListBox_DoDeleteItems(plv);
|
|
|
|
if (plv->rgpch != NULL)
|
|
{
|
|
ControlFree(GetProcessHeap(), plv->rgpch);
|
|
plv->rgpch = NULL;
|
|
}
|
|
|
|
if (plv->hStrings != NULL)
|
|
{
|
|
ControlFree(GetProcessHeap(), plv->hStrings);
|
|
plv->hStrings = NULL;
|
|
}
|
|
|
|
if (plv->iTabPixelPositions != NULL)
|
|
{
|
|
ControlFree(GetProcessHeap(), (HANDLE)plv->iTabPixelPositions);
|
|
plv->iTabPixelPositions = NULL;
|
|
}
|
|
|
|
if (plv->pszTypeSearch)
|
|
{
|
|
ControlFree(GetProcessHeap(), plv->pszTypeSearch);
|
|
}
|
|
|
|
|
|
if (plv->hTheme != NULL)
|
|
{
|
|
CloseThemeData(plv->hTheme);
|
|
}
|
|
|
|
//
|
|
// If we're part of a combo box, let it know we're gone
|
|
//
|
|
if (plv->hwndParent && plv->pcbox)
|
|
{
|
|
ComboBox_WndProc(plv->hwndParent, WM_PARENTNOTIFY,
|
|
MAKEWPARAM(WM_DESTROY, GetWindowID(hwnd)), (LPARAM)hwnd);
|
|
}
|
|
|
|
UserLocalFree(plv);
|
|
}
|
|
|
|
TraceMsg(TF_STANDARD, "LISTBOX: Clearing listbox instance pointer.");
|
|
ListBox_SetPtr(hwnd, NULL);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
void ListBox_SetFont(PLBIV plb, HANDLE hFont, BOOL fRedraw)
|
|
{
|
|
HDC hdc;
|
|
HANDLE hOldFont = NULL;
|
|
SIZE size;
|
|
|
|
plb->hFont = hFont;
|
|
|
|
hdc = GetDC(plb->hwnd);
|
|
|
|
if (hFont)
|
|
{
|
|
hOldFont = SelectObject(hdc, hFont);
|
|
|
|
if (!hOldFont)
|
|
{
|
|
plb->hFont = NULL;
|
|
}
|
|
}
|
|
|
|
GetCharDimensions(hdc, &size);
|
|
if ((size.cx == 0) || (size.cy == 0))
|
|
{
|
|
TraceMsg(TF_STANDARD, "LISTBOX: GetCharDimensions failed.");
|
|
size.cx = SYSFONT_CXCHAR;
|
|
size.cy = SYSFONT_CYCHAR;
|
|
}
|
|
plb->cxChar = size.cx;
|
|
|
|
if (!plb->OwnerDraw && (plb->cyChar != size.cy))
|
|
{
|
|
//
|
|
// We don't want to mess up the cyChar height for owner draw listboxes
|
|
// so don't do this.
|
|
//
|
|
plb->cyChar = size.cy;
|
|
|
|
//
|
|
// Only resize the listbox for 4.0 dudes, or combo dropdowns.
|
|
// Macromedia Director 4.0 GP-faults otherwise.
|
|
//
|
|
if (!plb->fNoIntegralHeight &&
|
|
(plb->pcbox || TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT)))
|
|
{
|
|
RECT rcClient;
|
|
|
|
GetClientRect(plb->hwnd, &rcClient);
|
|
ListBox_Size(plb, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, FALSE);
|
|
}
|
|
}
|
|
|
|
if (hOldFont)
|
|
{
|
|
SelectObject(hdc, hOldFont);
|
|
}
|
|
|
|
ReleaseDC(plb->hwnd, hdc);
|
|
|
|
if (plb->fMultiColumn)
|
|
{
|
|
ListBox_CalcItemRowsAndColumns(plb);
|
|
}
|
|
|
|
ListBox_SetCItemFullMax(plb);
|
|
|
|
if (fRedraw)
|
|
{
|
|
ListBox_CheckRedraw(plb, FALSE, 0);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
void ListBox_Size(PLBIV plb, INT cx, INT cy, BOOL fSizeMinVisible)
|
|
{
|
|
RECT rc, rcWindow;
|
|
int iTopOld;
|
|
int cBorder;
|
|
BOOL fSizedSave;
|
|
|
|
if (!plb->fNoIntegralHeight)
|
|
{
|
|
int cBdrs = GetWindowBorders(GET_STYLE(plb), GET_EXSTYLE(plb), TRUE, TRUE);
|
|
|
|
GetWindowRect(plb->hwnd, &rcWindow);
|
|
cBorder = SYSMET(CYBORDER);
|
|
CopyRect(&rc, &rcWindow);
|
|
InflateRect(&rc, 0, -cBdrs * cBorder);
|
|
|
|
//
|
|
// Size the listbox to fit an integral # of items in its client
|
|
//
|
|
if ((plb->cyChar && ((rc.bottom - rc.top) % plb->cyChar)) || fSizeMinVisible)
|
|
{
|
|
int iItems = (rc.bottom - rc.top);
|
|
|
|
//
|
|
// B#2285 - If its a 3.1 app its SetWindowPos needs
|
|
// to be window based dimensions not Client !
|
|
// this crunches Money into using a scroll bar
|
|
//
|
|
if (!TESTFLAG(GET_STATE2(plb), WS_S2_WIN40COMPAT))
|
|
{
|
|
//
|
|
// so add it back in
|
|
//
|
|
iItems += (cBdrs * SYSMET(CYEDGE));
|
|
}
|
|
|
|
iItems /= plb->cyChar;
|
|
|
|
//
|
|
// If we're in a dropdown list, size the listbox to accomodate
|
|
// a minimum number of items before needing to show scrolls.
|
|
//
|
|
if (plb->pcbox &&
|
|
(plb->pcbox->CBoxStyle & SDROPPABLE) &&
|
|
(((iItems < plb->pcbox->iMinVisible) &&
|
|
(iItems < plb->cMac)) || fSizeMinVisible))
|
|
{
|
|
iItems = min(plb->pcbox->iMinVisible, plb->cMac);
|
|
}
|
|
|
|
SetWindowPos(plb->hwnd, HWND_TOP, 0, 0, rc.right - rc.left,
|
|
iItems * plb->cyChar + (SYSMET(CYEDGE) * cBdrs),
|
|
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
//
|
|
// Changing the size causes us to recurse. Upon return
|
|
// the state is where it should be and nothing further
|
|
// needs to be done.
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (plb->fMultiColumn)
|
|
{
|
|
//
|
|
// Compute the number of DISPLAYABLE rows and columns in the listbox
|
|
//
|
|
ListBox_CalcItemRowsAndColumns(plb);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Adjust the current horizontal position to eliminate as much
|
|
// empty space as possible from the right side of the items.
|
|
//
|
|
GetClientRect(plb->hwnd, &rc);
|
|
|
|
if ((plb->maxWidth - plb->xOrigin) < (rc.right - rc.left))
|
|
{
|
|
plb->xOrigin = max(0, plb->maxWidth - (rc.right - rc.left));
|
|
}
|
|
}
|
|
|
|
ListBox_SetCItemFullMax(plb);
|
|
|
|
//
|
|
// Adjust the top item in the listbox to eliminate as much empty space
|
|
// after the last item as possible
|
|
// (fix for bugs #8490 & #3836)
|
|
//
|
|
iTopOld = plb->iTop;
|
|
fSizedSave = plb->fSized;
|
|
plb->fSized = FALSE;
|
|
ListBox_NewITop(plb, plb->iTop);
|
|
|
|
//
|
|
// If changing the top item index caused a resize, there is no
|
|
// more work to be done here.
|
|
//
|
|
if (plb->fSized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
plb->fSized = fSizedSave;
|
|
|
|
if (IsLBoxVisible(plb))
|
|
{
|
|
//
|
|
// This code no longer blows because it's fixed right!!! We could
|
|
// optimize the fMultiColumn case with some more code to figure out
|
|
// if we really need to invalidate the whole thing but note that some
|
|
// 3.0 apps depend on this extra invalidation (AMIPRO 2.0, bug 14620)
|
|
//
|
|
// For 3.1 apps, we blow off the invalidaterect in the case where
|
|
// cx and cy are 0 because this happens during the processing of
|
|
// the posted WM_SIZE message when we are created which would otherwise
|
|
// cause us to flash.
|
|
//
|
|
if ((plb->fMultiColumn && !(cx == 0 && cy == 0)) || plb->iTop != iTopOld)
|
|
{
|
|
InvalidateRect(plb->hwnd, NULL, TRUE);
|
|
}
|
|
else if (plb->iSelBase >= 0)
|
|
{
|
|
//
|
|
// Invalidate the item with the caret so that if the listbox
|
|
// grows horizontally, we redraw it properly.
|
|
//
|
|
ListBox_GetItemRectHandler(plb, plb->iSelBase, &rc);
|
|
InvalidateRect(plb->hwnd, &rc, FALSE);
|
|
}
|
|
}
|
|
else if (!plb->fRedraw)
|
|
{
|
|
plb->fDeferUpdate = TRUE;
|
|
}
|
|
|
|
//
|
|
// Send "fake" scroll bar messages to update the scroll positions since we
|
|
// changed size.
|
|
//
|
|
if (TESTFLAG(GET_STYLE(plb), WS_VSCROLL))
|
|
{
|
|
ListBox_VScroll(plb, SB_ENDSCROLL, 0);
|
|
}
|
|
|
|
//
|
|
// We count on this to call ListBox_ShowHideScrollBars except when plb->cMac == 0!
|
|
//
|
|
ListBox_HScroll(plb, SB_ENDSCROLL, 0);
|
|
|
|
//
|
|
// Show/hide scroll bars depending on how much stuff is visible...
|
|
//
|
|
// Note: Now we only call this guy when cMac == 0, because it is
|
|
// called inside the ListBox_HScroll with SB_ENDSCROLL otherwise.
|
|
//
|
|
if (plb->cMac == 0)
|
|
{
|
|
ListBox_ShowHideScrollBars(plb);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_SetTabStopsHandler
|
|
//
|
|
// Sets the tab stops for this listbox. Returns TRUE if successful else FALSE.
|
|
//
|
|
BOOL ListBox_SetTabStopsHandler(PLBIV plb, INT count, LPINT lptabstops)
|
|
{
|
|
PINT ptabs;
|
|
|
|
if (!plb->fUseTabStops)
|
|
{
|
|
TraceMsg(TF_STANDARD, "Calling SetTabStops without the LBS_TABSTOPS style set");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (count)
|
|
{
|
|
//
|
|
// Allocate memory for the tab stops. The first byte in the
|
|
// plb->iTabPixelPositions array will contain a count of the number
|
|
// of tab stop positions we have.
|
|
//
|
|
ptabs = (LPINT)ControlAlloc(GetProcessHeap(), (count + 1) * sizeof(int));
|
|
|
|
if (ptabs == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (plb->iTabPixelPositions != NULL)
|
|
{
|
|
ControlFree(GetProcessHeap(), plb->iTabPixelPositions);
|
|
}
|
|
|
|
plb->iTabPixelPositions = ptabs;
|
|
|
|
//
|
|
// Set the count of tab stops
|
|
//
|
|
*ptabs++ = count;
|
|
|
|
for (; count > 0; count--)
|
|
{
|
|
//
|
|
// Convert the dialog unit tabstops into pixel position tab stops.
|
|
//
|
|
*ptabs++ = MultDiv(*lptabstops, plb->cxChar, 4);
|
|
lptabstops++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set default 8 system font ave char width tabs. So free the memory
|
|
// associated with the tab stop list.
|
|
//
|
|
if (plb->iTabPixelPositions != NULL)
|
|
{
|
|
ControlFree(GetProcessHeap(), (HANDLE)plb->iTabPixelPositions);
|
|
plb->iTabPixelPositions = NULL;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
void ListBox_InitHStrings(PLBIV plb)
|
|
{
|
|
if (plb->fHasStrings)
|
|
{
|
|
plb->ichAlloc = 0;
|
|
plb->cchStrings = 0;
|
|
plb->hStrings = ControlAlloc(GetProcessHeap(), 0);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_DropObjectHandler
|
|
//
|
|
// Handles a WM_DROPITEM message on this listbox
|
|
//
|
|
void ListBox_DropObjectHandler(PLBIV plb, PDROPSTRUCT pds)
|
|
{
|
|
LONG mouseSel;
|
|
|
|
if (ListBox_ISelFromPt(plb, pds->ptDrop, &mouseSel))
|
|
{
|
|
//
|
|
// User dropped in empty space at bottom of listbox
|
|
//
|
|
pds->dwControlData = (DWORD)-1L;
|
|
}
|
|
else
|
|
{
|
|
pds->dwControlData = mouseSel;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_GetSetItemHeightHandler()
|
|
//
|
|
// Sets/Gets the height associated with each item. For non ownerdraw
|
|
// and fixed height ownerdraw, the item number is ignored.
|
|
//
|
|
int ListBox_GetSetItemHeightHandler(PLBIV plb, UINT message, int item, UINT height)
|
|
{
|
|
if (message == LB_GETITEMHEIGHT)
|
|
{
|
|
//
|
|
// All items are same height for non ownerdraw and for fixed height
|
|
// ownerdraw.
|
|
//
|
|
if (plb->OwnerDraw != OWNERDRAWVAR)
|
|
{
|
|
return plb->cyChar;
|
|
}
|
|
|
|
if (plb->cMac && item >= plb->cMac)
|
|
{
|
|
TraceMsg(TF_STANDARD,
|
|
"Invalid parameter \"item\" (%ld) to ListBox_GetSetItemHeightHandler", item);
|
|
|
|
return LB_ERR;
|
|
}
|
|
|
|
return (int)ListBox_GetVarHeightItemHeight(plb, (INT)item);
|
|
}
|
|
|
|
if (!height || height > 255)
|
|
{
|
|
TraceMsg(TF_STANDARD,
|
|
"Invalid parameter \"height\" (%ld) to ListBox_GetSetItemHeightHandler", height);
|
|
|
|
return LB_ERR;
|
|
}
|
|
|
|
if (plb->OwnerDraw != OWNERDRAWVAR)
|
|
{
|
|
plb->cyChar = height;
|
|
}
|
|
else
|
|
{
|
|
if (item < 0 || item >= plb->cMac)
|
|
{
|
|
TraceMsg(TF_STANDARD,
|
|
"Invalid parameter \"item\" (%ld) to ListBox_GetSetItemHeightHandler", item);
|
|
|
|
return LB_ERR;
|
|
}
|
|
|
|
ListBox_SetVarHeightItemHeight(plb, (INT)item, (INT)height);
|
|
}
|
|
|
|
if (plb->fMultiColumn)
|
|
{
|
|
ListBox_CalcItemRowsAndColumns(plb);
|
|
}
|
|
|
|
ListBox_SetCItemFullMax(plb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// ListBox_Event()
|
|
//
|
|
// This is for item focus & selection events in listboxes.
|
|
//
|
|
void ListBox_Event(PLBIV plb, UINT uEvent, int iItem)
|
|
{
|
|
|
|
switch (uEvent)
|
|
{
|
|
case EVENT_OBJECT_SELECTIONREMOVE:
|
|
if (plb->wMultiple != SINGLESEL)
|
|
{
|
|
break;
|
|
}
|
|
iItem = -1;
|
|
|
|
//
|
|
// FALL THRU
|
|
//
|
|
|
|
case EVENT_OBJECT_SELECTIONADD:
|
|
if (plb->wMultiple == MULTIPLESEL)
|
|
{
|
|
uEvent = EVENT_OBJECT_SELECTION;
|
|
}
|
|
break;
|
|
|
|
case EVENT_OBJECT_SELECTIONWITHIN:
|
|
iItem = -1;
|
|
break;
|
|
}
|
|
|
|
NotifyWinEvent(uEvent, plb->hwnd, OBJID_CLIENT, iItem+1);
|
|
}
|