home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 217 / DPCS0306DVD.ISO / Toolkit / Internet / FileZilla / Server / FileZilla_Server-0.9.11.exe / source / interface / UsersListCtrl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-12-01  |  21.8 KB  |  818 lines

  1. // FileZilla Server - a Windows ftp server
  2.  
  3. // Copyright (C) 2002-2004 - Tim Kosse <tim.kosse@gmx.de>
  4.  
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU General Public License
  7. // as published by the Free Software Foundation; either version 2
  8. // of the License, or (at your option) any later version.
  9.  
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. // GNU General Public License for more details.
  14.  
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. // UsersListCtrl.cpp: Implementierungsdatei
  20. //
  21.  
  22. #include "stdafx.h"
  23. #include "filezilla server.h"
  24. #include "UsersListCtrl.h"
  25. #include "mainfrm.h"
  26.  
  27. #if defined(_DEBUG) && !defined(MMGR)
  28. #define new DEBUG_NEW
  29. #undef THIS_FILE
  30. static char THIS_FILE[] = __FILE__;
  31. #endif
  32.  
  33. #define NUMCOLUMNS 6
  34. #define COLUMN_ID 0
  35. #define COLUMN_USER 1
  36. #define COLUMN_IP 2
  37. #define COLUMN_TRANSFERINIT 3
  38. #define COLUMN_TRANSFERPROGRESS 4
  39. #define COLUMN_TRANSFERSPEED 5
  40.  
  41. #define SPEED_MEAN_SECONDS 10
  42.  
  43. class CConnectionData
  44. {
  45. public:
  46.     CConnectionData()
  47.     {
  48.         for (int i = 1; i < NUMCOLUMNS; i++)
  49.             itemImages[i] = -1;
  50.         itemImages[0] = 5;
  51.  
  52.         ResetSpeed();
  53.     }
  54.  
  55.     ~CConnectionData() { }
  56.     int userid;
  57.     CString user;
  58.     unsigned int port;
  59.     unsigned char transferMode;
  60.     CString physicalFile;
  61.     CString logicalFile;
  62.     __int64 totalSize;
  63.     __int64 currentOffset;
  64.     unsigned int speed;
  65.     
  66.     int listIndex;
  67.     CString columnText[NUMCOLUMNS];
  68.     int itemImages[NUMCOLUMNS];
  69.  
  70.     inline void AddBytes(int bytes)
  71.     {
  72.         *current_speed += bytes;
  73.         UpdateSpeed();
  74.     }
  75.     
  76.     inline void UpdateSpeed()
  77.     {
  78.         speed = 0;
  79.         int max = speedDidWrap ? SPEED_MEAN_SECONDS : (current_speed - speed_mean + 1);
  80.         for (int i = 0; i < max; i++)
  81.             speed += speed_mean[i];
  82.         speed /= max;
  83.     }
  84.  
  85.     inline void NextSpeed()
  86.     {
  87.         if (!*current_speed)
  88.             UpdateSpeed();
  89.  
  90.         if ((++current_speed - speed_mean) >= SPEED_MEAN_SECONDS)
  91.         {
  92.             speedDidWrap = true;
  93.             current_speed = speed_mean;
  94.         }
  95.         *current_speed = 0;
  96.     }
  97.  
  98.     inline void ResetSpeed()
  99.     {
  100.         speedDidWrap = false;
  101.         current_speed = speed_mean;
  102.         *current_speed = 0;
  103.     }
  104.  
  105. private:
  106.     unsigned int speed_mean[SPEED_MEAN_SECONDS];
  107.     unsigned int *current_speed;
  108.     bool speedDidWrap;
  109. };
  110.  
  111. /////////////////////////////////////////////////////////////////////////////
  112. // CUsersListCtrl
  113.  
  114. CUsersListCtrl::CUsersListCtrl(CMainFrame *pOwner)
  115. {
  116.     ASSERT(pOwner);
  117.     m_pOwner = pOwner;
  118.     m_sortColumn = 0;
  119.     m_sortDir = 0;
  120. }
  121.  
  122. CUsersListCtrl::~CUsersListCtrl()
  123. {
  124.     for (std::vector<CConnectionData*>::iterator iter = m_connectionDataArray.begin(); iter != m_connectionDataArray.end(); iter++)
  125.         delete *iter;
  126. }
  127.  
  128.  
  129. BEGIN_MESSAGE_MAP(CUsersListCtrl, CListCtrl)
  130.     //{{AFX_MSG_MAP(CUsersListCtrl)
  131.     ON_WM_CREATE()
  132.     ON_COMMAND(ID_USERVIEWCONTEXT_KICK, OnContextmenuKick)
  133.     ON_WM_CONTEXTMENU()
  134.     ON_WM_SIZE()
  135.     ON_WM_TIMER()
  136.     ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetdispinfo)
  137.     ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick)
  138.     //}}AFX_MSG_MAP
  139. END_MESSAGE_MAP()
  140.  
  141. /////////////////////////////////////////////////////////////////////////////
  142. // Behandlungsroutinen fⁿr Nachrichten CUsersListCtrl 
  143.  
  144. int CUsersListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  145. {
  146.     if (CListCtrl::OnCreate(lpCreateStruct) == -1)
  147.         return -1;
  148.     
  149.     m_ImageList.Create(IDB_TRANSFERINFO, 16, 6, RGB(255, 0, 255));
  150.     SetImageList(&m_ImageList, LVSIL_SMALL);
  151.  
  152.     SetExtendedStyle(LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT);
  153.  
  154.     InsertColumn(COLUMN_ID, "ID", LVCFMT_RIGHT, 75);
  155.     InsertColumn(COLUMN_USER, "Account", LVCFMT_LEFT, 150);
  156.     InsertColumn(COLUMN_IP, "IP", LVCFMT_RIGHT, 100);
  157.     InsertColumn(COLUMN_TRANSFERINIT, "Transfer", LVCFMT_LEFT, 250);
  158.     InsertColumn(COLUMN_TRANSFERPROGRESS, "Progress", LVCFMT_RIGHT, 150);
  159.     InsertColumn(COLUMN_TRANSFERSPEED, "Speed", LVCFMT_LEFT, 80);
  160.  
  161.     m_SortImg.Create( 8, 8, ILC_MASK, 3, 3 );
  162.     HICON Icon;
  163.     Icon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_EMPTY));
  164.     m_SortImg.Add(Icon);
  165.     Icon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_UP));
  166.     m_SortImg.Add(Icon);
  167.     Icon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_DOWN));
  168.     m_SortImg.Add(Icon);
  169.     m_SortImg.SetBkColor(CLR_NONE);
  170.  
  171.     CHeaderCtrl *header = GetHeaderCtrl( );
  172.     if (header)
  173.         header->SetImageList(&m_SortImg);
  174.  
  175.     m_nSpeedinfoTimer = SetTimer(232, 1000, 0);
  176.  
  177.     return 0;
  178. }
  179.  
  180. bool CUsersListCtrl::ProcessConnOp(unsigned char *pData, DWORD dwDataLength)
  181. {
  182.     int op = pData[1];
  183.  
  184.     if (op < 0 || op > 4)
  185.         return FALSE;
  186.  
  187.     if (dwDataLength < 6)
  188.         return FALSE;
  189.     
  190.     if (op == USERCONTROL_CONNOP_ADD)
  191.     {
  192.         int userid;
  193.         memcpy(&userid, pData + 2, 4);
  194.         CConnectionData* pConnectionData = new CConnectionData;
  195.         pConnectionData->currentOffset = 0;
  196.         pConnectionData->totalSize = -1;
  197.  
  198.         pConnectionData->userid = userid;
  199.  
  200.         unsigned int pos = 6;
  201.  
  202.         if (dwDataLength < 8)
  203.         {
  204.             delete pConnectionData;
  205.             return FALSE;
  206.         }
  207.  
  208.         unsigned int len = pData[pos] * 256 + pData[pos+1];
  209.         pos += 2;
  210.         if (pos+len > dwDataLength)
  211.         {
  212.             delete pConnectionData;
  213.             return FALSE;
  214.         }
  215.  
  216.         memcpy(pConnectionData->columnText[COLUMN_IP].GetBuffer(len), pData + pos, len);
  217.         pConnectionData->columnText[COLUMN_IP].ReleaseBuffer(len);
  218.         pos += len;
  219.  
  220.         if ((pos+4) > dwDataLength)
  221.         {
  222.             delete pConnectionData;
  223.             return FALSE;
  224.         }
  225.         memcpy(&pConnectionData->port, pData + pos, 4);
  226.  
  227.         pConnectionData->columnText[COLUMN_ID].Format("%06d", userid);
  228.         m_connectionDataMap[userid] = pConnectionData;
  229.         pConnectionData->listIndex = m_connectionDataArray.size();
  230.         m_connectionDataArray.push_back(pConnectionData);
  231.  
  232.         pConnectionData->columnText[COLUMN_USER] = "(not logged in)";
  233.         SetItemCount(GetItemCount() + 1);
  234.         SetSortColumn(m_sortColumn, m_sortDir);
  235.  
  236.         if (GetItemCount() == 1)
  237.             m_pOwner->SetIcon();
  238.     }
  239.     else if (op == USERCONTROL_CONNOP_CHANGEUSER)
  240.     {
  241.         int userid;
  242.         memcpy(&userid, pData + 2, 4);
  243.  
  244.         if (dwDataLength < 8)
  245.             return FALSE;
  246.  
  247.         std::map<int, CConnectionData*>::iterator iter = m_connectionDataMap.find(userid);
  248.         if (iter == m_connectionDataMap.end())
  249.             return FALSE;
  250.  
  251.         CConnectionData* pConnectionData = iter->second;
  252.  
  253.         unsigned int pos = 6;
  254.  
  255.         unsigned int len = pData[pos] * 256 + pData[pos+1];
  256.         pos += 2;
  257.         if ((pos + len) > dwDataLength)
  258.             return FALSE;
  259.  
  260.         memcpy(pConnectionData->columnText[COLUMN_USER].GetBuffer(len), pData + pos, len);
  261.         pConnectionData->columnText[COLUMN_USER].ReleaseBuffer(len);
  262.  
  263.         if (pConnectionData->columnText[COLUMN_USER] == "")
  264.         {
  265.             pConnectionData->itemImages[COLUMN_ID] = 5;
  266.             pConnectionData->columnText[COLUMN_USER] = "(not logged in)";
  267.         }
  268.         else
  269.         {
  270.             pConnectionData->itemImages[COLUMN_ID] = 4;
  271.         }
  272.         RedrawItems(pConnectionData->listIndex, pConnectionData->listIndex);
  273.         SetSortColumn(m_sortColumn, m_sortDir);
  274.     }
  275.     else if (op == USERCONTROL_CONNOP_REMOVE)
  276.     {
  277.         int userid;
  278.         memcpy(&userid, pData + 2, 4);
  279.  
  280.         std::map<int, CConnectionData*>::iterator iter = m_connectionDataMap.find(userid);
  281.         if (iter == m_connectionDataMap.end())
  282.             return FALSE;
  283.  
  284.         CConnectionData *pConnectionData = iter->second;
  285.  
  286.         m_connectionDataMap.erase(iter);
  287.         for (std::vector<CConnectionData*>::iterator iter = m_connectionDataArray.begin() + pConnectionData->listIndex + 1; iter != m_connectionDataArray.end(); iter++)
  288.             (*iter)->listIndex--;
  289.         m_connectionDataArray.erase(m_connectionDataArray.begin() + pConnectionData->listIndex);
  290.         delete pConnectionData;
  291.  
  292.         SetItemCount(m_connectionDataArray.size());
  293.  
  294.         if (!GetItemCount())
  295.             m_pOwner->SetIcon();
  296.     }
  297.     else if (op == USERCONTROL_CONNOP_TRANSFERINFO)
  298.     {
  299.         int userid;
  300.         memcpy(&userid, pData + 2, 4);
  301.  
  302.         if (dwDataLength < 7)
  303.             return FALSE;
  304.         std::map<int, CConnectionData*>::iterator iter = m_connectionDataMap.find(userid);
  305.         if (iter == m_connectionDataMap.end())
  306.             return FALSE;
  307.  
  308.         CConnectionData* pConnectionData = iter->second;
  309.  
  310.         pConnectionData->transferMode = pData[6];
  311.  
  312.         if (!pConnectionData->transferMode)
  313.         {
  314.             pConnectionData->physicalFile = "";
  315.             pConnectionData->logicalFile = "";
  316.             pConnectionData->currentOffset = 0;
  317.             pConnectionData->totalSize = -1;
  318.             pConnectionData->ResetSpeed();
  319.  
  320.             pConnectionData->columnText[COLUMN_TRANSFERPROGRESS] =  "";
  321.             pConnectionData->columnText[COLUMN_TRANSFERSPEED] =  "";
  322.         }
  323.         else
  324.         {
  325.             unsigned int pos = 7;
  326.             if ((pos + 2) > dwDataLength)
  327.                 return FALSE;
  328.  
  329.             unsigned int len = pData[pos] * 256 + pData[pos+1];
  330.             pos += 2;
  331.             if ((pos + len + 2) > dwDataLength)
  332.                 return FALSE;
  333.  
  334.             memcpy(pConnectionData->physicalFile.GetBuffer(len), pData + pos, len);
  335.             pConnectionData->physicalFile.ReleaseBuffer(len);
  336.             pos += len;
  337.  
  338.             len = pData[pos] * 256 + pData[pos+1];
  339.             pos += 2;
  340.             if ((pos + len) > dwDataLength)
  341.                 return FALSE;
  342.  
  343.             memcpy(pConnectionData->logicalFile.GetBuffer(len), pData + pos, len);
  344.             pConnectionData->logicalFile.ReleaseBuffer(len);
  345.             pos += len;
  346.  
  347.             if (pConnectionData->transferMode & 0x20)
  348.             {
  349.                 memcpy(&pConnectionData->currentOffset, pData + pos, 8);
  350.                 pos += 8;
  351.             }
  352.             else
  353.                 pConnectionData->currentOffset = 0;
  354.  
  355.             if (pConnectionData->transferMode & 0x40)
  356.             {
  357.                 memcpy(&pConnectionData->totalSize, pData + pos, 8);
  358.                 pos += 8;
  359.             }
  360.             else
  361.                 pConnectionData->totalSize = -1;
  362.  
  363.             // Filter out indicator bits
  364.             pConnectionData->transferMode &= 0x9F;
  365.         }
  366.             
  367.         pConnectionData->columnText[COLUMN_TRANSFERINIT] =  m_showPhysical ? pConnectionData->physicalFile : pConnectionData->logicalFile;
  368.         pConnectionData->itemImages[COLUMN_TRANSFERINIT] =  pConnectionData->transferMode;
  369.  
  370.         RedrawItems(pConnectionData->listIndex, pConnectionData->listIndex);
  371.     }
  372.     else if (op == USERCONTROL_CONNOP_TRANSFEROFFSETS)
  373.     {
  374.         std::map<int, CConnectionData*>::iterator iter = m_connectionDataMap.begin();
  375.         unsigned char* p = pData + 2;
  376.         int max = dwDataLength - 12;
  377.         while ((p - pData) <= max)
  378.         {
  379.             int* userid = (int*)p;
  380.  
  381.             CConnectionData *pConnectionData;
  382.             while (true)
  383.             {
  384.                 if (iter == m_connectionDataMap.end())
  385.                     return FALSE;
  386.  
  387.                 if (iter->first == *userid)
  388.                 {
  389.                     pConnectionData = iter->second;
  390.                     break;
  391.                 }
  392.  
  393.                 iter++;
  394.             }
  395.             __int64* currentOffset = (__int64*)(p + 4);
  396.  
  397.             pConnectionData->AddBytes((int)(*currentOffset - pConnectionData->currentOffset));
  398.             pConnectionData->currentOffset = *currentOffset;
  399.  
  400.             CString str;
  401.             if (pConnectionData->totalSize != -1)
  402.             {
  403.                 double percent = (double)pConnectionData->currentOffset / pConnectionData->totalSize * 100;
  404.                 str.Format("%I64d bytes (%1.1f%%)", pConnectionData->currentOffset, percent);
  405.             }
  406.             else
  407.                 str.Format("%I64d bytes", pConnectionData->currentOffset);
  408.             pConnectionData->columnText[COLUMN_TRANSFERPROGRESS] =  str;
  409.  
  410.             if (pConnectionData->speed > 1024 * 1024)
  411.                 str.Format("%1.1f MB/s", (double)pConnectionData->speed / 1024 / 1024);
  412.             else if (pConnectionData->speed > 1024)
  413.                 str.Format("%1.1f KB/s", (double)pConnectionData->speed / 1024);
  414.             else
  415.                 str.Format("%1.1f bytes/s", (double)pConnectionData->speed);
  416.             pConnectionData->columnText[COLUMN_TRANSFERSPEED] =  str;
  417.             
  418.             p += 12;
  419.         }
  420.         RedrawItems(GetTopIndex(), GetTopIndex() + GetCountPerPage());
  421.     }
  422.  
  423.     return TRUE;
  424. }
  425.  
  426. void CUsersListCtrl::OnContextmenuKick() 
  427. {
  428.     if (AfxMessageBox("Do you really want to kick the selected user?", MB_ICONQUESTION|MB_YESNO)!=IDYES)
  429.         return;
  430.     POSITION pos = GetFirstSelectedItemPosition();
  431.     while (pos)
  432.     {
  433.         int nItem = GetNextSelectedItem(pos);
  434.         
  435.         CConnectionData *data = m_connectionDataArray[nItem];
  436.         
  437.         unsigned char buffer[5];
  438.         buffer[0]=USERCONTROL_KICK;
  439.         memcpy(buffer+1, &data->userid, 4);
  440.         m_pOwner->SendCommand(3, &buffer, 5);
  441.     }    
  442. }
  443.  
  444. void CUsersListCtrl::OnContextMenu(CWnd* pWnd, CPoint point) 
  445. {
  446.     CMenu menu;
  447.     menu.LoadMenu(IDR_USERVIEWCONTEXT);
  448.  
  449.     CMenu* pPopup = menu.GetSubMenu(0);
  450.     ASSERT(pPopup != NULL);
  451.     CWnd* pWndPopupOwner = this;
  452.     //while (pWndPopupOwner->GetStyle() & WS_CHILD)
  453.     //    pWndPopupOwner = pWndPopupOwner->GetParent();
  454.  
  455.     POSITION pos=GetFirstSelectedItemPosition();
  456.     if (!pos)
  457.     {
  458.         pPopup->EnableMenuItem(ID_USERVIEWCONTEXT_KICK,MF_GRAYED);
  459.     }
  460.         
  461.     pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
  462.         pWndPopupOwner);
  463. }
  464.  
  465. BOOL CUsersListCtrl::ParseUserControlCommand(unsigned char *pData, DWORD dwDataLength)
  466. {
  467.     int type = *pData;
  468.     if (type < 0 || type > 4)
  469.     {
  470.         m_pOwner->ShowStatus(_T("Protocol error: Invalid data"), 1);
  471.         return FALSE;
  472.     }
  473.     switch (type)
  474.     {
  475.     case USERCONTROL_GETLIST:
  476.         if (dwDataLength < 3)
  477.             return FALSE;
  478.         else
  479.         {
  480.             for (std::vector<CConnectionData*>::iterator iter = m_connectionDataArray.begin(); iter != m_connectionDataArray.end(); iter++)
  481.                 delete *iter;
  482.  
  483.             m_connectionDataMap.clear();
  484.             m_connectionDataArray.clear();
  485.             
  486.             int num = pData[1] * 256 + pData[2];
  487.             unsigned int pos = 3;
  488.             for (int i = 0; i < num; i++)
  489.             {
  490.                 if ((pos + 6) > dwDataLength)
  491.                     return FALSE;
  492.                 CConnectionData* pConnectionData = new CConnectionData;;
  493.                 memcpy(&pConnectionData->userid, pData+pos, 4);
  494.                 pos += 4;
  495.                 int len = pData[pos] * 256 + pData[pos+1];
  496.                 pos+=2;
  497.                 if (pos+len > dwDataLength)
  498.                 {
  499.                     delete pConnectionData;
  500.                     return FALSE;
  501.                 }
  502.                 
  503.                 memcpy(pConnectionData->columnText[COLUMN_IP].GetBuffer(len), pData + pos, len);
  504.                 pConnectionData->columnText[COLUMN_IP].ReleaseBuffer(len);
  505.                 pos+=len;
  506.                 
  507.                 if ((pos+6) > dwDataLength)
  508.                 {
  509.                     delete pConnectionData;
  510.                     return FALSE;
  511.                 }
  512.                 memcpy(&pConnectionData->port, pData+pos, 4);
  513.                 
  514.                 pos+=4;
  515.                 
  516.                 len = pData[pos] * 256 + pData[pos+1];
  517.                 pos+=2;
  518.                 if ((pos + len + 1) > dwDataLength)
  519.                 {
  520.                     delete pConnectionData;
  521.                     return FALSE;
  522.                 }
  523.  
  524.                 memcpy(pConnectionData->user.GetBuffer(len), pData + pos, len);
  525.                 pConnectionData->user.ReleaseBuffer(len);
  526.                 pos += len;
  527.  
  528.                 pConnectionData->transferMode = pData[pos++];
  529.  
  530.                 if (pConnectionData->transferMode)
  531.                 {
  532.                     if ((pos + 2) > dwDataLength)
  533.                     {
  534.                         delete pConnectionData;
  535.                         return FALSE;
  536.                     }
  537.                     len = pData[pos] * 256 + pData[pos+1];
  538.                     pos += 2;
  539.  
  540.                     if ((pos+len) > dwDataLength)
  541.                     {
  542.                         delete pConnectionData;
  543.                         return FALSE;
  544.                     }
  545.  
  546.                     memcpy(pConnectionData->physicalFile.GetBuffer(len), pData + pos, len);
  547.                     pConnectionData->physicalFile.ReleaseBuffer(len);
  548.                     pos += len;
  549.  
  550.                     if ((pos + 2) > dwDataLength)
  551.                     {
  552.                         delete pConnectionData;
  553.                         return FALSE;
  554.                     }
  555.                     len = pData[pos] * 256 + pData[pos+1];
  556.                     pos += 2;
  557.  
  558.                     
  559.                     if ((pos+len) > dwDataLength)
  560.                     {
  561.                         delete pConnectionData;
  562.                         return FALSE;
  563.                     }
  564.  
  565.                     memcpy(pConnectionData->logicalFile.GetBuffer(len), pData + pos, len);
  566.                     pConnectionData->logicalFile.ReleaseBuffer(len);
  567.                     pos += len;
  568.  
  569.                     if (pConnectionData->transferMode & 0x20)
  570.                     {
  571.                         memcpy(&pConnectionData->currentOffset, pData + pos, 8);
  572.                         pos += 8;
  573.                     }
  574.                     else
  575.                         pConnectionData->currentOffset = 0;
  576.  
  577.                     if (pConnectionData->transferMode & 0x40)
  578.                     {
  579.                         memcpy(&pConnectionData->totalSize, pData + pos, 8);
  580.                         pos += 8;
  581.                     }
  582.                     else
  583.                         pConnectionData->totalSize = -1;
  584.  
  585.                     // Filter out indicator bits
  586.                     pConnectionData->transferMode &= 0x9F;
  587.                 }
  588.                 else
  589.                 {
  590.                     pConnectionData->currentOffset = 0;
  591.                     pConnectionData->totalSize = -1;
  592.                 }
  593.  
  594.                 pConnectionData->columnText[COLUMN_ID].Format("%06d", pConnectionData->userid);
  595.                 m_connectionDataMap[pConnectionData->userid] = pConnectionData;
  596.                 pConnectionData->listIndex = m_connectionDataArray.size();
  597.                 m_connectionDataArray.push_back(pConnectionData);
  598.  
  599.                 if (pConnectionData->user == "")
  600.                     pConnectionData->columnText[COLUMN_USER] = "(not logged in)";
  601.                 else
  602.                     pConnectionData->columnText[COLUMN_USER] = pConnectionData->user;
  603.                 
  604.                 pConnectionData->itemImages[COLUMN_TRANSFERINIT] = pConnectionData->transferMode;
  605.                 pConnectionData->columnText[COLUMN_TRANSFERINIT] = m_showPhysical ? pConnectionData->physicalFile : pConnectionData->logicalFile;
  606.             }
  607.             SetSortColumn(m_sortColumn, m_sortDir);
  608.             SetItemCount(m_connectionDataArray.size());
  609.             m_pOwner->SetIcon();
  610.         }
  611.         break;
  612.     case USERCONTROL_CONNOP:
  613.         return ProcessConnOp(pData, dwDataLength);
  614.         break;
  615.     case USERCONTROL_KICK:
  616.         break;
  617.     default:
  618.         m_pOwner->ShowStatus(_T("Protocol error: Specified usercontrol option not implemented"), 1);
  619.         return FALSE;
  620.         break;
  621.     }
  622.     return TRUE;
  623. }
  624.  
  625. void CUsersListCtrl::OnSize(UINT nType, int cx, int cy)
  626. {
  627.     CListCtrl::OnSize(nType, cx, cy);
  628. }
  629.  
  630. void CUsersListCtrl::SetDisplayPhysicalNames(bool showPhysical)
  631. {
  632.     m_showPhysical = showPhysical;
  633.  
  634.     // Iterate through all items and reset the transfer column text
  635.     int count = GetItemCount();
  636.     for (std::vector<CConnectionData*>::iterator iter = m_connectionDataArray.begin(); iter != m_connectionDataArray.end(); iter++)
  637.     {
  638.         CConnectionData* pData = *iter;
  639.         pData->columnText[COLUMN_TRANSFERINIT] = showPhysical ? pData->physicalFile : pData->logicalFile;
  640.     }
  641.     RedrawItems(0, GetItemCount() - 1);
  642. }
  643.  
  644. void CUsersListCtrl::OnTimer(UINT_PTR nIDEvent)
  645. {
  646.     if (nIDEvent != m_nSpeedinfoTimer)
  647.         return;
  648.  
  649.     for (std::vector<CConnectionData*>::iterator iter = m_connectionDataArray.begin(); iter != m_connectionDataArray.end(); iter++)
  650.     {
  651.         CConnectionData* pConnectionData = *iter;
  652.         if (pConnectionData->transferMode)
  653.             pConnectionData->NextSpeed();
  654.     }
  655.     RedrawItems(0, GetItemCount() - 1);
  656. }
  657.  
  658. void CUsersListCtrl::SetSortColumn(int sortColumn /*=-1*/, int dir /*=-1*/)
  659. {
  660.     if (m_sortColumn == sortColumn || sortColumn == -1)
  661.     {
  662.         if (dir == -1)
  663.             m_sortDir = m_sortDir ? 0 : 1;
  664.         else
  665.             m_sortDir = dir ? 1 : 0;
  666.  
  667.         CHeaderCtrl *header = GetHeaderCtrl();
  668.         if (header)
  669.         {
  670.             HDITEM hdi;
  671.             hdi.mask = HDI_IMAGE | HDI_FORMAT;
  672.             hdi.fmt = HDF_IMAGE | HDF_STRING | HDF_BITMAP_ON_RIGHT;
  673.             hdi.iImage = m_sortDir + 1;
  674.             header->SetItem(m_sortColumn, &hdi);
  675.         }
  676.     }
  677.     else
  678.     {
  679.         if (dir == -1)
  680.             m_sortDir = 0;
  681.         else
  682.             m_sortDir = dir ? 1 : 0;
  683.  
  684.         if (sortColumn < 0 || sortColumn > 2)
  685.             sortColumn = 0;
  686.         
  687.         CHeaderCtrl *header = GetHeaderCtrl();
  688.         if (header)
  689.         {
  690.             HDITEM hdi;
  691.             hdi.mask = HDI_IMAGE | HDI_FORMAT;
  692.             hdi.fmt = HDF_STRING;
  693.             hdi.iImage = 0;
  694.             header->SetItem(m_sortColumn, &hdi);
  695.  
  696.             hdi.fmt = HDF_IMAGE | HDF_STRING | HDF_BITMAP_ON_RIGHT;
  697.             hdi.iImage = m_sortDir + 1;
  698.             header->SetItem(sortColumn, &hdi);
  699.         }
  700.  
  701.         m_sortColumn = sortColumn;
  702.     }
  703.  
  704.     if (m_connectionDataArray.size() < 2)
  705.         return;
  706.  
  707.     if (sortColumn == 1)
  708.         QSortList(m_sortDir, 0, m_connectionDataArray.size() - 1, CmpUser);
  709.     else if (sortColumn == 2)
  710.         QSortList(m_sortDir, 0, m_connectionDataArray.size() - 1, CmpIP);
  711.     else
  712.         QSortList(m_sortDir, 0, m_connectionDataArray.size() - 1, CmpUserid);
  713.  
  714.     for (unsigned int i = 0; i < m_connectionDataArray.size(); i++)
  715.         m_connectionDataArray[i]->listIndex = i;
  716.  
  717.     RedrawItems(0, m_connectionDataArray.size() - 1);
  718. }
  719.  
  720. void CUsersListCtrl::QSortList(const unsigned int dir, int anf, int ende, int (*comp)(const CUsersListCtrl *pList, unsigned int index, const CConnectionData* refData))
  721. {
  722.     int l = anf;
  723.     int r = ende;
  724.     const unsigned int ref = (l + r) / 2;
  725.     const CConnectionData* refData = m_connectionDataArray[ref];
  726.     do
  727.     {
  728.         if (!dir)
  729.         {
  730.             while ((comp(this, l, refData) < 0) && (l<ende)) l++;
  731.             while ((comp(this, r, refData) > 0) && (r>anf)) r--;
  732.         }
  733.         else
  734.         {
  735.             while ((comp(this, l, refData) > 0) && (l<ende)) l++;
  736.             while ((comp(this, r, refData) < 0) && (r>anf)) r--;
  737.         }
  738.         if (l<=r)
  739.         {
  740.             CConnectionData* tmp = m_connectionDataArray[l];
  741.             m_connectionDataArray[l] = m_connectionDataArray[r];
  742.             m_connectionDataArray[r] = tmp;
  743.             l++;
  744.             r--;
  745.         }
  746.     } 
  747.     while (l<=r);
  748.  
  749.     if (anf<r) QSortList(dir, anf, r, comp);
  750.     if (l<ende) QSortList(dir, l, ende, comp);
  751. }
  752.  
  753. int CUsersListCtrl::CmpUserid(const CUsersListCtrl *pList, unsigned int index, const CConnectionData* refData)
  754. {
  755.     const CConnectionData* data = pList->m_connectionDataArray[index];
  756.  
  757.     if (data->userid > refData->userid)
  758.         return 1;
  759.     else if (data->userid < refData->userid)
  760.         return -1;
  761.  
  762.     return 0;
  763. }
  764.  
  765. int CUsersListCtrl::CmpUser(const CUsersListCtrl *pList, unsigned int index, const CConnectionData* refData)
  766. {
  767.     const CConnectionData* data = pList->m_connectionDataArray[index];
  768.  
  769.     int res = data->columnText[COLUMN_USER].CompareNoCase(refData->columnText[COLUMN_USER]);
  770.     if (res)
  771.         return res;
  772.  
  773.     if (data->userid > refData->userid)
  774.         return 1;
  775.     else if (data->userid < refData->userid)
  776.         return -1;
  777.  
  778.     return 0;
  779. }
  780.  
  781. int CUsersListCtrl::CmpIP(const CUsersListCtrl *pList, unsigned int index, const CConnectionData* refData)
  782. {
  783.     const CConnectionData* data = pList->m_connectionDataArray[index];
  784.  
  785.     int res = data->columnText[COLUMN_IP].CompareNoCase(refData->columnText[COLUMN_IP]);
  786.     if (res)
  787.         return res;
  788.  
  789.     if (data->userid > refData->userid)
  790.         return 1;
  791.     else if (data->userid < refData->userid)
  792.         return -1;
  793.  
  794.     return 0;
  795. }
  796.  
  797. void CUsersListCtrl::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult)
  798. {
  799.     LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
  800.     LV_ITEM* pItem= &(pDispInfo)->item;
  801.  
  802.     if (static_cast<int>(m_connectionDataArray.size()) <= pItem->iItem)
  803.         return;
  804.  
  805.     if (pItem->mask & LVIF_TEXT)
  806.         lstrcpy(pItem->pszText, m_connectionDataArray[pItem->iItem]->columnText[pItem->iSubItem]);
  807.     if (pItem->mask & LVIF_IMAGE)
  808.         pItem->iImage = m_connectionDataArray[pItem->iItem]->itemImages[pItem->iSubItem];
  809. }
  810.  
  811. void CUsersListCtrl::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult) 
  812. {
  813.     NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
  814.     SetSortColumn(pNMListView->iSubItem);
  815.     
  816.     *pResult = 0;
  817. }
  818.