📄 osdlistview.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 + -