📄 xlistbox.cpp
字号:
for (i=0;i<x->num_items;++i) {
free(x->items[i].text1);
free(x->items[i].text2);
}
free(x->items);
free(x->vitems);
free(x);
break;
case WM_PAINT: {
PAINTSTRUCT ps;
hDC=::BeginPaint(hWnd,&ps);
XLB_PaintAll(x,hDC);
::EndPaint(hWnd,&ps);
break; }
case WM_VSCROLL:
i=-1;
j=x->visible_items*x->item_height-x->scroll_page;
switch (LOWORD(wParam)) {
case SB_BOTTOM:
i=j;
break;
case SB_LINEDOWN:
i=min(x->top_offset+x->item_height,j);
break;
case SB_LINEUP:
i=max(x->top_offset-x->item_height,0);
break;
case SB_PAGEDOWN:
i=min(x->top_offset+x->scroll_page,j);
break;
case SB_PAGEUP:
i=max(x->top_offset-x->scroll_page,0);
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK: {
SCROLLINFO si;
si.cbSize=sizeof(si);
si.fMask=SIF_TRACKPOS;
::GetScrollInfo(hWnd,SB_VERT,&si);
i=min(si.nTrackPos,j);
break; }
case SB_TOP:
i=0;
break;
}
if (i>=0 && i<=j && i!=x->top_offset)
XLB_ScrollTo(x,i);
break;
case WM_KEYDOWN:
i=-1;
j=XLB_GetVFromI(x,x->selection);
switch (wParam) {
case VK_RIGHT: case VK_ADD:
// expand item
if (x->flags & XLF_TREE && x->selection>=0 &&
x->items[x->selection].flags & XIF_COLLAPSED &&
XLB_HasChildren(x,x->selection))
{
x->items[x->selection].flags &= ~XIF_COLLAPSED;
XLB_UpdateVisibleItems(x);
XLB_UpdateScrollbar(x);
::InvalidateRect(x->hWnd,NULL,FALSE);
}
return 0;
case VK_LEFT: case VK_SUBTRACT:
// collapse item
if (x->flags & XLF_TREE && x->selection>=0 &&
!(x->items[x->selection].flags & XIF_COLLAPSED) &&
XLB_HasChildren(x,x->selection))
{
x->items[x->selection].flags |= XIF_COLLAPSED;
XLB_UpdateVisibleItems(x);
XLB_UpdateScrollbar(x);
::InvalidateRect(x->hWnd,NULL,FALSE);
}
return 0;
case VK_UP:
i=j-1;
break;
case VK_DOWN:
i=j+1;
break;
case VK_PRIOR:
i=max(j-(x->scroll_page+x->item_height-1)/x->item_height,0);
break;
case VK_NEXT:
i=min(j+(x->scroll_page+x->item_height-1)/x->item_height,x->visible_items-1);
break;
case VK_HOME:
i=0;
break;
case VK_END:
i=x->visible_items-1;
break;
}
if (i>=0 && i<x->visible_items && i!=j) {
j=i*x->item_height-x->top_offset;
// if the item is not fully visible, then
// scroll and repaint completely
if (j<0 || j+x->item_height>x->scroll_page) {
x->selection=XLB_GetIFromV(x,i);
XLB_EnsureVisible2(x,x->selection);
::InvalidateRect(x->hWnd,NULL,FALSE);
} else
XLB_SetSelection(hWnd,XLB_GetIFromV(x,i));
}
break;
case WM_LBUTTONDOWN:
i=(HIWORD(lParam)+x->top_offset)/x->item_height;
if (i>=0 && i<x->visible_items) {
j=XLB_GetIFromV(x,i);
XLB_SetSelection(hWnd,j);
// if we are in tree mode and hit a control icon,
// then collapse/uncollapse an item
if (x->flags & XLF_TREE && x->tree_icons) {
int iw,ih;
::ImageList_GetIconSize(x->tree_icons,&iw,&ih);
if (LOWORD(lParam) < x->items[j].level*INDENT_PER_LEVEL + iw &&
XLB_HasChildren(x,j))
{
x->items[j].flags ^= XIF_COLLAPSED;
XLB_UpdateVisibleItems(x);
XLB_UpdateScrollbar(x);
::InvalidateRect(x->hWnd,NULL,FALSE);
}
}
#if POCKETPC
SHRGINFO shrg;
memset(&shrg,0,sizeof(shrg));
shrg.cbSize=sizeof(shrg);
shrg.hwndClient=hWnd;
shrg.ptDown.x=LOWORD(lParam);
shrg.ptDown.y=HIWORD(lParam);
shrg.dwFlags=SHRG_RETURNCMD;
if (SHRecognizeGesture(&shrg)==GN_CONTEXTMENU)
::SendMessage(::GetParent(hWnd),XLM_CONTEXTMENU,lParam,(LPARAM)hWnd);
#endif
}
break;
case WM_LBUTTONUP:
i=(HIWORD(lParam)+x->top_offset)/x->item_height;
if (i>=0 && i<x->visible_items && x->selection>=0)
::SendMessage(::GetParent(hWnd),XLM_CLICK,lParam,(LPARAM)hWnd);
break;
case WM_RBUTTONDOWN:
i=(HIWORD(lParam)+x->top_offset)/x->item_height;
if (i>=0 && i<x->visible_items) {
XLB_SetSelection(hWnd,XLB_GetIFromV(x,i));
::SendMessage(::GetParent(hWnd),XLM_CONTEXTMENU,lParam,(LPARAM)hWnd);
}
break;
case WM_LBUTTONDBLCLK:
i=(HIWORD(lParam)+x->top_offset)/x->item_height;
if (i>=0 && i<x->visible_items)
::SendMessage(::GetParent(hWnd),XLM_DBLCLK,lParam,(LPARAM)hWnd);
break;
case WM_GETDLGCODE:
return DLGC_WANTARROWS | DLGC_WANTCHARS;
#if defined(WM_MOUSEWHEEL) && defined(SPI_GETWHEELSCROLLLINES) && defined(WHEEL_DELTA)
case WM_MOUSEWHEEL: {
UINT lines=3;
::SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,&lines,0);
int scrl=(short)HIWORD(wParam)*(int)lines*x->item_height;
scrl/=WHEEL_DELTA;
j=x->visible_items*x->item_height-x->scroll_page;
i=max(min(x->top_offset-scrl,j),0);
if (i!=x->top_offset)
XLB_ScrollTo(x,i);
break; }
#endif
default:
return ::DefWindowProc(hWnd,uMsg,wParam,lParam);
}
return 0;
}
void XLB_SetImageList(HWND hWnd,HIMAGELIST hIml,bool shared) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x)
return;
if (x->icons && !x->icons_shared)
::ImageList_Destroy(x->icons);
x->icons=hIml;
x->icons_shared=shared;
if (hIml) {
int dummy;
::ImageList_GetIconSize(hIml,&x->uicon_w,&dummy);
x->uicon_w+=UICON_PAD*2;
} else
x->uicon_w=0;
}
void XLB_UpdateScrollbar(XLB *x) {
RECT rc;
::GetClientRect(x->hWnd,&rc);
x->scroll_page=rc.bottom-rc.top;
SCROLLINFO si;
memset(&si,0,sizeof(si));
si.cbSize=sizeof(si);
si.fMask=SIF_PAGE|SIF_RANGE;
si.nMin=0;
si.nMax=x->visible_items*x->item_height-1;
if (si.nMax<x->scroll_page) {
si.nMax=0;
si.nPage=0;
} else
si.nPage=x->scroll_page;
::SetScrollInfo(x->hWnd,SB_VERT,&si,TRUE);
}
struct XLB_Handle *XLB_GetHandle(HWND hWnd) {
return (XLB_Handle *)::GetWindowLong(hWnd,0);
}
bool XLB_AppendItem(struct XLB_Handle *handle,
const TCHAR *text1,const TCHAR *text2,
int icon,int level,
LONG user_data)
{
XLB *x=(XLB*)handle;
if (!x)
return false;
if (x->num_items >= x->max_items && !XLB_GrowList(x))
return false;
XLBItem *ii=&x->items[x->num_items];
ii->text1=ii->text2=NULL;
if (text1) {
ii->text1=_tcsdup(text1);
if (ii->text1==NULL)
return false;
}
if (text2) {
ii->text2=_tcsdup(text2);
if (ii->text2==NULL) {
free(ii->text1);
return false;
}
}
ii->icon_index=icon;
ii->level=level;
ii->user_data=user_data;
ii->flags=0;
x->num_items++;
return true;
}
int XLB_GetSelection(HWND hWnd) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x)
return -1;
return x->selection;
}
static void XLB_GetItemRect(XLB *x,int item,RECT& r) {
memset(&r,0,sizeof(r));
if (item<0 || item>=x->num_items)
return;
item=XLB_GetVFromI(x,item);
if (item<0)
return;
::GetClientRect(x->hWnd,&r);
r.top+=x->item_height*item;
r.top-=x->top_offset;
r.bottom=r.top+x->item_height;
}
static void XLB_InvalidateItem(XLB *x,int item) {
RECT ri;
XLB_GetItemRect(x,item,ri);
::InvalidateRect(x->hWnd,&ri,FALSE);
}
void XLB_SetSelection(HWND hWnd,int sel) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x || sel<0 || sel>=x->num_items || sel==x->selection)
return;
if (XLB_GetVFromI(x,sel)<0) {
XLB_Restore(x,sel);
XLB_UpdateScrollbar(x);
}
if (x->selection!=-1)
XLB_InvalidateItem(x,x->selection);
if (sel!=-1)
XLB_InvalidateItem(x,sel);
x->selection=sel;
}
int XLB_GetItemCount(HWND hWnd) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x)
return 0;
return x->num_items;
}
LONG XLB_GetData(HWND hWnd,int item) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x || item<0 || item>=x->num_items)
return 0;
return x->items[item].user_data;
}
void XLB_EnsureVisible(HWND hWnd,int item,bool middle) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x || item<0 || item>=x->num_items)
return;
int v=XLB_GetVFromI(x,item);
if (v<0) { // item is not currently visble
XLB_Restore(x,item);
XLB_UpdateScrollbar(x);
v=XLB_GetVFromI(x,item);
}
int cvis=x->scroll_page/x->item_height; // completely visible items
if (cvis==0)
cvis=1;
if (middle) { // scroll the view so the item is in the middle
int new_offset=(v-cvis/2)*x->item_height;
if (new_offset+x->scroll_page > x->visible_items*x->item_height)
new_offset=x->visible_items*x->item_height - x->scroll_page;
if (new_offset<0)
new_offset=0;
if (x->top_offset != new_offset)
XLB_ScrollTo(x,new_offset);
return;
}
int item_offset=v*x->item_height-x->top_offset;
// if the item is not fully visible, then
// scroll and repaint completely
if (item_offset<0)
XLB_ScrollTo(x,v*x->item_height);
else if (item_offset+x->item_height>x->scroll_page)
XLB_ScrollTo(x,(v-cvis+1)*x->item_height);
}
// TODO: avoid unneeded redraws
void XLB_DeleteItem(HWND hWnd,int item) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x || item<0 || item>=x->num_items)
return;
free(x->items[item].text1);
free(x->items[item].text2);
memmove(&x->items[item],&x->items[item+1],sizeof(XLBItem)*(x->num_items-item-1));
x->num_items--;
if (x->selection>=x->num_items)
x->selection=x->num_items-1;
XLB_UpdateVisibleItems(x);
XLB_EnsureVisible(hWnd,x->selection);
::InvalidateRect(hWnd,NULL,FALSE);
XLB_UpdateScrollbar(x);
}
void XLB_SetItemText1(HWND hWnd,int item,const TCHAR *text) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x || item<0 || item>=x->num_items)
return;
free(x->items[item].text1);
x->items[item].text1=_tcsdup(text);
XLB_InvalidateItem(x,item);
}
void XLB_DeleteAllItems(HWND hWnd) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x)
return;
for (int i=0;i<x->num_items;++i) {
free(x->items[i].text1);
free(x->items[i].text2);
}
x->num_items=0;
x->visible_items=0;
x->selection=-1;
x->top_offset=0;
::SetScrollPos(hWnd,SB_VERT,0,TRUE);
XLB_UpdateScrollbar(x);
}
void XLB_UpdateState(HWND hWnd) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x)
return;
XLB_UpdateVisibleItems(x);
XLB_UpdateScrollbar(x);
}
void XLB_CollapseLevel(HWND hWnd,int level) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x)
return;
if (level<0)
level = 0x7fffffff; // XXX should be maxint
XLBItem *ii=x->items;
XLBItem *jj=ii+x->num_items;
while (ii<jj) {
if (ii->level >= level)
ii->flags |= XIF_COLLAPSED;
else
ii->flags &= ~XIF_COLLAPSED;
++ii;
}
if (x->top_offset >= x->visible_items)
x->top_offset = 0; // XXX can do something better
XLB_UpdateState(hWnd);
::InvalidateRect(hWnd,NULL,FALSE); // XXX can be avoided sometimes
}
// custom sort function
static int compare_items(const void *v1,const void *v2) {
const XLBItem *i1=(XLBItem*)v1;
const XLBItem *i2=(XLBItem*)v2;
int i=i1->user_data-i2->user_data;
if (i!=0)
return i;
bool e1=!i1->text1 || !*i1->text1;
bool e2=!i2->text1 || !*i2->text1;
if (e1)
return e2 ? 0 : -1;
if (e2)
return 1;
return CmpI(i1->text1,i2->text1);
}
void XLB_SortItems(HWND hWnd) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x)
return;
qsort(x->items,x->num_items,sizeof(XLBItem),compare_items);
}
const TCHAR *XLB_GetItemText1(HWND hWnd,int item) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x || item<0 || item>=x->num_items)
return NULL;
return x->items[item].text1;
}
void XLB_SetGTFunc(HWND hWnd,XLB_GetText fn,void *ugtdata) {
XLB *x=(XLB*)::GetWindowLong(hWnd,0);
if (!x)
return;
x->gtf=fn;
x->gtdata=ugtdata;
}
void XLB_Init() {
WNDCLASS wc;
memset(&wc,0,sizeof(wc));
wc.style=CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc=XLB_WndProc;
wc.cbWndExtra=sizeof(XLB*);
wc.hInstance=::GetModuleHandle(NULL);
// assume the SDK always defines this via macro
#if defined(LoadCursor)
wc.hCursor=::LoadCursor(NULL,IDC_ARROW);
#endif
wc.lpszClassName=_T("XListBox");
ATOM cls=::RegisterClass(&wc);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -