📄 listbox.c
字号:
max_items += LB_ARRAY_GRANULARITY;
if (descr->items)
item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
max_items * sizeof(LB_ITEMDATA) );
else
item = HeapAlloc( GetProcessHeap(), 0,
max_items * sizeof(LB_ITEMDATA) );
if (!item)
{
SEND_NOTIFICATION( descr, LBN_ERRSPACE );
return LB_ERRSPACE;
}
descr->items = item;
}
/* Insert the item structure */
item = &descr->items[index];
if (index < descr->nb_items)
RtlMoveMemory( item + 1, item,
(descr->nb_items - index) * sizeof(LB_ITEMDATA) );
item->str = str;
item->data = data;
item->height = 0;
item->selected = FALSE;
descr->nb_items++;
/* Get item height */
if (descr->style & LBS_OWNERDRAWVARIABLE)
{
MEASUREITEMSTRUCT mis;
UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
mis.CtlType = ODT_LISTBOX;
mis.CtlID = id;
mis.itemID = index;
mis.itemData = descr->items[index].data;
mis.itemHeight = descr->item_height;
SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
item->height = mis.itemHeight ? mis.itemHeight : 1;
TRACE("[%p]: measure item %d (%s) = %d\n",
descr->self, index, str ? debugstr_w(str) : "", item->height );
}
/* Repaint the items */
LISTBOX_UpdateScroll( descr );
LISTBOX_InvalidateItems( descr, index );
/* Move selection and focused item */
/* If listbox was empty, set focus to the first item */
if (descr->nb_items == 1)
LISTBOX_SetCaretIndex( descr, 0, FALSE );
/* single select don't change selection index in win31 */
else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
{
descr->selected_item++;
LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
}
else
{
if (index <= descr->selected_item)
{
descr->selected_item++;
descr->focus_item = oldfocus; /* focus not changed */
}
}
return LB_OKAY;
}
/***********************************************************************
* LISTBOX_InsertString
*/
static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index,
LPCWSTR str )
{
LPWSTR new_str = NULL;
DWORD data = 0;
LRESULT ret;
if (HAS_STRINGS(descr))
{
static const WCHAR empty_stringW[] = { 0 };
if (!str) str = empty_stringW;
if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
{
SEND_NOTIFICATION( descr, LBN_ERRSPACE );
return LB_ERRSPACE;
}
strcpyW(new_str, str);
}
else data = (DWORD)str;
if (index == -1) index = descr->nb_items;
if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
{
HeapFree( GetProcessHeap(), 0, new_str );
return ret;
}
TRACE("[%p]: added item %d %s\n",
descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
return index;
}
/***********************************************************************
* LISTBOX_DeleteItem
*
* Delete the content of an item. 'index' must be a valid index.
*/
static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
{
/* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
* while Win95 sends it for all items with user data.
* It's probably better to send it too often than not
* often enough, so this is what we do here.
*/
if (IS_OWNERDRAW(descr) || descr->items[index].data)
{
DELETEITEMSTRUCT dis;
UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
dis.CtlType = ODT_LISTBOX;
dis.CtlID = id;
dis.itemID = index;
dis.hwndItem = descr->self;
dis.itemData = descr->items[index].data;
SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
}
if (HAS_STRINGS(descr) && descr->items[index].str)
HeapFree( GetProcessHeap(), 0, descr->items[index].str );
}
/***********************************************************************
* LISTBOX_RemoveItem
*
* Remove an item from the listbox and delete its content.
*/
static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
{
LB_ITEMDATA *item;
INT max_items;
if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
/* We need to invalidate the original rect instead of the updated one. */
LISTBOX_InvalidateItems( descr, index );
LISTBOX_DeleteItem( descr, index );
/* Remove the item */
item = &descr->items[index];
if (index < descr->nb_items-1)
RtlMoveMemory( item, item + 1,
(descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
descr->nb_items--;
if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
/* Shrink the item array if possible */
max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
{
max_items -= LB_ARRAY_GRANULARITY;
item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
max_items * sizeof(LB_ITEMDATA) );
if (item) descr->items = item;
}
/* Repaint the items */
LISTBOX_UpdateScroll( descr );
/* if we removed the scrollbar, reset the top of the list
(correct for owner-drawn ???) */
if (descr->nb_items == descr->page_size)
LISTBOX_SetTopItem( descr, 0, TRUE );
/* Move selection and focused item */
if (!IS_MULTISELECT(descr))
{
if (index == descr->selected_item)
descr->selected_item = -1;
else if (index < descr->selected_item)
{
descr->selected_item--;
if (ISWIN31) /* win 31 do not change the selected item number */
LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
}
}
if (descr->focus_item >= descr->nb_items)
{
descr->focus_item = descr->nb_items - 1;
if (descr->focus_item < 0) descr->focus_item = 0;
}
return LB_OKAY;
}
/***********************************************************************
* LISTBOX_ResetContent
*/
static void LISTBOX_ResetContent( LB_DESCR *descr )
{
INT i;
for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( descr, i );
HeapFree( GetProcessHeap(), 0, descr->items );
descr->nb_items = 0;
descr->top_item = 0;
descr->selected_item = -1;
descr->focus_item = 0;
descr->anchor_item = -1;
descr->items = NULL;
}
/***********************************************************************
* LISTBOX_SetCount
*/
static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
{
LRESULT ret;
if (HAS_STRINGS(descr))
{
SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
return LB_ERR;
}
/* FIXME: this is far from optimal... */
if (count > descr->nb_items)
{
while (count > descr->nb_items)
if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
return ret;
}
else if (count < descr->nb_items)
{
while (count < descr->nb_items)
if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
return ret;
}
return LB_OKAY;
}
/***********************************************************************
* LISTBOX_Directory
*/
static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
LPCWSTR filespec, BOOL long_names )
{
HANDLE handle;
LRESULT ret = LB_OKAY;
WIN32_FIND_DATAW entry;
int pos;
/* don't scan directory if we just want drives exclusively */
if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
/* scan directory */
if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
{
int le = GetLastError();
if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
}
else
{
do
{
WCHAR buffer[270];
if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
static const WCHAR bracketW[] = { ']',0 };
static const WCHAR dotW[] = { '.',0 };
if (!(attrib & DDL_DIRECTORY) ||
!strcmpW( entry.cFileName, dotW )) continue;
buffer[0] = '[';
if (!long_names && entry.cAlternateFileName[0])
strcpyW( buffer + 1, entry.cAlternateFileName );
else
strcpyW( buffer + 1, entry.cFileName );
strcatW(buffer, bracketW);
}
else /* not a directory */
{
#define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
if ((attrib & DDL_EXCLUSIVE) &&
((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
continue;
#undef ATTRIBS
if (!long_names && entry.cAlternateFileName[0])
strcpyW( buffer, entry.cAlternateFileName );
else
strcpyW( buffer, entry.cFileName );
}
if (!long_names) CharLowerW( buffer );
pos = LISTBOX_FindFileStrPos( descr, buffer );
if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
break;
} while (FindNextFileW( handle, &entry ));
FindClose( handle );
}
}
/* scan drives */
if ((ret >= 0) && (attrib & DDL_DRIVES))
{
WCHAR buffer[] = {'[','-','a','-',']',0};
WCHAR root[] = {'A',':','\\',0};
int drive;
for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
{
if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
break;
}
}
return ret;
}
/***********************************************************************
* LISTBOX_HandleVScroll
*/
static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
{
SCROLLINFO info;
if (descr->style & LBS_MULTICOLUMN) return 0;
switch(scrollReq)
{
case SB_LINEUP:
LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
break;
case SB_LINEDOWN:
LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
break;
case SB_PAGEUP:
LISTBOX_SetTopItem( descr, descr->top_item -
LISTBOX_GetCurrentPageSize( descr ), TRUE );
break;
case SB_PAGEDOWN:
LISTBOX_SetTopItem( descr, descr->top_item +
LISTBOX_GetCurrentPageSize( descr ), TRUE );
break;
case SB_THUMBPOSITION:
LISTBOX_SetTopItem( descr, pos, TRUE );
break;
case SB_THUMBTRACK:
info.cbSize = sizeof(info);
info.fMask = SIF_TRACKPOS;
GetScrollInfo( descr->self, SB_VERT, &info );
LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
break;
case SB_TOP:
LISTBOX_SetTopItem( descr, 0, TRUE );
break;
case SB_BOTTOM:
LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
break;
}
return 0;
}
/***********************************************************************
* LISTBOX_HandleHScroll
*/
static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
{
SCROLLINFO info;
INT page;
if (descr->style & LBS_MULTICOLUMN)
{
switch(scrollReq)
{
case SB_LINELEFT:
LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
TRUE );
break;
case SB_LINERIGHT:
LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -