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

📄 osdlistview.c

📁 MiniWinOuterSM MiniWinOuterSM
💻 C
字号:
#include"osdwindows.h"
#include"osdwindef.h"
#include"osdcommctrl.h"
typedef struct{
	char*Text;
	DWORD Data;
}SUBITEM;
typedef struct _LISTVIEWITEM
{
    //char* Text;//列表项目文本
	UCHAR Checked;//定义是否被选中
	UCHAR Selected;//是否是选择状态
	UINT Data;//自定义列表数据
	SUBITEM*SubItems;
	struct _LISTVIEWITEM*next;
}LISTVIEWITEM;
typedef struct LISTVIEWDATA
{
	INT Cols;
	INT ColumnWidth;
	INT TopItem;
    INT ItemIndex;//当前被选择列表索引
    INT Rows;//列表项目数量
	INT Capacity;//列表空间容量
	HWND hwndHeader;//列表头控件窗口句柄
    LISTVIEWITEM*Items;//列表头指针
}LISTVIEWDATA;
static INT GetCountByHeight(HWND hwnd,INT from,INT dir)
{
	RECT rc;
	INT wh,i,count,dh;
	LISTVIEWDATA*dt=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	count=0;	dh=0;
	GetWindowRect(hwnd,&rc);
	if(from<0)from=0;
	if(GetWindowLong(hwnd,GWL_STYLE)&LVS_REPORT){
		wh=RECTH(&rc);
		for(i=from;(i+dir>=0)&&(i<dt->Rows)&&(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->Rows)&&(dh<wh);i+=dir){
			if(++j%rows==0)
				dh+=dt->ColumnWidth;
			if(dh<=wh)
				count++;
		}
	}
	return count;
}
static char* lvGetItemText(HWND hwnd,int idx,LVITEM*lv)
{
	LISTVIEWDATA*lst=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	LISTVIEWITEM*itm;
	if((idx>=0)&&(idx<lst->Rows)){
		itm=lst->Items+idx;
		return itm->SubItems[lv->iSubItem].Text;
	}
	return NULL;
}
static void DrawSubItem(HWND hwnd,HDC hdc,const RECT*rc,LISTVIEWITEM*itm)
{
	DWORD dwStyle=GetWindowLong(hwnd,GWL_STYLE);
	LISTVIEWDATA*lvCtl=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	SUBITEM*sitm=itm->SubItems;
	int i;
	RECT rs=*rc;
	int ColumnCount=SendMessage(lvCtl->hwndHeader,HDM_GETITEMCOUNT,0,0);
	rs.right=rs.left;
	for(i=0;i<ColumnCount;i++){
		HDITEM hd;
		char txt[128];
		hd.pszText=txt;
		SendMessage(lvCtl->hwndHeader,HDM_GETITEMA,i,(LPARAM)&hd);
		rs.right+=hd.cxy;
		if((rs.right<rc->right)&&(i==ColumnCount-1))rs.right=rc->right;
		DrawText(hdc,itm->SubItems[i].Text,-1,&rs,DT_LEFT);
		rs.left+=hd.cxy;
		if((dwStyle&LVS_REPORT)==0)
			break;
	}
}

static void DrawListView(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);
	if((dwStyle&WS_VISIBLE)==0)
		return;
	GetClientRect(hwnd,&rb);
	r=rb;
	SendMessage(GetParent(hwnd),WM_CTLCOLORLISTBOX,(WPARAM)hdc,(LPARAM)hwnd);
	SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)hdc,0);
	itmCount=SendMessage(hwnd,LVM_GETITEMCOUNT,0,0);
	for(i=SendMessage(hwnd,LVM_GETTOPINDEX,0,0);i<itmCount;i++){
			LISTVIEWDATA*lst=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
			LISTVIEWITEM*itm=lst->Items+i;
			RECT rcItem;
			SendMessage(hwnd,LVM_GETITEMRECT,i,(LPARAM)&rcItem);
			if(DoesIntersect(&rcItem,&rb)==0)break;
			{
				RECT r=rcItem;
				COLORREF clBk,clTxt;
				clBk=i==lst->ItemIndex?COLOR_HOTLIGHT:COLOR_WINDOW;
				FillRect(hdc,&r,(HBRUSH)clBk);
				SetTextColor(hdc,clTxt);
				DrawSubItem(hwnd,hdc,&r,itm);
			}
			r.top=rcItem.bottom;
	}
}

static int lvSetItemText(HWND hwnd,int idx,LVITEM*lv)
{
	LISTVIEWDATA*lst=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	LISTVIEWITEM*itm;
	if((lst==NULL)||(idx<0)||(idx>=lst->Rows))
		return 0;
	itm=lst->Items+idx;
	if((lv->pszText!=NULL) && (lv->iSubItem<lst->Cols) && (lv->iSubItem>=0) ){
		SUBITEM*sitm=itm->SubItems+lv->iSubItem;
		if(sitm->Text)PrFree(sitm->Text);
		sitm->Text=(char*)PrStrdup(lv->pszText);
	}
	return 1;
}
static int lvInsertColumn(HWND hwnd,int iCol,LVCOLUMN*lc)
{
	LISTVIEWDATA *lst=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	HDITEM itm;
	itm.cxy=lc->cx;
	itm.pszText=lc->pszText;
	itm.mask=HDI_TEXT|HDI_WIDTH;
	SendMessage(lst->hwndHeader,HDM_INSERTITEMA,0,(LPARAM)&itm);
	return lst->Cols++;
}
static int lvInsertItem(HWND hwnd,LVITEM*lv)
{
	int i,idx=-1;
	LISTVIEWDATA *lst=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	LISTVIEWITEM*itm=NULL;
	if(lst->Rows>=lst->Capacity){
		lst->Capacity+=16;
		lst->Items=PrRealloc(lst->Items,sizeof(LISTVIEWITEM)*lst->Capacity);
	}
	itm=lst->Items+lst->Rows++;
	itm->SubItems=PrMalloc(sizeof(SUBITEM)*lst->Cols);
	for(i=0;i<lst->Cols;i++){
		itm->SubItems[i].Text=NULL;
		itm->SubItems[i].Data=0;
	}
	if(lv->mask&LVIF_TEXT) itm->SubItems[0].Text=(char*)PrStrdup(lv->pszText);
	if(lv->mask&LVIF_IMAGE);
	if(lv->mask&LVIF_STATE);
	if(lv->mask&LVIF_PARAM);
	SetScrollRange(hwnd,SB_VERT,0,lst->Rows-1,TRUE);
	return lst->Rows-1;
}
static int lvDeleteItem(HWND hwnd,INT idx,int redraw)
{
	LISTVIEWDATA *lst=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	RECT r,rc;
	DELETEITEMSTRUCT ds;
	ds.CtlID=GetDlgCtrlID(hwnd);
	ds.CtlType=ODT_LISTBOX;
	if((idx<lst->Rows)&&(idx>=0)){	
		int icol;
		for(icol=0;icol<lst->Cols;icol++){
			SUBITEM*sitm=lst->Items[idx].SubItems+icol;
			if(sitm->Text){
				PrFree(sitm->Text);
				sitm->Text=NULL;
			}
		}
		PrFree(lst->Items[idx].SubItems);
		ds.hwndItem=(HWND)idx;		ds.itemData=lst->Items[idx].Data;
		//向该控件的所有者发送消息,要求释放用户私有数据
		SendMessage(GetParent(hwnd),LVM_DELETEITEM,ds.CtlID,(LPARAM)&ds);
		memcpy(lst->Items+idx,lst->Items+idx+1,sizeof(LISTVIEWITEM)*(lst->Rows-idx-1));
		lst->Rows--;
		if(lst->ItemIndex>=lst->Rows)
			lst->ItemIndex--;
		if(redraw){
			SendMessage(hwnd,LVM_GETITEMRECT,idx,(UINT)&r);
			GetClientRect(hwnd,&rc);
			r.bottom=rc.bottom;
			InvalidateRect(hwnd,&r,FALSE);
		}
	}
	return lst->Rows;
}
static int lbFindItem (LISTVIEWDATA*pData, int start, char* key, BOOL bExact)
{
    int i,keylen = strlen (key);
    for(i=start;i<pData->Rows;i++){
	    LISTVIEWITEM*plbi=pData->Items+start;
        //if (PrStrCaseCmp(key, plbi->Text) == 0)
        //    return i;       
    }
    return -1;//LB_ERR;
}
static void lvDeleteAllItems(HWND hwnd)
{//释放所有列表项目
	INT i;
	LISTVIEWDATA *lst=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	for(i=lst->Rows-1;i>=0;i--)
		lvDeleteItem(hwnd,i,0);
	if(lst->Items!=NULL)	PrFree(lst->Items);	
	lst->Items=NULL;
	lst->Capacity=0;		lst->Rows=0;
	lst->TopItem=0;			lst->ItemIndex=-1;
	InvalidateRect(hwnd,NULL,FALSE);
}

static void lvDestroy(HWND hwnd)
{//释放ListView所有数据,
	LISTVIEWDATA*dt=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	if(dt!=NULL){
		lvDeleteAllItems(hwnd);
		PrFree(dt);
	}
}
static void lvGetItemRect(HWND hwnd,int idx,RECT*rc)
{
	int i,cols,rows,itmHeight;
	LISTVIEWDATA*dt=(LISTVIEWDATA*)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&LVS_REPORT){
		RECT rcHdr;
		GetWindowRect(dt->hwndHeader,&rcHdr);
		rc->bottom=rc->top+itmHeight;
		for(i=dt->TopItem;i<idx;i++){
			INT h=SendMessage(hwnd,LB_GETITEMHEIGHT,i,0);
			OffsetRect(rc,0,h);
		}
		OffsetRect(rc,0,RECTH(&rcHdr));
	}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 ListViewProc(HWND hwnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
	LISTVIEWDATA*dt=(LISTVIEWDATA*)GetWindowLong(hwnd,0);
	INT vc,oidx;
	char topChange=0;
	switch(msgID){
	case WM_NCCREATE:
		{
			LISTVIEWDATA*lst=(LISTVIEWDATA*)PrMalloc(sizeof(LISTVIEWDATA));
			LPCREATESTRUCT lpc=(LPCREATESTRUCT)lParam;
			RECT rc;
			GetClientRect(hwnd,&rc);
			lst->Items=0;		lst->Rows=0;
			lst->Capacity=0;	lst->ItemIndex=-1;
			lst->TopItem=0;		lst->ColumnWidth=1;
			lst->Cols=0;		//lst->Headers=NULL;
			lst->hwndHeader=CreateWindow("Header",NULL,WS_VISIBLE|WS_CHILD,
				0,0,RECTW(&rc),24,hwnd,0,NULL,NULL);
			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->Rows==0))
					return DLGC_WANTCHARS|DLGC_WANTTAB;
			return DLGC_WANTARROWS|DLGC_WANTCHARS;
		}break;
	case WM_SETFOCUS:
		NotifyParent(hwnd,GetDlgCtrlID(hwnd),LBN_SETFOCUS);break;
	case WM_LBUTTONDOWN:
		{
			int i=0;
			POINT pt;
			pt.x=LOWORD(lParam);pt.y=HIWORD(lParam);
			for(i=dt->TopItem;i<dt->Rows;i++){
				RECT rc;
				SendMessage(hwnd,LVM_GETITEMRECT,i,(LPARAM)&rc);
				MapWindowPoints(hwnd,(HWND)NULL,(POINT*)&rc,2);//ClientToScreen(hwnd,&rc);
				if(PtInRect(&rc,pt)){
					SendMessage(hwnd,LB_SETCURSEL,i,0);break;
				}
			}
			PrDbgPrintf("WM_LBUTTONDOWN(%d,%d)\n",LOWORD(lParam),HIWORD(lParam));
		}
		break;
	case WM_KEYDOWN:
		switch(wParam){
		case VK_UP:
			if(dt->Rows>0){
				oidx=dt->ItemIndex;
				if(GetWindowLong(hwnd,GWL_STYLE)&LBS_WRAP)
					dt->ItemIndex=(dt->ItemIndex+dt->Rows-1)%dt->Rows;
				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){
					//NotifyParent(hwnd,ctrl->wID,LBN_SELCHANGE);
					if(!topChange){
						RECT r1,r2;
						SendMessage(hwnd,LVM_GETITEMRECT,oidx,(UINT)&r1);
						SendMessage(hwnd,LVM_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);
					//NotifyParent(hwnd,ctrl->wID,LBN_SELCHANGE);
					SetScrollPos(hwnd,SB_VERT,dt->ItemIndex,TRUE);
				}
				break;
		case VK_DOWN:
			if(dt->Rows){
				oidx=dt->ItemIndex;
				vc=GetCountByHeight(hwnd,dt->TopItem,1);
				if(GetWindowLong(hwnd,GWL_STYLE)&LBS_WRAP)
					dt->ItemIndex=(dt->ItemIndex+1)%dt->Rows;
				else if(dt->ItemIndex<dt->Rows-1)
					dt->ItemIndex++;
				if((dt->ItemIndex<dt->TopItem)||(dt->ItemIndex>dt->TopItem+vc-1)){
					dt->TopItem=(dt->ItemIndex+vc<dt->Rows)?dt->ItemIndex:dt->Rows-vc;
					topChange=1;
				}
				if(oidx!=dt->ItemIndex){
					if(!topChange){
						RECT r1,r2;
						SendMessage(hwnd,LVM_GETITEMRECT,oidx,(UINT)&r1);
						SendMessage(hwnd,LVM_GETITEMRECT,dt->ItemIndex,(UINT)&r2);
						InvalidateRect(hwnd,&r1,FALSE);InvalidateRect(hwnd,&r2,FALSE);
					}else{
						InvalidateRect(hwnd,NULL,FALSE);
					}
					//NotifyParent(hwnd,ctrl->wID,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->Rows-GetCountByHeight(hwnd,dt->Rows-1,-1);
				if(dt->TopItem>=ct)
					dt->TopItem=ct;
				dt->ItemIndex+=GetCountByHeight(hwnd,dt->ItemIndex+1,1);
				if(oidx!=dt->ItemIndex){
					//NotifyParent(hwnd,ctrl->wID,LBN_SELCHANGE);
					InvalidateRect(hwnd,NULL,FALSE);
					SetScrollPos(hwnd,SB_VERT,dt->ItemIndex,TRUE);
				}
			}break;
		case VK_RETURN:NotifyParent(hwnd,GetDlgCtrlID(hwnd),LBN_RETURN);break;
		}break;
    case LVM_FINDITEMA:
        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 LVM_INSERTITEMA:	lvInsertItem(hwnd,(LVITEM*)lParam);		break;
	case LVM_SETITEMTEXTA:	lvSetItemText(hwnd,wParam,(LVITEM*)lParam);		break;
	case LVM_INSERTCOLUMNA:	
		lvInsertColumn(hwnd,wParam,(LVCOLUMN*)lParam);	break;
	case LVM_DELETEITEM:
		lvDeleteItem(hwnd,wParam,1);
		SetScrollRange(hwnd,SB_VERT,0,dt->Rows,TRUE);
		return dt->Rows;//返回余下的数量
	case LVM_DELETEALLITEMS:
		lvDeleteAllItems(hwnd);
		SetScrollRange(hwnd,SB_VERT,0,0,TRUE);
		break;
	case LVM_SETCOLUMNWIDTH:
		dt->ColumnWidth=wParam;break;
	case LVM_GETSELECTEDCOUNT:return 0;
	case WM_DESTROY:
		//SendMessage(dt->hwndHeader,WM_DESTROY,0,0);
		lvDestroy(hwnd);
		//DefWindowProc(hwnd,msgID,wParam,lParam);
		break;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc=BeginPaint(hwnd,&ps);
			DrawListView(hwnd,hdc);
			EndPaint(hwnd,&ps);
		}break;
	case LB_GETITEMHEIGHT:{
			//const char*txt=lvGetItemText(hwnd,wParam);
			//如果这里调用GetTextExtentPoint会导致绘图速度超级慢
			return 24;//GetTextExtentPoint(GetDC(hwnd),txt,-1).cy;//<<16;
		}break;
	case LVM_GETITEMRECT:lvGetItemRect(hwnd,(int)wParam,(RECT*)lParam);break;
	case LVM_GETITEMTEXTA:
		{
			INT idx=(INT)wParam;
			LVITEM*lv=(LVITEM*)lParam;
			const char*txt=lvGetItemText(hwnd,idx,lv);
			if(lv){
				if(lv->pszText)strcpy(lv->pszText,txt);
				return strlen(txt);
			}
		}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->Rows)||(idx<0)||(dt->ItemIndex==idx))
				return 0;
			GetClientRect(hwnd,&rcw);
			SendMessage(hwnd,LVM_GETITEMRECT,dt->ItemIndex,(LPARAM)&rco);
			SendMessage(hwnd,LVM_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->Rows)
				(dt->Items+idx)->Data=lParam;
		}break;
	case LB_GETITEMDATA:
		{
			INT idx=(INT)wParam;
			if(idx>=0&&idx<dt->Rows)
				return (dt->Items+idx)->Data;
		}return (UINT)-1;
	case LVM_GETTOPINDEX:	return dt->TopItem;
	case LVM_GETITEMCOUNT:	return dt->Rows;
	default:DefWindowProc(hwnd,msgID,wParam,lParam);break;
	}
	return 0;
}
int RegisterListView(void)
{
	WNDCLASS ci;
	ci.lpszClassName="ListView";
	ci.style=0;	
	ci.hIcon=0;
	ci.hCursor=0;
	ci.cbWndExtra=0;
	//ci.ExStyle=0;
	ci.cbWndExtra=sizeof(LISTVIEWDATA*);
	ci.lpfnWndProc=ListViewProc;
	ci.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
	return RegisterClass(&ci);
}             

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -