📄 mulelistctrl.cpp
字号:
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);
else
m_Params.InsertAfter(m_Params.FindIndex(lResult - 1), pItem->lParam);
}
return *pResult = lResult;
}
break;
case LVM_UPDATE:
//better fix for old problem... normally Update(int) causes entire list to redraw
if(wParam == UpdateLocation(wParam)) { //no need to invalidate rect if item moved
RECT rcItem;
BOOL bResult = GetItemRect(wParam, &rcItem, LVIR_BOUNDS);
if(bResult)
InvalidateRect(&rcItem, FALSE);
return *pResult = bResult;
}
return *pResult = TRUE;
}
return CListCtrl::OnWndMsg(message, wParam, lParam, pResult);
}
void CMuleListCtrl::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)
{
if (nChar == 'A' && ::GetAsyncKeyState(VK_CONTROL)<0)
{
// Ctrl+A: Select all items
LV_ITEM theItem;
theItem.mask= LVIF_STATE;
theItem.iItem= -1;
theItem.iSubItem= 0;
theItem.state= LVIS_SELECTED;
theItem.stateMask= 2;
SetItemState(-1, &theItem);
}
else if (nChar==VK_DELETE)
PostMessage(WM_COMMAND, MPG_DELETE, 0);
else if (nChar==VK_F2)
PostMessage(WM_COMMAND, MPG_F2, 0);
else if (nChar == 'C' && (GetKeyState(VK_CONTROL) & 0x8000))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -