|
|
#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;
return (RegisterClass(&wc) || (GetLastError() == ERROR_CLASS_ALREADY_EXISTS)); }
//---------------------------------------------------------------------------//
//
// 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); }
|