📄 listbox.c
字号:
*
* Update the page size. Should be called when the size of
* the client area or the item height changes.
*/
static void LISTBOX_UpdatePage( LB_DESCR *descr )
{
INT page_size;
if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
page_size = 1;
if (page_size == descr->page_size) return;
descr->page_size = page_size;
if (descr->style & LBS_MULTICOLUMN)
InvalidateRect( descr->self, NULL, TRUE );
LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
}
/***********************************************************************
* LISTBOX_UpdateSize
*
* Update the size of the listbox. Should be called when the size of
* the client area changes.
*/
static void LISTBOX_UpdateSize( LB_DESCR *descr )
{
RECT rect;
GetClientRect( descr->self, &rect );
descr->width = rect.right - rect.left;
descr->height = rect.bottom - rect.top;
if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
{
INT remaining;
RECT rect;
GetWindowRect( descr->self, &rect );
if(descr->item_height != 0)
remaining = descr->height % descr->item_height;
else
remaining = 0;
if ((descr->height > descr->item_height) && remaining)
{
#ifndef __REACTOS__
if (is_old_app(hwnd))
{ /* give a margin for error to 16 bits programs - if we need
less than the height of the nonclient area, round to the
*next* number of items */
int ncheight = rect.bottom - rect.top - descr->height;
if ((descr->item_height - remaining) <= ncheight)
remaining = remaining - descr->item_height;
}
#endif
TRACE("[%p]: changing height %d -> %d\n",
descr->self, descr->height, descr->height - remaining );
SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
rect.bottom - rect.top - remaining,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
return;
}
}
TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
LISTBOX_UpdatePage( descr );
LISTBOX_UpdateScroll( descr );
/* Invalidate the focused item so it will be repainted correctly */
if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
{
InvalidateRect( descr->self, &rect, FALSE );
}
}
/***********************************************************************
* LISTBOX_GetItemRect
*
* Get the rectangle enclosing an item, in listbox client coordinates.
* Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
*/
static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
{
/* Index <= 0 is legal even on empty listboxes */
if (index && (index >= descr->nb_items))
{
memset(rect, 0, sizeof(*rect));
SetLastError(ERROR_INVALID_INDEX);
return LB_ERR;
}
SetRect( rect, 0, 0, descr->width, descr->height );
if (descr->style & LBS_MULTICOLUMN)
{
INT col = (index / descr->page_size) -
(descr->top_item / descr->page_size);
rect->left += col * descr->column_width;
rect->right = rect->left + descr->column_width;
rect->top += (index % descr->page_size) * descr->item_height;
rect->bottom = rect->top + descr->item_height;
}
else if (descr->style & LBS_OWNERDRAWVARIABLE)
{
INT i;
rect->right += descr->horz_pos;
if ((index >= 0) && (index < descr->nb_items))
{
if (index < descr->top_item)
{
for (i = descr->top_item-1; i >= index; i--)
rect->top -= descr->items[i].height;
}
else
{
for (i = descr->top_item; i < index; i++)
rect->top += descr->items[i].height;
}
rect->bottom = rect->top + descr->items[index].height;
}
}
else
{
rect->top += (index - descr->top_item) * descr->item_height;
rect->bottom = rect->top + descr->item_height;
rect->right += descr->horz_pos;
}
return ((rect->left < descr->width) && (rect->right > 0) &&
(rect->top < descr->height) && (rect->bottom > 0));
}
/***********************************************************************
* LISTBOX_GetItemFromPoint
*
* Return the item nearest from point (x,y) (in client coordinates).
*/
static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
{
INT index = descr->top_item;
if (!descr->nb_items) return -1; /* No items */
if (descr->style & LBS_OWNERDRAWVARIABLE)
{
INT pos = 0;
if (y >= 0)
{
while (index < descr->nb_items)
{
if ((pos += descr->items[index].height) > y) break;
index++;
}
}
else
{
while (index > 0)
{
index--;
if ((pos -= descr->items[index].height) <= y) break;
}
}
}
else if (descr->style & LBS_MULTICOLUMN)
{
if (y >= descr->item_height * descr->page_size) return -1;
if (y >= 0) index += y / descr->item_height;
if (x >= 0) index += (x / descr->column_width) * descr->page_size;
else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
}
else
{
index += (y / descr->item_height);
}
if (index < 0) return 0;
if (index >= descr->nb_items) return -1;
return index;
}
/***********************************************************************
* LISTBOX_PaintItem
*
* Paint an item.
*/
static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc,
const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
{
LB_ITEMDATA *item = NULL;
if (index < descr->nb_items) item = &descr->items[index];
if (IS_OWNERDRAW(descr))
{
DRAWITEMSTRUCT dis;
RECT r;
HRGN hrgn;
if (!item)
{
if (action == ODA_FOCUS)
DrawFocusRect( hdc, rect );
else
FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
return;
}
/* some programs mess with the clipping region when
drawing the item, *and* restore the previous region
after they are done, so a region has better to exist
else everything ends clipped */
GetClientRect(descr->self, &r);
hrgn = CreateRectRgnIndirect(&r);
SelectClipRgn( hdc, hrgn);
DeleteObject( hrgn );
dis.CtlType = ODT_LISTBOX;
dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID );
dis.hwndItem = descr->self;
dis.itemAction = action;
dis.hDC = hdc;
dis.itemID = index;
dis.itemState = 0;
if (item && item->selected) dis.itemState |= ODS_SELECTED;
if (!ignoreFocus && (descr->focus_item == index) &&
(descr->caret_on) &&
(descr->in_focus)) dis.itemState |= ODS_FOCUS;
if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
dis.itemData = item ? item->data : 0;
dis.rcItem = *rect;
TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
descr->self, index, item ? debugstr_w(item->str) : "", action,
dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
}
else
{
COLORREF oldText = 0, oldBk = 0;
if (action == ODA_FOCUS)
{
DrawFocusRect( hdc, rect );
return;
}
if (item && item->selected)
{
oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
descr->self, index, item ? debugstr_w(item->str) : "", action,
rect->left, rect->top, rect->right, rect->bottom );
if (!item)
ExtTextOutW( hdc, rect->left + 1, rect->top,
ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
else if (!(descr->style & LBS_USETABSTOPS))
ExtTextOutW( hdc, rect->left + 1, rect->top,
ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
strlenW(item->str), NULL );
else
{
/* Output empty string to paint background in the full width. */
ExtTextOutW( hdc, rect->left + 1, rect->top,
ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
TabbedTextOutW( hdc, rect->left + 1 , rect->top,
item->str, strlenW(item->str),
descr->nb_tabs, descr->tabs, 0);
}
if (item && item->selected)
{
SetBkColor( hdc, oldBk );
SetTextColor( hdc, oldText );
}
if (!ignoreFocus && (descr->focus_item == index) &&
(descr->caret_on) &&
(descr->in_focus)) DrawFocusRect( hdc, rect );
}
}
/***********************************************************************
* LISTBOX_SetRedraw
*
* Change the redraw flag.
*/
static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
{
if (on)
{
if (!(descr->style & LBS_NOREDRAW)) return;
descr->style &= ~LBS_NOREDRAW;
if (descr->style & LBS_DISPLAYCHANGED)
{ /* page was changed while setredraw false, refresh automatically */
InvalidateRect(descr->self, NULL, TRUE);
if ((descr->top_item + descr->page_size) > descr->nb_items)
{ /* reset top of page if less than number of items/page */
descr->top_item = descr->nb_items - descr->page_size;
if (descr->top_item < 0) descr->top_item = 0;
}
descr->style &= ~LBS_DISPLAYCHANGED;
}
LISTBOX_UpdateScroll( descr );
}
else descr->style |= LBS_NOREDRAW;
}
/***********************************************************************
* LISTBOX_RepaintItem
*
* Repaint a single item synchronously.
*/
static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index,
UINT action )
{
HDC hdc;
RECT rect;
HFONT oldFont = 0;
HBRUSH hbrush, oldBrush = 0;
/* Do not repaint the item if the item is not visible */
if (!IsWindowVisible(descr->self)) return;
if (descr->style & LBS_NOREDRAW)
{
descr->style |= LBS_DISPLAYCHANGED;
return;
}
if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
if (descr->font) oldFont = SelectObject( hdc, descr->font );
hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
(WPARAM)hdc, (LPARAM)descr->self );
if (hbrush) oldBrush = SelectObject( hdc, hbrush );
if (!IsWindowEnabled(descr->self))
SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
LISTBOX_PaintItem( descr, hdc, &rect, index, action, FALSE );
if (oldFont) SelectObject( hdc, oldFont );
if (oldBrush) SelectObject( hdc, oldBrush );
ReleaseDC( descr->self, hdc );
}
/***********************************************************************
* LISTBOX_InitStorage
*/
static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
{
LB_ITEMDATA *item;
nb_items += LB_ARRAY_GRANULARITY - 1;
nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
if (descr->items) {
nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
nb_items * sizeof(LB_ITEMDATA));
}
else {
item = HeapAlloc( GetProcessHeap(), 0,
nb_items * sizeof(LB_ITEMDATA));
}
if (!item)
{
SEND_NOTIFICATION( descr, LBN_ERRSPACE );
return LB_ERRSPACE;
}
descr->items = item;
return LB_OKAY;
}
/***********************************************************************
* LISTBOX_SetTabStops
*/
static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count,
LPINT tabs, BOOL short_ints )
{
INT i;
if (!(descr->style & LBS_USETABSTOPS))
{
SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
return FALSE;
}
HeapFree( GetProcessHeap(), 0, descr->tabs );
if (!(descr->nb_tabs = count))
{
descr->tabs = NULL;
return TRUE;
}
if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
descr->nb_tabs * sizeof(INT) )))
return FALSE;
#ifndef __REACTOS__
if (short_ints)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -