⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 osdlistbox.c

📁 MiniWinOuterSM MiniWinOuterSM
💻 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 + -