//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       statsdlg.cpp
//
//--------------------------------------------------------------------------

// StatsDlg.cpp : implementation file
//

#include "stdafx.h"
#include "StatsDlg.h"
#include "coldlg.h"
#include "modeless.h"   // ModelessThread

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


BEGIN_MESSAGE_MAP(CStatsListCtrl, CListCtrl)
    //{{AFX_MSG_MAP(CStatsListCtrl)
    ON_WM_KEYDOWN()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CStatsListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    BOOL fControlDown;
    BOOL fShiftDown;

    fControlDown = (GetKeyState(VK_CONTROL) < 0);
    fShiftDown = (GetKeyState(VK_SHIFT) < 0);

    switch(nChar)
    {
        case 'c':
        case 'C':
        case VK_INSERT:
            if (fControlDown)
                CopyToClipboard();
            break;
    }
}

void CStatsListCtrl::CopyToClipboard()
{
    CString     strText, strLine, strData;
    int         nCount = GetItemCount();
    int         nColumns = 0;
    TCHAR       szBuffer[256];
    LV_COLUMN   ColumnInfo = {0};
    
    ColumnInfo.mask = LVCF_TEXT;
    ColumnInfo.pszText = szBuffer;
    ColumnInfo.cchTextMax = sizeof(szBuffer);

    // build up the column info
    while (GetColumn(nColumns, &ColumnInfo))
    {
        if (!strLine.IsEmpty())
            strLine += _T(",");

        strLine += ColumnInfo.pszText;

        nColumns++;
    }

    strLine += _T("\r\n");
    strData += strLine;
    strLine.Empty();

    // now get the other data
    for (int i = 0; i < nCount; i++)
    {
        for (int j = 0; j < nColumns; j++)
        {
            if (!strLine.IsEmpty())
                strLine += _T(",");
            
            strText = GetItemText(i, j);
    
            strLine += strText;
        }

        strLine += _T("\r\n");

        strData += strLine;
        strLine.Empty();
    }
 
    int nLength = strData.GetLength() + 1;
    nLength *= sizeof(TCHAR);

    HGLOBAL hMem = GlobalAlloc(GPTR, nLength);
    if (hMem)
    {
        memcpy (hMem, strData, nLength);
    
        if (!OpenClipboard())
	    {
		    GlobalFree(hMem);
            return;
	    }

        EmptyClipboard();

        SetClipboardData(CF_UNICODETEXT, hMem);

        CloseClipboard();
    }
}

/*!--------------------------------------------------------------------------
   StatsDialog::StatsDialog
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
StatsDialog::StatsDialog(DWORD dwOptions) :
   m_dwOptions(dwOptions),
   m_ulId(0),
   m_pConfig(NULL),
   m_bAfterInitDialog(FALSE)
{
   m_sizeMinimum.cx = m_sizeMinimum.cy = 0;

   m_hEventThreadKilled = ::CreateEvent(NULL, FALSE, FALSE, NULL);
   Assert(m_hEventThreadKilled);

   // Initialize the array of buttons
   ::ZeroMemory(m_rgBtn, sizeof(m_rgBtn));
   m_rgBtn[INDEX_CLOSE].m_ulId = IDCANCEL;
   m_rgBtn[INDEX_REFRESH].m_ulId = IDC_STATSDLG_BTN_REFRESH;
   m_rgBtn[INDEX_SELECT].m_ulId = IDC_STATSDLG_BTN_SELECT_COLUMNS;
   m_rgBtn[INDEX_CLEAR].m_ulId = IDC_STATSDLG_BTN_CLEAR;

   // Bug 134785 - create the ability to default to an ascending
   // rather than a descending sort.
   m_fSortDirection = !((dwOptions & STATSDLG_DEFAULTSORT_ASCENDING) != 0);
   m_fDefaultSortDirection = m_fSortDirection;

   // Multiply text header width with 2 for width of columns
   m_ColWidthMultiple = 2;
   m_ColWidthAdder = 0;
}


/*!--------------------------------------------------------------------------
   StatsDialog::~StatsDialog
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
StatsDialog::~StatsDialog()
{
   if (m_hEventThreadKilled)
      ::CloseHandle(m_hEventThreadKilled);
   m_hEventThreadKilled = 0;
}

/*!--------------------------------------------------------------------------
   StatsDialog::DoDataExchange
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::DoDataExchange(CDataExchange* pDX)
{
   CBaseDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(StatsDialog)
      // NOTE: the ClassWizard will add DDX and DDV calls here
   DDX_Control(pDX, IDC_STATSDLG_LIST, m_listCtrl);
   //}}AFX_DATA_MAP
}



BEGIN_MESSAGE_MAP(StatsDialog, CBaseDialog)
   //{{AFX_MSG_MAP(StatsDialog)
      ON_COMMAND(IDC_STATSDLG_BTN_REFRESH, OnRefresh)
      ON_COMMAND(IDC_STATSDLG_BTN_SELECT_COLUMNS, OnSelectColumns)
      ON_WM_MOVE()
      ON_WM_SIZE()
      ON_WM_GETMINMAXINFO()
      ON_WM_CONTEXTMENU()
      ON_NOTIFY(LVN_COLUMNCLICK, IDC_STATSDLG_LIST, OnNotifyListControlClick)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// StatsDialog message handlers




/*!--------------------------------------------------------------------------
   StatsDialog::SetColumnInfo
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT StatsDialog::SetColumnInfo(const ContainerColumnInfo *pColumnInfo, UINT cColumnInfo)
{
   if (m_pConfig)
   {
      m_pConfig->InitViewInfo(m_ulId, TRUE, cColumnInfo, m_fDefaultSortDirection, pColumnInfo);
   }
   else
   {
      m_viewInfo.InitViewInfo(cColumnInfo, TRUE, m_fDefaultSortDirection, pColumnInfo);
   }
   return hrOK;
}

/*!--------------------------------------------------------------------------
   StatsDialog::MapColumnToSubitem
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
int StatsDialog::MapColumnToSubitem(UINT nColumnId)
{
   if (m_pConfig)
      return m_pConfig->MapColumnToSubitem(m_ulId, nColumnId);
   else
      return m_viewInfo.MapColumnToSubitem(nColumnId);
}

/*!--------------------------------------------------------------------------
   StatsDialog::MapSubitemToColumn
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
int StatsDialog::MapSubitemToColumn(UINT nSubitemId)
{
   if (m_pConfig)
      return m_pConfig->MapSubitemToColumn(m_ulId, nSubitemId);
   else
      return m_viewInfo.MapSubitemToColumn(nSubitemId);
}

/*!--------------------------------------------------------------------------
   StatsDialog::IsSubitemVisible
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
BOOL StatsDialog::IsSubitemVisible(UINT nSubitemId)
{
   if (m_pConfig)
      return m_pConfig->IsSubitemVisible(m_ulId, nSubitemId);
   else
      return m_viewInfo.IsSubitemVisible(nSubitemId);
}

/*!--------------------------------------------------------------------------
   StatsDialog::RefreshData
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT StatsDialog::RefreshData(BOOL fGrabNewData)
{
   return hrOK;
}


/*!--------------------------------------------------------------------------
   StatsDialog::OnInitDialog
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
BOOL StatsDialog::OnInitDialog()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());	
        
    RECT  rcWnd, rcBtn;
    
    CBaseDialog::OnInitDialog();
    
    m_bAfterInitDialog = TRUE;
    
    // If this is the first time, get the location of the buttons and
    // list control relative to the edge of the screen
    if (m_sizeMinimum.cx == 0)
    {
        ::GetWindowRect(GetSafeHwnd(), &rcWnd);
        //    m_sizeMinimum.cx = rcWnd.right - rcWnd.left;
        //    m_sizeMinimum.cy = rcWnd.bottom - rcWnd.top;
        m_sizeMinimum.cx = 100;
        m_sizeMinimum.cy = 100;
        
        ::GetClientRect(GetSafeHwnd(), &rcWnd);
        
        // what are the button locations?
        for (int i=0; i<INDEX_COUNT; i++)
        {
            ::GetWindowRect(GetDlgItem(m_rgBtn[i].m_ulId)->GetSafeHwnd(),
                            &rcBtn);
            ScreenToClient(&rcBtn);
            m_rgBtn[i].m_rc.left = rcWnd.right - rcBtn.left;
            m_rgBtn[i].m_rc.right = rcWnd.right - rcBtn.right;
            m_rgBtn[i].m_rc.top = rcWnd.bottom - rcBtn.top;
            m_rgBtn[i].m_rc.bottom = rcWnd.bottom - rcBtn.bottom;
        }
        
        // what is the list control location?
        // The list control top, left is locked in position
        ::GetWindowRect(GetDlgItem(IDC_STATSDLG_LIST)->GetSafeHwnd(), &rcBtn);
        ScreenToClient(&rcBtn);
        m_rcList.left = rcBtn.left;
        m_rcList.top = rcBtn.top;
        
        // The bottom, right corner follows the expansion
        m_rcList.right = rcWnd.right - rcBtn.right;
        m_rcList.bottom = rcWnd.bottom - rcBtn.bottom;
    }

    // If we have a preferred position and size do that
    if (m_pConfig)
    {
        m_pConfig->GetStatsWindowRect(m_ulId, &m_rcPosition);
        m_fSortDirection = m_pConfig->GetSortDirection(m_ulId);
    }
    if (m_pConfig && (m_rcPosition.top != m_rcPosition.bottom))
    {
        MoveWindow(m_rcPosition.left, m_rcPosition.top,
                   m_rcPosition.right - m_rcPosition.left,
                   m_rcPosition.bottom - m_rcPosition.top);
    }
    
    if (m_dwOptions & STATSDLG_FULLWINDOW)
    {
        RECT  rcClient;
        
        // Resize the list control if needed
        GetClientRect(&rcClient);
        OnSize(SIZE_MAXIMIZED,  rcClient.right - rcClient.left,
               rcClient.bottom - rcClient.top);
        
        // Disable the buttons also
        for (int i=0; i<INDEX_COUNT; i++)
        {
            GetDlgItem(m_rgBtn[i].m_ulId)->ShowWindow(SW_HIDE);
            
            if (i != INDEX_CLOSE)
                GetDlgItem(m_rgBtn[i].m_ulId)->EnableWindow(FALSE);
        }
    }
    
    // If we do not have the select columns then we hide and disable
    // the select columns button.
    if ((m_dwOptions & STATSDLG_SELECT_COLUMNS) == 0)
    {
        GetDlgItem(m_rgBtn[INDEX_SELECT].m_ulId)->ShowWindow(SW_HIDE);
        GetDlgItem(m_rgBtn[INDEX_SELECT].m_ulId)->EnableWindow(FALSE);
    }
    
    // If we do not have the clear button then we hide and disable
    // the clear button.
    if ((m_dwOptions & STATSDLG_CLEAR) == 0)
    {
        GetDlgItem(m_rgBtn[INDEX_CLEAR].m_ulId)->ShowWindow(SW_HIDE);
        GetDlgItem(m_rgBtn[INDEX_CLEAR].m_ulId)->EnableWindow(FALSE);
    }
    
    ListView_SetExtendedListViewStyle(GetDlgItem(IDC_STATSDLG_LIST)->GetSafeHwnd(), LVS_EX_FULLROWSELECT);
    
    // Now initialize the headers
    LoadHeaders();
    
    RefreshData(TRUE);
    
    if (m_pConfig)
    {
        Sort( m_pConfig->GetSortColumn(m_ulId) );
    }
    
    if ((m_dwOptions & STATSDLG_FULLWINDOW) == 0)
    {
        GetDlgItem(IDCANCEL)->SetFocus();
        return FALSE;
    }
    
   return TRUE;
}

/*!--------------------------------------------------------------------------
   StatsDialog::OnOK
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::OnOK()
{
}

/*!--------------------------------------------------------------------------
   StatsDialog::OnCancel
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::OnCancel()
{
   DeleteAllItems();
   
   DestroyWindow();

   // Explicitly kill this thread.
   AfxPostQuitMessage(0);
}

/*!--------------------------------------------------------------------------
   StatsDialog::PostNcDestroy
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::PostNcDestroy()
{
   // Make sure that this is NULL since this is how we detect that
   // the dialog is showing
   m_hWnd = NULL;
   m_bAfterInitDialog = FALSE;
}

/*!--------------------------------------------------------------------------
   StatsDialog::PreCreateWindow
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
BOOL StatsDialog::PreCreateWindow(CREATESTRUCT& cs)
{
   // Have to refresh the event
   Verify( ResetEvent(m_hEventThreadKilled) );
   return CBaseDialog::PreCreateWindow(cs);
}


/*!--------------------------------------------------------------------------
   StatsDialog::OnRefresh
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::OnRefresh()
{
   if ((m_dwOptions & STATSDLG_VERTICAL) == 0)
   {
      DeleteAllItems();
   }

   RefreshData(TRUE);
}

/*!--------------------------------------------------------------------------
   StatsDialog::OnSelectColumns
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::OnSelectColumns()
{
   // We should bring up the columns dialog
   ColumnDlg   columnDlg(NULL);
   ColumnData *pColumnData;
   ULONG    cColumns;
   ULONG    cVisible;
   int         i;
   DWORD    dwWidth;

   if (m_pConfig)
   {
      cColumns = m_pConfig->GetColumnCount(m_ulId);
      cVisible = m_pConfig->GetVisibleColumns(m_ulId);
   }
   else
   {
      cColumns = m_viewInfo.GetColumnCount();
      cVisible = m_viewInfo.GetVisibleColumns();
   }

   pColumnData = (ColumnData *) alloca(sizeof(ColumnData) * cColumns);

   if (m_pConfig)
      m_pConfig->GetColumnData(m_ulId, cColumns, pColumnData);
   else
      m_viewInfo.GetColumnData(cColumns, pColumnData);

   // Save the column width information
   if ((m_dwOptions & STATSDLG_VERTICAL) == 0)
   {
      for (i=0; i<(int) cVisible; i++)
      {
         dwWidth = m_listCtrl.GetColumnWidth(i);
         if (m_pConfig)
            pColumnData[m_pConfig->MapColumnToSubitem(m_ulId, i)].m_dwWidth = dwWidth;
         else
            pColumnData[m_viewInfo.MapColumnToSubitem(i)].m_dwWidth = dwWidth;
      }
   }

   columnDlg.Init(m_pConfig ?
                  m_pConfig->GetColumnInfo(m_ulId) :
                  m_viewInfo.GetColumnInfo(),
               cColumns,
               pColumnData
              );

   if (columnDlg.DoModal() == IDOK)
   {
      if (m_dwOptions & STATSDLG_VERTICAL)
      {
         //$ HACK HACK
         // To save the column info for vertical columns we will save the
         // width data in the first two "columns"
         pColumnData[0].m_dwWidth = m_listCtrl.GetColumnWidth(0);
         pColumnData[1].m_dwWidth = m_listCtrl.GetColumnWidth(1);
      }
      
      // Set the information back in
      if (m_pConfig)
         m_pConfig->SetColumnData(m_ulId, cColumns, pColumnData);
      else
         m_viewInfo.SetColumnData(cColumns, pColumnData);

      // Clear out the data
      DeleteAllItems();
      
      // Remove all of the columns
      if (m_dwOptions & STATSDLG_VERTICAL)
      {
         m_listCtrl.DeleteColumn(1);
         m_listCtrl.DeleteColumn(0);
      }
      else
      {
         for (i=(int) cVisible; --i >= 0; )
            m_listCtrl.DeleteColumn(i);
      }

      // Readd all of the columns
      LoadHeaders();
      
      // Do a refresh
      RefreshData(FALSE);
   }
}

void StatsDialog::OnMove(int x, int y)
{
   if (!m_bAfterInitDialog)
      return;
   
   GetWindowRect(&m_rcPosition);
   if (m_pConfig)
      m_pConfig->SetStatsWindowRect(m_ulId, m_rcPosition);
}

/*!--------------------------------------------------------------------------
   StatsDialog::OnSize
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::OnSize(UINT nType, int cx, int cy)
{
   RECT  rcWnd;
   RECT  rcBtn;
   RECT  rcDlg;
   
   if (nType == SIZE_MINIMIZED)
      return;

   if (m_dwOptions & STATSDLG_FULLWINDOW)
   {
      // If we're full window, resize the list control to fill
      // the entire client area
      ::SetWindowPos(::GetDlgItem(GetSafeHwnd(), IDC_STATSDLG_LIST), NULL,
                  0, 0, cx, cy, SWP_NOZORDER);
   }
   else if (m_sizeMinimum.cx)
   {

      ::GetClientRect(GetSafeHwnd(), &rcDlg);

      // reposition the buttons

      // The widths are caluclated opposite of the normal order
      // since the positions are relative to the right and bottom.
      for (int i=0; i<INDEX_COUNT; i++)
      {
         ::SetWindowPos(::GetDlgItem(GetSafeHwnd(), m_rgBtn[i].m_ulId),
                     NULL,
                     rcDlg.right - m_rgBtn[i].m_rc.left,
                     rcDlg.bottom - m_rgBtn[i].m_rc.top,
                     m_rgBtn[i].m_rc.left - m_rgBtn[i].m_rc.right,
                     m_rgBtn[i].m_rc.top - m_rgBtn[i].m_rc.bottom,
                     SWP_NOZORDER);
      }

      // resize the list control

      ::SetWindowPos(::GetDlgItem(GetSafeHwnd(), IDC_STATSDLG_LIST),
                  NULL,
                  m_rcList.left,
                  m_rcList.top,
                  rcDlg.right - m_rcList.right - m_rcList.left,
                  rcDlg.bottom - m_rcList.bottom - m_rcList.top,
                  SWP_NOZORDER);
   }
   

   if (m_bAfterInitDialog)
   {
      GetWindowRect(&m_rcPosition);
      if (m_pConfig)
         m_pConfig->SetStatsWindowRect(m_ulId, m_rcPosition);
   }
}

/*!--------------------------------------------------------------------------
   StatsDialog::OnGetMinMaxInfo
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::OnGetMinMaxInfo(MINMAXINFO *pMinMax)
{
   pMinMax->ptMinTrackSize.x = m_sizeMinimum.cx;
   pMinMax->ptMinTrackSize.y = m_sizeMinimum.cy;
}

/*!--------------------------------------------------------------------------
   StatsDialog::LoadHeaders
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::LoadHeaders()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());	
    ULONG cVis;
    ULONG i, iPos;
    ULONG ulId;
    CString  st;
    DWORD dwWidth;
    ColumnData  rgColumnData[2];  // used for vertical format
    
    // Load those headers that we have data for
    
    // Go through the column data finding the headers that we have
    if (m_pConfig)
        cVis = m_pConfig->GetVisibleColumns(m_ulId);
    else
        cVis = m_viewInfo.GetVisibleColumns();
    
    if (m_dwOptions & STATSDLG_VERTICAL)
    {
        if (m_pConfig)
            m_pConfig->GetColumnData(m_ulId, 2, rgColumnData);
        else
            m_viewInfo.GetColumnData(2, rgColumnData);
        
        // For the vertical format, the data is on a column
        // Thus we add two columns and fill in the data for the
        // first column
        st.LoadString(IDS_STATSDLG_DESCRIPTION);
        dwWidth = rgColumnData[0].m_dwWidth;
        if (dwWidth == AUTO_WIDTH)
        {
            dwWidth = m_ColWidthAdder + static_cast<DWORD>(m_ColWidthMultiple*m_listCtrl.GetStringWidth((LPCTSTR) st));
        }
        m_listCtrl.InsertColumn(0, st, rgColumnData[0].fmt, dwWidth, 0);
        
        st.LoadString(IDS_STATSDLG_DETAILS);
        dwWidth = rgColumnData[1].m_dwWidth;
        if (dwWidth == AUTO_WIDTH)
        {
            dwWidth = m_ColWidthAdder + static_cast<DWORD>(m_ColWidthMultiple*m_listCtrl.GetStringWidth((LPCTSTR) st));
        }  
        m_listCtrl.InsertColumn(1, st, rgColumnData[1].fmt, dwWidth, 1);
        
        // Now go through and add the rows for each of our "columns"
        for (i=0; i<cVis; i++)
        {
            // Now get the info for iPos
            if (m_pConfig)
                ulId = m_pConfig->GetStringId(m_ulId, i);
            else
                ulId = m_viewInfo.GetStringId(i);
            st.LoadString(ulId);
            Assert(st.GetLength());
            
            m_listCtrl.InsertItem(i, _T(""));
            m_listCtrl.SetItemText(i, 0, (LPCTSTR) st);
        }
    }
    else
    {
        // For the normal horizontal format, the data is on a row
        // so we need to add the various columnar data
        for (i=0; i<cVis; i++)
        {
			int fmt = LVCFMT_LEFT;

            iPos = MapColumnToSubitem(i);
            
            // Now get the info for iPos
            if (m_pConfig)
                ulId = m_pConfig->GetStringId(m_ulId, i);
            else
                ulId = m_viewInfo.GetStringId(i);

            st.LoadString(ulId);
            Assert(st.GetLength());
            
            if (m_pConfig)
			{
                dwWidth = m_pConfig->GetColumnWidth(m_ulId, i);
				m_pConfig->GetColumnData(m_ulId, i, 1, rgColumnData);
				fmt = rgColumnData[0].fmt;
			}
            else
			{
                dwWidth = m_viewInfo.GetColumnWidth(i);
				m_viewInfo.GetColumnData(i, 1, rgColumnData);
				fmt = rgColumnData[0].fmt;
			}
			
            if (dwWidth == AUTO_WIDTH)
            {
                dwWidth = m_ColWidthAdder + static_cast<DWORD>(m_ColWidthMultiple*m_listCtrl.GetStringWidth((LPCTSTR) st));
            }  
            m_listCtrl.InsertColumn(i, st, fmt, dwWidth, iPos);
        }
    }
}

HRESULT StatsDialog::AddToContextMenu(CMenu* pMenu)
{
   return S_OK;
}


/*!--------------------------------------------------------------------------
   StatsDialog::OnContextMenu
      -
   Author: KennT
 ---------------------------------------------------------------------------*/
void StatsDialog::OnContextMenu(CWnd *pWnd, CPoint pos)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());	
    CMenu menu;
    CString  st;
    
    if ((m_dwOptions & STATSDLG_CONTEXTMENU) == 0)
        return;
    
    if (pWnd->GetDlgCtrlID() != IDC_STATSDLG_LIST)
        return;
    
    // Bring up a context menu if we need to
    menu.CreatePopupMenu();
    
    st.LoadString(IDS_STATSDLG_MENU_REFRESH);
    menu.AppendMenu(MF_STRING, IDC_STATSDLG_BTN_REFRESH, st);
    
    if (m_dwOptions & STATSDLG_SELECT_COLUMNS)
    {
        st.LoadString(IDS_STATSDLG_MENU_SELECT);
        menu.AppendMenu(MF_STRING, IDC_STATSDLG_BTN_SELECT_COLUMNS, st);
    }
    
    //virtual override to add additional context menus
    AddToContextMenu(&menu);
    
    menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
                        pos.x,
                        pos.y,
                        this,
                        NULL);
}


void StatsDialog::OnNotifyListControlClick(NMHDR *pNmHdr, LRESULT *pResult)
{
   NM_LISTVIEW *  pnmlv = reinterpret_cast<NM_LISTVIEW *>(pNmHdr);

   if (m_pConfig)
      m_pConfig->SetSortColumn(m_ulId, pnmlv->iSubItem);

   // Call through to the user to sort
   Sort(pnmlv->iSubItem);

   if (m_pConfig)
      m_pConfig->SetSortDirection(m_ulId, m_fSortDirection);
}


void StatsDialog::Sort(UINT nColumn)
{
   // Default is to do nothing
}

void StatsDialog::PreDeleteAllItems()
{
}

void StatsDialog::DeleteAllItems()
{
   PreDeleteAllItems();
   m_listCtrl.DeleteAllItems();
}

void StatsDialog::PostRefresh()
{
   if (GetSafeHwnd())
      PostMessage(WM_COMMAND, IDC_STATSDLG_BTN_REFRESH);
}

/*!--------------------------------------------------------------------------
   StatsDialog::SetColumnWidths
      Loops through all items and calculates the max width for columns
        in a listbox.
   Author: EricDav
 ---------------------------------------------------------------------------*/
void StatsDialog::SetColumnWidths(UINT uNumColumns)
{
    // Set the default column widths to the width of the widest column
    int * aColWidth = (int *) alloca(uNumColumns * sizeof(int));
    int nRow, nCol;
    CString strTemp;
    
    ZeroMemory(aColWidth, uNumColumns * sizeof(int));

    // for each item, loop through each column and calculate the max width
    for (nRow = 0; nRow < m_listCtrl.GetItemCount(); nRow++)
    {
        for (nCol = 0; nCol < (int) uNumColumns; nCol++)
        {
            strTemp = m_listCtrl.GetItemText(nRow, nCol);
            if (aColWidth[nCol] < m_listCtrl.GetStringWidth(strTemp))
                aColWidth[nCol] = m_listCtrl.GetStringWidth(strTemp);
        }
    }
    
    // now update the column widths based on what we calculated
    for (nCol = 0; nCol < (int) uNumColumns; nCol++)
    {
        // GetStringWidth doesn't seem to report the right thing,
        // so we have to add a fudge factor of 15.... oh well.
        m_listCtrl.SetColumnWidth(nCol, aColWidth[nCol] + 15);
    }
}


