DIALER.C

/*++  

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

dialer.c

--*/


#include "dialer.h"
#include "string.h"
#include "stdlib.h"
#include "shellapi.h"


#defineISDIGIT(x)(((x) - '0') >= 0) && (((x) - '0') <= 9)
enum NumberTypes
{
LOCAL_NUMBER = 7,
EXTENDED_LOCAL_NUMBER,
LONG_DISTANCE_NUMBER = 10,
EXTENDED_LONG_DISTANCE_NUMBER
};


// structs
typedef struct tagLINEINFO
{
DWORD nAddr; // Number of avail. addresses on the line
BOOL fVoiceLine; // Is this a voice line?
DWORD dwAPIVersion; // API version which the line supports
HLINE hLine; // line handle returned by lineOpen
DWORD dwPermanentLineID; // Permanent line ID retreived from devcaps
char szLineName[MAXBUFSIZE]; // the line's name

} LINEINFO, *LPLINEINFO;


// Global variables

// window/instance variables
HWND ghWndMain;
HWND ghWndDialing = NULL;
HINSTANCE ghInst = 0;

// file name vars.
static TCHAR gszAppName[64];
static TCHAR gszINIfilename [] = "DIALER.INI";
static TCHAR gszHELPfilename [] = "DIALER.HLP";
static TCHAR gszDialerClassName[] = "DialerClass";
TCHAR const gszNULL[] = "";

// window item variables
HFONT ghFontBtn; // handle to 1, 2, ..., 0's font
HFONT ghFontBtnText; // handle to number button's text font
HFONT ghFontBtnStar; // handle to * and # button's font
HLINEAPP ghLineApp = NULL; // Dialer's usage handle (regist. w/TAPI)
HCALL ghCall = NULL; // call handle for Dialer's call

LPSTR gszCurrentNumber = NULL; // number of destination of current call
LPSTR gszCurrentName = NULL; // name of destination of current call

BOOL gfRegistered; // was lineRegisterRequestRecipient()
// successful?

//BOOL gfDropping = FALSE; // is Dialer currently dropping a call?
BOOL gfNeedToReinit = FALSE; // does Dialer need to re-initialize?

BOOL gfCallRequest = FALSE; // Does a Simple TAPI app want a call?
BOOL gfCurrentLineAvail = TRUE; // Simple TAPI requests are only carried
// out if the current chosen line is avail.
BOOLgfMakeCallReplyPending = FALSE;

LONG gMakeCallRequestID = 0; // request ID returned by async TAPI fns.
LONG gDropCallRequestID = 0; // request ID returned by async TAPI fns.

DWORD gnAvailDevices = 0; // # of line devices avail. to Dialer
LINEINFO gCurrentLineInfo;
DWORD * gnAddr;

// global to remember where the cursor is in the edit control
DWORD gdwStartSel;
DWORD gdwEndSel;

DWORD * gdwPLID; // current line's permanent line ID
DWORD giCurrentLine = (DWORD)-1; // the line selected by the user
DWORD giCurrentAddress = 0; // the address selected by the user

// + 1 so we can work 1-based rather than 0-based (for convenience only)
// global varibles to hold the names and address of the
TCHAR gszSDNumber[ NSPEEDDIALS + 1 ][ TAPIMAXDESTADDRESSSIZE ];


// Function declarations

// button related functions
VOID ButtonFontSetup(VOID);
VOID DrawButton(HDC hdc, RECT rcBtn, BOOL fHighlighted);
VOID DrawButtonText(HDC hdc, RECT rcBtn, BOOL fHighlighted, UINT IDD_Btn);
VOID DisableDialButtons(BOOL fDisable);
VOID FitTextToButton( HWND, INT, LPSTR );

// Callback functions
BOOL CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DialingProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK ConnectUsingProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK LineInUseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK SpeedDial1Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK SpeedDial2Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
VOID CALLBACK tapiCallback (
DWORD hDevice, DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2,
DWORD dwParam3
);

// tapi related functions
VOID ManageAssistedTelephony(VOID);
VOID InitiateCall(LPCSTR szNumber, LPCSTR szName);

VOID DialerLineClose(VOID);
VOID DialerCleanup(VOID);
VOID CloseTAPI(VOID);

DWORD GetLineInfo(DWORD iLine, LPLINEINFO lpLineInfo);
VOID GetLineInfoFailed (
DWORD iLine, LPLINEDEVCAPS lpDevCaps,
LPLINEINFO lpLineInfo
);
LPSTR GetAddressName(DWORD iLine, DWORD iAddress);
BOOL MakeCanonicalNumber( LPCSTR szName, LPSTR szCanNumber );

// misc. helper functions
VOID ReadINI(VOID);
int errString(HWND hWnd, UINT errCode, UINT uFlags);
VOID AddToRedialList(LPCSTR szNumber);
BOOL InitializeLineBox(HWND hwndLineBox);
BOOL InitializeAddressBox(HWND hwndLineBox, HWND hwndAddressBox);
BOOL Is911 ( LPLINETRANSLATEOUTPUT lpTransOut );
VOID AmpersandCompensate( LPCSTR lpszSrc, LPSTR lpszDst );
VOID AmpersandDeCompensate( LPCSTR lpszSrc, LPSTR lpszDst );

// Dialer memory management functions
LPVOID DialerAlloc(size_t cbToAlloc);
LPVOID DialerFree(LPVOID lpMem);


// Function definitions


//***************************************************************************
//***************************************************************************
//***************************************************************************
DWORD InitializeTAPI (VOID)
{
INT cvLine;

DWORD iLine;
DWORD dwPreferredPLID, dwID = (DWORD) -1;

MSG msg;

LPLINEINFO lpLineInfo = NULL;// LINEINFO for each available line

DWORD errCode;
DWORD tc = GetTickCount();
DWORD dwReturn = ERR_NONE;

char szBuffer[MAXBUFSIZE];// to read in dwPreferredPLID as a string first



errCode = lineInitialize (
&ghLineApp,
ghInst,
(LINECALLBACK) tapiCallback,
gszAppName,
&gnAvailDevices
);
if ( errCode == LINEERR_REINIT )
{
// take away dialer functionality
EnableWindow( ghWndMain, FALSE );
DisableDialButtons(TRUE);

// keep trying until the user cancels
// or we stop getting LINEERR_REINIT
while ( ( errCode = lineInitialize (
&ghLineApp,
ghInst,
(LINECALLBACK)tapiCallback,
gszAppName,
&gnAvailDevices
) )
== LINEERR_REINIT )
{
// flush queue & yield
while ( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

// bring up the box if 5 seconds have passed since
if(GetTickCount() > 5000 + tc)
{
if ( errString( ghWndMain, ikszWarningTapiReInit, MB_RETRYCANCEL )
== IDCANCEL )
{
break;
}
// reset the relative counter
tc = GetTickCount();
}
}

// give back dialer functionality
DisableDialButtons( FALSE );
EnableWindow( ghWndMain, TRUE );
}

if ( errCode )
{
dwReturn = errCode;
goto tapiinit_exit;
}

// retrieve preferred line info from INI file
GetPrivateProfileString (
"Preference",
"Preferred Line",
gszNULL,
szBuffer,
MAXBUFSIZE,
gszINIfilename
);

// if szBuffer is not empty.
if ( lstrcmp ( gszNULL, szBuffer ) )
{
dwPreferredPLID = (DWORD) atoi( szBuffer );
}
else
{
dwPreferredPLID = (DWORD) -1;
}

// -1 default - tells us if it ever gets set
giCurrentLine = (DWORD) -1;

// allocate buffer for storing LINEINFO for all of the available lines
// always allocate space for at least one line
if ( gnAvailDevices == 0 )
{
gnAddr = (DWORD *) DialerAlloc( sizeof( DWORD ) );
gdwPLID = (DWORD *) DialerAlloc( sizeof( DWORD ) );
lpLineInfo = (LPLINEINFO) DialerAlloc( sizeof( LINEINFO ) );
}
else
{
gnAddr = (DWORD *) DialerAlloc( sizeof( DWORD ) * (int)gnAvailDevices);
gdwPLID = (DWORD *) DialerAlloc( sizeof( DWORD ) * (int)gnAvailDevices);
lpLineInfo = (LPLINEINFO) DialerAlloc( sizeof( LINEINFO ) * (int)gnAvailDevices );
}

// if no space was set aside...
if ( lpLineInfo == NULL || gnAddr == NULL )
{
dwReturn = LINEERR_NOMEM;
goto tapiinit_exit;
}

// fill lpLineInfo[] and open each line
for ( iLine = 0, cvLine = 0; iLine < gnAvailDevices; ++iLine )
{
// skip remaining processing for this if it didn't open
if ( GetLineInfo( iLine, &lpLineInfo[iLine] ) != ERR_NONE )
continue;

gnAddr [ iLine ] = lpLineInfo[iLine].nAddr;
gdwPLID[ iLine ] = lpLineInfo[iLine].dwPermanentLineID;

if ( lpLineInfo[iLine].dwPermanentLineID == dwPreferredPLID )
giCurrentLine = iLine;

// note number of lines with Interactive voice caps.
// used to select a preferred line by default
if ( lpLineInfo [ iLine ].fVoiceLine )
{
cvLine++;
dwID = iLine;
}
}

// if we couldn't find the preferred line,
// try and assign one by default
// else bring up connect using dialog
if ( giCurrentLine == (DWORD)-1 )
{
// check if there is only one line
// that has interactive voice caps,
// make it default line
if ( cvLine == 1 )
{
giCurrentLine = dwID;

// if the preferred address read from the INI file
// was different i.e we are changing setting, inform
// the user
if ( dwPreferredPLID != -1 )
{
errString( ghWndMain, ERR_NEWDEFAULT, MB_ICONEXCLAMATION | MB_OK );
}
}
else
{
gCurrentLineInfo = lpLineInfo[0];
if ( DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CONNECTUSING),
ghWndMain,
(DLGPROC)ConnectUsingProc,
INVALID_LINE
)
== -1)
{
dwReturn = (DWORD) -1;
}
else
{
dwReturn = ERR_NONE;
}

goto tapiinit_exit;
}
}
gCurrentLineInfo = lpLineInfo[ giCurrentLine ];


// select default address
giCurrentAddress = 0;

// get the name of the preferred address from ini file
GetPrivateProfileString (
"Preference",
"Preferred Address",
gszNULL,
szBuffer,
MAXBUFSIZE,
gszINIfilename
);

// if preferred address read from INI file
if ( lstrcmp( gszNULL, szBuffer ) )
{
giCurrentAddress = (DWORD) atoi( szBuffer );

// if the address is invalid, set default
if ( giCurrentAddress >= gCurrentLineInfo.nAddr )
giCurrentAddress = 0;
}


tapiinit_exit:

if (lpLineInfo)
{
DialerFree(lpLineInfo);
}

return dwReturn;;
}

//***************************************************************************
//***************************************************************************
//***************************************************************************
int WINAPI WinMain (
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
HACCEL hAccel;
MSG msg;
DWORD errCode;
HANDLE hImHere;


ghInst = GetModuleHandle( NULL );
LoadString( ghInst, ikszAppFriendlyName, gszAppName, sizeof(gszAppName)/sizeof(TCHAR) );

//
// Now, let's see if we've already got an instance of ourself
hImHere = CreateMutex(NULL, TRUE, "DialersIveBeenStartedMutex");

//
// Is there another one of us already here?
if ( ERROR_ALREADY_EXISTS == GetLastError() )
{
HWND hDialerWnd;

hDialerWnd = FindWindow(gszDialerClassName,
NULL);

SetForegroundWindow(hDialerWnd);

CloseHandle( hImHere );
return 0;
}


{
WNDCLASS wc;
wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
wc.lpfnWndProc = DefDlgProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = DLGWINDOWEXTRA;
wc.hInstance = ghInst;
wc.hIcon = LoadIcon(ghInst, MAKEINTRESOURCE(IDI_DIALER) );
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject (COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = gszDialerClassName;
RegisterClass(&wc);
}


// create the dialog box and set it with info
// from the .INI file
ghWndMain = CreateDialog (
ghInst,
(LPCSTR)MAKEINTRESOURCE(IDD_DIALER),
(HWND)NULL,
MainWndProc
);

ReadINI();
ButtonFontSetup();

ShowWindow(ghWndMain, SW_SHOW);
UpdateWindow(ghWndMain);

// limit text in Number field to TAPIMAXDESTADDRESSSIZE
SendDlgItemMessage (
ghWndMain,
IDD_DCOMBO,
CB_LIMITTEXT,
(WPARAM)TAPIMAXDESTADDRESSSIZE,
0
);

// 0 (ERR_NONE) error code registers success - otherwise terminate
errCode = InitializeTAPI();
if(errCode)
{
errString(ghWndMain, errCode, MB_APPLMODAL | MB_ICONEXCLAMATION );

DialerCleanup();
return errCode;
}

errCode = lineRegisterRequestRecipient (
ghLineApp,
0, // registration instance
LINEREQUESTMODE_MAKECALL,
TRUE
);

if(errCode)
{
gfRegistered = FALSE;
errString(ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
}
else
{
gfRegistered = TRUE;
}


hAccel = LoadAccelerators(ghInst, gszAppName);

while ( GetMessage( &msg, NULL, 0, 0 ) )
{
if ( ghWndMain == NULL || !IsDialogMessage( ghWndMain, &msg ) )
{
if(!TranslateAccelerator(ghWndMain, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

// If: 1) Dialer is a call manager (if not, ignore requests)
// 2) the currently chosen line is available
// 3) there is a Simple TAPI request
// Then: process the request
if ( gfCurrentLineAvail && gfCallRequest )
{
ManageAssistedTelephony();
}
}



DialerCleanup();

CloseHandle( hImHere );

return msg.wParam;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
LPVOID DialerAlloc(size_t cbToAlloc)
{
return LocalAlloc(LPTR, cbToAlloc);
}


LPVOID DialerFree(LPVOID lpMem)
{
return LocalFree( lpMem );
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID ReadINI( VOID )
{
WORD cSDEntry, cLastDialed;
DWORD cComma;

POINT ptLeftTop;

char szName[ TAPIMAXCALLEDPARTYSIZE ];
char szTemp[ TAPIMAXCALLEDPARTYSIZE ];

char szNum[MAXBUFSIZE];
char szFieldName[MAXBUFSIZE];
char szPt[MAXBUFSIZE];


// get speed dial settings from INI file
for(cSDEntry = 1; cSDEntry <= NSPEEDDIALS; ++cSDEntry)
{

wsprintf(szFieldName, "Name%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szName,
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);

wsprintf(szFieldName, "Number%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
gszSDNumber[cSDEntry],
TAPIMAXDESTADDRESSSIZE - 1,
gszINIfilename
);

if ( !lstrcmp( gszNULL, szName ) )
{
lstrcpy( szName, gszSDNumber[ cSDEntry ] );
}

FitTextToButton( ghWndMain, IDD_DSPEEDDIAL1 + cSDEntry - 1, szName );

AmpersandCompensate( szName, szTemp );
SetDlgItemText (
ghWndMain,
IDD_DSPEEDDIAL1 + cSDEntry - 1,
(LPCSTR)szTemp
); // Label the speed dial button
}


// set up last dialed numbers in combo box (read from INI)
for(cLastDialed = 1; cLastDialed <= NLASTDIALED; ++cLastDialed)
{
wsprintf(szFieldName, "Last dialed %d", cLastDialed);
GetPrivateProfileString (
"Last dialed numbers",
szFieldName,
gszNULL,
szNum,
MAXBUFSIZE - 1,
gszINIfilename
);
if ( szNum[0] ) // i.e. if szNum isn't simply "" - if we read something
// from the INI - put it in the combo box
SendDlgItemMessage(
ghWndMain,
IDD_DCOMBO,
CB_ADDSTRING,
0,
(LPARAM)(LPCSTR)szNum
);
}

// set defaults
ptLeftTop.x = 100;
ptLeftTop.y = 100;

// set the window position based on the INI data
GetPrivateProfileString (
"Preference",
"Main Window Left/Top",
gszNULL,
szPt,
MAXBUFSIZE - 1,
gszINIfilename
);

if ( szPt[0] )
{
cComma = strcspn(szPt, ",");
szPt[cComma] = 0;
ptLeftTop.x = atoi(szPt);

// a possibly absurd check to see that the string
// wasn't akin to "320," with no second entry
if ( *(szPt + cComma + 1 ) )
ptLeftTop.y = atoi( szPt + cComma + 1 );

// check to see that the box is on the screen - the upper left
// must be on the screen, along with a 50x50 box below and to
// the right of it
if ( ptLeftTop.x < 0
|| ptLeftTop.x + 50 >= GetSystemMetrics(SM_CXSCREEN)
|| ptLeftTop.y < 0
|| ptLeftTop.y + 50 >= GetSystemMetrics(SM_CYSCREEN)
)
{
ptLeftTop.x = 100; // set defaults if the box is off of the screen
ptLeftTop.y = 100; // set defaults if the box is off of the screen
}
}

SetWindowPos (
ghWndMain,
NULL,
ptLeftTop.x,
ptLeftTop.y,
0,
0,
SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOZORDER
);
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID ButtonFontSetup(VOID)
{
HDC hdc;
int IDD;

// define the fonts for the buttons
hdc = GetDC(GetDesktopWindow());

ghFontBtn = CreateFont(
(-10)*GetDeviceCaps(hdc, LOGPIXELSY)/72,
0,
0,
0,
FW_BOLD,
FALSE,
FALSE,
FALSE,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
PROOF_QUALITY,
VARIABLE_PITCH | FF_SWISS,
(LPSTR)"Arial"
);

ghFontBtnText = CreateFont(
(-6)*GetDeviceCaps(hdc, LOGPIXELSY)/72,
0,
0,
0,
FW_NORMAL,
FALSE,
FALSE,
FALSE,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
PROOF_QUALITY,
VARIABLE_PITCH | FF_SWISS,
NULL
);

ghFontBtnStar = CreateFont(
(-18)*GetDeviceCaps(hdc, LOGPIXELSY)/72,
0,
0,
0,
FW_BOLD,
FALSE,
FALSE,
FALSE,
SYMBOL_CHARSET,
OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS,
PROOF_QUALITY,
VARIABLE_PITCH | FF_DONTCARE,
(LPSTR)"Symbol"
);

ReleaseDC(GetDesktopWindow(), hdc);


// set the fonts for the buttons
if(ghFontBtn)
{
// set fonts on number buttons
for(IDD = IDD_DBUTTON1; IDD <= IDD_DBUTTON0; ++IDD)
// the order is IDD_DBUTTON1, 2, 3, ..., 0 (thus from 1 to 0)
SendMessage(
GetDlgItem(ghWndMain, IDD),
WM_SETFONT,
(WPARAM)ghFontBtn,
0L
);

// set fonts on * and # buttons
SendMessage(
GetDlgItem(ghWndMain, IDD_DBUTTONSTAR),
WM_SETFONT,
(WPARAM)ghFontBtnStar,
0L
);
SendMessage(
GetDlgItem(ghWndMain, IDD_DBUTTONPOUND),
WM_SETFONT,
(WPARAM)ghFontBtnStar,
0L
);
}
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
// Draws a 3D button within rcBtn on hDC
VOID DrawButton(HDC hDC, RECT rcBtn, BOOL fHighlighted)
{
HPEN hPenPrev, hPenShadow, hPenHighlight, hPenBlack;
HBRUSH hBrushPrev, hBrushFace;
int RopPrev;

--rcBtn.right;
--rcBtn.bottom;

// set up pens/brush
hPenShadow = CreatePen(PS_SOLID,0,GetSysColor(COLOR_3DSHADOW));
hPenHighlight = CreatePen(PS_SOLID,0,GetSysColor(COLOR_3DHILIGHT));
hPenBlack = GetStockObject(BLACK_PEN);
hBrushFace = GetSysColorBrush(COLOR_3DFACE);

// get current state so we can put it back at the end of DrawButton
hPenPrev = SelectObject(hDC, hPenBlack);
RopPrev = SetROP2(hDC, R2_COPYPEN);
hBrushPrev = SelectObject(hDC, hBrushFace);

PatBlt(
hDC,
rcBtn.left + 1,
rcBtn.top + 1,
rcBtn.right - rcBtn.left - 1,
rcBtn.bottom - rcBtn.top - 1,
PATCOPY
);

if(fHighlighted)
{
SelectObject(hDC, hPenBlack);
MoveToEx(hDC, rcBtn.left, rcBtn.bottom - 1, NULL);
LineTo(hDC, rcBtn.left, rcBtn.top); // _
LineTo(hDC, rcBtn.right, rcBtn.top); // |

SelectObject(hDC, hPenHighlight);
MoveToEx(hDC, rcBtn.right, rcBtn.top, NULL);
LineTo(hDC, rcBtn.right, rcBtn.bottom);
LineTo(hDC, rcBtn.left - 1, rcBtn.bottom); // _|

SelectObject(hDC, hPenShadow);
MoveToEx(hDC, rcBtn.left + 1, rcBtn.bottom - 2, NULL);
LineTo(hDC, rcBtn.left + 1, rcBtn.top + 1);
LineTo(hDC, rcBtn.right - 1, rcBtn.top + 1);
}
else
{
SelectObject(hDC, hPenHighlight);
MoveToEx(hDC, rcBtn.left, rcBtn.bottom - 1, NULL);
LineTo(hDC, rcBtn.left, rcBtn.top); // _
LineTo(hDC, rcBtn.right, rcBtn.top); // |

SelectObject(hDC, hPenBlack);
MoveToEx(hDC, rcBtn.right, rcBtn.top, NULL);
LineTo(hDC, rcBtn.right, rcBtn.bottom);
LineTo(hDC, rcBtn.left - 1, rcBtn.bottom); // _|

SelectObject(hDC, hPenShadow);
MoveToEx(hDC, rcBtn.left + 1, rcBtn.bottom - 1, NULL);
LineTo(hDC, rcBtn.right - 1, rcBtn.bottom - 1);
LineTo(hDC, rcBtn.right - 1, rcBtn.top);
}

// put everything back how it was
SetROP2(hDC, RopPrev);
SelectObject(hDC, hBrushPrev);
SelectObject(hDC, hPenPrev);

DeleteObject(hPenBlack);
DeleteObject(hPenShadow);
DeleteObject(hPenHighlight);
DeleteObject(hBrushFace);
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID DrawButtonText(HDC hDC, RECT rcBtn, BOOL fHighlighted, UINT IDD_Btn)
{
char szLine1[MAXBUFSIZE]; // text in button up to '\n'
LPSTR pszLine2; // text in button after '\n'
int BkModePrev;
HFONT hFontPrev = NULL;
TEXTMETRIC tm;
RECT rcText = rcBtn;

BkModePrev = SetBkMode(hDC, TRANSPARENT);

GetDlgItemText(ghWndMain, IDD_Btn, szLine1, MAXBUFSIZE);
pszLine2 = strstr(szLine1, "\n");
if(pszLine2)
{
*pszLine2 = 0; // 1 -> "TEST1 \n TEST2" becomes "TEST 1 \0"
++pszLine2; // now 2 -> " TEST 2"
}

// now szLine1 points to the null terminated first string and
// pszLine2 points to either the null terminated second string or NULL
// if there was no second string

if(szLine1[0])
{
if(ghFontBtnText && pszLine2)
hFontPrev = SelectObject(hDC, ghFontBtnText);

GetTextMetrics(hDC, &tm);
rcText.bottom = (rcBtn.bottom + rcBtn.top)/2 - 2;
rcText.top = rcText.bottom - (tm.tmHeight - 1);

if(pszLine2 == NULL)
OffsetRect(&rcText, 0, (rcText.bottom - rcText.top)/2);

if(fHighlighted)
OffsetRect(&rcText, 1, 1);

DrawText(hDC, szLine1, -1, &rcText, DT_SINGLELINE | DT_CENTER);

if(hFontPrev)
SelectObject(hDC, hFontPrev);
}
if(pszLine2) // recall that pszLine2 == NULL to represent no second string
{
GetTextMetrics(hDC, &tm);
if(IDD_Btn == IDD_DBUTTONSTAR || IDD_Btn == IDD_DBUTTONPOUND)
rcText.top = (rcBtn.bottom + rcBtn.top)/2 - (tm.tmHeight)/2;
else
rcText.top = (rcBtn.bottom + rcBtn.top)/2 - 2;
rcText.bottom = rcText.top + tm.tmHeight;

if(fHighlighted)
OffsetRect(&rcText, 1, 1);

DrawText(hDC, pszLine2, -1, &rcText, DT_SINGLELINE | DT_CENTER);
}

SetBkMode(hDC, BkModePrev);
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID DisableDialButtons(BOOL fDisable)
{
int IDD;

// Disable/enable Dial button
EnableWindow( GetDlgItem( ghWndMain, IDD_DDIAL ),!fDisable) ;

// Disable/enable Speed dial buttons
for ( IDD = IDD_DSPEEDDIAL1; IDD <= IDD_DSPEEDDIAL8; ++IDD )
{
EnableWindow(GetDlgItem(ghWndMain, IDD),!fDisable);
}
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID DialerCleanup(VOID)
{
RECT rc;
WORD cItem; // count of numbers in combo box
DWORD cLastDialed;
char szPt[MAXBUFSIZE];
char szNumber[TAPIMAXDESTADDRESSSIZE];
char szFieldName[MAXBUFSIZE];

CloseTAPI(); // unregister and line close

if(!IsIconic(ghWndMain)) // if the window is not minimized, record position
{
GetWindowRect(ghWndMain, &rc);
wsprintf(szPt, "%ld, %ld", rc.left, rc.top);
WritePrivateProfileString(
"Preference",
"Main Window Left/Top",
szPt,
gszINIfilename
);
}

cItem = (WORD)SendDlgItemMessage(ghWndMain, IDD_DCOMBO, CB_GETCOUNT, 0, 0);

// write out last dialed numbers from combo box (write to INI)
for(cLastDialed = 1; cLastDialed <= NLASTDIALED; ++cLastDialed)
{
if(cLastDialed <= cItem)
SendDlgItemMessage(
ghWndMain,
IDD_DCOMBO,
CB_GETLBTEXT,
cLastDialed - 1, // it's a zero-based count
(LPARAM)(LPCSTR)szNumber);

else
szNumber[0] = 0;

wsprintf(szFieldName, "Last dialed %d", cLastDialed);
WritePrivateProfileString(
"Last dialed numbers",
szFieldName,
szNumber,
gszINIfilename
);

}

WinHelp(ghWndMain, gszHELPfilename, HELP_QUIT, 0); // unload help

DestroyWindow(ghWndMain);
ghWndMain = NULL;

DeleteObject(ghFontBtn);
DeleteObject(ghFontBtnText);
DeleteObject(ghFontBtnStar);
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
// unregister and line close
VOID CloseTAPI(VOID)
{

// unregister as call manager
lineRegisterRequestRecipient (
ghLineApp,
0, // registration instance
LINEREQUESTMODE_MAKECALL,
FALSE
);

if ( gCurrentLineInfo.hLine )
{
lineClose ( gCurrentLineInfo.hLine );
gfCurrentLineAvail = FALSE;
gCurrentLineInfo.hLine = NULL;
}

lineShutdown(ghLineApp);
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{ 
static HICON hIcon;
static const DWORD aMenuHelpIDs[] =
{
IDD_DSPEEDDIALGRP, (DWORD)-1,
IDD_DNUMTODIAL, IDH_DIALER_DIAL_NUMBER,
IDD_DCOMBO, IDH_DIALER_DIAL_NUMBER,
IDD_DDIAL, IDH_DIALER_DIAL_BUTTON,
IDD_DSPEEDDIAL1, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL2, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL3, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL4, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL5, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL6, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL7, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIAL8, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT1, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT2, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT3, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT4, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT5, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT6, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT7, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DSPEEDDIALTEXT8, IDH_DIALER_DIAL_SPEED_CHOOSE,
IDD_DBUTTON1, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON2, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON3, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON4, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON5, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON6, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON7, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON8, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON9, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTONSTAR, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTON0, IDH_DIALER_DIAL_KEYPAD,
IDD_DBUTTONPOUND, IDH_DIALER_DIAL_KEYPAD,
0, 0
};

switch (msg)
{
case WM_INITDIALOG:
hIcon = LoadIcon( ghInst, (LPCSTR) MAKEINTRESOURCE( IDI_DIALER ) );
return TRUE;

case WM_SYSCOMMAND:
switch( (DWORD) wParam )
{
case SC_CLOSE:
PostQuitMessage(0);
}
break;

// processes clicks on controls when
// context mode help is selected
case WM_HELP:
WinHelp (
( (LPHELPINFO) lParam)->hItemHandle,
gszHELPfilename,
HELP_WM_HELP,
(DWORD)(LPVOID) aMenuHelpIDs
);
return TRUE;

// processes right-clicks on controls
case WM_CONTEXTMENU:
WinHelp (
(HWND)wParam,
gszHELPfilename,
HELP_CONTEXTMENU,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_INITMENUPOPUP:
// if edit menu
if ( LOWORD(lParam) == 1 )
{
UINT wEnable;

if ( GetParent( GetFocus() ) != GetDlgItem( ghWndMain, IDD_DCOMBO ) )
{
wEnable = MF_GRAYED;
}
else
{
LONG lSelect = SendDlgItemMessage (
ghWndMain,
IDD_DCOMBO,
CB_GETEDITSEL,
0,
0
);

if ( HIWORD( lSelect ) != LOWORD( lSelect ) )
wEnable = MF_ENABLED;
else
wEnable = MF_GRAYED;
}

EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, wEnable);
EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, wEnable);
EnableMenuItem((HMENU)wParam, IDM_EDIT_DELETE, wEnable);

// enable paste option is there is data
// in the clipboard
if ( IsClipboardFormatAvailable( CF_TEXT ) )
{
if ( GetClipboardData ( CF_TEXT ) )
{
wEnable = MF_ENABLED;
}
else
{
wEnable = MF_GRAYED;
}
}
else
{
wEnable = MF_GRAYED;
}

}
break;


case WM_COMMAND:
{
char szName[TAPIMAXCALLEDPARTYSIZE] = {'\0'};
char szNumber[TAPIMAXDESTADDRESSSIZE] = {'\0'};

switch( LOWORD( (DWORD)wParam ) )
{
// FILE menu
case IDM_EXIT:
PostQuitMessage(0);
return TRUE;


// EDIT menu
case IDM_EDIT_CUT:
SendDlgItemMessage(ghWndMain, IDD_DCOMBO, WM_CUT, 0, 0);
return TRUE;

case IDM_EDIT_COPY:
SendDlgItemMessage(ghWndMain, IDD_DCOMBO, WM_COPY, 0, 0);
return TRUE;

case IDM_EDIT_PASTE:
SendDlgItemMessage(ghWndMain, IDD_DCOMBO, WM_PASTE, 0, 0);
return TRUE;

case IDM_EDIT_DELETE:
SendDlgItemMessage(ghWndMain, IDD_DCOMBO, WM_CLEAR, 0, 0);
return TRUE;

case IDM_EDIT_SPEEDDIAL:
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_SD1),
ghWndMain,
(DLGPROC)SpeedDial1Proc,
0
);
SetFocus(GetDlgItem(ghWndMain, IDD_DDIAL));
return TRUE;

// TOOLS menu
case IDM_CONNECTUSING:
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CONNECTUSING),
ghWndMain,
(DLGPROC)ConnectUsingProc,
MENU_CHOICE
);
return TRUE;

case IDM_LOCATION:
{
char szCanNumber[ TAPIMAXDESTADDRESSSIZE ] = "";

// fetch the number to be dialed
if ( GetDlgItemText (
ghWndMain,
IDD_DCOMBO,
szNumber,
TAPIMAXDESTADDRESSSIZE
)
)
{
// if a number exists, convert it to
// its canonical form.
if ( !MakeCanonicalNumber ( szNumber, szCanNumber ) )
{
lstrcpy( szCanNumber, szNumber );
}
}

lineTranslateDialog (
ghLineApp,
0,
TAPI_CURRENT_VERSION,
ghWndMain,
szCanNumber
);
return TRUE;

}
// HELP menu
case IDM_HELP_CONTENTS:
WinHelp(ghWndMain, gszHELPfilename, HELP_CONTENTS, 0);
return TRUE;

case IDM_HELP_WHATSTHIS:
PostMessage(ghWndMain, WM_SYSCOMMAND, SC_CONTEXTHELP, 0);
return TRUE;

case IDM_ABOUT:
DialogBoxParam(
ghInst,
MAKEINTRESOURCE(IDD_ABOUT),
ghWndMain,
(DLGPROC)AboutProc,
0
);

return TRUE;


// Accelerator processing
case IDM_ACCEL_NUMTODIAL:
if(GetActiveWindow() == ghWndMain)
SetFocus(GetDlgItem(ghWndMain, IDD_DCOMBO));
return TRUE;


// Buttons
case IDD_DDIAL:

{
DWORD cSDEntry;
char szSDNumber[TAPIMAXDESTADDRESSSIZE];
char szFieldName[MAXBUFSIZE];

// check if number entered is dialable
if ( SendMessage (
GetDlgItem(ghWndMain, IDD_DCOMBO),
WM_GETTEXTLENGTH,
0,
0
) > 0
)
{
// get the number to be dialed
GetDlgItemText (
ghWndMain,
IDD_DCOMBO,
(LPSTR)szNumber,
TAPIMAXDESTADDRESSSIZE
);

// check if it is a speed dial number.
// If so choose the name to be displayed.
for( cSDEntry = 1; cSDEntry <= NSPEEDDIALS; ++cSDEntry)
{
wsprintf(szFieldName, "Number%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szSDNumber,
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);

// if the number matches, get the name
if ( lstrcmp(szSDNumber, szNumber) == 0 )
{
wsprintf( szFieldName, "Name%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szName,
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);
break;
}
}

SetFocus( GetDlgItem( ghWndMain, IDD_DDIAL ) );

// once the currentline has been set
// using the connect proc
// the user must hit dial again
if ( giCurrentLine == (DWORD)-1 )
{
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CONNECTUSING),
ghWndMain,
(DLGPROC)ConnectUsingProc,
INVALID_LINE
);
}
else
{
AddToRedialList(szNumber);
InitiateCall(szNumber, szName);
}
}
return TRUE;
}


case IDD_DBUTTON1:
case IDD_DBUTTON2:
case IDD_DBUTTON3:
case IDD_DBUTTON4:
case IDD_DBUTTON5:
case IDD_DBUTTON6:
case IDD_DBUTTON7:
case IDD_DBUTTON8:
case IDD_DBUTTON9:
case IDD_DBUTTON0:
case IDD_DBUTTONSTAR:
case IDD_DBUTTONPOUND:
{
int i;
TCHAR szBuffer[TAPIMAXDESTADDRESSSIZE+1];

static const char digits[] = { '1', '2', '3', '4',
'5', '6', '7', '8',
'9', '0', '*', '#' };

i = SendDlgItemMessage(ghWndMain,
IDD_DCOMBO,
WM_GETTEXT,
(WPARAM)TAPIMAXDESTADDRESSSIZE+1,
(LPARAM)szBuffer);

if (i < TAPIMAXDESTADDRESSSIZE)
{
MoveMemory(szBuffer+gdwStartSel+1,
szBuffer+gdwEndSel,
i - ( gdwEndSel ) + 1 );

szBuffer[gdwStartSel] = digits[LOWORD(wParam) - IDD_DBUTTON1];

SendDlgItemMessage(ghWndMain,
IDD_DCOMBO,
WM_SETTEXT,
0,
(LPARAM)szBuffer);

gdwStartSel++;
gdwEndSel = gdwStartSel;
}

SetFocus(GetDlgItem(ghWndMain, IDD_DDIAL));
EnableWindow(GetDlgItem(ghWndMain, IDD_DDIAL), TRUE);

return TRUE;
}


case IDD_DCOMBO:

if (HIWORD(wParam) == CBN_SELENDOK)
{
EnableWindow( GetDlgItem(ghWndMain, IDD_DDIAL), TRUE );
}

if ((HIWORD(wParam) == CBN_SELENDOK) ||
(HIWORD(wParam) == CBN_SELENDCANCEL))
{

(DWORD)SendDlgItemMessage(ghWndMain,
IDD_DCOMBO,
CB_GETEDITSEL,
(WPARAM)&gdwStartSel,
(LPARAM)&gdwEndSel);
return FALSE;
}

if ( HIWORD( wParam ) == CBN_EDITCHANGE )
{
EnableWindow (
GetDlgItem( ghWndMain, IDD_DDIAL ),
(BOOL) GetWindowTextLength (
GetDlgItem (
ghWndMain,
IDD_DCOMBO
)
)
);
return TRUE;
}

break;

case IDD_DSPEEDDIAL1:
case IDD_DSPEEDDIAL2:
case IDD_DSPEEDDIAL3:
case IDD_DSPEEDDIAL4:
case IDD_DSPEEDDIAL5:
case IDD_DSPEEDDIAL6:
case IDD_DSPEEDDIAL7:
case IDD_DSPEEDDIAL8:
{
DWORD cSDEntry = LOWORD( (DWORD) wParam) - IDD_DSPEEDDIAL1 + 1;
char szFieldName [MAXBUFSIZE];

// get information for the speed dial button
// from the INI file
wsprintf(szFieldName, "Name%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szName,
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);

wsprintf(szFieldName, "%s%d", "Number", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
gszSDNumber[cSDEntry],
TAPIMAXDESTADDRESSSIZE - 1,
gszINIfilename
);

// entry not set yet
if( gszSDNumber[cSDEntry][0] == 0 )
{
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_SD2),
ghWndMain,
(DLGPROC)SpeedDial2Proc,
MAKELPARAM(wParam,0)
);
}

// no line open
// once the currentline has been set
// using the connect proc
// the user must hit dial again
else if ( giCurrentLine == (DWORD)-1)
{
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CONNECTUSING),
ghWndMain,
(DLGPROC)ConnectUsingProc,
INVALID_LINE
);
}
// entry is set and valid voice line is open
else
{
// add number to list box combo.
AddToRedialList( gszSDNumber[cSDEntry] );
InitiateCall( gszSDNumber[cSDEntry], szName );
}
break;
}
} // end switch (LOWORD((DWORD)wParam)) { ... }

break; // end case WM_COMMAND
}


case WM_PAINT:
{
PAINTSTRUCT ps;


BeginPaint(ghWndMain, &ps);

if(IsIconic(ghWndMain))
DrawIcon(ps.hdc, 0, 0, hIcon);
else
{
HBRUSH hBrush;

hBrush = GetSysColorBrush( COLOR_3DFACE );
// FillRect(ps.hdc, &ps.rcPaint, GetStockObject(LTGRAY_BRUSH));
FillRect(ps.hdc, &ps.rcPaint, hBrush);
}

EndPaint(ghWndMain, &ps);

return TRUE;
}


case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
BOOL fHighlighted = (SendDlgItemMessage(
ghWndMain,
lpdis->CtlID,
BM_GETSTATE,
0,
0
) & 0x0004);

DrawButton(lpdis->hDC, lpdis->rcItem, fHighlighted);
DrawButtonText(
lpdis->hDC,
lpdis->rcItem,
fHighlighted,
lpdis->CtlID
);
return TRUE;
}


case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC:
SetBkColor((HDC)wParam, GetSysColor(COLOR_BTNFACE));
return (BOOL)GetSysColorBrush( COLOR_3DFACE );


default:
;
// return DefDlgProc( hwnd, msg, wParam, lParam );
// return DefWindowProc( hwnd, msg, wParam, lParam );


} // switch (msg) { ... }

return FALSE;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID AddToRedialList( LPCSTR szNumber )
{
// NLASTDIALED == 10
WORD cNum;
HWND hWndCombo = GetDlgItem(ghWndMain, IDD_DCOMBO);
DWORD nMatch;

// if valid number
if ( szNumber[0] )
{
// if list box has entries, check if this number
// is already present. If so delete old entry
cNum = (WORD) SendMessage(hWndCombo, CB_GETCOUNT, 0, 0);
if ( cNum != 0 )
{
nMatch = SendMessage ( hWndCombo, CB_FINDSTRING, 0, (LPARAM)szNumber );
if ( nMatch != CB_ERR )
{
SendMessage(hWndCombo, CB_DELETESTRING, nMatch, 0);
}
else
{
// if the list is full, remove oldest
if ( cNum == NLASTDIALED )
{
SendMessage( hWndCombo, CB_DELETESTRING, NLASTDIALED - 1, 0 );
}
}
}
SendMessage(hWndCombo, CB_INSERTSTRING, 0, (LPARAM)szNumber);
SendMessage(hWndCombo, CB_SETCURSEL, 0, 0L);
EnableWindow ( GetDlgItem( ghWndMain, IDD_DDIAL ), TRUE );
}
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID InitiateCall ( LPCSTR szNumber, LPCSTR szName )
{
HLINE hLine = NULL;

DWORD errCode;

// struct size info
DWORD dwLTPSize= sizeof ( LINETRANSLATEOUTPUT );
DWORD dwNameLen= lstrlen( szName ) + 1;
DWORD dwLCPSize= sizeof( LINECALLPARAMS );

LPLINETRANSLATEOUTPUT lpTransOut = NULL;
LPLINECALLPARAMS lpLineCallParams = NULL;

char szCanNumber[ TAPIMAXDESTADDRESSSIZE ];

// Open a line
errCode = lineOpen (
ghLineApp,
giCurrentLine,
&hLine,
gCurrentLineInfo.dwAPIVersion,
0,
0,
LINECALLPRIVILEGE_NONE,
0,
NULL
);
if (errCode)
{
errString ( ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
goto error;
}


// call translate address before dialing
do
{
lpTransOut = (LPLINETRANSLATEOUTPUT) DialerAlloc( dwLTPSize );
if ( !lpTransOut )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}
lpTransOut-> dwTotalSize = dwLTPSize;


if ( !MakeCanonicalNumber( szNumber, szCanNumber ) )
{
lstrcpy( szCanNumber, szNumber );
}

errCode = lineTranslateAddress (
ghLineApp,
giCurrentLine,
gCurrentLineInfo.dwAPIVersion,
szCanNumber,
0,
0,
lpTransOut
);
if ( ((LONG)errCode) < 0 )
{
errString( ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
goto error;
}

if ( lpTransOut-> dwNeededSize <= lpTransOut->dwTotalSize )
{
// ok we are done
break;
}
else
{
dwLTPSize = lpTransOut-> dwNeededSize;
DialerFree ( lpTransOut );
lpTransOut = NULL;
}

} while ( TRUE );


// if number dialed is 911, bring up a warning
if ( Is911( lpTransOut) )
{
INT nRes = errString ( ghWndMain, ERR_911WARN, MB_ICONSTOP | MB_YESNO );
if ( nRes == IDNO )
{
goto error;
}
}


// set call parameters
dwLCPSize += dwNameLen + lpTransOut-> dwDisplayableStringSize;

lpLineCallParams = (LPLINECALLPARAMS) DialerAlloc( dwLCPSize );
if ( !lpLineCallParams )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}

lpLineCallParams->dwTotalSize = dwLCPSize;
lpLineCallParams->dwBearerMode = LINEBEARERMODE_VOICE;
lpLineCallParams->dwMediaMode = LINEMEDIAMODE_INTERACTIVEVOICE;
lpLineCallParams->dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
lpLineCallParams->dwAddressMode = LINEADDRESSMODE_ADDRESSID;
lpLineCallParams->dwAddressID = giCurrentAddress;

if ( szName[ 0 ] )
{
lpLineCallParams->dwCalledPartySize = dwNameLen;
lpLineCallParams->dwCalledPartyOffset = sizeof( LINECALLPARAMS );
lstrcpy (
(LPSTR) lpLineCallParams + sizeof(LINECALLPARAMS),
szName
);
}

lpLineCallParams-> dwDisplayableAddressSize = lpTransOut-> dwDisplayableStringSize;
lpLineCallParams-> dwDisplayableAddressOffset = sizeof( LINECALLPARAMS ) + dwNameLen;

lstrcpy (
(LPSTR) lpLineCallParams + sizeof(LINECALLPARAMS) + dwNameLen,
(LPSTR) lpTransOut + lpTransOut-> dwDisplayableStringOffset
);


// save dialing information
// Free old allocs.
if ( gszCurrentName )
{
DialerFree ( gszCurrentName );
}

if ( gszCurrentNumber )
{
DialerFree ( gszCurrentNumber );
}

// save new stuff
gszCurrentName = (LPSTR) DialerAlloc( dwNameLen );
if ( !gszCurrentName )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}
lstrcpy ( gszCurrentName, szName );

gszCurrentNumber = (LPSTR) DialerAlloc( lpTransOut-> dwDisplayableStringSize );
if ( !gszCurrentNumber )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}
lstrcpy (
gszCurrentNumber,
(LPSTR) lpTransOut + lpTransOut-> dwDisplayableStringOffset
);

gCurrentLineInfo.hLine = hLine;
ghCall = NULL;


// finally make the call.
gMakeCallRequestID = 0;

gMakeCallRequestID = lineMakeCall (
hLine,
&ghCall,
(LPSTR) lpTransOut + lpTransOut-> dwDialableStringOffset,
0,
lpLineCallParams
);

// async request ID
// - the call is going out
if ( (LONG) gMakeCallRequestID > 0 )
{
gfCurrentLineAvail = FALSE;
gfMakeCallReplyPending = TRUE;
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_DIALING),
ghWndMain,
(DLGPROC)DialingProc,
0
);

}

else
{
if ( gMakeCallRequestID == LINEERR_CALLUNAVAIL )
{
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CALLFAILED),
ghWndMain,

(DLGPROC)LineInUseProc, 
0
);
}

else
{
errString( ghWndMain, gMakeCallRequestID, MB_ICONEXCLAMATION | MB_OK );
}

DialerLineClose();
gfCurrentLineAvail = TRUE;
}

error :
if ( lpLineCallParams )
{
DialerFree( lpLineCallParams );
}

if ( lpTransOut )
{
DialerFree( lpTransOut );
}

// if makecall did not succeed but line
// was opened, close it.
if ( ( gMakeCallRequestID <= 0 ) && ( gCurrentLineInfo.hLine ) )
{
DialerLineClose ();
gfCurrentLineAvail = TRUE;
}

SetFocus( GetDlgItem( ghWndMain, IDD_DCOMBO ) );

return;

}



//***************************************************************************
//***************************************************************************
//***************************************************************************
DWORD GetLineInfo ( DWORD iLine, LPLINEINFO lpLineInfo )
{
DWORD errCode = 0;
DWORD dwNeededSize = 0;
LINEEXTENSIONID ExtensionID;

LPSTR pszLineName = NULL;
LPLINEDEVCAPS lpDevCaps = NULL;


errCode = lineNegotiateAPIVersion (
ghLineApp,
iLine,
TAPI_VERSION_1_0,
TAPI_CURRENT_VERSION,
&( lpLineInfo->dwAPIVersion ),
&ExtensionID
);
if ( errCode )
{
GetLineInfoFailed( iLine, lpDevCaps, lpLineInfo );
goto error;
}

dwNeededSize = sizeof( LINEDEVCAPS );
do
{
lpDevCaps = ( LPLINEDEVCAPS ) DialerAlloc( dwNeededSize );
if ( !lpDevCaps )
{
GetLineInfoFailed( iLine, lpDevCaps, lpLineInfo );
errCode = LINEERR_NOMEM;
goto error;
}

lpDevCaps->dwTotalSize = dwNeededSize;
errCode = lineGetDevCaps (
ghLineApp,
iLine,
lpLineInfo->dwAPIVersion,
0,
lpDevCaps
);
if ( errCode )
{
GetLineInfoFailed( iLine, lpDevCaps, lpLineInfo );
goto error;
}

if ( lpDevCaps-> dwNeededSize <= lpDevCaps-> dwTotalSize )
{
break;
}

dwNeededSize = lpDevCaps->dwNeededSize;
DialerFree( lpDevCaps );
lpDevCaps = NULL;

} while ( TRUE );


lpLineInfo->nAddr = lpDevCaps->dwNumAddresses;
lpLineInfo->fVoiceLine =
( (lpDevCaps->dwMediaModes & LINEMEDIAMODE_INTERACTIVEVOICE) != 0 );

pszLineName = (LPSTR) DialerAlloc( MAXBUFSIZE );
if ( !pszLineName )
{
errCode = LINEERR_NOMEM;
goto error;
}

if ( lpDevCaps->dwLineNameSize > 0 )
{
if ( lpDevCaps-> dwLineNameSize > (MAXBUFSIZE - 1) )
{
strncpy (
pszLineName,
(LPSTR) lpDevCaps + lpDevCaps->dwLineNameOffset,
MAXBUFSIZE - 1
);
pszLineName[ MAXBUFSIZE - 1 ] = '\0';
}
else
{
lstrcpy( pszLineName, (LPSTR) lpDevCaps + lpDevCaps-> dwLineNameOffset );
}
}
else
{
wsprintf ( pszLineName, "Line %d", iLine );
}


lstrcpy( lpLineInfo->szLineName, pszLineName );
lpLineInfo->dwPermanentLineID = lpDevCaps->dwPermanentLineID;


error:
if ( lpDevCaps )
DialerFree( lpDevCaps );

if ( pszLineName )
DialerFree( pszLineName );

return errCode;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID GetLineInfoFailed ( DWORD iLine, LPLINEDEVCAPS lpDevCaps, LPLINEINFO lpLineInfo )
{
if ( lpDevCaps )
DialerFree(lpDevCaps);

lpLineInfo->nAddr = 0;
lpLineInfo->fVoiceLine = FALSE;
lpLineInfo->dwAPIVersion = 0;
lpLineInfo->hLine = (HLINE)0;
lpLineInfo->dwPermanentLineID = 0;
lpLineInfo->szLineName[0] = 0;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
LPSTR GetAddressName(DWORD iLine, DWORD iAddress)
{
DWORD errCode = 0;
DWORD dwNeededSize = 0;
LPSTR pszAddressName = NULL;
LPLINEADDRESSCAPS lpAddressCaps = NULL;

// allocate space for lineGetAddressCaps data
dwNeededSize = sizeof( LINEADDRESSCAPS );

do
{
lpAddressCaps = ( LPLINEADDRESSCAPS )DialerAlloc( dwNeededSize );
if ( !lpAddressCaps )
{
goto error;
}

lpAddressCaps->dwTotalSize = dwNeededSize;
errCode = lineGetAddressCaps (
ghLineApp,
iLine,
iAddress,
gCurrentLineInfo.dwAPIVersion,
0,
lpAddressCaps
);
if ( errCode )
{
errString( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
goto error;
}

if ( lpAddressCaps-> dwNeededSize <= lpAddressCaps-> dwTotalSize )
{
break;
}

dwNeededSize = lpAddressCaps->dwNeededSize;
DialerFree( lpAddressCaps );
lpAddressCaps = NULL;

} while( TRUE );


// get the address name
pszAddressName = DialerAlloc( MAXBUFSIZE );
if ( !pszAddressName )
{
goto error;
}

if ( lpAddressCaps-> dwAddressSize > 0 )
{
// keep string length bounded
if ( lpAddressCaps-> dwAddressSize > (MAXBUFSIZE - 1 ) )
{
strncpy(
pszAddressName,
(LPSTR) lpAddressCaps + lpAddressCaps->dwAddressOffset,
MAXBUFSIZE - 1
);
pszAddressName[ MAXBUFSIZE - 1] = '\0';
}
else
{
lstrcpy (
pszAddressName,
(LPSTR) lpAddressCaps + lpAddressCaps->dwAddressOffset
);
}
}
else
// use default name
{
wsprintf(pszAddressName, "Address %d", iAddress);
}

error:
if ( lpAddressCaps )
{
DialerFree( lpAddressCaps );
}

return pszAddressName;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK DialingProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

switch(msg)
{
char szTemp[ TAPIMAXCALLEDPARTYSIZE ];

case WM_INITDIALOG:
// set global handle to window
ghWndDialing = hwnd;

AmpersandCompensate( gszCurrentName, szTemp );

SetDlgItemText(hwnd, IDD_DGNUMBERTEXT, gszCurrentNumber);
SetDlgItemText(hwnd, IDD_DGNAMETEXT, szTemp );
break;

case WM_COMMAND:
switch ( LOWORD( (DWORD)wParam ) )
{
// hang up
case IDCANCEL:
//gfDropping = TRUE;

// if lineMakeCall has completed
// only then drop call.
if ( !gfMakeCallReplyPending && ghCall )
{
if ( ( gDropCallRequestID = lineDrop ( ghCall, NULL, 0 ) ) < 0 )
{
errString ( ghWndDialing, gDropCallRequestID, MB_ICONSTOP | MB_OK );
}
}
else
{
DialerLineClose();
gfCurrentLineAvail = TRUE;
gfMakeCallReplyPending = FALSE;
}

ghWndDialing = NULL;
EndDialog(hwnd, FALSE);

return TRUE;


// something else terminated the call
// all we have to do is terminate this dialog box
case IDOK:
ghWndDialing = NULL;
EndDialog(hwnd, TRUE);

return TRUE;
}
break;

default:
;
}
return FALSE;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
{
return TRUE;
}

case WM_CLOSE:
EndDialog(hwnd, TRUE);
return TRUE;

case WM_COMMAND:
if(LOWORD((DWORD)wParam) == IDOK)
{
EndDialog(hwnd, TRUE);
return TRUE;
}

break;
}

return FALSE;

}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK ConnectUsingProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
static const DWORD aMenuHelpIDs[] =
{
IDD_CUTEXTLINE, IDH_DIALER_OPTIONS_LINE,
IDD_CULISTLINE, IDH_DIALER_OPTIONS_LINE,
IDD_CUTEXTADDRESS, IDH_DIALER_OPTIONS_ADDRESS,
IDD_CULISTADDRESS, IDH_DIALER_OPTIONS_ADDRESS,
IDD_CUSIMPLETAPICHKBOX, IDH_DIALER_OPTIONS_VOICE,
IDD_CUPROPERTIES, IDH_DIALER_OPTIONS_PROPERTIES,
0, 0
};

switch(msg)
{
case WM_HELP:
// processes clicks on controls when
// context mode help is selected
WinHelp (
((LPHELPINFO)lParam)->hItemHandle,
gszHELPfilename,
HELP_WM_HELP,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_CONTEXTMENU:
// processes right-clicks on controls
WinHelp (
(HWND)wParam,
gszHELPfilename,
HELP_CONTEXTMENU,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_INITDIALOG:
{
BOOL fEnable;
DWORD dwPriority;

//
// Is there any point in even showing this dialog box?
if ( gnAvailDevices == 0 )
{
// Nope. Let's tell the user what we don't like.
errString ( ghWndMain, ERR_NOLINES, MB_ICONEXCLAMATION | MB_OK );

EndDialog(hwnd, FALSE);
return TRUE;
}

// if not brought up by InitializeTAPI()
if ( lParam != INVALID_LINE )
{
// hide error text
EnableWindow( GetDlgItem( hwnd, IDD_CUERRORTEXT ), FALSE );
}

// get list of lines into the line list box.
fEnable = InitializeLineBox( GetDlgItem(hwnd, IDD_CULISTLINE) );
EnableWindow( GetDlgItem( hwnd, IDD_CULISTLINE ), fEnable);

// get list of addresses into the address list box.
fEnable =fEnable &&
InitializeAddressBox (
GetDlgItem(hwnd, IDD_CULISTLINE),
GetDlgItem(hwnd, IDD_CULISTADDRESS)
);
EnableWindow( GetDlgItem( hwnd, IDD_CULISTADDRESS ), fEnable );
EnableWindow( GetDlgItem( hwnd, IDOK ), fEnable );

EnableWindow( GetDlgItem( hwnd, IDD_CUPROPERTIES ), fEnable );

lineGetAppPriority (
"DIALER.EXE",
0, // checking app priority for Assisted Telephony requests
NULL,
LINEREQUESTMODE_MAKECALL,
NULL,
&dwPriority
);
CheckDlgButton(hwnd, IDD_CUSIMPLETAPICHKBOX, (dwPriority == 1));

// if dwPriority == 1, we're supporting Assisted Telephony AND
// have the highest priority.
EnableWindow (
GetDlgItem(hwnd, IDD_CUSIMPLETAPICHKBOX),
gfRegistered
);

return FALSE;
}

case WM_COMMAND:
{
switch ( LOWORD( (DWORD)wParam ) )
{
case IDD_CULISTLINE:
if ( HIWORD( wParam ) == CBN_SELENDOK )
// update address box
InitializeAddressBox (
GetDlgItem(hwnd, IDD_CULISTLINE),
GetDlgItem(hwnd, IDD_CULISTADDRESS)
);
break;

case IDD_CUPROPERTIES:
{
HWND hW = GetDlgItem(hwnd, IDD_CULISTLINE);

lineConfigDialog (
// device ID
(DWORD) SendMessage (
hW,
CB_GETITEMDATA,
(WORD) SendMessage(hW, CB_GETCURSEL, 0, 0),
0
),
hwnd,
NULL
);
break;
}

case IDOK:
{
HWND hwndBox;
char szBuffer[MAXBUFSIZE];
DWORD dwPriority;

// Update line
hwndBox = GetDlgItem( hwnd, IDD_CULISTLINE );
giCurrentLine = SendMessage (
hwndBox,
CB_GETITEMDATA,
SendMessage( hwndBox, CB_GETCURSEL, 0, 0 ),
0
);

// base 10
itoa( gdwPLID[giCurrentLine], szBuffer, 10 );
WritePrivateProfileString (
"Preference",
"Preferred Line",
szBuffer,
gszINIfilename
);


// Update address
hwndBox = GetDlgItem( hwnd, IDD_CULISTADDRESS );
giCurrentAddress = SendMessage (
hwndBox,
CB_GETITEMDATA,
SendMessage(hwndBox, CB_GETCURSEL, 0, 0),
0
);

itoa( giCurrentAddress, szBuffer, 10 );
WritePrivateProfileString (
"Preference",
"Preferred Address",
szBuffer,
gszINIfilename
);


// Update application priority
if ( SendDlgItemMessage (
hwnd,
IDD_CUSIMPLETAPICHKBOX,
BM_GETCHECK,
0,
0L
)
== 0)
{
dwPriority = 0;
}
else
{
dwPriority = 1;
}

lineSetAppPriority (
"DIALER.EXE",
0,
NULL,
LINEREQUESTMODE_MAKECALL,
NULL,
dwPriority
);

EndDialog(hwnd, TRUE);
return TRUE;
}

case IDCANCEL:
EndDialog(hwnd, FALSE);
return TRUE;
}
}

default:
;

}

return FALSE;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK LineInUseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPARAM lNewParam = lParam;
PTSTR ptStr;


switch(msg)
{
case WM_INITDIALOG:
{
switch(lParam)
{
case LINEDISCONNECTMODE_REJECT:
lNewParam = ikszDisconnectedReject;
break;

case LINEDISCONNECTMODE_BUSY:
lNewParam = ikszDisconnectedBusy;
break;

case LINEDISCONNECTMODE_NOANSWER:
lNewParam = ikszDisconnectedNoAnswer;
break;

case LINEDISCONNECTMODE_CONGESTION:
lNewParam = ikszDisconnectedNetwork;
break;

case LINEDISCONNECTMODE_INCOMPATIBLE:
lNewParam = ikszDisconnectedIncompatible;
break;

case LINEDISCONNECTMODE_NODIALTONE:
lNewParam = ikszDisconnectedNoDialTone;
break;

default:
lNewParam = ikszDisconnectedCantDo;
break;
}
return TRUE;
}

case WM_COMMAND:
if(LOWORD((DWORD)wParam) == IDOK)
{
EndDialog(hwnd, TRUE);
return TRUE;
}
break;

default:
;
// return DefDlgProc( hwnd, msg, wParam, lParam );

}


ptStr = DialerAlloc( MAXBUFSIZE );

LoadString( ghInst, lNewParam, ptStr, MAXBUFSIZE );

SetDlgItemText(
hwnd,
IDD_CFTEXT,
ptStr
);

DialerFree( ptStr );


return FALSE;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK SpeedDial1Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static DWORD nCurrentSpeedDial;

static const DWORD aMenuHelpIDs[] =
{
IDOK, IDH_DIALER_SPEED_SAVE,
IDD_SD1SPEEDDIAL1, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL2, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL3, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL4, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL5, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL6, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL7, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIAL8, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT1, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT2, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT3, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT4, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT5, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT6, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT7, IDH_DIALER_BUTTONS,
IDD_SD1SPEEDDIALTEXT8, IDH_DIALER_BUTTONS,
IDD_SD1TEXTNAME, IDH_DIALER_SPEED_NAME,
IDD_SD1EDITNAME, IDH_DIALER_SPEED_NAME,
IDD_SD1TEXTNUMBER, IDH_DIALER_SPEED_NUMBER,
IDD_SD1EDITNUMBER, IDH_DIALER_SPEED_NUMBER,
IDD_SD1TEXTCHOOSE, (DWORD)-1,
IDD_SD1TEXTENTER, (DWORD)-1,
0, 0
};

// buffer to store speed dial names till they are saved.
static TCHAR szSDName[NSPEEDDIALS + 1][TAPIMAXDESTADDRESSSIZE];

switch(msg)
{
case WM_HELP:
// processes clicks on controls when
// context mode help is selected
WinHelp(
((LPHELPINFO)lParam)->hItemHandle,
gszHELPfilename,
HELP_WM_HELP,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_CONTEXTMENU: // processes right-clicks on controls
WinHelp(
(HWND)wParam,
gszHELPfilename,
HELP_CONTEXTMENU,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_INITDIALOG:
{
DWORD cSDEntry;
DWORD idFirstEmpty = (DWORD) -1;

char szName[TAPIMAXCALLEDPARTYSIZE];
char szTemp[TAPIMAXCALLEDPARTYSIZE];
char szFieldName[MAXBUFSIZE];

// Retrieve speed dial info from INI file
for(cSDEntry = 1; cSDEntry <= NSPEEDDIALS; ++cSDEntry)
{
wsprintf(szFieldName, "Name%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szSDName[ cSDEntry ],
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);

// set the first empty speed dial button
if ( idFirstEmpty == -1 &&
szSDName[ cSDEntry ][0] == '\0' &&
gszSDNumber[ cSDEntry ][ 0 ] == '\0' )
idFirstEmpty = cSDEntry;

wsprintf(szFieldName, "Number%d", cSDEntry);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
gszSDNumber[cSDEntry],
MAXBUFSIZE - 1,
gszINIfilename
);

// get a copy of the name for editing
// if name is empty, use the number as the
// name.
if ( lstrcmp( gszNULL, szSDName[ cSDEntry ] ) )
{
lstrcpy( szName, szSDName[ cSDEntry] );
}
else
{
lstrcpy( szName, gszSDNumber[ cSDEntry ] );
}

FitTextToButton( hwnd, IDD_SD1SPEEDDIAL1 + cSDEntry - 1, szName );
AmpersandCompensate( szName, szTemp );

SetDlgItemText (
hwnd,
IDD_SD1SPEEDDIAL1 + cSDEntry - 1,
(LPCSTR) szTemp
);

}

// for the edit speed dial dialog
// limit the lengths of text
SendDlgItemMessage (
hwnd,
IDD_SD1EDITNAME,
EM_LIMITTEXT,
(WPARAM)(TAPIMAXCALLEDPARTYSIZE - 1),
0
);

SendDlgItemMessage (
hwnd,
IDD_SD1EDITNUMBER,
EM_LIMITTEXT,
(WPARAM)(TAPIMAXDESTADDRESSSIZE - 1),
0
);

// select the first empty button
// nothing empty, then edit #1
if ( -1 == idFirstEmpty )
{
nCurrentSpeedDial = 1;
SetDlgItemText(
hwnd,
IDD_SD1EDITNAME,
(LPCSTR) szSDName[ 1 ]
);

SetDlgItemText(
hwnd,
IDD_SD1EDITNUMBER,
(LPCSTR) gszSDNumber[ 1 ]
);
}
else
{
nCurrentSpeedDial = idFirstEmpty;
}

SetFocus( GetDlgItem( hwnd, IDD_SD1EDITNAME ) );
return FALSE;
}

case WM_COMMAND:
{
char szName[TAPIMAXCALLEDPARTYSIZE];
char szTemp[ TAPIMAXCALLEDPARTYSIZE ];

switch( LOWORD( (DWORD) wParam ) )
{
case IDOK:
{
DWORD cSDEntry;
char szFieldName[MAXBUFSIZE];

// save new speed dial settings
for ( cSDEntry = 1; cSDEntry <= NSPEEDDIALS; ++cSDEntry )
{
wsprintf(szFieldName, "Name%d", cSDEntry);
WritePrivateProfileString (
"Speed Dial Settings",
szFieldName,
szSDName [cSDEntry],
gszINIfilename
);

wsprintf(szFieldName, "Number%d", cSDEntry);
WritePrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszSDNumber[cSDEntry],
gszINIfilename
);

// set the text for the corresponding
// main window button
if ( szSDName[ cSDEntry ][ 0 ] == '\0' )
{
lstrcpy( szName, gszSDNumber[ cSDEntry ] );
}
else
{
lstrcpy( szName, szSDName[ cSDEntry ] );
}

FitTextToButton(
ghWndMain,
IDD_DSPEEDDIAL1 + cSDEntry - 1,
szName
);

AmpersandCompensate( szName, szTemp );
SetDlgItemText (
ghWndMain,
IDD_DSPEEDDIAL1 + cSDEntry - 1,
(LPCSTR) szTemp
);
}

EndDialog(hwnd, TRUE);
return TRUE;
}

case IDCANCEL:
EndDialog(hwnd, FALSE);
return TRUE;

case IDD_SD1SPEEDDIAL1:
case IDD_SD1SPEEDDIAL2:
case IDD_SD1SPEEDDIAL3:
case IDD_SD1SPEEDDIAL4:
case IDD_SD1SPEEDDIAL5:
case IDD_SD1SPEEDDIAL6:
case IDD_SD1SPEEDDIAL7:
case IDD_SD1SPEEDDIAL8:

nCurrentSpeedDial =LOWORD( (DWORD) wParam ) - IDD_SD1SPEEDDIAL1 + 1;

SetDlgItemText (
hwnd,
IDD_SD1EDITNAME,
szSDName [ nCurrentSpeedDial ]
);
SetDlgItemText (
hwnd,
IDD_SD1EDITNUMBER,
gszSDNumber[nCurrentSpeedDial]
);

SetFocus( GetDlgItem( hwnd, IDD_SD1EDITNAME ) );
SendDlgItemMessage(
hwnd,
IDD_SD1EDITNAME,
EM_SETSEL,
0,
MAKELPARAM(0, -1)
);
break;

case IDD_SD1EDITNAME:
if ( HIWORD( wParam ) == EN_CHANGE )
{

GetDlgItemText (
hwnd,
IDD_SD1EDITNAME,
szName,
TAPIMAXCALLEDPARTYSIZE
);

// if there is no name, label the button with
// the number
if ( szName[ 0 ] == '\0' )
{
szSDName[ nCurrentSpeedDial ][ 0 ] = '\0';
lstrcpy( szName, gszSDNumber[ nCurrentSpeedDial ] );
}
else
{
lstrcpy( szSDName[ nCurrentSpeedDial ], szName );
}

FitTextToButton (
hwnd,
IDD_SD1SPEEDDIAL1 + nCurrentSpeedDial - 1,
szName
);
AmpersandCompensate( szName, szTemp );

SetDlgItemText (
hwnd,
IDD_SD1SPEEDDIAL1 + nCurrentSpeedDial - 1,
szTemp
);
}
break;

case IDD_SD1EDITNUMBER:
if ( HIWORD( wParam ) == EN_CHANGE )
{
GetDlgItemText (
hwnd,
IDD_SD1EDITNUMBER,
gszSDNumber[nCurrentSpeedDial],
TAPIMAXDESTADDRESSSIZE
);

if ( gszSDNumber[ nCurrentSpeedDial ][ 0 ] == '\0' )
{
GetDlgItemText (
hwnd,
IDD_SD1EDITNAME,
szName,
TAPIMAXDESTADDRESSSIZE
);

if ( szName[ 0 ] == '\0' )
{
SetDlgItemText (
hwnd,
IDD_SD1SPEEDDIAL1 + nCurrentSpeedDial - 1,
szName
);

}
}
}
break;
} // switch(LOWORD((DWORD)wParam))
break;

} // case WM_COMMAND:

default:
;

} // switch(msg)

return FALSE;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL CALLBACK SpeedDial2Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static DWORD nCurrentSpeedDial;

static const DWORD aMenuHelpIDs[] =
{
IDOK, IDH_DIALER_SPEED_SAVE,
IDD_SD2SAVEANDDIAL, IDH_DIALER_SPEED_SAVE_DIAL,
IDD_SD2TEXTNAME, IDH_DIALER_SPEED_NAME,
IDD_SD2EDITNAME, IDH_DIALER_SPEED_NAME,
IDD_SD2TEXTNUMBER, IDH_DIALER_SPEED_NUMBER,
IDD_SD2EDITNUMBER, IDH_DIALER_SPEED_NUMBER,
0, 0
};

switch(msg)
{
case WM_HELP:
// processes clicks on controls when
// context mode help is selected
WinHelp (
((LPHELPINFO)lParam)->hItemHandle,
gszHELPfilename,
HELP_WM_HELP,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_CONTEXTMENU:
// processes right-clicks on controls
WinHelp (
(HWND)wParam,
gszHELPfilename,
HELP_CONTEXTMENU,
(DWORD)(LPVOID)aMenuHelpIDs
);
return TRUE;

case WM_INITDIALOG:
{
char szFieldName [MAXBUFSIZE];
char szName [TAPIMAXCALLEDPARTYSIZE];

nCurrentSpeedDial = LOWORD( lParam ) - IDD_DSPEEDDIAL1 + 1;

// retrieve speed dial button info
wsprintf(szFieldName, "Name%d", nCurrentSpeedDial);
GetPrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszNULL,
szName,
TAPIMAXCALLEDPARTYSIZE - 1,
gszINIfilename
);
SetDlgItemText (
hwnd,
IDD_SD2EDITNAME,
szName
);

SetDlgItemText (
hwnd,
IDD_SD2EDITNUMBER,
gszSDNumber[nCurrentSpeedDial]
);

// limit the lengths of the texts
SendDlgItemMessage (
hwnd,
IDD_SD2EDITNAME,
EM_LIMITTEXT,
(WPARAM)(TAPIMAXCALLEDPARTYSIZE - 1),
0
);

SendDlgItemMessage (
hwnd,
IDD_SD2EDITNUMBER,
EM_LIMITTEXT,
(WPARAM)(TAPIMAXDESTADDRESSSIZE - 1),
0
);


SetFocus( GetDlgItem( hwnd, IDD_SD2EDITNAME ) );
SendDlgItemMessage (
hwnd,
IDD_SD2EDITNAME,
EM_SETSEL,
0,
MAKELPARAM(0, -1)
);

return FALSE;
}

case WM_COMMAND:
{
char szName[ TAPIMAXCALLEDPARTYSIZE ];
char szTemp[ TAPIMAXCALLEDPARTYSIZE ];
char szFieldName[MAXBUFSIZE];

switch ( LOWORD( (DWORD) wParam ) )
{
case IDOK:
case IDD_SD2SAVEANDDIAL:
{
GetDlgItemText (
hwnd,
IDD_SD2EDITNAME,
(LPTSTR) szName,
TAPIMAXCALLEDPARTYSIZE
);

GetDlgItemText (
hwnd,
IDD_SD2EDITNUMBER,
(LPTSTR) gszSDNumber[nCurrentSpeedDial],
TAPIMAXCALLEDPARTYSIZE
);

wsprintf ( szFieldName, "Name%d", nCurrentSpeedDial );

WritePrivateProfileString (
"Speed Dial Settings",
szFieldName,
szName,
gszINIfilename
);

wsprintf ( szFieldName, "Number%d", nCurrentSpeedDial );
WritePrivateProfileString (
"Speed Dial Settings",
szFieldName,
gszSDNumber[nCurrentSpeedDial],
gszINIfilename
);

// update main window buttons
// is only number has been entered, label button with it.
if ( szName[ 0 ] == '\0' )
{
lstrcpy( szName, gszSDNumber[ nCurrentSpeedDial ] );
}

FitTextToButton (
ghWndMain,
IDD_DSPEEDDIAL1 + nCurrentSpeedDial - 1,
(LPSTR) szName
);

AmpersandCompensate( szName, szTemp );

SetDlgItemText (
ghWndMain,
IDD_DSPEEDDIAL1 + nCurrentSpeedDial - 1,
(LPCSTR)szTemp
);

// if save and dial, then post dial message to main window
if ( LOWORD( (DWORD) wParam ) == IDD_SD2SAVEANDDIAL )
{
PostMessage (

ghWndMain, 
WM_COMMAND,
MAKEWPARAM (
nCurrentSpeedDial + IDD_DSPEEDDIAL1 - 1,
BN_CLICKED
),
(LPARAM) GetDlgItem (
ghWndMain,
nCurrentSpeedDial + IDD_DSPEEDDIAL1 - 1
)
);
}
EndDialog(hwnd, TRUE);
return TRUE;
}

case IDCANCEL:
EndDialog(hwnd, FALSE);
return TRUE;

case IDD_SD2EDITNAME:
case IDD_SD2EDITNUMBER:
if ( HIWORD( wParam ) == EN_CHANGE)
{
EnableWindow (
GetDlgItem( hwnd, IDD_SD2SAVEANDDIAL ),
GetWindowTextLength ( GetDlgItem( hwnd, IDD_SD2EDITNUMBER ) ) > 0
);
}
break;

} // switch(LOWORD((DWORD)wParam))
break;
}


default:
;

} // switch(msg)

return FALSE;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID CALLBACK
tapiCallback (
DWORD hDevice,
DWORD dwMsg,
DWORD dwCBInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3
)
{
switch (dwMsg)
{
INT errCode;

case LINE_ADDRESSSTATE:
break;

case LINE_CALLINFO:
break;

case LINE_CALLSTATE:
if ( (HCALL)hDevice != ghCall )
return;

switch ( dwParam1 ) // new state
{
case LINECALLSTATE_IDLE:

// tell "Dialing" window to terminate
if ( ghWndDialing )
{
SendMessage (
ghWndDialing,
WM_COMMAND,
MAKEWPARAM( IDOK, 0 ),
0
);
}

// tapi call cleanup
if ( !gfMakeCallReplyPending && ghCall )
{
if ( ( errCode = lineDeallocateCall( ghCall ) ) < 0 )
{
errString ( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
}
ghCall = NULL;
}
DialerLineClose();
gfCurrentLineAvail = TRUE;

// update main window
DisableDialButtons( FALSE );
break;

case LINECALLSTATE_BUSY:
tapiCallback (
hDevice,
dwMsg,
dwCBInstance,
LINECALLSTATE_DISCONNECTED,
LINEDISCONNECTMODE_BUSY,
dwParam3
);
break;

case LINECALLSTATE_SPECIALINFO:
tapiCallback (
hDevice,
dwMsg,
dwCBInstance,
LINECALLSTATE_DISCONNECTED,
LINEDISCONNECTMODE_UNREACHABLE,
dwParam3
);
break;

case LINECALLSTATE_DISCONNECTED:
{
BOOL fCallOK;
DWORD LineDisconnectMode;


if ( dwParam2 == 0 )
LineDisconnectMode = LINEDISCONNECTMODE_NORMAL;
else
LineDisconnectMode = dwParam2;

fCallOK = ( LineDisconnectMode == LINEDISCONNECTMODE_NORMAL||
LineDisconnectMode == LINEDISCONNECTMODE_UNKNOWN||
LineDisconnectMode == LINEDISCONNECTMODE_PICKUP||
LineDisconnectMode == LINEDISCONNECTMODE_FORWARDED||
LineDisconnectMode == LINEDISCONNECTMODE_UNAVAIL
);


if ( !gfMakeCallReplyPending && ghCall )
{
//gfDropping = TRUE;
if ( ( gDropCallRequestID = lineDrop ( ghCall, NULL, 0 ) ) < 0 )
{
errString ( ghWndMain, gDropCallRequestID, MB_ICONSTOP | MB_OK );
}
}

if ( !fCallOK )
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CALLFAILED),
ghWndMain,
(DLGPROC)LineInUseProc,
LineDisconnectMode
);
break;
}
}
break;


case LINE_CLOSE:
if ( gCurrentLineInfo.hLine == (HLINE)hDevice )
{
errString(ghWndMain, ERR_LINECLOSE, MB_ICONEXCLAMATION | MB_OK );
gCurrentLineInfo.hLine = NULL;
gfCurrentLineAvail = FALSE;
DisableDialButtons(FALSE);
}
break;

case LINE_CREATE:
// dwParam1 is the new device's ID
if ( dwParam1 >= gnAvailDevices )
{
DWORD* gnAddrTemp;
DWORD iLine;
LINEINFO LineInfo;

// we record new device's address count.

// we are assuming here that we're just adding a new
// line and it's sequential and it's the last one

gnAvailDevices = dwParam1 + 1;

gnAddrTemp = (DWORD *) DialerAlloc ( sizeof(DWORD) * (int)(gnAvailDevices) );

for ( iLine = 0; iLine < (gnAvailDevices-1); ++iLine )
gnAddrTemp[iLine] = gnAddr[iLine];

DialerFree( gnAddr );

// we have effectively added one more
// space in the gnAddr array
gnAddr = gnAddrTemp;

if ( GetLineInfo( dwParam1, &LineInfo ) != ERR_NONE )
break;

gnAddr[dwParam1] = LineInfo.nAddr;
}
break;

case LINE_DEVSPECIFIC:
break;

case LINE_DEVSPECIFICFEATURE:
break;

case LINE_GATHERDIGITS:
break;

case LINE_GENERATE:
break;

case LINE_LINEDEVSTATE:
if ( dwParam1 & LINEDEVSTATE_REINIT )
{
if(dwParam2 != 0)
{
// this is another msg translated into REINIT
tapiCallback( hDevice, dwParam2, dwCBInstance, dwParam3, 0, 0 );
}
else
{
// Re-initialize TAPI
gfNeedToReinit = TRUE;
}
}

if ( dwParam1 & LINEDEVSTATE_REMOVED )
{
DialerLineClose();
tapiCallback(hDevice, LINE_CLOSE, dwCBInstance, 0, 0, 0); // is this needed?
}
break;

case LINE_MONITORDIGITS:
break;

case LINE_MONITORMEDIA:
break;

case LINE_MONITORTONE:
break;

// async reply from lineMakeCall() or lineDrop()
case LINE_REPLY:

// reply for lineMakeCall
if ( (LONG) dwParam1 == gMakeCallRequestID )
{
// error on make call
if ( dwParam2 != ERR_NONE )
{
// Get rid of the Dialing Dialog box if it's up
if ( ghWndDialing )
{
SendMessage(
ghWndDialing,
WM_COMMAND,
MAKEWPARAM(IDOK,0),
0
);
}

if ( dwParam2 == LINEERR_CALLUNAVAIL )
{
DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CALLFAILED),
ghWndMain,
(DLGPROC)LineInUseProc,
0
);
}
else
{
errString ( ghWndMain, dwParam2, MB_ICONEXCLAMATION | MB_OK );
}

ghCall = NULL;
DialerLineClose();
gfCurrentLineAvail = TRUE;
}

gfMakeCallReplyPending = FALSE;
}

// reply from lineDrop()
if ( (LONG) dwParam1 == gDropCallRequestID )
{
//gfDropping = FALSE;

// tell "Dialing" window to terminate
if ( ghWndDialing )
{
SendMessage (
ghWndDialing,
WM_COMMAND,
MAKEWPARAM( IDOK,0 ),
0
);
}

// tapi call cleanup
if ( dwParam2 == ERR_NONE )
{
if ( !gfMakeCallReplyPending && ghCall )
{
if ( ( errCode = lineDeallocateCall( ghCall ) ) < 0 )
{
errString ( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
}
ghCall = NULL;
}
}
DialerLineClose ();
gfCurrentLineAvail = TRUE;
}

break;

case LINE_REQUEST:
// Simple TAPI request
if ( dwParam1 == LINEREQUESTMODE_MAKECALL )
{
gfCallRequest = TRUE;
}
break;
}
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL InitializeLineBox(HWND hwndLineBox)
{

DWORD iLine, iItem, iItemCurrent = (DWORD)-1;
DWORD errCode;

LPLINEINFO lpLineInfo = NULL;

// allocate buffer for storing LINEINFO for all of
// the available lines. Always allocate space for
// at least one line
if ( gnAvailDevices == 0 )
{
lpLineInfo = (LPLINEINFO) DialerAlloc( sizeof(LINEINFO) );
}
else
{
lpLineInfo = (LPLINEINFO) DialerAlloc ( sizeof(LINEINFO) * (int)gnAvailDevices );
}

// if no space was set aside...
if ( lpLineInfo == NULL )
return LINEERR_NOMEM;

// fill lpLineInfo[] and open each line
for ( iLine = 0; iLine < gnAvailDevices; ++iLine )
{
// skip remaining processing for this line if it didn't open
if ( GetLineInfo( iLine, &lpLineInfo[iLine] ) != ERR_NONE )
{
continue;
}

iItem = SendMessage (
hwndLineBox,
CB_ADDSTRING,
0,
(LPARAM)(LPCSTR)(lpLineInfo[iLine].szLineName)
);

// error, bail out.
if ( iItem == CB_ERR || iItem == CB_ERRSPACE )
{
if (lpLineInfo)
{
DialerFree(lpLineInfo);
}

return FALSE;
}

errCode = SendMessage (
hwndLineBox,
CB_SETITEMDATA,
(WPARAM)iItem,
(LPARAM)iLine
);

if ( iLine == giCurrentLine )
{
iItemCurrent = iItem;
}
else if ( iItemCurrent != -1 && iItem <= iItemCurrent )
{
// if the item we are putting is before the
// "current" item, we must increment iItemCurrent
// to reflect that something is being placed before
// it, due to sorting
++iItemCurrent;
}
}

if ( iItemCurrent == (DWORD)-1 )
iItemCurrent = 0;

if ( SendMessage( hwndLineBox, CB_GETCOUNT, 0, 0) != 0 )
{
SendMessage( hwndLineBox, CB_SETCURSEL, (WPARAM)iItemCurrent, 0 );
return TRUE;
}

DialerFree(lpLineInfo);
return FALSE;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
BOOL InitializeAddressBox( HWND hwndLineBox, HWND hwndAddressBox )
{
DWORD errCode;
DWORD iAddress, iItem, iItemCurrent = (DWORD)-1;
DWORD iLineBoxCurrent;
LPSTR pszAddressName;

if ( SendMessage( hwndLineBox, CB_GETCOUNT, 0, 0 ) == 0 )
{
return FALSE;
}

// select current entry in line box
iLineBoxCurrent = SendMessage (
hwndLineBox,
CB_GETITEMDATA,
SendMessage( hwndLineBox, CB_GETCURSEL, 0, 0 ),
0
);
// empty address list box
SendMessage ( hwndAddressBox, CB_RESETCONTENT, 0, 0);

// get all the address for this line
for ( iAddress = 0; iAddress < gnAddr[iLineBoxCurrent]; ++iAddress )
{
pszAddressName = GetAddressName( iLineBoxCurrent, iAddress );

// if this address if fails, try the next one
if ( !pszAddressName )
continue;

iItem = SendMessage (
hwndAddressBox,
CB_ADDSTRING,
0,
(LPARAM) (LPCSTR) (pszAddressName)
);

// error, bail out
if ( iItem == CB_ERR || iItem == CB_ERRSPACE )
return FALSE;

errCode = SendMessage (
hwndAddressBox,
CB_SETITEMDATA,
(WPARAM) iItem,
(LPARAM) iAddress
);

if ( iLineBoxCurrent == giCurrentLine )
{
if(iAddress == giCurrentAddress)
{
iItemCurrent = iItem;
}
else
{
// if the item we are putting is before the
// "current" item, we must increment iItemCur
// to reflect that something is being placed
// before it, due to sorting
if ( iItemCurrent != -1 && iItem <= iItemCurrent )
{
++iItemCurrent;
}
}
}

DialerFree( pszAddressName );
}

if ( iLineBoxCurrent != giCurrentLine )
{
// if we're not looking at the current line
// then highlight address 0
iItemCurrent = 0;
}

SendMessage (
hwndAddressBox,
CB_SETCURSEL,
iItemCurrent,
0
);
return TRUE;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID ManageAssistedTelephony(VOID)
{
DWORD errCode;
LPLINEREQMAKECALL lpRequestBuffer;

lpRequestBuffer = (LPLINEREQMAKECALL) DialerAlloc( sizeof( LINEREQMAKECALL ) );
if ( !lpRequestBuffer )
{
goto error;
}

// bring window to front
SetForegroundWindow(ghWndMain);

// get next queued request.
errCode = lineGetRequest (
ghLineApp,
LINEREQUESTMODE_MAKECALL,
lpRequestBuffer

);
if ( errCode )
{
// if no more call requests pending, reset flag.
if ( errCode == LINEERR_NOREQUEST )
{
gfCallRequest = FALSE;
}
else
{
errString ( ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
}
goto error;
}


// if a line has not been selected
if ( giCurrentLine == (DWORD)-1 )
{
if (!DialogBoxParam (
ghInst,
MAKEINTRESOURCE(IDD_CONNECTUSING),
ghWndMain,
(DLGPROC) ConnectUsingProc,
INVALID_LINE
))
{
// failed to get a line
goto error;
}
}

// make the reuested call.
InitiateCall (
lpRequestBuffer->szDestAddress,
lpRequestBuffer->szCalledParty
);

error :
if ( lpRequestBuffer )
{
DialerFree( lpRequestBuffer );
}
return;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
VOID DialerLineClose()
{
DWORD errCode;

if ( gCurrentLineInfo.hLine )
{
if ( errCode = lineClose ( gCurrentLineInfo.hLine ) )
{
errString ( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
}
gCurrentLineInfo.hLine = NULL;
}


// re-initialize TAPI if it needs to be re-initialized
if ( gfNeedToReinit )
{
CloseTAPI();

errCode = InitializeTAPI();
if(errCode)
{
errString(ghWndMain, errCode, MB_APPLMODAL | MB_ICONEXCLAMATION );
DialerCleanup(); // terminate program if we can't init
return;
}

errCode = lineRegisterRequestRecipient (
ghLineApp,
0,
LINEREQUESTMODE_MAKECALL,
TRUE
);
if (errCode)
{
errString(ghWndMain, errCode, MB_ICONEXCLAMATION | MB_OK );
}

gfNeedToReinit = FALSE;
}
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
int errString( HWND hWndOwner, UINT errCode, UINT uFlags )
{
PTSTR ptStrTitle;
PTSTR ptStrError;
int nResult;
BOOL bDefault = FALSE;

ptStrTitle = DialerAlloc( MAXBUFSIZE );
if ( NULL == ptStrTitle )
{
// Now, _this_ is a problem.
return 0;
}

ptStrError = DialerAlloc( MAXBUFSIZE );
if ( NULL == ptStrError )
{
// Now, _this_ is a problem.
DialerFree( ptStrTitle);
return 0;
}

switch(errCode)
{
case ERR_NOLINES:
errCode = ikszErrNoVoiceLine;
break;

case ERR_NOVOICELINE:
errCode = ikszErrNoVoiceLine;
break;

case ERR_LINECLOSE:
errCode = ikszErrLineClose;
break;

case ERR_911WARN:
errCode = ikszWarningFor911;
break;

case ERR_NEWDEFAULT:
errCode = ikszWarningNewDefault;
break;

case LINEERR_NODRIVER:
errCode = ikszErrLineInitNoDriver;
break;

case LINEERR_NODEVICE:
errCode = ikszErrLineInitNoDevice;
break;

case LINEERR_INIFILECORRUPT:
errCode = ikszErrLineInitBadIniFile ;
break;

case LINEERR_NOMEM:
errCode = ikszErrOOM;
break;

case LINEERR_INCOMPATIBLEAPIVERSION:
errCode = ikszErrLineInitWrongDrivers ;
break;

case LINEERR_OPERATIONFAILED:
errCode = ikszErrTAPI;
break;

case LINEERR_INVALADDRESS:
errCode = ikszErrInvalAddress;
break;

case LINEERR_ADDRESSBLOCKED:
errCode = ikszErrAddrBlocked;
break;

case LINEERR_BILLINGREJECTED:
errCode = ikszErrBillingRejected;
break;

case LINEERR_RESOURCEUNAVAIL:
case LINEERR_ALLOCATED:
case LINEERR_INUSE:
errCode = ikszErrResUnavail;
break;

case LINEERR_NOMULTIPLEINSTANCE:
errCode = ikszErrNoMultipleInstance;
break;

case LINEERR_INVALCALLSTATE:
errCode = ikszErrInvalCallState;
break;

case LINEERR_INVALCOUNTRYCODE:
errCode = ikszErrInvalidCountryCode;
break;

default:
bDefault = TRUE;
break;

}


if (bDefault)
{
// if using default error, get TAPI's
// error message from FormatError()
if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
(LPCVOID)GetModuleHandle(TEXT("TAPI32.DLL")),
(DWORD)TAPIERROR_FORMATMESSAGE(errCode),
0,
(LPTSTR)ptStrError,
MAXBUFSIZE,
NULL))
{
// if this fails, fall back on default
LoadString( ghInst, ikszErrDefault, ptStrError, MAXBUFSIZE);
}

}
else // not the default error message
{

if ( 0 == LoadString( ghInst, errCode, ptStrError, MAXBUFSIZE ) )
{
#if DBG
TCHAR buf[200];
wsprintf( buf, "Cannot load string: hinst:0x%08lx errcode: %ld", (DWORD)ghInst,(DWORD)errCode);
OutputDebugString( buf );
MessageBox( NULL, buf, "Dialer", MB_OK);
#endif

LoadString( ghInst, ikszErrDefault, ptStrError, MAXBUFSIZE );
}
}

LoadString( ghInst, ikszWarningTitle, ptStrTitle, MAXBUFSIZE );

nResult = MessageBox( hWndOwner, ptStrError, ptStrTitle, uFlags );


DialerFree( ptStrTitle );
DialerFree( ptStrError );


return nResult;
}


/*
*Name :
*FitTextToButton
*
*Arguements :
*hDlg handle for the dialog in which this button is embedded
*nButtonID button id of this button
*szNameName to fit on the button. Max size TAPIMAXCALLEDPARTYSIZE
*
*Return :
*None
*
*Comments :
*Function first checks to see if the button text specified fits in the
*button. If it does not it truncates it appropriately and adds trailing
*ellipses.
*/
VOID FitTextToButton ( HWND hDlg, INT nButtonID, LPSTR szName )
{

HDC hDC;
HFONT hFont, hOldFont;
HWND hWnd;

do
{
// calculate number of chars. that can fit on
// the button
intnLen;
RECT rect;
SIZE size;
POINT pt;
char buf [TAPIMAXCALLEDPARTYSIZE + 1];

// get button dimensions
hWnd = GetDlgItem( hDlg, nButtonID );
if ( hWnd == NULL )
break;

if ( !GetClientRect( hWnd, &rect ) )
break;

// get character dimensions
hDC = GetDC( hWnd );
if ( hDC == NULL )
break;

hFont = (HFONT) SendMessage( hWnd, WM_GETFONT, 0, 0 );
if ( hFont == NULL )
hOldFont = SelectObject( hDC, GetStockObject( SYSTEM_FONT ) );
else
hOldFont = SelectObject( hDC, hFont );

// add an extra char at the end to compensate for
// leading space,
lstrcpy ( buf, szName );
nLen = lstrlen( buf );
buf [ nLen ] = 'X';
buf [ nLen + 1 ] = '\0';

if ( !GetTextExtentPoint32( hDC, buf, nLen + 1, &size ) )
break;

pt.x = size.cx;
if ( !LPtoDP( hDC, &pt, 1 ) )
break;

// check if name fits on button
if ( pt.x > rect.right )
{
// find how much of the name fits
int i = 0;

nLen = lstrlen( szName );
for ( i = 0; i < nLen; i++ )
{
buf[ i ] = szName[ i ];
// an extra char is stuffed to compensate for the
// leading space left by the left alignment
buf [ i + 1 ] = 'X';
buf [ i + 2 ] = '\0';

// break out in cases of error condition
if ( !GetTextExtentPoint32( hDC, buf, i + 2, &size ) )
{
i = nLen;
break;
}

pt.x = size.cx;
if ( !LPtoDP( hDC, &pt, 1 ) )
{
i = nLen;
break;
}

if ( pt.x > rect.right )
break;
}

// error
if ( i >= nLen )
break;

// name is too long. truncate and add ellipses
szName [i - 3] = '\0';
lstrcat( szName, "..." );
}

} while( FALSE );

if ( hDC )
{
SelectObject( hDC, hOldFont );
ReleaseDC( hWnd, hDC );
}

return;
}



/*
*Name :
*Is911
*
*Arguements :
*lpTransOut -Translated address contained the dialable string
*
*Returns
*TRUE -If number to be dialed (in the US) is prefixed by 911
*FALSE -Otherwise
*
* Comments
*
*/
BOOL Is911 ( LPLINETRANSLATEOUTPUT lpTransOut )
{

DWORD i = 0, j = 0;
LPSTR lpDialDigits = (LPSTR)lpTransOut + lpTransOut-> dwDialableStringOffset;
char sz3Pref [ 4 ] = "";


// if this is not the US
if ( lpTransOut-> dwCurrentCountry != 1 )
return FALSE;

// skip non digit characters and extract
// the first 3 digits in the dialable number
for ( i = 0, j = 0; i < lpTransOut-> dwDialableStringSize ; i++ )
{
if ( ISDIGIT( lpDialDigits[i] ) )
{
sz3Pref[ j++ ] = lpDialDigits [ i ];
sz3Pref[ j ] = '\0';
if ( j == 3 )
break;
}
}

if ( !lstrcmp( sz3Pref, "911" ) )
{
return TRUE;
}

return FALSE;
}


/*
*Name :
*MakeCanonicalNumber
*
*Arguements :
*szNumber Number to convert into canonical form. Max size TAPIMAXDESTADDRESSSIZE
*szCanNumberCanonical representation of number specified in szNumber
*
*Return :
*TRUEIf the conversion was successful.
*FALSEotherwise
*
*Comments :
*Function first checks if given number is already in canonical form.
*If it is, it returns. If it is not, then it performs the conversion.
*/

BOOL MakeCanonicalNumber ( LPCSTR szNumber, LPSTR szCanNumber )
{
char szDigits [ TAPIMAXDESTADDRESSSIZE ];
char szPref [ TAPIMAXDESTADDRESSSIZE ];

BOOL bRes = FALSE;

INT errCode = -1;
INT nLenPref, nLenDigits, cPos, i;

DWORD dwSize = 0;
DWORD dwInd = 0;

LPLINETRANSLATEOUTPUT lpTransOut = NULL;
LPLINETRANSLATECAPS lpTransCaps = NULL;


dwSize = sizeof ( LINETRANSLATEOUTPUT );
do
{
lpTransOut = ( LPLINETRANSLATEOUTPUT ) DialerAlloc ( dwSize );
if ( !lpTransOut )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}

lpTransOut-> dwTotalSize = dwSize;
errCode = lineTranslateAddress (
ghLineApp,
giCurrentLine,
gCurrentLineInfo.dwAPIVersion,
szNumber,
0,
0,
lpTransOut
);
if ( errCode )
{
goto error;
}

if ( lpTransOut-> dwNeededSize <= lpTransOut-> dwTotalSize )
break;

dwSize = lpTransOut-> dwNeededSize;
DialerFree( lpTransOut );

} while( TRUE );


// check if input number is already in
// canonical form.
if ( lpTransOut-> dwTranslateResults & LINETRANSLATERESULT_CANONICAL )
goto error;

// ensure country is the USA.
if ( lpTransOut-> dwCurrentCountry != 1 )
goto error;


// Extract the digits from given string
// allowed formatting characters that are ignored are
// space, (, ), -, .
// presence of other characters will render the string invalid.

// find the prefix of the address upto the | mark.
// the rest of the string can be ignored
nLenPref = strcspn ( szNumber, "|" );
strncpy( szPref, szNumber, nLenPref );
szPref[ nLenPref ] = '\0';

// if string is not composed entirely of digits
// and allowable formating characters, quit conversion
if ( strspn( szPref, " 0123456789()-." ) != (size_t) nLenPref )
goto error;

// collect digits ignoring formating characters.
szDigits[ 0 ] = '\0';
for ( i = 0, nLenDigits = 0; i < nLenPref; i++ )
{
if ( ISDIGIT( szNumber[ i ] ) )
{
szDigits[ nLenDigits++ ] = szNumber[ i ];
}
}
szDigits[ nLenDigits ] = '\0';

// if "internal" number
if ( nLenDigits < LOCAL_NUMBER )
goto error;

switch ( nLenDigits )
{
// Local number ( 7 digits) preceeded by a 0/1
// Strip leading 0/1 and treat as a local number
case EXTENDED_LOCAL_NUMBER:
if ( szDigits[ 0 ] == '0' || szDigits[ 0 ] == '1' )
{
nLenDigits--;
memmove( szDigits, &(szDigits[1]), nLenDigits );
szDigits[ nLenDigits ] = '\0';

cPos = strcspn( szPref, "01" );
nLenPref--;
memmove( &(szPref[ cPos ]), &(szPref[ cPos + 1 ]), nLenPref - cPos );
szPref[ nLenPref ] = '\0';
}
else
{
goto error;
}

case LOCAL_NUMBER :
{
LPLINELOCATIONENTRY lpLocLst;

// if leading digit is 0 or 1, it is
// illegal in the US
if ( szDigits[ 0 ] == '0' || szDigits[ 0 ] == '1' )
{
goto error;
}

// get area code nformation for local number
dwSize = sizeof( LINETRANSLATECAPS );
do
{
lpTransCaps = (LPLINETRANSLATECAPS) DialerAlloc( dwSize );
if ( !lpTransCaps )
{
errString( ghWndMain, LINEERR_NOMEM, MB_ICONSTOP | MB_OK );
goto error;
}

lpTransCaps-> dwTotalSize = dwSize;
errCode = lineGetTranslateCaps (
ghLineApp,
gCurrentLineInfo.dwAPIVersion,
lpTransCaps
);
if ( errCode )
{
errString( ghWndMain, errCode, MB_ICONSTOP | MB_OK );
goto error;
}

if ( lpTransCaps-> dwNeededSize <= lpTransCaps-> dwTotalSize )
{
break;
}

dwSize = lpTransCaps-> dwNeededSize;
DialerFree( lpTransCaps );

} while ( TRUE );

// skip entries till you locate information for current location
dwSize = sizeof( LINELOCATIONENTRY );
lpLocLst = (LPLINELOCATIONENTRY) ( (LPSTR) lpTransCaps +
lpTransCaps-> dwLocationListOffset );

for ( dwInd = 0; dwInd < lpTransCaps-> dwNumLocations ; dwInd++ )
{
if ( lpLocLst[ dwInd ].dwPermanentLocationID == lpTransCaps-> dwCurrentLocationID )
break;
}

// current location no found ?????
// login error
if ( dwInd == lpTransCaps-> dwNumLocations )
{
goto error;
}

// construct canonical form as
szCanNumber[ 0 ]= '\0';
lstrcat( szCanNumber, "+1 (" );
lstrcat( szCanNumber, (LPSTR) lpTransCaps + lpLocLst[ dwInd ].dwCityCodeOffset );
lstrcat( szCanNumber, ") " );
lstrcat( szCanNumber, szDigits );

cPos = strcspn( szNumber, "|" );
if ( cPos != lstrlen( szNumber ) )
{
lstrcat( szCanNumber, &(szNumber[ cPos ]) );
}

bRes = TRUE;
break;
}

case EXTENDED_LONG_DISTANCE_NUMBER:
{
// Long distance number ( 10 digits) preceeded by a 0/1
// Strip leading 0/1 and treat as a long distance number
if ( szDigits[ 0 ] == '0' || szDigits[ 0 ] == '1' )
{
nLenDigits--;
memmove( szDigits, &(szDigits[1]), nLenDigits );
szDigits[ nLenDigits ] = '\0';

cPos = strcspn( szPref, "01" );
nLenPref--;
memmove( &(szPref[ cPos ]), &(szPref[ cPos + 1 ]), nLenPref - cPos );
szPref[ nLenPref ] = '\0';
}
else
{
goto error;
}

}

case LONG_DISTANCE_NUMBER:
{
// if first or fourth digit is 0/1, illegal number
if ( szDigits[ 0 ] == '0' || szDigits[ 0 ] == '1' ||
szDigits[ 3 ] == '0' || szDigits[ 3 ] == '1' )
{
goto error;
}

szCanNumber[ 0 ] = '\0';
lstrcat( szCanNumber, "+1 (" );
strncat( szCanNumber, szDigits, 3 );
lstrcat( szCanNumber, ") " );

lstrcat( szCanNumber, &(szDigits[ 3 ]) );

bRes = TRUE;
}
break;

default :
goto error;
}

error:
if ( lpTransOut )
DialerFree( lpTransOut );

if ( lpTransCaps )
DialerFree( lpTransCaps );

return bRes;
}


/*
*Name :
*AmpersandCompensate
*
*Arguements :
*lpszSrc:Src string containing &s

*lpszDst:Dest string    
*
*Return :
*
*Comments :
*Copies string pointed to by lpszSrc to lpszDst character by
*character. If an & is encountered in this process in lpszSrc
*it is copied as && into lpszDst.
*Assumes lpszDst and lpszSrc are of size TAPIMAXCALLEDPARTYSIZE
*/
VOID AmpersandCompensate ( LPCSTR lpszSrc, LPSTR lpszDst )
{
// check if the name has an & in it. If so replace
// it with &&.
INT cCnt, cInd;

for ( cCnt = 0, cInd = 0;
cInd < TAPIMAXCALLEDPARTYSIZE;
cInd++, cCnt++ )
{
if ( lpszSrc[ cCnt ] == '&' )
{
lpszDst[ cInd++ ] = '&';
}
lpszDst[ cInd ] = lpszSrc[ cCnt ];

if ( lpszSrc[ cCnt ] == '\0' )
break;
}

// make sure string is null terminated.
lpszDst[ TAPIMAXCALLEDPARTYSIZE - 1 ] = '\0';

return;
}


/*
*Name :
*AmpersandDeCompensate
*
*Arguements :
*lpszSrc:Src string containing &s
*lpszDst:Dest string
*
*Return :
*
*Comments :
*Copies string pointed to by lpszSrc to lpszDst character by
*character. If an && is encountered in this process in lpszSrc
*it is copied as & into lpszDst.
*Assumes lpszDst and lpszSrc are of size TAPIMAXCALLEDPARTYSIZE
*/
VOID AmpersandDeCompensate ( LPCSTR lpszSrc, LPSTR lpszDst )
{
// check if the name has an & in it. If so replace
// it with &&.
INT cCnt, cInd;

for ( cCnt = 0, cInd = 0;
cInd < TAPIMAXCALLEDPARTYSIZE;
cInd++, cCnt++ )
{
if ( ( lpszSrc[ cInd ] == '&' ) &&
( lpszSrc[ cInd + 1 ] == '&' ) )
{
cInd++;
}
lpszDst[ cCnt ] = lpszSrc[ cInd ] ;

if ( lpszSrc [ cInd ] == '\0' )
{
break;
}
}

lpszDst[ TAPIMAXCALLEDPARTYSIZE - 1 ] = '\0';

return;
}