// tMonitorView.cpp : implementation of the CtMonitorView class
//

#include "stdafx.h"
#include "tMonitor.h"

#include "tMonitorDoc.h"
#include "tMonitorView.h"
#include "tPhoneNo.h"
#include "CallScreenDlg.h"
#include "LinesDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// CtMonitorView

enum ListColumn
{
    colName = 0,
    colPhoneNo,
    colDateTime,
    colDuration,
    colRings,
    colState,
    colColumns,
};

static
const char* g_pszStateName[] = 
{
    "Over", // Idle
    "Offering",
    "Accepted",
    "Dial Tone",
    "Dialing",
    "Ringing",
    "Busy",
    "Special Info",
    "Connect",
    "Proceeding",
    "On Hold",
    "Conferenced",
    "On Hold Pending Conference",
    "On Hold Pending Transfer",
    "Disconnected",
    "Unknown",
};

// Translate bit field into the single set bit
// (0-based, of course)
static
int BitSet(DWORD dw)
{
    if( !dw ) return -1;

    int nBitSet = 0;
    while( !(dw & 1) )
    {
        //dw /= 2;
        dw = dw >> 1;
        nBitSet++;
    }
    
    return nBitSet;
}

static
void GetBestCallerID(
    const CtCallInfo&   ci,
    CString*            psName,
    CString*            psPhoneNo)
{
    LPCSTR      pszName = 0;
    LPCSTR      pszPhoneNo = 0;
    CtPhoneNo   pno;
    
    if( ci.GetCallerIDFlags() & LINECALLPARTYID_BLOCKED )
    {
        pszName = "<blocked>";
        pszPhoneNo = "<blocked>";
    }
    else if( ci.GetCallerIDFlags() & LINECALLPARTYID_OUTOFAREA )
    {
        pszName = "<out of area>";
        pszPhoneNo = "<out of area>";
    }
    else if( ci.GetCallerIDFlags() & LINECALLPARTYID_UNKNOWN )
    {
        pszName = "<unknown>";
        pszPhoneNo = "<unknown>";
    }
    else if( ci.GetCallerIDFlags() & LINECALLPARTYID_UNAVAIL )
    {
        pszName = "<unavailable>";
        pszPhoneNo = "<unavailable>";
    }
    else
    {
        pszName = ci.GetCallerIDName();

        pno.SetWholePhoneNo(ci.GetCallerID());
        pszPhoneNo = pno.GetTranslatable(0);
    }
    
    *psName = (pszName ? pszName : "<unknown>");
    *psPhoneNo = (pszPhoneNo ? pszPhoneNo : "<unknown>");
}

IMPLEMENT_DYNCREATE(CtMonitorView, CListView)

BEGIN_MESSAGE_MAP(CtMonitorView, CListView)
	//{{AFX_MSG_MAP(CtMonitorView)
	ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick)
	ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
	ON_NOTIFY_REFLECT(LVN_DELETEITEM, OnDeleteitem)
	ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetdispinfo)
	ON_NOTIFY_REFLECT(NM_RETURN, OnReturn)
	ON_WM_DESTROY()
	ON_COMMAND(ID_MONITOR_DIAL, OnMonitorDial)
	ON_UPDATE_COMMAND_UI(ID_MONITOR_DIAL, OnUpdateMonitorDial)
	ON_COMMAND(ID_VIEW_AUTOSIZECOLUMNS, OnViewAutosizecolumns)
	ON_UPDATE_COMMAND_UI(ID_VIEW_AUTOSIZECOLUMNS, OnUpdateViewAutosizecolumns)
	ON_WM_TIMER()
	ON_COMMAND(ID_VIEW_MESSAGES, OnViewMessages)
	ON_COMMAND(ID_MONITOR_CALLSCREENSETTINGS, OnMonitorCallScreenSettings)
	ON_COMMAND(ID_VIEW_LINES, OnViewLines)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CtMonitorView construction/destruction

CtMonitorView::CtMonitorView()
    :
    m_sp(0, TRUE),
    m_bViewInited(FALSE),
    m_nActiveCalls(0),
    m_nTimer(0),
    m_rgLines(0)
{
}

CtMonitorView::~CtMonitorView()
{
    delete[] m_rgLines;
}

BOOL CtMonitorView::PreCreateWindow(CREATESTRUCT& cs)
{
    cs.style |= LVS_REPORT | LVS_SINGLESEL | LVS_OWNERDRAWFIXED;
	return CListViewEx::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CtMonitorView drawing

void CtMonitorView::OnDraw(CDC* pDC)
{
	CtMonitorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
}

void CtMonitorView::OpenValidLines()
{
    // Open all of the lines to watch for new calls
    if( m_rgLines = new CtLine[::TfxGetNumLines()] )
    {
        for( DWORD nLineID = 0; nLineID < ::TfxGetNumLines(); nLineID++ )
        {
            TRESULT tr;

            // NOTE: Need automated voice to be able to generate digits
            // NOTE: Need unknown to be able to get incoming calls
            tr = m_rgLines[nLineID].Open(nLineID, this,
                                         LINECALLPRIVILEGE_MONITOR | LINECALLPRIVILEGE_OWNER,
                                         LINEMEDIAMODE_UNKNOWN | LINEMEDIAMODE_AUTOMATEDVOICE);
            if( FAILED(tr) )
            {
                // If we can't do automated voice, drop back and punt
                tr = m_rgLines[nLineID].Open(nLineID, this,
                                             LINECALLPRIVILEGE_MONITOR);
            }

            // Check for existing calls
            if( TSUCCEEDED(tr) )
            {
                CtCallList  cl;
                if( TSUCCEEDED(cl.GetNewCalls(m_rgLines[nLineID].GetHandle())) )
                {
                    for( DWORD nCall = 0; nCall < cl.GetNumCalls(); nCall++ )
                    {
                        MonitorCall(&m_rgLines[nLineID], cl.GetCall(nCall));
                    }
                }
            }
        }

        SetNumRings(::AfxGetApp()->GetProfileInt("Settings", "Rings", 4));
    }
}

void CtMonitorView::OnInitialUpdate()
{
    // Guard against re-initing during SDI view reuse
    if( !m_bViewInited )
    {
        CWinApp*    pApp = ::AfxGetApp();
        m_sp.nColumn = pApp->GetProfileInt("Settings", "SortColumn", m_sp.nColumn);
        m_sp.bAscending = pApp->GetProfileInt("Settings", "SortAscending", m_sp.bAscending);

        int         acxColumns[colColumns];
        char        szEntry[16];

        // Set up column headers
        CRect       rect;
        GetClientRect(&rect);
        int         nDefaultWidth = rect.Width()/colColumns;

        for( int i = 0; i < colColumns; i++ )
        {
            ::wsprintf(szEntry, "ColumnWidth%d", i);
            acxColumns[i] = pApp->GetProfileInt("Settings", szEntry, nDefaultWidth);
        }

        CListCtrl&  lc = GetListCtrl();

        lc.InsertColumn(colName, "Name", LVCFMT_LEFT, acxColumns[colName]);
        lc.InsertColumn(colPhoneNo, "Phone No.", LVCFMT_LEFT, acxColumns[colPhoneNo]);
        lc.InsertColumn(colDateTime, "Date/Time",  LVCFMT_LEFT, acxColumns[colDateTime]);
        lc.InsertColumn(colDuration, "Duration",  LVCFMT_LEFT, acxColumns[colDuration]);
        lc.InsertColumn(colRings, "Rings",  LVCFMT_LEFT, acxColumns[colRings]);
        lc.InsertColumn(colState, "State",  LVCFMT_LEFT, acxColumns[colState]);

        OpenValidLines();

        m_bViewInited = TRUE;
    }

    // Call parent (which calls OnUpdate())
    CListViewEx::OnInitialUpdate();
}

BOOL CtMonitorView::AppendCallRecord(
    CALLRECORD* pcr)
{
    if( pcr )
    {
        CListCtrl&  lc = GetListCtrl();
        return (lc.InsertItem(LVIF_TEXT | LVIF_PARAM, lc.GetItemCount(),
                              LPSTR_TEXTCALLBACK, 0, 0, 0, LPARAM(pcr)) >= 0
                ? TRUE : FALSE);
    }
    else
    {
        return FALSE;
    }
}

BOOL CtMonitorView::FindCallRecord(
    CtCall*         pCall,
    CALLRECORD**    ppcr,
    int*            pnItem)
{
    ASSERT(pCall);
    ASSERT(ppcr);
    ASSERT(pnItem);

    CListCtrl&  lc = GetListCtrl();
    for( int nItem = 0; nItem < lc.GetItemCount(); nItem++ )
    {
        CALLRECORD* pcr = (CALLRECORD*)lc.GetItemData(nItem);
        ASSERT(pcr);

        if( pcr->pCall == pCall )
        {
            *ppcr = pcr;
            *pnItem = nItem;
            return TRUE;
        }
    }

    *ppcr = 0;
    *pnItem = 0;
    return FALSE;
}

BOOL CtMonitorView::FindCallRecord(
    CtLine*         pLine,
    CALLRECORD**    ppcr,
    int*            pnItem)
{
    ASSERT(pLine);
    ASSERT(ppcr);
    ASSERT(pnItem);

    CListCtrl&  lc = GetListCtrl();
    for( int nItem = 0; nItem < lc.GetItemCount(); nItem++ )
    {
        CALLRECORD* pcr = (CALLRECORD*)lc.GetItemData(nItem);
        ASSERT(pcr);

        if( pcr->pCall &&
            pcr->pCall->GetLine() == pLine )
        {
            *ppcr = pcr;
            *pnItem = nItem;
            return TRUE;
        }
    }

    *ppcr = 0;
    *pnItem = 0;
    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CtMonitorView diagnostics

#ifdef _DEBUG
void CtMonitorView::AssertValid() const
{
	CListViewEx::AssertValid();
}

void CtMonitorView::Dump(CDumpContext& dc) const
{
	CListViewEx::Dump(dc);
}

CtMonitorDoc* CtMonitorView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CtMonitorDoc)));
	return (CtMonitorDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CtMonitorView message handlers

void CtMonitorView::DoDial()
{
    CListCtrl&  lc = GetListCtrl();
    int         nItem = lc.GetNextItem(-1, LVNI_SELECTED);

    if( nItem >= 0 )
    {
        CALLRECORD* pcr = (CALLRECORD*)lc.GetItemData(nItem);
        ASSERT(pcr);

        // Check for a non-empty and non-special phone number
        if( !pcr->sPhoneNo.IsEmpty() && pcr->sPhoneNo[0] != '<' )
        {
            // Translate the phone number the best we can
            // to canonical or at least all number format
            CtPhoneNo   pno(pcr->sPhoneNo);
            TRESULT     tr = ::tapiRequestMakeCall(pno.GetTranslatable(),
                                                   ::AfxGetAppName(), pcr->sName, 0);
            switch( tr )
            {
            case TAPIERR_NOREQUESTRECIPIENT:
                ::AfxMessageBox(IDS_NOREQUESTRECIPIENT);
            break;

            case TAPIERR_REQUESTQUEUEFULL:
                ::AfxMessageBox(IDS_REQUESTQUEUEFULL);
            break;

            case TAPIERR_INVALDESTADDRESS:
                ::AfxMessageBox(IDS_INVALDESTADDRESS);
            break;

            case TAPIERR_INVALPOINTER:
                TRACE("TAPIERR_INVALPOINTER\n");
                ASSERT(FALSE);
            break;
            }
        }
    }
}

void CtMonitorView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) 
{
    DoDial();
	*pResult = 0;
}

int CALLBACK CompareItems(LPARAM lP1, LPARAM lP2, LPARAM lPSort)
{
    CtMonitorView::CALLRECORD*  pcr1 = (CtMonitorView::CALLRECORD*)lP1;
    CtMonitorView::CALLRECORD*  pcr2 = (CtMonitorView::CALLRECORD*)lP2;
    CtMonitorView::SORTPARAMS*  psp = (CtMonitorView::SORTPARAMS*)lPSort;
    int                         nCompare;

    // Compare assuming ascending sorting
    switch( psp->nColumn )
    {
    // Sort by name and date/time
    case colName:
        nCompare = pcr1->sName.CompareNoCase(pcr2->sName);
        if( !nCompare )
        {
            nCompare = pcr1->tmStart - pcr2->tmStart;
        }
    break;

    // Sort by phone no., name and date/time
    case colPhoneNo:
        nCompare = pcr1->sPhoneNo.CompareNoCase(pcr2->sPhoneNo);
        if( !nCompare )
        {
            nCompare = pcr1->sName.CompareNoCase(pcr2->sName);
            if( !nCompare )
            {
                nCompare = pcr1->tmStart - pcr2->tmStart;
            }
        }
    break;

    // Sort by date/time and name
    case colDateTime:
        nCompare = pcr1->tmStart - pcr2->tmStart;
        if( !nCompare )
        {
            nCompare = pcr1->sName.CompareNoCase(pcr2->sName);
        }
    break;

    // Sort by duration and name
    case colDuration:
        nCompare = pcr1->GetDuration() - pcr2->GetDuration();
        if( !nCompare )
        {
            nCompare = pcr1->sName.CompareNoCase(pcr2->sName);
        }
    break;

    // Sort by rings, date/time and name
    case colRings:
        nCompare = pcr1->nRings - pcr2->nRings;
        if( !nCompare )
        {
            nCompare = pcr1->tmStart - pcr2->tmStart;
            if( !nCompare )
            {
                nCompare = pcr1->sName.CompareNoCase(pcr2->sName);
            }
        }
    break;

    // Sort by result, date/time and name
    case colState:
        nCompare = strcmp(g_pszStateName[BitSet(pcr1->nState)],
                          g_pszStateName[BitSet(pcr2->nState)]);
        if( !nCompare )
        {
            nCompare = pcr1->tmStart - pcr2->tmStart;
            if( !nCompare )
            {
                nCompare = pcr1->sName.CompareNoCase(pcr2->sName);
            }
        }
    break;

    default:
        TRACE1("CompareItems(): Unknown nSortCol= %d\n", psp->nColumn);
        ASSERT(FALSE);
    break;
    }

    // Check for descending sort
    if( !psp->bAscending )
    {
        nCompare = (0 - nCompare);
    }
    return nCompare;
}

void CtMonitorView::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult) 
{
    m_sp.nColumn = ((NM_LISTVIEW*)pNMHDR)->iSubItem;
    m_sp.bAscending = (::GetKeyState(VK_SHIFT) < 0 ? FALSE : TRUE);

    GetListCtrl().SortItems(CompareItems, LPARAM(&m_sp));
	*pResult = 0;
}

// Let the document handle this
void CtMonitorView::OnDeleteitem(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
    delete (CALLRECORD*)(pNMListView->lParam);
	*pResult = 0;
}

// Override so base class can do it's magic
LPCSTR CtMonitorView::GetColumnText(int nColumn, LPARAM lParam)
{
    CALLRECORD* pcr = (CALLRECORD*)(lParam);
    
    switch( nColumn )
    {
    case colName:
        return pcr->sName;
    break;
    
    case colPhoneNo:
        return pcr->sPhoneNo;
    break;    
        
    case colDateTime:
    {
        static char szBuf[64];
        strftime(szBuf, sizeof(szBuf), "%#c", localtime(&(pcr->tmStart)));
        return szBuf;
    }
    break;    

    case colDuration:
    {
        static char szBuf[32];
        sprintf(szBuf, "%d min. %d sec.",
                pcr->GetDuration()/60, pcr->GetDuration()%60);
        return szBuf;
    }
    break;
        
    case colRings:
    {
        static char szBuf[8];
        return ::ltoa(pcr->nRings, szBuf, 10);
    }
    break;
        
    case colState:
        return g_pszStateName[BitSet(pcr->nState)];
    break;
        
    default:
        TRACE1("Unknown nColumn= %d\n", nColumn);
        return 0;
    break;
    }
}

// Don't need this if I'm doing owner-draw,
// except to handle finding entries by typing
void CtMonitorView::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_DISPINFO*    pDispInfo = (LV_DISPINFO*)pNMHDR;

    if( pDispInfo->item.mask & LVIF_TEXT )
    {
        LV_ITEM&    lvi = pDispInfo->item;
        lvi.pszText = LPSTR(GetColumnText(lvi.iSubItem, lvi.lParam));
    }
	
	*pResult = 0;
}

void CtMonitorView::OnReturn(NMHDR* pNMHDR, LRESULT* pResult) 
{
    DoDial();
	*pResult = 0;
}

void CtMonitorView::OnDestroy() 
{
    CWinApp*    pApp = ::AfxGetApp();
    pApp->WriteProfileInt("Settings", "SortColumn", m_sp.nColumn);
    pApp->WriteProfileInt("Settings", "SortAscending", m_sp.bAscending);

    CListCtrl&  lc = GetListCtrl();
    char        szEntry[16];

    // Save column headers
    CHeaderCtrl*    pHeader = GetHeaderCtrl();
    ASSERT(pHeader);

    for( int i = 0; i < pHeader->GetItemCount(); i++ )
    {
        ::wsprintf(szEntry, "ColumnWidth%d", i);
        pApp->WriteProfileInt("Settings", szEntry, lc.GetColumnWidth(i));
    }

    CListViewEx::OnDestroy();
}

CHeaderCtrl* CtMonitorView::GetHeaderCtrl()
{
    return (CHeaderCtrl*)GetDlgItem(0);
}

void CtMonitorView::OnMonitorDial() 
{
	DoDial();
}

void CtMonitorView::OnUpdateMonitorDial(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(GetListCtrl().GetNextItem(-1, LVNI_SELECTED) >= 0);
}

void CtMonitorView::OnViewAutosizecolumns() 
{
    CListCtrl&      lc = GetListCtrl();
    CHeaderCtrl*    pHeader = GetHeaderCtrl();
	for( int i = 0; i < pHeader->GetItemCount(); i++ )
    {
        lc.SetColumnWidth(i, LVSCW_AUTOSIZE);
        //lc.SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
    }
}

void CtMonitorView::OnUpdateViewAutosizecolumns(CCmdUI* pCmdUI) 
{
    pCmdUI->Enable(GetListCtrl().GetItemCount() > 0 ? TRUE : FALSE);
}

void CtMonitorView::MonitorCall(
    CtLine* pLine,
    HCALL   hCall)
{
    CtCallInfo  ci;
    CString     sName;
    CString     sPhoneNo;
    BOOL        bInbound = FALSE;

    if( SUCCEEDED(ci.GetCallInfo(hCall)) )
    {
        switch( ci.GetOrigin() )
        {
        case LINECALLORIGIN_OUTBOUND:
            sName = ci.GetCalledParty();
            sPhoneNo = ci.GetDisplayableAddress();

            if( sName.IsEmpty() ) sName = "<unknown>";
            if( sPhoneNo.IsEmpty() ) sPhoneNo = "<unknown>";

            /*
            TRACE0("Outbound Call Info:\n");
            TRACE1("CallerID= %s\n", ci.GetCallerID());
            TRACE1("CallerIDName= %s\n", ci.GetCallerIDName());
            TRACE1("CalledID= %s\n", ci.GetCalledID());
            TRACE1("CalledIDName= %s\n", ci.GetCalledIDName());
            TRACE1("ConnectedID= %s\n", ci.GetConnectedID());
            TRACE1("ConnectedIDName= %s\n", ci.GetConnectedIDName());
            TRACE1("RedirectionID= %s\n", ci.GetRedirectionID());
            TRACE1("RedirectionIDName= %s\n", ci.GetRedirectionIDName());
            TRACE1("RedirectingID= %s\n", ci.GetRedirectingID());
            TRACE1("RedirectingIDName= %s\n", ci.GetRedirectingIDName());
            TRACE1("AppName= %s\n", ci.GetAppName());
            TRACE1("DisplayableAddress= %s\n", ci.GetDisplayableAddress());
            TRACE1("CalledParty= %s\n", ci.GetCalledParty());
            TRACE1("Comment= %s\n", ci.GetComment());
            TRACE1("Display= %s\n", ci.GetDisplay());
            */
        break;

        case LINECALLORIGIN_INTERNAL:
        case LINECALLORIGIN_EXTERNAL:
        case LINECALLORIGIN_UNKNOWN:
        case LINECALLORIGIN_UNAVAIL:
        case LINECALLORIGIN_INBOUND:
            bInbound = TRUE;
        // no break;

        default:
            GetBestCallerID(ci, &sName, &sPhoneNo);
        break;
        }
    }

    CtCall*     pCall = new CtCall(pLine, hCall, this);
    CALLRECORD* pcr = new CALLRECORD(sName, sPhoneNo, LINECALLSTATE_UNKNOWN, pCall, bInbound);

    if( pcr && AppendCallRecord(pcr) )
    {
        // If this is the first active call,
        // set the timer to update duration
        if( ++m_nActiveCalls == 1 )
        {
            m_nTimer = SetTimer(1, 5000, 0);
        }
    }
}

// Handle telephony events of interest
void CtMonitorView::OnLineNewCall(
    CtLine* pLine,
    HCALL   hCall,
    DWORD   nAddressID,
    DWORD   nCallPriviledge)
{
    MonitorCall(pLine, hCall);
}

void CtMonitorView::OnLineDevState(
    CtLine* pLine,
    DWORD   nDevState,
    DWORD   dwParam2,
    DWORD   dwParam3)
{
    if( nDevState == LINEDEVSTATE_RINGING )
    {
        // Update the view
        CALLRECORD* pcr;
        int         nItem;
        if( FindCallRecord(pLine, &pcr, &nItem) )
        {
            ASSERT(pcr);

            pcr->nState = LINECALLSTATE_RINGBACK;
            pcr->nRings = dwParam3;

            GetListCtrl().Update(nItem);
        }

        // If we've reached the number of rings, take a message
        if( pcr->nMaxRings && pcr->nRings >= pcr->nMaxRings )
        {
            pcr->pdlg->OnTakeMessage();
        }
    }
}

void CtMonitorView::OnCallInfo(
    CtCall* pCall,
    DWORD   nCallInfo)
{
    // Possible to get several callinfo
    // notifications in one message.
    if( nCallInfo & LINECALLINFOSTATE_CALLERID )
    {
        CALLRECORD* pcr;
        int         nItem;
        if( FindCallRecord(pCall, &pcr, &nItem) )
        {
            ASSERT(pcr);
            
            CtCallInfo  ci;
            if( TSUCCEEDED(ci.GetCallInfo(pCall)) )
            {
                GetBestCallerID(ci, &(pcr->sName), &(pcr->sPhoneNo));
                pcr->UpdateDialog();
                GetListCtrl().Update(nItem);
            }
        }
    }
}

void CtMonitorView::OnCallState(
    CtCall* pCall,
    DWORD   nCallState,
    DWORD   dwParam2,
    DWORD   nCallPriviledge)
{
    CALLRECORD* pcr;
    int         nItem;
    if( FindCallRecord(pCall, &pcr, &nItem) )
    {
        ASSERT(pcr);
        pcr->nState = nCallState;

        switch( nCallState )
        {
        case LINECALLSTATE_RINGBACK:
            // Update the number of rings
            // (outgoing calls only)
            pcr->nRings++;
        break;

        case LINECALLSTATE_DISCONNECTED:
            pCall->Drop();
        break;

        case LINECALLSTATE_IDLE:
            // When TAPI's done w/ a call, so are we
            pcr->SetCallIdle();

            // If this is the last active call,
            // kill the timer
            if( --m_nActiveCalls == 0 )
            {
                KillTimer(m_nTimer);
                m_nTimer = 0;
            }
        break;
        }

        GetListCtrl().Update(nItem);
    }
}


void CtMonitorView::OnTimer(UINT nIDEvent) 
{
	// Walk the list for active calls
    CListCtrl&  lc = GetListCtrl();
    for( int nItem = 0; nItem < lc.GetItemCount(); nItem++ )
    {
        CALLRECORD* pcr = (CALLRECORD*)lc.GetItemData(nItem);
        ASSERT(pcr);

        // Updating the item will show a new duration
        if( !pcr->tmEnd )
        {
            lc.Update(nItem);
        }
    }
}

void CtMonitorView::OnViewMessages() 
{
    const CString   sMessages = "Messages";

    CString sPathName;
    GetModuleFileName(0, sPathName.GetBuffer(MAX_PATH+1), MAX_PATH);
    sPathName.ReleaseBuffer();
    sPathName = sPathName.Left(sPathName.ReverseFind('\\'));
    sPathName += '\\';
    sPathName += sMessages;
    sPathName += '\\';

    if( !SetCurrentDirectory(sPathName) &&
        !CreateDirectory(sPathName, 0) )
    {
        TRACE1("Failed to create messages directory: %s\n", (LPCSTR)sPathName);
    }

    if( (int)ShellExecute(GetSafeHwnd(), "open", sPathName, 0, sPathName, SW_SHOWDEFAULT) <= 32 )
    {
        AfxMessageBox(IDS_CANT_OPEN_MESSAGES);
    }
}

void CtMonitorView::OnMonitorCallScreenSettings() 
{
    CWinApp*        pApp = ::AfxGetApp();
    CCallScreenDlg  dlg;
    dlg.m_nRings = pApp->GetProfileInt("Settings", "Rings", 4);
    if( dlg.DoModal() == IDOK )
    {
        pApp->WriteProfileInt("Settings", "Rings", dlg.m_nRings);
        SetNumRings(dlg.m_nRings);
    }
}

void CtMonitorView::SetNumRings(DWORD nRings)
{
    if( m_rgLines )
    {
        for( DWORD nLineID = 0; nLineID < ::TfxGetNumLines(); nLineID++ )
        {
            if( m_rgLines[nLineID].GetHandle() )
            {
                CtLineDevCaps   ldc;
                if( TSUCCEEDED(ldc.GetDevCaps(nLineID)) )
                {
                    for( DWORD nAddressID = 0; nAddressID < ldc.GetNumAddresses(); nAddressID++ )
                    {
                        m_rgLines[nLineID].SetNumRings(nAddressID, nRings);
                    }
                }
            }
        }
    }
}

void CtMonitorView::OnViewLines() 
{
	CLinesDlg   dlg;
    dlg.m_rgLines = m_rgLines;
    dlg.DoModal();
}