void StatsDialog::SetConfigInfo(ConfigStream *pConfig, ULONG ulId)
{
   m_pConfig = pConfig;
   m_ulId = ulId;
}

void StatsDialog::SetPosition(RECT rc)
{
   m_rcPosition = rc;
}

void StatsDialog::GetPosition(RECT *prc)
{
   *prc = m_rcPosition;
}

void CreateNewStatisticsWindow(StatsDialog *pWndStats,
                        HWND hWndParent,
                        UINT  nIDD)
{                         
   ModelessThread *  pMT;

   // If the dialog is still up, don't create a new one
   if (pWndStats->GetSafeHwnd())
   {
      ::SetActiveWindow(pWndStats->GetSafeHwnd());
      return;
   }

   pMT = new ModelessThread(hWndParent,
                      nIDD,
                      pWndStats->GetSignalEvent(),
                      pWndStats);
   pMT->CreateThread();
}

void WaitForStatisticsWindow(StatsDialog *pWndStats)
{
   if (pWndStats->GetSafeHwnd())
   {
      // Post a cancel to that window
      // Do an explicit post so that it executes on the other thread
      pWndStats->PostMessage(WM_COMMAND, IDCANCEL, 0);

      // Now we need to wait for the event to be signalled so that
      // its memory can be cleaned up
      WaitForSingleObject(pWndStats->GetSignalEvent(), INFINITE);
   }
   
}