// FileZilla Server - a Windows ftp server // Copyright (C) 2002-2004 - Tim Kosse // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // UsersListCtrl.cpp: Implementierungsdatei // #include "stdafx.h" #include "filezilla server.h" #include "UsersListCtrl.h" #include "mainfrm.h" #include "OutputFormat.h" #if defined(_DEBUG) && !defined(MMGR) #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define NUMCOLUMNS 6 #define COLUMN_ID 0 #define COLUMN_USER 1 #define COLUMN_IP 2 #define COLUMN_TRANSFERINIT 3 #define COLUMN_TRANSFERPROGRESS 4 #define COLUMN_TRANSFERSPEED 5 #define SPEED_MEAN_SECONDS 10 class CConnectionData { public: CConnectionData() { for (int i = 1; i < NUMCOLUMNS; i++) itemImages[i] = -1; itemImages[0] = 5; ResetSpeed(); } ~CConnectionData() { } int userid; unsigned int port; unsigned char transferMode; CString physicalFile; CString logicalFile; __int64 totalSize; __int64 currentOffset; unsigned int speed; int listIndex; CString columnText[NUMCOLUMNS]; int itemImages[NUMCOLUMNS]; inline void AddBytes(int bytes) { *current_speed += bytes; UpdateSpeed(); } inline void UpdateSpeed() { speed = 0; int max = speedDidWrap ? SPEED_MEAN_SECONDS : (current_speed - speed_mean + 1); for (int i = 0; i < max; i++) speed += speed_mean[i]; speed /= max; } inline void NextSpeed() { if (!*current_speed) UpdateSpeed(); if ((++current_speed - speed_mean) >= SPEED_MEAN_SECONDS) { speedDidWrap = true; current_speed = speed_mean; } *current_speed = 0; } inline void ResetSpeed() { speedDidWrap = false; current_speed = speed_mean; *current_speed = 0; } private: unsigned int speed_mean[SPEED_MEAN_SECONDS]; unsigned int *current_speed; bool speedDidWrap; }; ///////////////////////////////////////////////////////////////////////////// // CUsersListCtrl CUsersListCtrl::CUsersListCtrl(CMainFrame *pOwner) { ASSERT(pOwner); m_pOwner = pOwner; m_sortColumn = 0; m_sortDir = 0; } CUsersListCtrl::~CUsersListCtrl() { for (std::vector::iterator iter = m_connectionDataArray.begin(); iter != m_connectionDataArray.end(); iter++) delete *iter; } BEGIN_MESSAGE_MAP(CUsersListCtrl, CListCtrl) //{{AFX_MSG_MAP(CUsersListCtrl) ON_WM_CREATE() ON_COMMAND(ID_USERVIEWCONTEXT_KICK, OnContextmenuKick) ON_COMMAND(ID_USERVIEWCONTEXT_BAN, OnContextmenuBan) ON_WM_CONTEXTMENU() ON_WM_SIZE() ON_WM_TIMER() ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetdispinfo) ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Behandlungsroutinen für Nachrichten CUsersListCtrl int CUsersListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CListCtrl::OnCreate(lpCreateStruct) == -1) return -1; m_ImageList.Create(IDB_TRANSFERINFO, 16, 6, RGB(255, 0, 255)); SetImageList(&m_ImageList, LVSIL_SMALL); SetExtendedStyle(LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT); InsertColumn(COLUMN_ID, _T("ID"), LVCFMT_RIGHT, 75); InsertColumn(COLUMN_USER, _T("Account"), LVCFMT_LEFT, 150); InsertColumn(COLUMN_IP, _T("IP"), LVCFMT_RIGHT, 100); InsertColumn(COLUMN_TRANSFERINIT, _T("Transfer"), LVCFMT_LEFT, 250); InsertColumn(COLUMN_TRANSFERPROGRESS, _T("Progress"), LVCFMT_RIGHT, 150); InsertColumn(COLUMN_TRANSFERSPEED, _T("Speed"), LVCFMT_LEFT, 80); m_SortImg.Create( 8, 8, ILC_MASK, 3, 3 ); HICON Icon; Icon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_EMPTY)); m_SortImg.Add(Icon); Icon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_UP)); m_SortImg.Add(Icon); Icon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_DOWN)); m_SortImg.Add(Icon); m_SortImg.SetBkColor(CLR_NONE); CHeaderCtrl *header = GetHeaderCtrl( ); if (header) header->SetImageList(&m_SortImg); m_nSpeedinfoTimer = SetTimer(232, 1000, 0); return 0; } bool CUsersListCtrl::ProcessConnOp(unsigned char *pData, DWORD dwDataLength) { int op = pData[1]; if (op < 0 || op > 4) return FALSE; if (dwDataLength < 6) return FALSE; if (op == USERCONTROL_CONNOP_ADD) { int userid; memcpy(&userid, pData + 2, 4); CConnectionData* pConnectionData = new CConnectionData; pConnectionData->currentOffset = 0; pConnectionData->totalSize = -1; pConnectionData->userid = userid; unsigned int pos = 6; if (dwDataLength < 8) { delete pConnectionData; return FALSE; } unsigned int len = pData[pos] * 256 + pData[pos+1]; pos += 2; if (pos+len > dwDataLength) { delete pConnectionData; return FALSE; } char* ip = new char[len + 1]; memcpy(ip, pData + pos, len); ip[len] = 0; pos += len; #ifdef _UNICODE pConnectionData->columnText[COLUMN_IP] = ConvFromNetwork(ip); #else pConnectionData->columnText[COLUMN_IP] = ConvToLocal(ConvFromNetwork(ip)); #endif delete [] ip; if ((pos+4) > dwDataLength) { delete pConnectionData; return FALSE; } memcpy(&pConnectionData->port, pData + pos, 4); pConnectionData->columnText[COLUMN_ID].Format(_T("%06d"), userid); m_connectionDataMap[userid] = pConnectionData; pConnectionData->listIndex = m_connectionDataArray.size(); m_connectionDataArray.push_back(pConnectionData); pConnectionData->columnText[COLUMN_USER] = _T("(not logged in)"); SetItemCount(GetItemCount() + 1); SetSortColumn(m_sortColumn, m_sortDir); if (GetItemCount() == 1) m_pOwner->SetIcon(); } else if (op == USERCONTROL_CONNOP_CHANGEUSER) { int userid; memcpy(&userid, pData + 2, 4); if (dwDataLength < 8) return FALSE; std::map::iterator iter = m_connectionDataMap.find(userid); if (iter == m_connectionDataMap.end()) return FALSE; CConnectionData* pConnectionData = iter->second; unsigned int pos = 6; unsigned int len = pData[pos] * 256 + pData[pos+1]; pos += 2; if ((pos + len) > dwDataLength) return FALSE; char* user = new char[len + 1]; memcpy(user, pData + pos, len); user[len] = 0; pos += len; #ifdef _UNICODE pConnectionData->columnText[COLUMN_USER] = ConvFromNetwork(user); #else pConnectionData->columnText[COLUMN_USER] = ConvToLocal(ConvFromNetwork(user)); #endif delete [] user; if (pConnectionData->columnText[COLUMN_USER] == _T("")) { pConnectionData->itemImages[COLUMN_ID] = 5; pConnectionData->columnText[COLUMN_USER] = _T("(not logged in)"); } else { pConnectionData->itemImages[COLUMN_ID] = 4; } RedrawItems(pConnectionData->listIndex, pConnectionData->listIndex); SetSortColumn(m_sortColumn, m_sortDir); } else if (op == USERCONTROL_CONNOP_REMOVE) { int userid; memcpy(&userid, pData + 2, 4); std::map::iterator iter = m_connectionDataMap.find(userid); if (iter == m_connectionDataMap.end()) return FALSE; CConnectionData *pConnectionData = iter->second; m_connectionDataMap.erase(iter); for (std::vector::iterator iter2 = m_connectionDataArray.begin() + pConnectionData->listIndex + 1; iter2 != m_connectionDataArray.end(); iter2++) (*iter2)->listIndex--; m_connectionDataArray.erase(m_connectionDataArray.begin() + pConnectionData->listIndex); delete pConnectionData; SetItemCount(m_connectionDataArray.size()); if (!GetItemCount()) m_pOwner->SetIcon(); } else if (op == USERCONTROL_CONNOP_TRANSFERINFO) { int userid; memcpy(&userid, pData + 2, 4); if (dwDataLength < 7) return FALSE; std::map::iterator iter = m_connectionDataMap.find(userid); if (iter == m_connectionDataMap.end()) return FALSE; CConnectionData* pConnectionData = iter->second; pConnectionData->transferMode = pData[6]; if (!pConnectionData->transferMode) { pConnectionData->physicalFile = _T(""); pConnectionData->logicalFile = _T(""); pConnectionData->currentOffset = 0; pConnectionData->totalSize = -1; pConnectionData->ResetSpeed(); pConnectionData->columnText[COLUMN_TRANSFERPROGRESS] = _T(""); pConnectionData->columnText[COLUMN_TRANSFERSPEED] = _T(""); } else { unsigned int pos = 7; if ((pos + 2) > dwDataLength) return FALSE; unsigned int len = pData[pos] * 256 + pData[pos+1]; pos += 2; if ((pos + len + 2) > dwDataLength) return FALSE; char* physicalFile = new char[len + 1]; memcpy(physicalFile, pData + pos, len); physicalFile[len] = 0; pos += len; #ifdef _UNICODE pConnectionData->physicalFile = ConvFromNetwork(physicalFile); #else pConnectionData->physicalFile = ConvToLocal(ConvFromNetwork(physicalFile)); #endif delete [] physicalFile; len = pData[pos] * 256 + pData[pos+1]; pos += 2; if ((pos + len) > dwDataLength) return FALSE; char* logicalFile = new char[len + 1]; memcpy(logicalFile, pData + pos, len); logicalFile[len] = 0; pos += len; #ifdef _UNICODE pConnectionData->logicalFile = ConvFromNetwork(logicalFile); #else pConnectionData->logicalFile = ConvToLocal(ConvFromNetwork(logicalFile)); #endif delete [] logicalFile; if (pConnectionData->transferMode & 0x20) { if ((pos + 8) > dwDataLength) return FALSE; memcpy(&pConnectionData->currentOffset, pData + pos, 8); pos += 8; } else pConnectionData->currentOffset = 0; if (pConnectionData->transferMode & 0x40) { if ((pos + 8) > dwDataLength) return FALSE; memcpy(&pConnectionData->totalSize, pData + pos, 8); pos += 8; } else pConnectionData->totalSize = -1; // Filter out indicator bits pConnectionData->transferMode &= 0x9F; } pConnectionData->columnText[COLUMN_TRANSFERINIT] = m_showPhysical ? pConnectionData->physicalFile : pConnectionData->logicalFile; pConnectionData->itemImages[COLUMN_TRANSFERINIT] = pConnectionData->transferMode; RedrawItems(pConnectionData->listIndex, pConnectionData->listIndex); } else if (op == USERCONTROL_CONNOP_TRANSFEROFFSETS) { std::map::iterator iter = m_connectionDataMap.begin(); unsigned char* p = pData + 2; int max = dwDataLength - 12; while ((p - pData) <= max) { int* userid = (int*)p; CConnectionData *pConnectionData; while (true) { if (iter == m_connectionDataMap.end()) return FALSE; if (iter->first == *userid) { pConnectionData = iter->second; break; } iter++; } __int64* currentOffset = (__int64*)(p + 4); pConnectionData->AddBytes((int)(*currentOffset - pConnectionData->currentOffset)); pConnectionData->currentOffset = *currentOffset; CString str; if (pConnectionData->totalSize != -1) { double percent = (double)pConnectionData->currentOffset / pConnectionData->totalSize * 100; str.Format(_T("%s bytes (%1.1f%%)"), makeUserFriendlyString(pConnectionData->currentOffset).GetString(), percent); } else str.Format(_T("%s bytes"), makeUserFriendlyString(pConnectionData->currentOffset).GetString()); pConnectionData->columnText[COLUMN_TRANSFERPROGRESS] = str; if (pConnectionData->speed > 1024 * 1024) str.Format(_T("%1.1f MB/s"), (double)pConnectionData->speed / 1024 / 1024); else if (pConnectionData->speed > 1024) str.Format(_T("%1.1f KB/s"), (double)pConnectionData->speed / 1024); else str.Format(_T("%1.1f bytes/s"), (double)pConnectionData->speed); pConnectionData->columnText[COLUMN_TRANSFERSPEED] = str; p += 12; } RedrawItems(GetTopIndex(), GetTopIndex() + GetCountPerPage()); } return TRUE; } void CUsersListCtrl::OnContextmenuKick() { if (AfxMessageBox(_T("Do you really want to kick the selected user?"), MB_ICONQUESTION|MB_YESNO)!=IDYES) return; POSITION pos = GetFirstSelectedItemPosition(); while (pos) { int nItem = GetNextSelectedItem(pos); CConnectionData *data = m_connectionDataArray[nItem]; unsigned char buffer[5]; buffer[0]=USERCONTROL_KICK; memcpy(buffer+1, &data->userid, 4); m_pOwner->SendCommand(3, &buffer, 5); } } void CUsersListCtrl::OnContextmenuBan() { if (AfxMessageBox(_T("Do you really want to kick the selected user and ban his IP address?"), MB_ICONQUESTION|MB_YESNO)!=IDYES) return; POSITION pos = GetFirstSelectedItemPosition(); while (pos) { int nItem = GetNextSelectedItem(pos); CConnectionData *data = m_connectionDataArray[nItem]; unsigned char buffer[5]; buffer[0] = USERCONTROL_BAN; memcpy(buffer+1, &data->userid, 4); m_pOwner->SendCommand(3, &buffer, 5); } } void CUsersListCtrl::OnContextMenu(CWnd* pWnd, CPoint point) { CMenu menu; menu.LoadMenu(IDR_USERVIEWCONTEXT); CMenu* pPopup = menu.GetSubMenu(0); ASSERT(pPopup != NULL); CWnd* pWndPopupOwner = this; //while (pWndPopupOwner->GetStyle() & WS_CHILD) // pWndPopupOwner = pWndPopupOwner->GetParent(); POSITION pos = GetFirstSelectedItemPosition(); if (!pos) { pPopup->EnableMenuItem(ID_USERVIEWCONTEXT_KICK, MF_GRAYED); pPopup->EnableMenuItem(ID_USERVIEWCONTEXT_BAN, MF_GRAYED); } pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, pWndPopupOwner); } BOOL CUsersListCtrl::ParseUserControlCommand(unsigned char *pData, DWORD dwDataLength) { int type = *pData; if (type < 0 || type > 4) { m_pOwner->ShowStatus(_T("Protocol error: Invalid data"), 1); return FALSE; } switch (type) { case USERCONTROL_GETLIST: if (dwDataLength < 3) return FALSE; else { for (std::vector::iterator iter = m_connectionDataArray.begin(); iter != m_connectionDataArray.end(); iter++) delete *iter; m_connectionDataMap.clear(); m_connectionDataArray.clear(); int num = pData[1] * 256 + pData[2]; unsigned int pos = 3; for (int i = 0; i < num; i++) { if ((pos + 6) > dwDataLength) return FALSE; CConnectionData* pConnectionData = new CConnectionData;; memcpy(&pConnectionData->userid, pData+pos, 4); pos += 4; int len = pData[pos] * 256 + pData[pos+1]; pos+=2; if (pos+len > dwDataLength) { delete pConnectionData; return FALSE; } char* ip = new char[len + 1]; memcpy(ip, pData + pos, len); ip[len] = 0; pos += len; #ifdef _UNICODE pConnectionData->columnText[COLUMN_IP] = ConvFromNetwork(ip); #else pConnectionData->columnText[COLUMN_IP] = ConvToLocal(ConvFromNetwork(ip)); #endif delete [] ip; if ((pos+6) > dwDataLength) { delete pConnectionData; return FALSE; } memcpy(&pConnectionData->port, pData+pos, 4); pos+=4; len = pData[pos] * 256 + pData[pos+1]; pos+=2; if ((pos + len + 1) > dwDataLength) { delete pConnectionData; return FALSE; } char* user = new char[len + 1]; memcpy(user, pData + pos, len); user[len] = 0; pos += len; #ifdef _UNICODE pConnectionData->columnText[COLUMN_USER] = ConvFromNetwork(user); #else pConnectionData->columnText[COLUMN_USER] = ConvToLocal(ConvFromNetwork(user)); #endif delete [] user; pConnectionData->transferMode = pData[pos++]; if (pConnectionData->transferMode) { if ((pos + 2) > dwDataLength) { delete pConnectionData; return FALSE; } len = pData[pos] * 256 + pData[pos+1]; pos += 2; if ((pos+len) > dwDataLength) { delete pConnectionData; return FALSE; } char* physicalFile = new char[len + 1]; memcpy(physicalFile, pData + pos, len); physicalFile[len] = 0; pos += len; #ifdef _UNICODE pConnectionData->physicalFile = ConvFromNetwork(physicalFile); #else pConnectionData->physicalFile = ConvToLocal(ConvFromNetwork(physicalFile)); #endif delete [] physicalFile; if ((pos + 2) > dwDataLength) { delete pConnectionData; return FALSE; } len = pData[pos] * 256 + pData[pos+1]; pos += 2; if ((pos+len) > dwDataLength) { delete pConnectionData; return FALSE; } char* logicalFile = new char[len + 1]; memcpy(logicalFile, pData + pos, len); logicalFile[len] = 0; pos += len; #ifdef _UNICODE pConnectionData->logicalFile = ConvFromNetwork(logicalFile); #else pConnectionData->logicalFile = ConvToLocal(ConvFromNetwork(logicalFile)); #endif delete [] logicalFile; if (pConnectionData->transferMode & 0x20) { memcpy(&pConnectionData->currentOffset, pData + pos, 8); pos += 8; } else pConnectionData->currentOffset = 0; if (pConnectionData->transferMode & 0x40) { memcpy(&pConnectionData->totalSize, pData + pos, 8); pos += 8; } else pConnectionData->totalSize = -1; // Filter out indicator bits pConnectionData->transferMode &= 0x9F; } else { pConnectionData->currentOffset = 0; pConnectionData->totalSize = -1; } pConnectionData->columnText[COLUMN_ID].Format(_T("%06d"), pConnectionData->userid); m_connectionDataMap[pConnectionData->userid] = pConnectionData; pConnectionData->listIndex = m_connectionDataArray.size(); m_connectionDataArray.push_back(pConnectionData); if (pConnectionData->columnText[COLUMN_USER] == _T("")) pConnectionData->columnText[COLUMN_USER] = _T("(not logged in)"); else pConnectionData->itemImages[COLUMN_ID] = 4; pConnectionData->itemImages[COLUMN_TRANSFERINIT] = pConnectionData->transferMode; pConnectionData->columnText[COLUMN_TRANSFERINIT] = m_showPhysical ? pConnectionData->physicalFile : pConnectionData->logicalFile; } SetSortColumn(m_sortColumn, m_sortDir); SetItemCount(m_connectionDataArray.size()); m_pOwner->SetIcon(); } break; case USERCONTROL_CONNOP: return ProcessConnOp(pData, dwDataLength); break; case USERCONTROL_KICK: case USERCONTROL_BAN: break; default: m_pOwner->ShowStatus(_T("Protocol error: Specified usercontrol option not implemented"), 1); return FALSE; break; } return TRUE; } void CUsersListCtrl::OnSize(UINT nType, int cx, int cy) { CListCtrl::OnSize(nType, cx, cy); } void CUsersListCtrl::SetDisplayPhysicalNames(bool showPhysical) { m_showPhysical = showPhysical; // Iterate through all items and reset the transfer column text for (std::vector::iterator iter = m_connectionDataArray.begin(); iter != m_connectionDataArray.end(); iter++) { CConnectionData* pData = *iter; pData->columnText[COLUMN_TRANSFERINIT] = showPhysical ? pData->physicalFile : pData->logicalFile; } RedrawItems(0, GetItemCount() - 1); } void CUsersListCtrl::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent != m_nSpeedinfoTimer) return; for (std::vector::iterator iter = m_connectionDataArray.begin(); iter != m_connectionDataArray.end(); iter++) { CConnectionData* pConnectionData = *iter; if (pConnectionData->transferMode) pConnectionData->NextSpeed(); } RedrawItems(0, GetItemCount() - 1); } void CUsersListCtrl::SetSortColumn(int sortColumn /*=-1*/, int dir /*=-1*/) { if (m_sortColumn == sortColumn || sortColumn == -1) { if (dir == -1) m_sortDir = m_sortDir ? 0 : 1; else m_sortDir = dir ? 1 : 0; CHeaderCtrl *header = GetHeaderCtrl(); if (header) { HDITEM hdi; hdi.mask = HDI_IMAGE | HDI_FORMAT; hdi.fmt = HDF_IMAGE | HDF_STRING | HDF_BITMAP_ON_RIGHT; hdi.iImage = m_sortDir + 1; header->SetItem(m_sortColumn, &hdi); } } else { if (dir == -1) m_sortDir = 0; else m_sortDir = dir ? 1 : 0; if (sortColumn < 0 || sortColumn > 2) sortColumn = 0; CHeaderCtrl *header = GetHeaderCtrl(); if (header) { HDITEM hdi; hdi.mask = HDI_IMAGE | HDI_FORMAT; hdi.fmt = HDF_STRING; hdi.iImage = 0; header->SetItem(m_sortColumn, &hdi); hdi.fmt = HDF_IMAGE | HDF_STRING | HDF_BITMAP_ON_RIGHT; hdi.iImage = m_sortDir + 1; header->SetItem(sortColumn, &hdi); } m_sortColumn = sortColumn; } if (m_connectionDataArray.size() < 2) return; if (sortColumn == 1) QSortList(m_sortDir, 0, m_connectionDataArray.size() - 1, CmpUser); else if (sortColumn == 2) QSortList(m_sortDir, 0, m_connectionDataArray.size() - 1, CmpIP); else QSortList(m_sortDir, 0, m_connectionDataArray.size() - 1, CmpUserid); for (unsigned int i = 0; i < m_connectionDataArray.size(); i++) m_connectionDataArray[i]->listIndex = i; RedrawItems(0, m_connectionDataArray.size() - 1); } void CUsersListCtrl::QSortList(const unsigned int dir, int anf, int ende, int (*comp)(const CUsersListCtrl *pList, unsigned int index, const CConnectionData* refData)) { int l = anf; int r = ende; const unsigned int ref = (l + r) / 2; const CConnectionData* refData = m_connectionDataArray[ref]; do { if (!dir) { while ((comp(this, l, refData) < 0) && (l 0) && (r>anf)) r--; } else { while ((comp(this, l, refData) > 0) && (lanf)) r--; } if (l<=r) { CConnectionData* tmp = m_connectionDataArray[l]; m_connectionDataArray[l] = m_connectionDataArray[r]; m_connectionDataArray[r] = tmp; l++; r--; } } while (l<=r); if (anfm_connectionDataArray[index]; if (data->userid > refData->userid) return 1; else if (data->userid < refData->userid) return -1; return 0; } int CUsersListCtrl::CmpUser(const CUsersListCtrl *pList, unsigned int index, const CConnectionData* refData) { const CConnectionData* data = pList->m_connectionDataArray[index]; int res = data->columnText[COLUMN_USER].CompareNoCase(refData->columnText[COLUMN_USER]); if (res) return res; if (data->userid > refData->userid) return 1; else if (data->userid < refData->userid) return -1; return 0; } int CUsersListCtrl::CmpIP(const CUsersListCtrl *pList, unsigned int index, const CConnectionData* refData) { const CConnectionData* data = pList->m_connectionDataArray[index]; int res = data->columnText[COLUMN_IP].CompareNoCase(refData->columnText[COLUMN_IP]); if (res) return res; if (data->userid > refData->userid) return 1; else if (data->userid < refData->userid) return -1; return 0; } void CUsersListCtrl::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR; LV_ITEM* pItem= &(pDispInfo)->item; if (static_cast(m_connectionDataArray.size()) <= pItem->iItem) return; if (pItem->mask & LVIF_TEXT) { if (_tcslen(m_connectionDataArray[pItem->iItem]->columnText[pItem->iSubItem]) >= static_cast(pItem->cchTextMax)) { _tcsncpy(pItem->pszText, m_connectionDataArray[pItem->iItem]->columnText[pItem->iSubItem], pItem->cchTextMax - 4); pItem->pszText[pItem->cchTextMax - 4] = '.'; pItem->pszText[pItem->cchTextMax - 3] = '.'; pItem->pszText[pItem->cchTextMax - 2] = '.'; pItem->pszText[pItem->cchTextMax - 1] = 0; } else lstrcpy(pItem->pszText, m_connectionDataArray[pItem->iItem]->columnText[pItem->iSubItem]); } if (pItem->mask & LVIF_IMAGE) pItem->iImage = m_connectionDataArray[pItem->iItem]->itemImages[pItem->iSubItem]; } void CUsersListCtrl::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; SetSortColumn(pNMListView->iSubItem); *pResult = 0; }