📄 osdlistbox.c
字号:
#include"osdwindows.h"
#include"PrString.h"
#define SEND_NOTIFY(hwnd,ctrlID,notifyCode) \
SendMessage(GetWindow((hwnd),GW_OWNER),WM_COMMAND,\
MAKELONG((ctrlID),(notifyCode)),(LPARAM)(hwnd))
typedef struct LISTBOXITEM
{
char* Text;//列表项目文本
UINT Data;//自定义列表数据
UCHAR Checked;//定义 是否被选中
}LISTBOXITEM;
typedef struct LISTBOXDATA
{
HWND self;
INT ColumnWidth;
INT TopItem;
INT ItemIndex;//当前被选择列表索引
INT Count;//列表项目数量
INT Capacity;//列表空间容量
LISTBOXITEM*Items;//列表头指针
}LISTBOXDATA;
static INT GetCountByHeight(HWND hwnd,INT from,INT dir)
{
RECT rc;
INT wh,i,count,dh;
LISTBOXDATA*dt=(LISTBOXDATA*)GetWindowLong(hwnd,0);
count=0; dh=0;
GetWindowRect(hwnd,&rc);
if(from<0)from=0;
if((GetWindowLong(hwnd,GWL_STYLE)&LBS_MULTICOLUMN)==0){
wh=RECTH(&rc);
for(i=from;(i+dir>=0)&&(i<dt->Count)&&(dh<wh);i+=dir){
UINT size;
size=SendMessage(hwnd,LB_GETITEMHEIGHT,i,0);
dh+=size;
if(dh<=wh)
count++;
}
}else{
int j=0,rows,cols;
wh=RECTW(&rc);
rows=RECTH(&rc)/SendMessage(hwnd,LB_GETITEMHEIGHT,0,0);//dt->ColumnWidth;
cols=RECTW(&rc)/dt->ColumnWidth;
for(i=from;(i+dir>=0)&&(i<dt->Count)&&(dh<wh);i+=dir){
UINT size;
if(++j%rows==0)
dh+=dt->ColumnWidth;
if(dh<=wh)
count++;
}
}
return count;
}
static char* lbGetItemText(HWND hwnd,int idx)
{
LISTBOXDATA*lst=(LISTBOXDATA*)GetWindowLong(hwnd,0);
LISTBOXITEM*itm;
if((idx>=0)&&(idx<lst->Count)){
itm=lst->Items+idx;
return itm->Text;
}
return NULL;
}
static void DrawListBox(HWND hwnd,HDC hdc)
{
RECT r,rb;
INT i,itmCount;
UINT dwStyle=GetWindowLong(hwnd,GWL_STYLE);
INT cursel=(INT)SendMessage(hwnd,LB_GETCURSEL,0,0);
DRAWITEMSTRUCT ds;
// WINDOW*ctrl=(WINDOW*)hwnd;
if((dwStyle&WS_VISIBLE)==0)
return;
GetClientRect(hwnd,&rb);
r=rb;
SendMessage(GetParent(hwnd),WM_CTLCOLORLISTBOX,(WPARAM)hdc,(LPARAM)hwnd);
ds.CtlID=GetDlgCtrlID(hwnd); ds.hDC=hdc;
ds.CtlType=ODT_LISTBOX; ds.hwndItem=hwnd;
// SendMessage(hwnd,WM_ERASEBKGND,hdc,0);
itmCount=SendMessage(hwnd,LB_GETCOUNT,0,0);
for(i=SendMessage(hwnd,LB_GETTOPINDEX,0,0);i<itmCount;i++){
ds.itemID=i;
SendMessage(hwnd,LB_GETITEMRECT,i,(UINT)&ds.rcItem);
if(DoesIntersect(&ds.rcItem,&rb)==0)break;
ds.itemState=(i==cursel)?ODS_SELECTED:0;
if(!SendMessage(GetParent(hwnd),WM_DRAWITEM,GetDlgCtrlID(hwnd),(LPARAM)&ds)){
char*txt=lbGetItemText(hwnd,i);
RECT r=ds.rcItem;
if(ds.itemState==ODS_SELECTED){
FillRect(hdc,&r,(HBRUSH)(COLOR_HIGHLIGHT+1));
SetTextColor(hdc,GetSysColor(COLOR_HIGHLIGHTTEXT));
}else{
FillRect(hdc,&r,(HBRUSH)(COLOR_WINDOW+1));
SetTextColor(hdc,GetSysColor(COLOR_WINDOWTEXT));
}
InflateRect(&r,-2,0);
if(GetWindowLong(hwnd,GWL_STYLE)&LBS_MULTICOLUMN)
DrawText(hdc,txt,-1,&r,DT_LEFT|DT_VCENTER);
else
DrawText(hdc,txt,-1,&r,DT_LEFT|DT_VCENTER|DT_SINGLELINE);
}
r.top=ds.rcItem.bottom;
}
r.bottom=rb.bottom;
FillRect(hdc,&r,(HBRUSH)(COLOR_WINDOW+1));
}
static int lbSetItemText(HWND hwnd,char*text,INT idx)
{
LISTBOXDATA*lst=(LISTBOXDATA*)GetWindowLong(hwnd,0);
LISTBOXITEM*itm;
if((lst==NULL)||(idx<0)||(idx>=lst->Count))
return 0;
itm=lst->Items+idx;
if(itm&&text!=NULL){
if(strlen(itm->Text)<strlen(text)){
PrFree(itm->Text);
itm->Text=(char*)PrMalloc(strlen(text)+1);
}
strcpy(itm->Text,text);
}
return 1;
}
static int lbDeleteString(HWND hwnd,INT idx,int redraw)
{
LISTBOXDATA *lst=(LISTBOXDATA*)GetWindowLong(hwnd,0);
RECT r,rc;
DELETEITEMSTRUCT ds;
ds.CtlID=GetDlgCtrlID(hwnd);
ds.CtlType=ODT_LISTBOX;
if((idx<lst->Count)&&(idx>=0)){
if(lst->Items[idx].Text){
PrFree(lst->Items[idx].Text);
lst->Items[idx].Text=NULL;
}
ds.hwndItem=(HWND)idx;
ds.itemData=lst->Items[idx].Data;
//向该控件的所有者发送消息,要求释放用户私有数据
SendMessage(GetParent(hwnd),WM_DELETEITEM,ds.CtlID,(LPARAM)&ds);
memcpy(lst->Items+idx,lst->Items+idx+1,sizeof(LISTBOXITEM)*(lst->Count-idx-1));
lst->Count--;
if(lst->ItemIndex>=lst->Count)
lst->ItemIndex--;
if(redraw){
SendMessage(hwnd,LB_GETITEMRECT,idx,(UINT)&r);
GetClientRect(hwnd,&rc);
r.bottom=rc.bottom;
InvalidateRect(hwnd,&r,FALSE);
}
}
return lst->Count;
}
static int lbFindItem (LISTBOXDATA*pData, int start, char* key, BOOL bExact)
{
int i,keylen = strlen (key);
for(i=start;i<pData->Count;i++){
LISTBOXITEM*plbi=pData->Items+start;
if (PrStrCaseCmp(key, plbi->Text) == 0)
return i;
}
return -1;//LB_ERR;
}
static void lbResetContent(HWND hwnd)
{//释放所有列表项目
INT i;
LISTBOXDATA *lst=(LISTBOXDATA*)GetWindowLong(hwnd,0);
for(i=lst->Count-1;i>=0;i--)
lbDeleteString(hwnd,i,0);
if(lst->Items!=NULL) PrFree(lst->Items);
lst->Items=NULL;
lst->Capacity=0; lst->Count=0;
lst->TopItem=0; lst->ItemIndex=-1;
InvalidateRect(hwnd,NULL,FALSE);
}
static int lbAddString(HWND hwnd,char*txt)
{//添加列表项目
LISTBOXDATA*dt=(LISTBOXDATA*)GetWindowLong(hwnd,0);
LISTBOXITEM*itm;
RECT rc,ritm;
GetClientRect(hwnd,&rc);
if(dt->Count>=dt->Capacity){
LISTBOXITEM*oitems=dt->Items;
dt->Capacity+=10;
dt->Items=(LISTBOXITEM*)PrMalloc(sizeof(LISTBOXITEM)*dt->Capacity);
if(dt->Items==NULL){
dt->Items=oitems; return (UINT)-1;
}
if(oitems!=NULL){
memcpy(dt->Items,oitems,sizeof(LISTBOXITEM)*dt->Count);
PrFree(oitems);
}
}
itm=dt->Items+dt->Count++;
itm->Text=NULL; itm->Data=0;
if(txt!=NULL)itm->Text=PrStrdup(txt);
itm->Data=0;
SendMessage(hwnd,LB_GETITEMRECT,dt->Count-1,(UINT)&ritm);
if(DoesIntersect(&rc,&ritm))
InvalidateRect(hwnd,&ritm,FALSE);
SetScrollRange(hwnd,SB_VERT,0,dt->Count,TRUE);
return dt->Count-1;
}
static void lbDestroyListBox(HWND hwnd)
{//释放ListBox所有数据,
LISTBOXDATA*dt=(LISTBOXDATA*)GetWindowLong(hwnd,0);
if(dt!=NULL){
lbResetContent(hwnd);
PrFree(dt);
}
}
static void lbGetItemRect(HWND hwnd,int idx,RECT*rc)
{
int i,cols,rows,itmHeight;
LISTBOXDATA*dt=(LISTBOXDATA*)GetWindowLong(hwnd,0);
DWORD dwStyle=GetWindowLong(hwnd,GWL_STYLE);
GetClientRect(hwnd,rc);
if(idx<dt->TopItem){
SetRectEmpty(rc);
return;
}
itmHeight=SendMessage(hwnd,LB_GETITEMHEIGHT,idx,0);
if((dwStyle&LBS_MULTICOLUMN)==0){
rc->bottom=rc->top+itmHeight;
for(i=dt->TopItem;i<idx;i++){
INT h=SendMessage(hwnd,LB_GETITEMHEIGHT,i,0);
OffsetRect(rc,0,h);
}
}else{//计算多列模式下的项目区域
cols=RECTW(rc)/dt->ColumnWidth;
rows=(RECTH(rc)+itmHeight)/itmHeight;
rc->bottom=rc->top+itmHeight;
rc->right=rc->left+dt->ColumnWidth;
i=idx-dt->TopItem;
OffsetRect(rc,dt->ColumnWidth*(i/rows),itmHeight*(i%rows));
}
}
static LRESULT WINAPI ListBoxProc(HWND hwnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
LISTBOXDATA*dt=(LISTBOXDATA*)GetWindowLong(hwnd,0);
INT vc,oidx;
char topChange=0;
switch(msgID){
case WM_NCCREATE:
{
LISTBOXDATA*lst=(LISTBOXDATA*)PrMalloc(sizeof(LISTBOXDATA));
lst->self=hwnd;
lst->Items=0; lst->Count=0;
lst->Capacity=0; lst->ItemIndex=-1;
lst->TopItem=0; lst->ColumnWidth=1;
SetWindowLong(hwnd,0,(DWORD)lst);
}break;
case WM_GETDLGCODE:
{
MSG*msg=(MSG*)lParam;
int K=msg&&(msg->message==WM_KEYDOWN)
&&((msg->wParam==VK_LEFT)||(msg->wParam==VK_RIGHT));
if((msg==NULL)||K||(dt->Count==0))
return DLGC_WANTCHARS|DLGC_WANTTAB;
return DLGC_WANTARROWS|DLGC_WANTCHARS;
}break;
case WM_SETFOCUS:
SEND_NOTIFY(hwnd,GetDlgCtrlID(hwnd),LBN_SETFOCUS);break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
{
int i=0;
POINT pt;
pt.x=LOWORD(lParam);
pt.y=HIWORD(lParam);
for(i=dt->TopItem;i<dt->Count;i++){
RECT rc;
SendMessage(hwnd,LB_GETITEMRECT,i,(DWORD)&rc);
if(PtInRect(&rc,pt)){
SendMessage(hwnd,LB_SETCURSEL,i,0);
if(msgID==WM_LBUTTONDBLCLK)
SEND_NOTIFY(hwnd,GetDlgCtrlID(hwnd),LBN_DBLCLK);
break;
}
}
PrDbgPrintf("WM_LBUTTONDOWN(%d,%d)\n",LOWORD(lParam),HIWORD(lParam));
}
break;
case WM_LBUTTONUP:
ReleaseCapture();
SEND_NOTIFY(hwnd,GetDlgCtrlID(hwnd),LBN_SELCHANGE);
break;
case WM_KEYDOWN:
switch(wParam){
case VK_UP:
if(dt->Count>0){
oidx=dt->ItemIndex;
if(GetWindowLong(hwnd,GWL_STYLE)&LBS_WRAP)
dt->ItemIndex=(dt->ItemIndex+dt->Count-1)%dt->Count;
else
dt->ItemIndex=(dt->ItemIndex>0)?dt->ItemIndex-1:0;
vc=GetCountByHeight(hwnd,dt->ItemIndex,-1);
if(dt->ItemIndex<dt->TopItem){
dt->TopItem=dt->ItemIndex-vc;
topChange=1;
}else if((dt->ItemIndex-dt->TopItem+1>=vc)&&
(dt->ItemIndex-vc>dt->TopItem)){
dt->TopItem=dt->ItemIndex-vc+1;
topChange=1;
}
if(oidx!=dt->ItemIndex){
SEND_NOTIFY(hwnd,GetDlgCtrlID(hwnd),LBN_SELCHANGE);
if(!topChange){
RECT r1,r2;
SendMessage(hwnd,LB_GETITEMRECT,oidx,(UINT)&r1);
SendMessage(hwnd,LB_GETITEMRECT,dt->ItemIndex,(UINT)&r2);
InvalidateRect(hwnd,&r1,FALSE);InvalidateRect(hwnd,&r2,FALSE);
}else{
InvalidateRect(hwnd,NULL,FALSE);
}
SetScrollPos(hwnd,SB_VERT,dt->ItemIndex,TRUE);
}
}break;
case VK_PAGE_UP:
oidx=dt->ItemIndex;
vc=GetCountByHeight(hwnd,dt->TopItem,-1);
dt->TopItem-=vc;
dt->ItemIndex-=GetCountByHeight(hwnd,dt->ItemIndex,-1);
if(oidx!=dt->ItemIndex){
InvalidateRect(hwnd,NULL,FALSE);
SEND_NOTIFY(hwnd,GetDlgCtrlID(hwnd),LBN_SELCHANGE);
SetScrollPos(hwnd,SB_VERT,dt->ItemIndex,TRUE);
}
break;
case VK_DOWN:
if(dt->Count){
oidx=dt->ItemIndex;
vc=GetCountByHeight(hwnd,dt->TopItem,1);
if(GetWindowLong(hwnd,GWL_STYLE)&LBS_WRAP)
dt->ItemIndex=(dt->ItemIndex+1)%dt->Count;
else if(dt->ItemIndex<dt->Count-1)
dt->ItemIndex++;
if((dt->ItemIndex<dt->TopItem)||(dt->ItemIndex>dt->TopItem+vc-1)){
dt->TopItem=(dt->ItemIndex+vc<dt->Count)?dt->ItemIndex:dt->Count-vc;
topChange=1;
}
if(oidx!=dt->ItemIndex){
if(!topChange){
RECT r1,r2;
SendMessage(hwnd,LB_GETITEMRECT,oidx,(UINT)&r1);
SendMessage(hwnd,LB_GETITEMRECT,dt->ItemIndex,(UINT)&r2);
//UnionRect(&r1,&r1,&r2);
InvalidateRect(hwnd,&r1,FALSE);InvalidateRect(hwnd,&r2,FALSE);
}else{
InvalidateRect(hwnd,NULL,FALSE);
}
SEND_NOTIFY(hwnd,GetDlgCtrlID(hwnd),LBN_SELCHANGE);
SetScrollPos(hwnd,SB_VERT,dt->ItemIndex,TRUE);
}
}break;
case VK_PAGE_DOWN:
{
INT ct;
oidx=dt->ItemIndex;
vc=GetCountByHeight(hwnd,dt->TopItem+1,1);
dt->TopItem+=vc;
ct=dt->Count-GetCountByHeight(hwnd,dt->Count-1,-1);
if(dt->TopItem>=ct)
dt->TopItem=ct;
dt->ItemIndex+=GetCountByHeight(hwnd,dt->ItemIndex+1,1);
if(oidx!=dt->ItemIndex){
SEND_NOTIFY(hwnd,GetDlgCtrlID(hwnd),LBN_SELCHANGE);
InvalidateRect(hwnd,NULL,FALSE);
SetScrollPos(hwnd,SB_VERT,dt->ItemIndex,TRUE);
}
}break;
case VK_RETURN:SEND_NOTIFY(hwnd,GetDlgCtrlID(hwnd),LBN_RETURN);break;
}break;
case LB_FINDSTRING:
if( *(char*)lParam == '\0' )
return (UINT)-1;//LB_ERR;
return lbFindItem(dt, (int)wParam, (char*)lParam, FALSE);
case LB_FINDSTRINGEXACT:
if( *(char*)lParam == '\0' )
return -1;//LB_ERR;
return lbFindItem(dt, (int)wParam, (char*)lParam, TRUE);
case LB_ADDSTRING:return lbAddString(hwnd,(char*)lParam);
case LB_DELETESTRING:
lbDeleteString(hwnd,wParam,1);
SetScrollRange(hwnd,SB_VERT,0,dt->Count,TRUE);
return dt->Count;//返回余下的数量
case LB_RESETCONTENT:
lbResetContent(hwnd);
SetScrollRange(hwnd,SB_VERT,0,0,TRUE);
break;
case LB_SETCOLUMNWIDTH:dt->ColumnWidth=wParam;break;
case WM_DESTROY:lbDestroyListBox(hwnd);break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps);
DrawListBox(hwnd,hdc);
EndPaint(hwnd,&ps);
}break;
case LB_GETITEMHEIGHT:{
//const char*txt=lbGetItemText(hwnd,wParam);
//如果这里调用GetTextExtentPoint会导致绘图速度超级慢
return 24;//GetTextExtentPoint(GetDC(hwnd),txt,-1).cy;//<<16;
}break;
case LB_GETITEMRECT:lbGetItemRect(hwnd,(int)wParam,(RECT*)lParam);break;
case WM_GETTEXT:
{
}break;
case LB_GETTEXT:
{
INT idx=(INT)wParam;
const char*txt=lbGetItemText(hwnd,idx);
if(lParam&&txt){
strcpy((char*)lParam,txt);
return strlen(txt);
}
}return 0;
case LB_GETTEXTLEN:
if( ( (INT)wParam<dt->Count) && ( (INT)wParam>=0) )
return strlen(dt->Items[wParam].Text);
return 0;
case LB_SELECTSTRING:
{//按照给定的字符串查找对应的第一个项目
INT i;
for(i=(INT)wParam;i<dt->Count;i++){
if(strstr(dt->Items[i].Text,(char*)lParam))
return i;
}
}return -1;
case WM_SETTEXT:
{
INT idx=SendMessage(hwnd,LB_GETCURSEL,0,0);
if(lbSetItemText(hwnd,(char*)lParam,idx))
InvalidateRect(hwnd,NULL,FALSE);
}return 0;
case LB_GETCURSEL:return dt->ItemIndex;
case LB_SETCURSEL:
{
INT vc,idx=(INT)wParam;
RECT rco,rcn,rcw;
if((dt==NULL)||(idx>=dt->Count)||(idx<0)||(dt->ItemIndex==idx))
return 0;
GetClientRect(hwnd,&rcw);
SendMessage(hwnd,LB_GETITEMRECT,dt->ItemIndex,(LPARAM)&rco);
SendMessage(hwnd,LB_GETITEMRECT,idx,(LPARAM)&rcn);
dt->ItemIndex=idx;
if(DoesIntersect(&rcw,&rcn)==0){
vc=GetCountByHeight(hwnd,wParam,-1);
dt->TopItem=wParam-vc;//SETCURSEL不需要调用 LBN_SELCHANGE
//SendMessage(hwnd,WM_ITEMCHANGED,wParam,0);
InvalidateRect(hwnd,NULL,FALSE);
}else{//如果新的选项和原来的选项都在客户显示区之内,则只刷新改两项所在区域
InvalidateRect(hwnd,&rco,FALSE);
InvalidateRect(hwnd,&rcn,FALSE);
}
SetScrollPos(hwnd,SB_VERT,idx,TRUE);
}break;
case LB_SETITEMDATA:
{
INT idx=(INT)wParam;
if(idx>=0&&idx<dt->Count)
(dt->Items+idx)->Data=lParam;
}break;
case LB_GETITEMDATA:
{
INT idx=(INT)wParam;
if(idx>=0&&idx<dt->Count)
return (dt->Items+idx)->Data;
}return (UINT)LB_ERR;
case LB_GETTOPINDEX:return dt->TopItem;
case LB_GETCOUNT:return dt->Count;
default:return DefWindowProc(hwnd,msgID,wParam,lParam);break;
}
return 0;
}
int _RegisterListBox(void)
{
WNDCLASS ci;
memset(&ci,0,sizeof(ci));
ci.lpszClassName="Listbox";
ci.lpfnWndProc=ListBoxProc;
ci.hCursor=0;
ci.hIcon=0;
ci.style=0;
ci.cbWndExtra=sizeof(LISTBOXDATA*);
ci.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
return RegisterClass(&ci);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -