📄 mulelistctrl.cpp
字号:
HINSTANCE hInstRes = AfxFindResourceHandle(MAKEINTRESOURCE(m_atSortArrow), RT_BITMAP);
if (hInstRes != NULL){
HBITMAP hbmSortStates = (HBITMAP)::LoadImage(hInstRes, MAKEINTRESOURCE(m_atSortArrow), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
if (hbmSortStates != NULL){
CBitmap bmSortStates;
bmSortStates.Attach(hbmSortStates);
CImageList imlSortStates;
if (imlSortStates.Create(14, 14, theApp.m_iDfltImageListColorFlags | ILC_MASK, 1, 0)){
VERIFY( imlSortStates.Add(&bmSortStates, RGB(255, 0, 255)) != -1 );
// To avoid drawing problems (which occure only with an image list *with* a mask) while
// resizing list view columns which have the header control bitmap right aligned, set
// the background color of the image list.
if (theApp.m_ullComCtrlVer < MAKEDLLVERULL(6,0,0,0))
imlSortStates.SetBkColor(GetSysColor(COLOR_BTNFACE));
// When setting the image list for the header control for the first time we'll get
// the image list of the listview control!! So, better store the header control imagelist separate.
(void)pHeaderCtrl->SetImageList(&imlSortStates);
m_imlHeaderCtrl.DeleteImageList();
m_imlHeaderCtrl.Attach(imlSortStates.Detach());
// Use smaller bitmap margins -- this saves some pixels which may be required for
// rather small column titles.
if (theApp.m_ullComCtrlVer >= MAKEDLLVERULL(5,8,0,0)){
int iBmpMargin = pHeaderCtrl->SendMessage(HDM_GETBITMAPMARGIN);
int iNewBmpMargin = GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXEDGE)/2;
if (iNewBmpMargin < iBmpMargin)
pHeaderCtrl->SendMessage(HDM_SETBITMAPMARGIN, iNewBmpMargin);
}
}
}
}
headerItem.mask |= HDI_IMAGE;
headerItem.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
headerItem.iImage = 0;
pHeaderCtrl->SetItem(iColumn, &headerItem);
}
}
int CMuleListCtrl::MoveItem(int iOldIndex, int iNewIndex) { //move item in list, returns index of new item
if(iNewIndex > iOldIndex)
iNewIndex--;
//save substrings
CSimpleArray<void*> aSubItems;
DWORD Style = GetStyle();
if((Style & LVS_OWNERDATA) == 0) {
TCHAR szText[256];
LVITEM lvi;
lvi.mask = LVIF_TEXT | LVIF_NORECOMPUTE;
lvi.iItem = iOldIndex;
for(int i = 1; i < m_iColumnsTracked; i++){
lvi.iSubItem = i;
lvi.cchTextMax = ARRSIZE(szText);
lvi.pszText = szText;
void* pstrSubItem = NULL;
if (GetItem(&lvi)){
if (lvi.pszText == LPSTR_TEXTCALLBACK)
pstrSubItem = LPSTR_TEXTCALLBACK;
else
pstrSubItem = new CString(lvi.pszText);
}
aSubItems.Add(pstrSubItem);
}
}
//copy item
LVITEM lvi;
TCHAR szText[256];
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM | LVIF_INDENT | LVIF_IMAGE | LVIF_NORECOMPUTE;
lvi.stateMask = (UINT)-1;
lvi.iItem = iOldIndex;
lvi.iSubItem = 0;
lvi.pszText = szText;
lvi.cchTextMax = sizeof(szText) / sizeof(szText[0]);
lvi.iIndent = 0;
if(GetItem(&lvi) == 0)
return -1;
lvi.iItem = iNewIndex;
//do the move
SetRedraw(FALSE);
//SetItemData(iOldIndex, 0); //should do this to be safe?
DeleteItem(iOldIndex);
iNewIndex = InsertItem(&lvi);
SetRedraw(TRUE);
//restore substrings
if((Style & LVS_OWNERDATA) == 0) {
for(int i = 1; i < m_iColumnsTracked; i++) {
LVITEM lvi;
lvi.iSubItem = i;
void* pstrSubItem = aSubItems[i-1];
if (pstrSubItem != NULL){
if (pstrSubItem == LPSTR_TEXTCALLBACK)
lvi.pszText = LPSTR_TEXTCALLBACK;
else
lvi.pszText = (LPTSTR)((LPCTSTR)*((CString*)pstrSubItem));
DefWindowProc(LVM_SETITEMTEXT, iNewIndex, (LPARAM)&lvi);
if (pstrSubItem != LPSTR_TEXTCALLBACK)
delete (CString*)pstrSubItem;
}
}
}
return iNewIndex;
}
int CMuleListCtrl::UpdateLocation(int iItem) {
int iItemCount = GetItemCount();
if(iItem >= iItemCount || iItem < 0)
return iItem;
BOOL notLast = iItem + 1 < iItemCount;
BOOL notFirst = iItem > 0;
DWORD_PTR dwpItemData = GetItemData(iItem);
if(dwpItemData == NULL)
return iItem;
if(notFirst) {
int iNewIndex = iItem - 1;
POSITION pos = m_Params.FindIndex(iNewIndex);
int iResult = m_SortProc(dwpItemData, GetParamAt(pos, iNewIndex), m_dwParamSort);
if(iResult < 0) {
POSITION posPrev = pos;
int iDist = iNewIndex / 2;
while(iDist > 1) {
for(int i = 0; i < iDist; i++)
m_Params.GetPrev(posPrev);
if(m_SortProc(dwpItemData, GetParamAt(posPrev, iNewIndex - iDist), m_dwParamSort) < 0) {
iNewIndex = iNewIndex - iDist;
pos = posPrev;
} else {
posPrev = pos;
}
iDist /= 2;
}
while(--iNewIndex >= 0) {
m_Params.GetPrev(pos);
if(m_SortProc(dwpItemData, GetParamAt(pos, iNewIndex), m_dwParamSort) >= 0)
break;
}
MoveItem(iItem, iNewIndex + 1);
return iNewIndex + 1;
}
}
if(notLast) {
int iNewIndex = iItem + 1;
POSITION pos = m_Params.FindIndex(iNewIndex);
int iResult = m_SortProc(dwpItemData, GetParamAt(pos, iNewIndex), m_dwParamSort);
if(iResult > 0) {
POSITION posNext = pos;
int iDist = (GetItemCount() - iNewIndex) / 2;
while(iDist > 1) {
for(int i = 0; i < iDist; i++)
m_Params.GetNext(posNext);
if(m_SortProc(dwpItemData, GetParamAt(posNext, iNewIndex + iDist), m_dwParamSort) > 0) {
iNewIndex = iNewIndex + iDist;
pos = posNext;
} else {
posNext = pos;
}
iDist /= 2;
}
while(++iNewIndex < iItemCount) {
m_Params.GetNext(pos);
if(m_SortProc(dwpItemData, GetParamAt(pos, iNewIndex), m_dwParamSort) <= 0)
break;
}
MoveItem(iItem, iNewIndex);
return iNewIndex;
}
}
return iItem;
}
DWORD_PTR CMuleListCtrl::GetItemData(int iItem) {
POSITION pos = m_Params.FindIndex(iItem);
LPARAM lParam = GetParamAt(pos, iItem);
MLC_ASSERT(lParam == CListCtrl::GetItemData(iItem));
return lParam;
}
//lower level than everything else so poorly overriden functions don't break us
BOOL CMuleListCtrl::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) {
//lets look for the important messages that are essential to handle
switch(message) {
case WM_NOTIFY:
if(wParam == 0) {
if(((NMHDR*)lParam)->code == NM_RCLICK) {
//handle right click on headers and show column menu
POINT point;
GetCursorPos (&point);
CTitleMenu tmColumnMenu;
tmColumnMenu.CreatePopupMenu();
if(m_Name.GetLength() != 0)
tmColumnMenu.AddMenuTitle(m_Name);
CHeaderCtrl *pHeaderCtrl = GetHeaderCtrl();
int iCount = pHeaderCtrl->GetItemCount();
for(int iCurrent = 1; iCurrent < iCount; iCurrent++) {
HDITEM item;
TCHAR text[255];
item.pszText = text;
item.mask = HDI_TEXT;
item.cchTextMax = ARRSIZE(text);
pHeaderCtrl->GetItem(iCurrent, &item);
tmColumnMenu.AppendMenu(MF_STRING | m_aColumns[iCurrent].bHidden ? 0 : MF_CHECKED,
MLC_IDC_MENU + iCurrent, item.pszText);
}
tmColumnMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
VERIFY( tmColumnMenu.DestroyMenu() );
return *pResult = TRUE;
} else if(((NMHDR*)lParam)->code == HDN_BEGINTRACKA || ((NMHDR*)lParam)->code == HDN_BEGINTRACKW) {
//stop them from changeing the size of anything "before" first column
HD_NOTIFY *pHDN = (HD_NOTIFY*)lParam;
if(m_aColumns[pHDN->iItem].bHidden)
return *pResult = TRUE;
} else if(((NMHDR*)lParam)->code == HDN_ENDDRAG) {
//stop them from moving first column
NMHEADER *pHeader = (NMHEADER*)lParam;
if(pHeader->iItem != 0 && pHeader->pitem->iOrder != 0) {
int iNewLoc = pHeader->pitem->iOrder - GetHiddenColumnCount();
if(iNewLoc > 0) {
if(m_aColumns[pHeader->iItem].iLocation != iNewLoc) {
if(m_aColumns[pHeader->iItem].iLocation > iNewLoc) {
int iMax = m_aColumns[pHeader->iItem].iLocation;
int iMin = iNewLoc;
for(int i = 0; i < m_iColumnsTracked; i++) {
if(m_aColumns[i].iLocation >= iMin && m_aColumns[i].iLocation < iMax)
m_aColumns[i].iLocation++;
}
}
else if(m_aColumns[pHeader->iItem].iLocation < iNewLoc) {
int iMin = m_aColumns[pHeader->iItem].iLocation;
int iMax = iNewLoc;
for(int i = 0; i < m_iColumnsTracked; i++) {
if(m_aColumns[i].iLocation > iMin && m_aColumns[i].iLocation <= iMax)
m_aColumns[i].iLocation--;
}
}
m_aColumns[pHeader->iItem].iLocation = iNewLoc;
Invalidate(FALSE);
break;
}
}
}
return *pResult = 1;
} else if(((NMHDR*)lParam)->code == HDN_DIVIDERDBLCLICKA || ((NMHDR*)lParam)->code == HDN_DIVIDERDBLCLICKW) {
if (GetStyle() & LVS_OWNERDRAWFIXED) {
NMHEADER *pHeader = (NMHEADER*)lParam;
// As long as we do not handle the HDN_DIVIDERDBLCLICK according the actual
// listview item contents it's better to resize to the header width instead of
// resizing to zero width. The complete solution for this would require a lot
// of rewriting in the owner drawn listview controls...
SetColumnWidth(pHeader->iItem, LVSCW_AUTOSIZE_USEHEADER);
return *pResult = 1;
}
}
}
break;
case WM_COMMAND:
//deal with menu clicks
if(wParam == MLC_IDC_UPDATE) {
UpdateLocation(lParam);
return *pResult = 1;
} else if(wParam >= MLC_IDC_MENU) {
CHeaderCtrl *pHeaderCtrl = GetHeaderCtrl();
int iCount = pHeaderCtrl->GetItemCount();
int iToggle = wParam - MLC_IDC_MENU;
if(iToggle >= iCount)
break;
if(m_aColumns[iToggle].bHidden)
ShowColumn(iToggle);
else
HideColumn(iToggle);
return *pResult = 1;
}
break;
case LVM_DELETECOLUMN:
//book keeping!
if(m_aColumns != NULL) {
for(int i = 0; i < m_iColumnsTracked; i++)
if(m_aColumns[i].bHidden)
ShowColumn(i);
delete[] m_aColumns;
m_aColumns = NULL; // 'new' may throw an exception
}
m_aColumns = new MULE_COLUMN[--m_iColumnsTracked];
for(int i = 0; i < m_iColumnsTracked; i++) {
m_aColumns[i].iLocation = i;
m_aColumns[i].bHidden = false;
}
break;
//case LVM_INSERTCOLUMN:
case LVM_INSERTCOLUMNA:
case LVM_INSERTCOLUMNW:
//book keeping!
if(m_aColumns != NULL) {
for(int i = 0; i < m_iColumnsTracked; i++)
if(m_aColumns[i].bHidden)
ShowColumn(i);
delete[] m_aColumns;
m_aColumns = NULL; // 'new' may throw an exception
}
m_aColumns = new MULE_COLUMN[++m_iColumnsTracked];
for(int i = 0; i < m_iColumnsTracked; i++) {
m_aColumns[i].iLocation = i;
m_aColumns[i].bHidden = false;
}
break;
case LVM_SETITEM:
//book keeping
{
POSITION pos = m_Params.FindIndex(((LPLVITEM)lParam)->iItem);
if(pos) {
m_Params.SetAt(pos, MLC_MAGIC);
PostMessage(LVM_UPDATE, ((LPLVITEM)lParam)->iItem);
}
}
break;
case LVN_KEYDOWN:{
break;}
case LVM_SETITEMTEXT:
//need to check for movement
*pResult = DefWindowProc(message, wParam, lParam);
if(*pResult)
PostMessage(WM_COMMAND, MLC_IDC_UPDATE, wParam);
return *pResult;
case LVM_SORTITEMS:
//book keeping...
m_dwParamSort = (LPARAM)wParam;
m_SortProc = (PFNLVCOMPARE)lParam;
for(POSITION pos = m_Params.GetHeadPosition(); pos != NULL; m_Params.GetNext(pos))
m_Params.SetAt(pos, MLC_MAGIC);
break;
case LVM_DELETEALLITEMS:
//book keeping...
if(!CListCtrl::OnWndMsg(message, wParam, lParam, pResult) && DefWindowProc(message, wParam, lParam))
m_Params.RemoveAll();
return *pResult = TRUE;
case LVM_DELETEITEM:
//book keeping.....
MLC_ASSERT(m_Params.GetAt(m_Params.FindIndex(wParam)) == CListCtrl::GetItemData(wParam));
if(!CListCtrl::OnWndMsg(message, wParam, lParam, pResult) && DefWindowProc(message, wParam, lParam))
m_Params.RemoveAt(m_Params.FindIndex(wParam));
return *pResult = TRUE;
//case LVM_INSERTITEM:
case LVM_INSERTITEMA:
case LVM_INSERTITEMW:
//try to fix position of inserted items
{
LPLVITEM pItem = (LPLVITEM)lParam;
int iItem = pItem->iItem;
int iItemCount = GetItemCount();
BOOL notLast = iItem < iItemCount;
BOOL notFirst = iItem > 0;
if(notFirst) {
int iNewIndex = iItem - 1;
POSITION pos = m_Params.FindIndex(iNewIndex);
int iResult = m_SortProc(pItem->lParam, GetParamAt(pos, iNewIndex), m_dwParamSort);
if(iResult < 0) {
POSITION posPrev = pos;
int iDist = iNewIndex / 2;
while(iDist > 1) {
for(int i = 0; i < iDist; i++)
m_Params.GetPrev(posPrev);
if(m_SortProc(pItem->lParam, GetParamAt(posPrev, iNewIndex - iDist), m_dwParamSort) < 0) {
iNewIndex = iNewIndex - iDist;
pos = posPrev;
} else {
posPrev = pos;
}
iDist /= 2;
}
while(--iNewIndex >= 0) {
m_Params.GetPrev(pos);
if(m_SortProc(pItem->lParam, GetParamAt(pos, iNewIndex), m_dwParamSort) >= 0)
break;
}
pItem->iItem = iNewIndex + 1;
notLast = false;
}
}
if(notLast) {
int iNewIndex = iItem;
POSITION pos = m_Params.FindIndex(iNewIndex);
int iResult = m_SortProc(pItem->lParam, GetParamAt(pos, iNewIndex), m_dwParamSort);
if(iResult > 0) {
POSITION posNext = pos;
int iDist = (GetItemCount() - iNewIndex) / 2;
while(iDist > 1) {
for(int i = 0; i < iDist; i++)
m_Params.GetNext(posNext);
if(m_SortProc(pItem->lParam, GetParamAt(posNext, iNewIndex + iDist), m_dwParamSort) > 0) {
iNewIndex = iNewIndex + iDist;
pos = posNext;
} else {
posNext = pos;
}
iDist /= 2;
}
while(++iNewIndex < iItemCount) {
m_Params.GetNext(pos);
if(m_SortProc(pItem->lParam, GetParamAt(pos, iNewIndex), m_dwParamSort) <= 0)
break;
}
pItem->iItem = iNewIndex;
}
}
if(pItem->iItem == 0) {
m_Params.AddHead(pItem->lParam);
return FALSE;
}
LRESULT lResult = DefWindowProc(message, wParam, lParam);
if(lResult != -1) {
if(lResult >= GetItemCount())
m_Params.AddTail(pItem->lParam);
else if(lResult == 0)
m_Params.AddHead(pItem->lParam);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -