📄 listbox.c
字号:
/*
* Listbox controls
*
* Copyright 1996 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <user32.h>
#include <wine/debug.h>
/* Start of hack section -------------------------------- */
typedef short *LPINT16;
BOOL is_old_app(HWND hwnd)
{
return FALSE;
}
#define WM_LBTRACKPOINT 0x0131
#define WS_EX_DRAGDETECT 0x00000002L
#define WM_BEGINDRAG 0x022C
UINT STDCALL SetSystemTimer(HWND,UINT_PTR,UINT,TIMERPROC);
BOOL STDCALL KillSystemTimer(HWND,UINT_PTR);
/* End of hack section -------------------------------- */
/* Unimplemented yet:
* - LBS_USETABSTOPS
* - Locale handling
*
* Probably needs improvement:
* - LBS_NOSEL
*/
/* Items array granularity */
#define LB_ARRAY_GRANULARITY 16
/* Scrolling timeout in ms */
#define LB_SCROLL_TIMEOUT 50
/* Listbox system timer id */
#define LB_TIMER_ID 2
/* flag listbox changed while setredraw false - internal style */
#define LBS_DISPLAYCHANGED 0x80000000
/* Item structure */
typedef struct
{
LPWSTR str; /* Item text */
BOOL selected; /* Is item selected? */
UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
DWORD data; /* User data */
} LB_ITEMDATA;
/* Listbox structure */
typedef struct
{
HWND self; /* Our own window handle */
HWND owner; /* Owner window to send notifications to */
UINT style; /* Window style */
INT width; /* Window width */
INT height; /* Window height */
LB_ITEMDATA *items; /* Array of items */
INT nb_items; /* Number of items */
INT top_item; /* Top visible item */
INT selected_item; /* Selected item */
INT focus_item; /* Item that has the focus */
INT anchor_item; /* Anchor item for extended selection */
INT item_height; /* Default item height */
INT page_size; /* Items per listbox page */
INT column_width; /* Column width for multi-column listboxes */
INT horz_extent; /* Horizontal extent (0 if no hscroll) */
INT horz_pos; /* Horizontal position */
INT nb_tabs; /* Number of tabs in array */
INT *tabs; /* Array of tabs */
INT avg_char_width; /* Average width of characters */
BOOL caret_on; /* Is caret on? */
BOOL captured; /* Is mouse captured? */
BOOL in_focus;
HFONT font; /* Current font */
LCID locale; /* Current locale for string comparisons */
LPHEADCOMBO lphc; /* ComboLBox */
} LB_DESCR;
#define IS_OWNERDRAW(descr) \
((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
#define HAS_STRINGS(descr) \
(!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
#define IS_MULTISELECT(descr) \
((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
!((descr)->style & LBS_NOSEL))
#define SEND_NOTIFICATION(descr,code) \
(SendMessageW( (descr)->owner, WM_COMMAND, \
MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
#define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
/* Current timer status */
typedef enum
{
LB_TIMER_NONE,
LB_TIMER_UP,
LB_TIMER_LEFT,
LB_TIMER_DOWN,
LB_TIMER_RIGHT
} TIMER_DIRECTION;
static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
/*********************************************************************
* listbox class descriptor
*/
const struct builtin_class_descr LISTBOX_builtin_class =
{
#ifdef __REACTOS__
L"ListBox", /* name */
CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
(WNDPROC)ListBoxWndProcW, /* procW */
(WNDPROC)ListBoxWndProcA, /* procA */
sizeof(LB_DESCR *), /* extra */
(LPCWSTR) IDC_ARROW, /* cursor */
0 /* brush */
#else
"ListBox", /* name */
CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
ListBoxWndProcA, /* procA */
ListBoxWndProcW, /* procW */
sizeof(LB_DESCR *), /* extra */
IDC_ARROW, /* cursor */
0 /* brush */
#endif
};
/*********************************************************************
* combolbox class descriptor
*/
const struct builtin_class_descr COMBOLBOX_builtin_class =
{
#ifdef __REACTOS__
L"ComboLBox", /* name */
CS_DBLCLKS | CS_SAVEBITS, /* style */
(WNDPROC)ListBoxWndProcW, /* procW */
(WNDPROC)ListBoxWndProcA, /* procA */
sizeof(LB_DESCR *), /* extra */
(LPCWSTR) IDC_ARROW, /* cursor */
0 /* brush */
#else
"ComboLBox", /* name */
CS_DBLCLKS | CS_SAVEBITS, /* style */
ListBoxWndProcA, /* procA */
ListBoxWndProcW, /* procW */
sizeof(LB_DESCR *), /* extra */
IDC_ARROW, /* cursor */
0 /* brush */
#endif
};
#ifndef __REACTOS__
/* check whether app is a Win 3.1 app */
inline static BOOL is_old_app( HWND hwnd )
{
return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
}
#endif
/***********************************************************************
* LISTBOX_Dump
*/
void LISTBOX_Dump( HWND hwnd )
{
INT i;
LB_ITEMDATA *item;
LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
TRACE( "Listbox:\n" );
TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
hwnd, (UINT)descr, descr->nb_items, descr->top_item );
for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
{
TRACE( "%4d: %-40s %d %08lx %3d\n",
i, debugstr_w(item->str), item->selected, item->data, item->height );
}
}
/***********************************************************************
* LISTBOX_GetCurrentPageSize
*
* Return the current page size
*/
static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
{
INT i, height;
if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
{
if ((height += descr->items[i].height) > descr->height) break;
}
if (i == descr->top_item) return 1;
else return i - descr->top_item;
}
/***********************************************************************
* LISTBOX_GetMaxTopIndex
*
* Return the maximum possible index for the top of the listbox.
*/
static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
{
INT max, page;
if (descr->style & LBS_OWNERDRAWVARIABLE)
{
page = descr->height;
for (max = descr->nb_items - 1; max >= 0; max--)
if ((page -= descr->items[max].height) < 0) break;
if (max < descr->nb_items - 1) max++;
}
else if (descr->style & LBS_MULTICOLUMN)
{
if ((page = descr->width / descr->column_width) < 1) page = 1;
max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
max = (max - page) * descr->page_size;
}
else
{
max = descr->nb_items - descr->page_size;
}
if (max < 0) max = 0;
return max;
}
/***********************************************************************
* LISTBOX_UpdateScroll
*
* Update the scrollbars. Should be called whenever the content
* of the listbox changes.
*/
static void LISTBOX_UpdateScroll( LB_DESCR *descr )
{
SCROLLINFO info;
/* Check the listbox scroll bar flags individually before we call
SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
no WS_VSCROLL, we end up with an uninitialized, visible horizontal
scroll bar when we do not need one.
if (!(descr->style & WS_VSCROLL)) return;
*/
/* It is important that we check descr->style, and not wnd->dwStyle,
for WS_VSCROLL, as the former is exactly the one passed in
argument to CreateWindow.
In Windows (and from now on in Wine :) a listbox created
with such a style (no WS_SCROLL) does not update
the scrollbar with listbox-related data, thus letting
the programmer use it for his/her own purposes. */
if (descr->style & LBS_NOREDRAW) return;
info.cbSize = sizeof(info);
if (descr->style & LBS_MULTICOLUMN)
{
info.nMin = 0;
info.nMax = (descr->nb_items - 1) / descr->page_size;
info.nPos = descr->top_item / descr->page_size;
info.nPage = descr->width / descr->column_width;
if (info.nPage < 1) info.nPage = 1;
info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
if (descr->style & LBS_DISABLENOSCROLL)
info.fMask |= SIF_DISABLENOSCROLL;
if (descr->style & WS_HSCROLL)
SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
info.nMax = 0;
info.fMask = SIF_RANGE;
if (descr->style & WS_VSCROLL)
SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
}
else
{
info.nMin = 0;
info.nMax = descr->nb_items - 1;
info.nPos = descr->top_item;
info.nPage = LISTBOX_GetCurrentPageSize( descr );
info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
if (descr->style & LBS_DISABLENOSCROLL)
info.fMask |= SIF_DISABLENOSCROLL;
if (descr->style & WS_VSCROLL)
SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
if (descr->horz_extent)
{
info.nMin = 0;
info.nMax = descr->horz_extent - 1;
info.nPos = descr->horz_pos;
info.nPage = descr->width;
info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
if (descr->style & LBS_DISABLENOSCROLL)
info.fMask |= SIF_DISABLENOSCROLL;
if (descr->style & WS_HSCROLL)
SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
}
}
}
/***********************************************************************
* LISTBOX_SetTopItem
*
* Set the top item of the listbox, scrolling up or down if necessary.
*/
static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index,
BOOL scroll )
{
INT max = LISTBOX_GetMaxTopIndex( descr );
if (index > max) index = max;
if (index < 0) index = 0;
if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
if (descr->top_item == index) return LB_OKAY;
if (descr->style & LBS_MULTICOLUMN)
{
INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
if (scroll && (abs(diff) < descr->width))
ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
else
scroll = FALSE;
}
else if (scroll)
{
INT diff;
if (descr->style & LBS_OWNERDRAWVARIABLE)
{
INT i;
diff = 0;
if (index > descr->top_item)
{
for (i = index - 1; i >= descr->top_item; i--)
diff -= descr->items[i].height;
}
else
{
for (i = index; i < descr->top_item; i++)
diff += descr->items[i].height;
}
}
else
diff = (descr->top_item - index) * descr->item_height;
if (abs(diff) < descr->height)
ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
else
scroll = FALSE;
}
if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
descr->top_item = index;
LISTBOX_UpdateScroll( descr );
return LB_OKAY;
}
/***********************************************************************
* LISTBOX_UpdatePage
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -