ACDSMPL.C

////////////////////////////////////////////////////////////////////////////// 
//
// ACDSMPL.C
//
// Handles all the UI for ACDSample
//
//////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <tapi.h>
#include <stdlib.h>
#include "resource.h"
#include "acdsmpl.h"


//////////////////////////////////////////////////////////////////////////////
// PROTOTYPES
//////////////////////////////////////////////////////////////////////////////
static BOOL CreateMainWindow (int nCmdShow);

static LRESULT CALLBACK MainDlgProc (HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
void MySetWindow(HWND, int);
void MySaveWindow(HWND);
BOOL ResizeWindows(BOOL bSizeBar, DWORD dwBarLocation);
HTREEITEM AddItemToTree(HTREEITEM hParent,
LPTSTR lpszName,
LPARAM lParam,
HTREEITEM * phItem);
BOOL DoPopupMenu(HTREEITEM hItem, POINT pt);
BOOL DeleteLeafAndStruct(HTREEITEM hItem);
BOOL CALLBACK ChangeGroupDlgProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
BOOL CALLBACK ChangeAgentDlgProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
BOOL CALLBACK AddGroupDlgProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
BOOL CALLBACK AddAgentDlgProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
LRESULT DoCommand(WPARAM wParam, LPARAM lParam);
void AddGroupsToMenu(HTREEITEM hItem,
HMENU hMenu);
BOOL CALLBACK GroupAddToListProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
BOOL CALLBACK AgentAddToListProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
BOOL BuildLineList(HWND hWnd,
DWORD dwDeviceID);
BOOL BuildAddressList(HWND hWnd,
HWND hParentWnd,
DWORD dwDeviceID);
BOOL InitializeTapi();
BOOL CleanUp();
BOOL UpdateGroupLeaf(PGROUP pGroup);
BOOL DoAgentView();
BOOL DoGroupView();
BOOL ReadInFile();
BOOL WriteToDisk();



//////////////////////////////////////////////////////////////////////////////
// GLOBALS
//////////////////////////////////////////////////////////////////////////////
ACDGLOBALS g;

TCHAR gszACDSampleKey[] = TEXT("Software\\Microsoft\\ACDSample");
TCHAR gszPlacementValue[] = TEXT("WindowPlacement");
TCHAR gszBarLocation[] = TEXT("BarLocation");



//////////////////////////////////////////////////////////////////////////////
//
// WinMain()
//
//////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow)
{
MSG msg;

// initialize global variables
g.hInstance = hInstance;
g.pAgents = NULL;
g.pGroups = NULL;

// init tapi stuff
if (!InitializeTapi())
{
MessageBox(NULL,
TEXT("TAPI could not be initialized.\nVerify that")
TEXT("your machine has TAPI devices installed"),
TEXT("Cannot start ACDSMPL"),
MB_OK);
}

if (!CreateMainWindow(nCmdShow))
{
return 0;
}

// main message loop
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(g.hMainWnd,
&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return 1;
}


/////////////////////////////////////////////////////////////////////////////
//
// CreateMainWindow()
//
//////////////////////////////////////////////////////////////////////////////
BOOL CreateMainWindow (int nCmdShow)
{

// InitCommonControls for TreeView control
InitCommonControls();

// Create the main window
g.hMainWnd = CreateDialog(g.hInstance,
MAKEINTRESOURCE(IDD_MAINDLG),
NULL,
MainDlgProc);

if (g.hMainWnd == NULL)
{
return FALSE;
}

// restore default location
MySetWindow(g.hMainWnd, nCmdShow);

// store global hwnds
g.hTreeWnd = GetDlgItem(g.hMainWnd,
IDC_TREEWND);

g.hLogWnd = GetDlgItem(g.hMainWnd,
IDC_EDITWND);

if ((g.hTreeWnd == FALSE) || (g.hLogWnd == FALSE))
{
return FALSE;
}

ResizeWindows(FALSE, 0);

return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
//
// MainDlgProc()
//
//////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK MainDlgProc (HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
static BOOL bButtonDown = FALSE;

switch (uMsg)
{
case WM_INITDIALOG:
return TRUE;

case WM_COMMAND:
{
LRESULT lResult;

lResult = DoCommand(wParam, lParam);
return lResult;
}

// button and mousemove messages tracked to move
// the bar between the treeview control and the
// edit control
case WM_LBUTTONDOWN:
{
bButtonDown = TRUE;
SetCapture(hWnd);
return 0;
}

case WM_LBUTTONUP:
{
bButtonDown = FALSE;
ReleaseCapture();
return 0;
}

case WM_MOUSEMOVE:
{
if (bButtonDown)
{
ResizeWindows(TRUE, (DWORD)LOWORD(lParam));
return 1;
}
break;
}

case WM_SIZE:
{
ResizeWindows(FALSE, 0);
return 1;
}

// catch right click in tree view to make
// popup menu
case WM_NOTIFY:
{
LPNMHDR pnmhdr;
POINT pt;
HTREEITEM hItem;
TV_HITTESTINFO hittestinfo;
RECT rc;

pnmhdr = (LPNMHDR)lParam;

// make sure it's a right click and it's in the treeview
if ((pnmhdr->code != NM_RCLICK) || (pnmhdr->hwndFrom != g.hTreeWnd))
{
break;
}

GetCursorPos(&pt);
GetWindowRect(g.hTreeWnd,
&rc);

hittestinfo.pt.x = pt.x - rc.left;
hittestinfo.pt.y = pt.y - rc.top;

// hittest to get the tree view item
hItem = TreeView_HitTest(g.hTreeWnd,
&hittestinfo);

// only display a menu if the mouse is actually
// over the item (TVHT_ONITEM)
if (hItem == NULL || (!(hittestinfo.flags & TVHT_ONITEM)) )
{
return TRUE;
}

// select that item (right clicking will not select
// by default
TreeView_Select(g.hTreeWnd,
hItem,
TVGN_CARET);

// create the menu
DoPopupMenu(hItem, pt);

return TRUE;


}

case WM_CLOSE:

// save the current window location
WriteToDisk();
CleanUp();
MySaveWindow(hWnd);
PostQuitMessage(0);
return 1;

default:
break;
}
return 0;
}


////////////////////////////////////////////////////////////////////////////////
//
// ResizeWindows - Handles resizing the two child windows of the
// main window. If bSizeBar is true, then the sizing is happening
// because the user is moving the bar. if bSizeBar is false, the sizing
// is happening because of the WM_SIZE or something like that.
//
////////////////////////////////////////////////////////////////////////////////
BOOL ResizeWindows(BOOL bSizeBar, DWORD dwBarLocation)
{
RECT rc, rc2;
int x;

// is the user moving the bar?
if (!bSizeBar)
{
dwBarLocation = g.dwBarLocation;
}

GetClientRect(g.hMainWnd, &rc);

// make sure the bar is in a OK location
if (bSizeBar)
{
if ((LONG)dwBarLocation < GetSystemMetrics(SM_CXSCREEN)/WINDOWSCALEFACTOR)
return FALSE;

if ((LONG)(rc.right - dwBarLocation) < GetSystemMetrics(SM_CXSCREEN)/WINDOWSCALEFACTOR)
return FALSE;
}

// save the bar location
g.dwBarLocation = dwBarLocation;

// get the size of the frame
x = GetSystemMetrics(SM_CXFRAME);

// move tree windows
MoveWindow(g.hTreeWnd,
0,
0,
dwBarLocation,
rc.bottom,
TRUE);

// get the size of the window (in case move window failed
GetClientRect(g.hTreeWnd, &rc2);

// move the edit window with respect to the tree window
MoveWindow(g.hLogWnd,
rc2.right-rc2.left+x+SIZEBAR,
0,
rc.right-(rc2.right-rc2.left)-x-SIZEBAR,
rc.bottom,
TRUE);

return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// MySetWindow - reads in the window placement from registry
// and sets the window and bar.
//
//////////////////////////////////////////////////////////////////////////////
void MySetWindow(HWND hWnd, int nCmdShow)
{
WINDOWPLACEMENT pwp;
HKEY hKey;
DWORD dwDataSize;
DWORD dwDataType;
RECT rc;

pwp.length = sizeof(WINDOWPLACEMENT);

// open the key and read in the WINDOWPLACEMENT structure
RegOpenKeyEx(HKEY_CURRENT_USER,
gszACDSampleKey,
0,
KEY_ALL_ACCESS,
&hKey);

dwDataSize = sizeof(pwp);

if ( RegQueryValueEx(hKey,
gszPlacementValue,
0,
&dwDataType,
(LPBYTE)&pwp,
&dwDataSize) )
{
// if it fails, default
ShowWindow(g.hMainWnd, nCmdShow);
GetWindowRect(g.hMainWnd, &rc);
g.dwBarLocation = (rc.right - rc.left) / 2;
}
else
{
// if it succeeds, set the window and bar
dwDataSize = sizeof(DWORD);

if (RegQueryValueEx(hKey,
gszBarLocation,
0,
&dwDataType,
(LPBYTE)&g.dwBarLocation,
&dwDataSize))
{
g.dwBarLocation = (pwp.rcNormalPosition.right - pwp.rcNormalPosition.left) / 2;
}

SetWindowPlacement(g.hMainWnd, &pwp);
}


RegCloseKey( hKey );
}

//////////////////////////////////////////////////////////////////////////////
//
// MySaveWindow() - save the current window placement and bar
//
//////////////////////////////////////////////////////////////////////////////
void MySaveWindow(HWND hWnd)
{
WINDOWPLACEMENT pwp;
HKEY hKey;
DWORD dwDisposition;


pwp.length = sizeof(WINDOWPLACEMENT);

// get and save
GetWindowPlacement(hWnd, &pwp);

RegCreateKeyEx(HKEY_CURRENT_USER,
gszACDSampleKey,
0,
TEXT(""),
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
0,
&hKey,
&dwDisposition);

RegSetValueEx(hKey,
gszPlacementValue,
0,
REG_BINARY,
(LPBYTE)&pwp,
sizeof(WINDOWPLACEMENT));

RegSetValueEx(hKey,
gszBarLocation,
0,
REG_DWORD,
(LPBYTE)&g.dwBarLocation,
sizeof(DWORD));

RegCloseKey( hKey );

}


//////////////////////////////////////////////////////////////////////////////
//
// AddItemToTree
//
// add a new leaf to the tree
//
//////////////////////////////////////////////////////////////////////////////
HTREEITEM AddItemToTree(HTREEITEM hParent,
LPTSTR lpszName,
LPARAM lParam,
HTREEITEM * phItem)
{
TV_ITEM tvi;
TV_INSERTSTRUCT tvins;
HTREEITEM hti;

tvi.mask = TVIF_TEXT | TVIF_PARAM;

// Set the text of the item.
tvi.pszText = lpszName;
tvi.cchTextMax = lstrlen(lpszName) * sizeof(TCHAR);

// Save the pointer to the buffer
tvi.lParam = lParam;

tvins.item = tvi;
tvins.hInsertAfter = TVI_SORT;

// Set the parent item
tvins.hParent = hParent;

// Add the item to the tree-view control.
hti = (HTREEITEM) SendMessage(g.hTreeWnd,
TVM_INSERTITEM,
0,
(LPARAM) (LPTV_INSERTSTRUCT) &tvins);

// save hitem
if (phItem)
{
*phItem = hti;
}

// select the item so it has focus
TreeView_Select(g.hTreeWnd,
hti,
TVGN_CARET);

return hti;
}

//////////////////////////////////////////////////////////////////////////////
//
// DoPopupMenu(HTREEITEM hItem,
// POINT pt)
//
// hItem - item to create menu for
// pt - location of mouse so we can create menu where it is
//
// creates a popup menu, depending on what kind of item is selected
//
//////////////////////////////////////////////////////////////////////////////
BOOL DoPopupMenu(HTREEITEM hItem, POINT pt)
{

HMENU hMenu;
TV_ITEM tvi;
TCHAR szNewGroup[] = TEXT("&New Group...");
TCHAR szNewAgent[] = TEXT("New &Agent...");
TCHAR szAddAgent[] = TEXT("A&dd Agent...");
TCHAR szGroupProperties[] = TEXT("&Group Properties...");
TCHAR szAgentStatus[] = TEXT("Agent Status...");
TCHAR szAddGroup[] = TEXT("Add Group...");
TCHAR szAgentProperties[] = TEXT("Agent Properties...");
TCHAR szGroupDelete[] = TEXT("Group Delete");
TCHAR szAgentDelete[] = TEXT("Agent Delete");
TCHAR szSignIn[] = TEXT("Agent Sign In");
TCHAR szSignOut[] = TEXT("Agent Sign Out");

// get the selected item
g.hTreeItemWithMenu = hItem;

// create the menu
hMenu = CreatePopupMenu();

// get the lParam, which is a pointer to the item
tvi.mask = TVIF_HANDLE | TVIF_PARAM;
tvi.hItem = hItem;

TreeView_GetItem(g.hTreeWnd,
&tvi);

if (!tvi.lParam)
{
return TRUE;
}

switch (((PGROUP)tvi.lParam)->dwKey)
{
// root item
case GROUPROOTKEY:

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
IDM_NEWGROUP,
szNewGroup);
break;

// root item
case AGENTROOTKEY:

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
IDM_NEWAGENT,
szNewAgent);
break;
// group leaf
case GROUPKEY:

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
(UINT)IDM_GROUPADDTOLIST,
szAddAgent);

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
(UINT)IDM_GROUPAGENTSTATUS,
szAgentStatus);

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
IDM_GROUPPROPERTIES,
szGroupProperties);

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
IDM_GROUPDELETE,
szGroupDelete);

break;
// agent leaf
case AGENTKEY:

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
(UINT)IDM_AGENTADDTOLIST,
szAddGroup);

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
IDM_AGENTPROPERTIES,
szAgentProperties);

AppendMenu(hMenu,
MF_ENABLED | MF_STRING,
IDM_AGENTDELETE,
szAgentDelete);

break;

default:
break;
}

// actually show menu
TrackPopupMenu(hMenu,
TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
pt.x,
pt.y,
0,
g.hMainWnd,
NULL);

return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
//
// LRESULT DoCommand(WPARAM wParam, LPARAM lParam)
// handle WM_COMMAND messages for MainDlgProc
//
///////////////////////////////////////////////////////////////////////////////
LRESULT DoCommand(WPARAM wParam, LPARAM lParam)
{
switch (LOWORD(wParam))
{
// New - create a new tree, so just init
// the items and return
case ID_FILE_NEW:
DoGroupView();
return 1;

// Open - read in a file
case ID_FILE_OPEN:
ReadInFile();
return 1;

case ID_FILE_EXIT:
// save the current window location
WriteToDisk();
CleanUp();
MySaveWindow(g.hMainWnd);
PostQuitMessage(0);
return 1;

// new group
case ID_EDIT_ADDGROUP:
case IDM_NEWGROUP:
DialogBox(g.hInstance,
MAKEINTRESOURCE(IDD_ADD),
g.hTreeWnd,
AddGroupDlgProc);

return 1;

// new agent
case ID_EDIT_ADDAGENT:
case IDM_NEWAGENT:
DialogBox(g.hInstance,
MAKEINTRESOURCE(IDD_ADDAGENT),
g.hTreeWnd,
AddAgentDlgProc);
return 1;

// properties
case IDM_GROUPPROPERTIES:
DialogBox(g.hInstance,
MAKEINTRESOURCE(IDD_ADD),
g.hMainWnd,
ChangeGroupDlgProc);

return 1;

// properties
case IDM_AGENTPROPERTIES:
DialogBox(g.hInstance,
MAKEINTRESOURCE(IDD_ADDAGENT),
g.hMainWnd,
ChangeAgentDlgProc);

return 1;

// delete
case IDM_GROUPDELETE:
case IDM_AGENTDELETE:
{
DeleteLeafAndStruct(g.hTreeItemWithMenu);

return 1;
}


// add to list
case IDM_GROUPADDTOLIST:
DialogBoxParam(g.hInstance,
MAKEINTRESOURCE(IDD_ADDTOLIST),
g.hMainWnd,
GroupAddToListProc,
TRUE);

return 1;

// add to list
case IDM_AGENTADDTOLIST:
DialogBoxParam(g.hInstance,
MAKEINTRESOURCE(IDD_ADDTOLIST),
g.hMainWnd,
AgentAddToListProc,
FALSE);

return 1;

case ID_VIEW_GROUP:
DoGroupView();
return 1;

case ID_VIEW_AGENT:
DoAgentView();
return 1;

default:
break;

}

return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// AddGroupDlgProc - Window proc for the add agent/group dialog box
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK AddGroupDlgProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:

// set text appropriately
SetWindowText(hWnd,
TEXT("Add Group"));

BuildLineList(GetDlgItem(hWnd,
IDC_LINECOMBO),
0);

BuildAddressList(GetDlgItem(hWnd,
IDC_ADDRESSCOMBO),
hWnd,
0);

// set focus on first control
SetFocus(GetDlgItem(hWnd,
IDC_NAME));

return 0;

case WM_COMMAND:

switch (LOWORD(wParam))
{
case IDC_LINECOMBO:
{
if (HIWORD(wParam) == CBN_SELENDOK)
{
// need to redo addresses
BuildAddressList(GetDlgItem(hWnd,
IDC_ADDRESSCOMBO),
hWnd,
0);

return 1;
}

return 0;
}
case IDOK:
{
TCHAR szName[128];
PGROUP pGroup;
DWORD dwLine, dwAddress;
int item;

// get info
SendDlgItemMessage(hWnd,
IDC_NAME,
WM_GETTEXT,
128,
(LPARAM)szName);

item = SendDlgItemMessage(hWnd,
IDC_LINECOMBO,
CB_GETCURSEL,
0,
0);

dwLine = (DWORD)SendDlgItemMessage(hWnd,
IDC_LINECOMBO,
CB_GETITEMDATA,
item,
0);

dwAddress = (DWORD)SendDlgItemMessage(hWnd,
IDC_ADDRESSCOMBO,
CB_GETCURSEL,
0,
0);

// create a structure
pGroup = AddGroup(szName,
dwLine,
dwAddress);

if (!pGroup)
{
return 1;
}

if (g.bGroupView)
{
// add it to the tree
AddItemToTree(g.hGroupParent,
pGroup->lpszName,
(LPARAM)pGroup,
&pGroup->hItem);
}

EndDialog(hWnd, 1);
return 1;
}

case IDCANCEL:
{
EndDialog(hWnd, 0);
return 1;
}

default:
return 0;

}

}

return 0;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// AddAgentDlgProc - Window proc for the add agent/group dialog box
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK AddAgentDlgProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:

BuildLineList(GetDlgItem(hWnd,
IDC_LINECOMBO),
0);

// set focus on first control
SetFocus(GetDlgItem(hWnd,
IDC_NAME));

return 0;

case WM_COMMAND:

switch (LOWORD(wParam))
{
case IDOK:
{
TCHAR szName[128];
TCHAR szNumber[128];
PAGENT pAgent;
DWORD dwLine;
int item;

// get info
SendDlgItemMessage(hWnd,
IDC_NAME,
WM_GETTEXT,
128,
(LPARAM)szName);

SendDlgItemMessage(hWnd,
IDC_DESTADDRESS,
WM_GETTEXT,
128,
(LPARAM)szNumber);

item = SendDlgItemMessage(hWnd,
IDC_LINECOMBO,
CB_GETCURSEL,
0,
0);

dwLine = (DWORD)SendDlgItemMessage(hWnd,
IDC_LINECOMBO,
CB_GETITEMDATA,
item,
0);

// create a structure
pAgent = AddAgent(szName,
szNumber,
dwLine);

if (!pAgent)
{
return 1;
}


if (!g.bGroupView)
{
// add it to the tree
AddItemToTree(g.hAgentParent,
pAgent->lpszName,
(LPARAM)pAgent,
&pAgent->hItem);
}

EndDialog(hWnd, 1);
return 1;
}

case IDCANCEL:
{
EndDialog(hWnd, 0);
return 1;
}

default:
return 0;

} 
}
return 0;
}

//////////////////////////////////////////////////////////////////////////////////
//
// ChangeGroupDlgProc - Window proc for the change (properties) dialog box
// for agents/groups
//
//////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChangeGroupDlgProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
static TV_ITEM tvi;

switch (uMsg)
{
case WM_INITDIALOG:

// set text appropriately
SetWindowText(hWnd,
TEXT("Change Group"));

// get PGROUP and set edit controls
tvi.mask = TVIF_HANDLE | TVIF_PARAM;
tvi.hItem = g.hTreeItemWithMenu;

TreeView_GetItem(g.hTreeWnd,
&tvi);

SendDlgItemMessage(hWnd,
IDC_NAME,
WM_SETTEXT,
0,
(LPARAM)((PGROUP)tvi.lParam)->lpszName);

BuildLineList(GetDlgItem(hWnd,
IDC_LINECOMBO),
(((PGROUP)tvi.lParam)->dwDeviceID));


BuildAddressList(GetDlgItem(hWnd,
IDC_ADDRESSCOMBO),
hWnd,
(((PGROUP)tvi.lParam)->dwAddress));

SetFocus(GetDlgItem(hWnd,
IDC_NAME));

return 0;

case WM_COMMAND:

switch (LOWORD(wParam))
{
case IDC_LINECOMBO:
{
if (HIWORD(wParam) == CBN_SELENDOK)
{
// need to redo addresses
BuildAddressList(GetDlgItem(hWnd,
IDC_ADDRESSCOMBO),
hWnd,
0);

return 1;
}

return 0;
}
case IDOK:
{
TCHAR szName[128];
PGROUP pGroup;

// get info
SendDlgItemMessage(hWnd,
IDC_NAME,
WM_GETTEXT,
128,
(LPARAM)szName);

// get struct
pGroup = (PGROUP)tvi.lParam;

/// get device and address
pGroup->dwDeviceID = SendDlgItemMessage(hWnd,
IDC_LINECOMBO,
CB_GETCURSEL,
0,
0);

pGroup->dwDeviceID = SendDlgItemMessage(hWnd,
IDC_LINECOMBO,
CB_GETITEMDATA,
(WPARAM)pGroup->dwDeviceID,
0);

pGroup->dwAddress = SendDlgItemMessage(hWnd,
IDC_ADDRESSCOMBO,
CB_GETCURSEL,
0,
0);

// save new info and free old info
ACDFree(pGroup->lpszName);
pGroup->lpszName = ACDAlloc((lstrlen(szName) + 1) * sizeof(TCHAR));
lstrcpy(pGroup->lpszName, szName);

// update item name
tvi.mask = TVIF_TEXT;
tvi.pszText = szName;
tvi.cchTextMax = lstrlen(szName) * sizeof(TCHAR);
TreeView_SetItem(g.hTreeWnd,
&tvi);

EndDialog(hWnd, 1);
return 1;
}

case IDCANCEL:
{
EndDialog(hWnd, 0);
return 1;
}

default:
return 0;

}
}
return 0;
}


//////////////////////////////////////////////////////////////////////////////////
//
// ChangeGroupDlgProc - Window proc for the change (properties) dialog box
// for agents/groups
//
//////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChangeAgentDlgProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
static TV_ITEM tvi;

switch (uMsg)
{
case WM_INITDIALOG:

// set text appropriately
SetWindowText(hWnd,
TEXT("Change Agent"));

// get PGROUP and set edit controls
tvi.mask = TVIF_HANDLE | TVIF_PARAM;
tvi.hItem = g.hTreeItemWithMenu;

TreeView_GetItem(g.hTreeWnd,
&tvi);

SendDlgItemMessage(hWnd,
IDC_NAME,
WM_SETTEXT,
0,
(LPARAM)((PAGENT)tvi.lParam)->lpszName);

SendDlgItemMessage(hWnd,
IDC_DESTADDRESS,
WM_SETTEXT,
0,
(LPARAM)((PAGENT)tvi.lParam)->lpszNumber);

BuildLineList(GetDlgItem(hWnd,
IDC_LINECOMBO),
(((PAGENT)tvi.lParam)->dwDeviceID));

SetFocus(GetDlgItem(hWnd,
IDC_NAME));

return 0;

case WM_COMMAND:

switch (LOWORD(wParam))
{
case IDOK:
{
TCHAR szName[128];
TCHAR szNumber[128];
PAGENT pAgent;

// get info
SendDlgItemMessage(hWnd,
IDC_NAME,
WM_GETTEXT,
128,
(LPARAM)szName);

SendDlgItemMessage(hWnd,
IDC_DESTADDRESS,
WM_GETTEXT,
128,
(LPARAM)szNumber);

// get struct
pAgent = (PAGENT)tvi.lParam;

/// get device and address
pAgent->dwDeviceID = SendDlgItemMessage(hWnd,
IDC_LINECOMBO,
CB_GETCURSEL,
0,
0);

pAgent->dwDeviceID = SendDlgItemMessage(hWnd,
IDC_LINECOMBO,
CB_GETITEMDATA,
(WPARAM)pAgent->dwDeviceID,
0);

// save new info and free old info
ACDFree(pAgent->lpszName);
pAgent->lpszName = ACDAlloc((lstrlen(szName) + 1) * sizeof(TCHAR));
lstrcpy(pAgent->lpszName, szName);


ACDFree(pAgent->lpszNumber);
pAgent->lpszNumber = ACDAlloc((lstrlen(szNumber) + 1) * sizeof(TCHAR));
lstrcpy(pAgent->lpszNumber, szNumber);

// update item name
tvi.mask = TVIF_TEXT;
tvi.pszText = szName;
tvi.cchTextMax = lstrlen(szName) * sizeof(TCHAR);
TreeView_SetItem(g.hTreeWnd,
&tvi);

EndDialog(hWnd, 1);
return 1;
}

case IDCANCEL:
{
EndDialog(hWnd, 0);
return 1;
}

default:
return 0;

}
}
return 0;
}

//////////////////////////////////////////////////////////////////////////////////////
//
// AddToList() - Window proc for Add Agent To Group and Add Group To Agent
// dialog box
//
//////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK GroupAddToListProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
static PGROUP pGroup;
PAGENT pAgent;
PLISTITEM pList;
TV_ITEM tvi;
DWORD dwListBox;
BOOL bFound;
int item;
TCHAR szBuffer[128];


switch (uMsg)
{
case WM_INITDIALOG:

// get the item in question
tvi.mask = TVIF_HANDLE | TVIF_PARAM;
tvi.hItem = g.hTreeItemWithMenu;

TreeView_GetItem(g.hTreeWnd,
&tvi);

pGroup = (PGROUP)tvi.lParam;

// init lists
if (pGroup)
{
// initialize text in dialog
wsprintf(szBuffer, TEXT("Add To %s"), pGroup->lpszName);

SetWindowText(hWnd,
TEXT("Add To Group"));

SetDlgItemText(hWnd,
IDC_STATICNOTINLIST,
TEXT("Not in Group"));
SetDlgItemText(hWnd,
IDC_STATICINLIST,
TEXT("Group Members"));

pAgent = g.pAgents;

// walk list and initialize list boxes
while (pAgent)
{
pList = pGroup->pAgentList;

bFound = FALSE;

while (pList)
{
if (pList->pAgent == pAgent)
{
bFound = TRUE;
break;
}

pList = pList->pNext;
}

// if it was found, it is already a member of
// the group
if (bFound)
{
dwListBox = IDC_LIST2;
}
else
{
dwListBox = IDC_LIST1;
}

// add to correct list box
item = SendDlgItemMessage(hWnd,
dwListBox,
LB_ADDSTRING,
0,
(LPARAM)pAgent->lpszName);

// set the item data to be the item so we can get back it.
if (item != LB_ERR)
{
SendDlgItemMessage(hWnd,
dwListBox,
LB_SETITEMDATA,
(WPARAM)item,
(LPARAM)pAgent);
}

pAgent = pAgent->pNext;
}
}



return 1;

case WM_COMMAND:

switch (LOWORD(wParam))
{
case IDC_ADD:
{
// get the item
item = SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_GETCURSEL,
0,
0);

if (item == 0)
{
if (!SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_GETSEL,
(WPARAM)item,
0))
{
item == -1;
}
}

if (item != -1)
{
// get the PAGENT associated with it
pAgent = (PAGENT)SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_GETITEMDATA,
(WPARAM)item,
0);

// delete it from this listbox
SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_DELETESTRING,
(WPARAM)item,
0);

// add it to this list box
item = SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_ADDSTRING,
0,
(LPARAM)pAgent->lpszName);

// set the item data again
SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_SETITEMDATA,
item,
(WPARAM)pAgent);

// add it to the group's list
InsertIntoGroupList(pGroup,
pAgent);

return 1;

}
}
break;

case IDC_REMOVE:
{
// get the item
item = SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_GETCURSEL,
0,
0);

if (item == 0)
{
if (!SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_GETSEL,
(WPARAM)item,
0))
{
item == -1;
}
}

if (item != -1)
{
// get the struct associated with it
pAgent = (PAGENT)SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_GETITEMDATA,
(WPARAM)item,
0);

// delete it from this list
SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_DELETESTRING,
(WPARAM)item,
0);

// add it to this list
item = SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_ADDSTRING,
0,
(LPARAM)pAgent->lpszName);

// set the item data
SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_SETITEMDATA,
item,
(WPARAM)pAgent);

// remove it from the lists
RemoveFromGroupList(pGroup,
pAgent);

return 1;

}

}
break;


// bug idcancel doesn't cancel
case IDOK:
case IDCANCEL:
{
UpdateGroupLeaf(pGroup);

EndDialog(hWnd, 1);
return 1;
}

default:
return 0;

}

}

return 0;
}


BOOL CALLBACK AgentAddToListProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
static PAGENT pAgent;
PGROUP pGroup;
PLISTITEM pList;
TV_ITEM tvi;
DWORD dwListBox;
BOOL bFound;
int item;
TCHAR szBuffer[128];


switch (uMsg)
{
case WM_INITDIALOG:

// get the item in question
tvi.mask = TVIF_HANDLE | TVIF_PARAM;
tvi.hItem = g.hTreeItemWithMenu;

TreeView_GetItem(g.hTreeWnd,
&tvi);

pAgent = (PAGENT)tvi.lParam;

// init lists
if (pAgent)
{
// initialize text in dialog
wsprintf(szBuffer, TEXT("Add To %s"), pAgent->lpszName);

SetWindowText(hWnd,
TEXT("Add To Agent"));

SetDlgItemText(hWnd,
IDC_STATICNOTINLIST,
TEXT("Not Member Of"));
SetDlgItemText(hWnd,
IDC_STATICINLIST,
TEXT("Member Of"));

pGroup = g.pGroups;

// walk list and initialize list boxes
while (pGroup)
{
pList = pGroup->pAgentList;

bFound = FALSE;

while (pList)
{
if (pList->pAgent == pAgent)
{
bFound = TRUE;
break;
}

pList = pList->pNext;
}

// if it was found, it is already a member of
// the group
if (bFound)
{
dwListBox = IDC_LIST2;
}
else
{
dwListBox = IDC_LIST1;
}

// add to correct list box
item = SendDlgItemMessage(hWnd,
dwListBox,
LB_ADDSTRING,
0,
(LPARAM)pGroup->lpszName);

// set the item data to be the item so we can get back it.
if (item != LB_ERR)
{
SendDlgItemMessage(hWnd,
dwListBox,
LB_SETITEMDATA,
(WPARAM)item,
(LPARAM)pGroup);
}

pGroup = pGroup->pNext;
}
}



return 1;

case WM_COMMAND:

switch (LOWORD(wParam))
{
case IDC_ADD:
{
// get the item
item = SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_GETCURSEL,
0,
0);

if (item == 0)
{
if (!SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_GETSEL,
(WPARAM)item,
0))
{
item == -1;
}
}

if (item != -1)
{
// get the PGROUP associated with it
pGroup = (PGROUP)SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_GETITEMDATA,
(WPARAM)item,
0);

// delete it from this listbox
SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_DELETESTRING,
(WPARAM)item,
0);

// add it to this list box
item = SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_ADDSTRING,
0,
(LPARAM)pGroup->lpszName);

// set the item data again
SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_SETITEMDATA,
item,
(WPARAM)pGroup);

// add it to the item's list
InsertIntoGroupList(pGroup,
pAgent);

return 1;

}
}
break;

case IDC_REMOVE:
{
// get the item
item = SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_GETCURSEL,
0,
0);

if (item == 0)
{
if (!SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_GETSEL,
(WPARAM)item,
0))
{
item == -1;
}
}

if (item != -1)
{
// get the struct associated with it
pGroup = (PGROUP)SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_GETITEMDATA,
(WPARAM)item,
0);

// delete it from this list
SendDlgItemMessage(hWnd,
IDC_LIST2,
LB_DELETESTRING,
(WPARAM)item,
0);

// add it to this list
item = SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_ADDSTRING,
0,
(LPARAM)pGroup->lpszName);

// set the item data
SendDlgItemMessage(hWnd,
IDC_LIST1,
LB_SETITEMDATA,
item,
(WPARAM)pGroup);

// remove it from the lists
RemoveFromGroupList(pGroup,
pAgent);

return 1;

}

}
break;


// bug idcancel doesn't cancel
case IDOK:
case IDCANCEL:
{
EndDialog(hWnd, 1);
return 1;
}

default:
return 0;
}
}
return 0;
}

//////////////////////////////////////////////////////////////
//
// BOOL DeleteLeafAndStruct(HTREEITEM hItem)
// delete hItem from the tree and deleted associated
// structure
//
//////////////////////////////////////////////////////////////
BOOL DeleteLeafAndStruct(HTREEITEM hItem)
{
TV_ITEM tvi;

// get the item
tvi.mask = TVIF_PARAM;
tvi.hItem = hItem;

TreeView_GetItem(g.hTreeWnd,
&tvi);

// delete the structure
if (((PGENERICSTRUCT)tvi.lParam)->dwKey == GROUPKEY)
{
DeleteGroup((PGROUP)tvi.lParam);
}
else
{
DeleteAgent((PAGENT)tvi.lParam);
}

// remove it from the tree
TreeView_DeleteItem(g.hTreeWnd,
hItem);

return TRUE;
}


////////////////////////////////////////////////////////////////////
//
// BOOL BuildLineList(HWND hWnd,
// DWORD dwDeviceID)
//
// Fill in ComboBox with names of all available TAPI
// devices
//
////////////////////////////////////////////////////////////////////
BOOL BuildLineList(HWND hWnd,
DWORD dwDeviceID)
{
DWORD dwDev;
LPLINEDEVCAPS pLineDevCaps;
int item;
BOOL bSet = FALSE;

// clear dropdown box
SendMessage(hWnd,
CB_RESETCONTENT,
0,
0);

// loop through all devices
for (dwDev = 0; dwDev < g.dwNumDevs; dwDev++)
{
pLineDevCaps = LineGetDevCaps(g.hLineApp,
dwDev);

// add the string to to list
if (pLineDevCaps == NULL || pLineDevCaps->dwLineNameSize == 0)
{
item = SendMessage(hWnd,
CB_ADDSTRING,

0, 
(LPARAM)TEXT("NoName"));
}
else
{
item = SendMessage(hWnd,
CB_ADDSTRING,
0,
(LPARAM)((LPTSTR)((LPBYTE)pLineDevCaps + pLineDevCaps->dwLineNameOffset)));
}

// save the device ID
SendMessage(hWnd,
CB_SETITEMDATA,
item,
dwDev);

// if this is the device we are looking for
// set it to be selected
if (dwDev == dwDeviceID)
{
SendMessage(hWnd,
CB_SETCURSEL,
(WPARAM)item,
0);

bSet = TRUE;
}

if (pLineDevCaps != NULL)
{
ACDFree((HLOCAL)pLineDevCaps);
}
}

// if we didn't set the selection, default
if (!bSet)
{
SendMessage(hWnd,
CB_SETCURSEL,
0,
0);
}

return TRUE;
}


///////////////////////////////////////////////////////////////////////////////
//
// BOOL BuildAddressList()
//
// Fill combo box with list of addresses on selected device ID
//
///////////////////////////////////////////////////////////////////////////////
BOOL BuildAddressList(HWND hWnd,
HWND hParentWnd,
DWORD dwAddress)
{
TCHAR szBuffer[32];
LPLINEDEVCAPS pLineDevCaps;
LPLINEADDRESSCAPS pLineAddressCaps;
DWORD dwCurAddress, dwDeviceID;
int iCurSel;

// clear box
SendMessage(hWnd,
CB_RESETCONTENT,
0,
0);

// get the current selected device
iCurSel = SendDlgItemMessage(hParentWnd,
IDC_LINECOMBO,
CB_GETCURSEL,
0,
0);

// get associated deviceid
dwDeviceID = (DWORD)SendDlgItemMessage(hParentWnd,
IDC_LINECOMBO,
CB_GETITEMDATA,
(WPARAM)iCurSel,
0);

pLineDevCaps = LineGetDevCaps(g.hLineApp,
dwDeviceID);

// loop through all addresses
for (dwCurAddress = 0; dwCurAddress < pLineDevCaps->dwNumAddresses; dwCurAddress++)
{
pLineAddressCaps = LineGetAddressCaps(g.hLineApp,
dwDeviceID,
dwCurAddress);

// add name to list box
if (pLineAddressCaps == NULL || pLineAddressCaps->dwAddressSize == 0)
{
wsprintf(szBuffer, TEXT("Address %d"), dwCurAddress);

SendMessage(hWnd,
CB_ADDSTRING,
0,
(LPARAM)szBuffer);
}
else
{
SendMessage(hWnd,
CB_ADDSTRING,
0,
(LPARAM)((LPTSTR)((LPBYTE)pLineAddressCaps + pLineAddressCaps->dwAddressOffset)));
}

ACDFree((HLOCAL)pLineAddressCaps);
}

SendMessage(hWnd,
CB_SETCURSEL,
(WPARAM)dwAddress,
0);

ACDFree(pLineDevCaps);

return TRUE;
}

///////////////////////////////////////////////////////////////////////////
//
// BOOL UpdateGroupLeaf(PGROUP pStruct)
//
// Updates a group in the tree view when a new agent is added to that
// group
//
///////////////////////////////////////////////////////////////////////////
BOOL UpdateGroupLeaf(PGROUP pStruct)
{
HTREEITEM hItem;
PLISTITEM pItem;
TV_ITEM tvi;

// get the item's first child
hItem = TreeView_GetChild(g.hTreeWnd,
pStruct->hItem);


while (hItem)
{
// delete all childre
TreeView_DeleteItem(g.hTreeWnd,
hItem);
hItem = TreeView_GetChild(g.hTreeWnd,
pStruct->hItem);

}

pItem = pStruct->pAgentList;

// walk the agent list
while (pItem)
{
// add all the agents
hItem = AddItemToTree(pStruct->hItem,
((PAGENT)pItem->pAgent)->lpszName,
(LPARAM)NULL,
(HTREEITEM *)NULL);


// if currently logged into that group
/// bold that item
if (pItem->bLoggedIn)
{
tvi.mask = TVIF_STATE | TVIF_HANDLE;
tvi.hItem = hItem;
tvi.state = TVIS_BOLD;
tvi.stateMask = TVIS_BOLD;

TreeView_SetItem(g.hTreeWnd,
&tvi);
}

pItem = pItem->pNext;
}


return TRUE;
}


///////////////////////////////////////////////////////////////////////
//
// BOOL DoGroupView()
//
// Display the tree in a "group view" (show groups, and under the
// group, the agents that can log into that group)
//
///////////////////////////////////////////////////////////////////////
BOOL DoGroupView()
{
PGROUP pGroupParent, pGroup;
TCHAR szGroupParentName[] = TEXT("Groups");
HTREEITEM hItem;
TV_ITEM tvi;

g.bGroupView = TRUE;

// get the root
hItem = TreeView_GetRoot(g.hTreeWnd);

// free resources allocated for root
if (hItem)
{
tvi.mask = TVIF_PARAM | TVIF_HANDLE;
tvi.hItem = hItem;
TreeView_GetItem(g.hTreeWnd,
&tvi);

ACDFree((PAGENT)tvi.lParam);
}

// clear tree
TreeView_DeleteAllItems(g.hTreeWnd);

// alloc memory for the structure for the Group parent
pGroupParent = (PGROUP)ACDAlloc(sizeof(GROUP));

// alloc memory and copy the fixed name
pGroupParent->lpszName = (LPTSTR)ACDAlloc((lstrlen(szGroupParentName) + 1) * sizeof(TCHAR));
pGroupParent->dwKey = GROUPROOTKEY;

lstrcpy(pGroupParent->lpszName, szGroupParentName);

// add it to the tree
g.hGroupParent = AddItemToTree(TVI_ROOT,
pGroupParent->lpszName,
(LPARAM)pGroupParent,
&pGroupParent->hItem);

pGroup = g.pGroups;

// walk groups and add them to tree
while (pGroup)
{
AddItemToTree(g.hGroupParent,
pGroup->lpszName,
(LPARAM)pGroup,
&pGroup->hItem);

UpdateGroupLeaf(pGroup);

pGroup = pGroup->pNext;
}

return TRUE;
}


////////////////////////////////////////////////////////////////////
//
// BOOL DoAgentView()
//
// Displays the tree in an "agent view"
//
////////////////////////////////////////////////////////////////////
BOOL DoAgentView()
{
PAGENT pAgentParent,pAgent;
TCHAR szAgentParentName[] = TEXT("Agents");
HTREEITEM hItem;
TV_ITEM tvi;

g.bGroupView = TRUE;

// get root, free resources
// and clear tree
hItem = TreeView_GetRoot(g.hTreeWnd);

if (hItem)
{
tvi.mask = TVIF_PARAM | TVIF_HANDLE;
tvi.hItem = hItem;
TreeView_GetItem(g.hTreeWnd,
&tvi);

ACDFree((PGROUP)tvi.lParam);
}

TreeView_DeleteAllItems(g.hTreeWnd);

// alloc memory for the structure for the Agent parent
pAgentParent = (PAGENT)ACDAlloc(sizeof(AGENT));

// alloc memory and copy the fixed name
pAgentParent->lpszName = (LPTSTR)ACDAlloc((lstrlen(szAgentParentName) + 1) * sizeof(TCHAR));
pAgentParent->dwKey = GROUPROOTKEY;

lstrcpy(pAgentParent->lpszName, szAgentParentName);

// add it to the tree
g.hAgentParent = AddItemToTree(TVI_ROOT,
pAgentParent->lpszName,
(LPARAM)pAgentParent,
&pAgentParent->hItem);

pAgent = g.pAgents;

// walk agents and add all of them
while (pAgent)
{
AddItemToTree(g.hAgentParent,
pAgent->lpszName,
(LPARAM)pAgent,
&pAgent->hItem);

pAgent = pAgent->pNext;
}

return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////
//
// FOLLOWING ARE ROUTINES TO SAVE AND RESTORE GROUP / AGENT INFORMATION
//
// An INI file has been used for this implementation.
//
// This format is used to make it easy for users to create an INI file that can be
// read in
//
// However, a real implementation
// may want to use the registry, or a private data file to store more detailed and/or
// secure information
//
//

#define SZGROUPS TEXT("Groups")
#define SZAGENTS TEXT("Agents")
#define SZGROUP TEXT("GROUP")
#define SZAGENT TEXT("AGENT")
#define SZINIFILE TEXT("ACDSMPL.INI")
#define SZGENERAL TEXT("General")
#define SZNUMAGENTS TEXT("NumAgents")
#define SZNUMGROUPS TEXT("NumGroups")

//////////////////////////////////////////////////////////////////////////////
//
// void MakeAgentIndex(PAGENT * ppAgents)
//
// creates an array of pagents
//
//////////////////////////////////////////////////////////////////////////////
void MakeAgentIndex(PAGENT * ppAgents)
{
PAGENT pAgent;

pAgent = g.pAgents;

while (pAgent)
{
*ppAgents = pAgent;
pAgent = pAgent->pNext;
ppAgents++;
}

}

///////////////////////////////////////////////////////////////////////////////
//
// int GetAgentIndex()
//
// retreives agent index
//
///////////////////////////////////////////////////////////////////////////////
int GetAgentIndex(PAGENT * ppAgents,
PAGENT pAgent)
{
DWORD dwCount;

for (dwCount = 0; dwCount < g.dwNumAgents; dwCount++)
{
if (ppAgents[dwCount] == pAgent)
{
return dwCount;
}
}

return -1;

}


///////////////////////////////////////////////////////////////////////////////
//
// BOOL WriteToDisk()
//
// save current group/agent config to acdsmpl.ini
//
///////////////////////////////////////////////////////////////////////////////
BOOL WriteToDisk()
{
int i;
PGROUP pGroup;
PAGENT pAgent;
PLISTITEM pEntry;
TCHAR szGroupName[32], szAgentName[32], szLineBuffer[512];
PAGENT * ppAgents;

// create an index of agents
ppAgents = (PAGENT *)ACDAlloc(sizeof(PAGENT) * g.dwNumAgents);
MakeAgentIndex(ppAgents);

pGroup = g.pGroups;

i = 0;

// walk groups
while (pGroup)
{
wsprintf(szGroupName,
TEXT("%s%d"),
SZGROUP,
i);

wsprintf(szLineBuffer,
TEXT("%s,%d,%d"),
pGroup->lpszName,
g.pdwPermIDs[pGroup->dwDeviceID],
pGroup->dwAddress);

// add group to [groups] section
WritePrivateProfileString(SZGROUPS,
szGroupName,
szLineBuffer,
SZINIFILE);

pEntry = pGroup->pAgentList;

// walk agents in group
while (pEntry)
{
wsprintf(szAgentName,
TEXT("%s%d"),
SZAGENT,
GetAgentIndex(ppAgents,
pEntry->pAgent));

// write agent index to [groupx] section
WritePrivateProfileString(szGroupName,
szAgentName,
TEXT("1"),
SZINIFILE);

pEntry = pEntry->pNext;
}

pGroup = pGroup->pNext;

i++;
}

pAgent = g.pAgents;

i = 0;

//walk agents
while (pAgent)
{
wsprintf(szAgentName,
TEXT("%s%d"),
SZAGENT,
i);

wsprintf(szLineBuffer,
TEXT("%s,%s,%lu"),
pAgent->lpszName,
pAgent->lpszNumber,
g.pdwPermIDs[pAgent->dwDeviceID]);

// write agent to [agents] section
WritePrivateProfileString(SZAGENTS,
szAgentName,
szLineBuffer,
SZINIFILE);

pAgent = pAgent->pNext;

i++;

}

// save # of agents and groups
wsprintf(szLineBuffer,
TEXT("%lu"),
g.dwNumGroups);

WritePrivateProfileString(SZGENERAL,
SZNUMGROUPS,
szLineBuffer,
SZINIFILE);

wsprintf(szLineBuffer,
TEXT("%lu"),
g.dwNumAgents);

WritePrivateProfileString(SZGENERAL,
SZNUMAGENTS,
szLineBuffer,
SZINIFILE);

ACDFree(ppAgents);

return TRUE;
}


////////////////////////////////////////////////////////////////////////////////////
//
// BOOL ReadInFile()
//
// Read in ACDSMPL.INI
//
////////////////////////////////////////////////////////////////////////////////////
BOOL ReadInFile()
{
TCHAR szAgentLabel[32],
szGroupLabel[32];
DWORD dwID, dwAddress, dwNumAgents, dwNumGroups;
DWORD dwCount, dwCount2;
PAGENT * ppAgents = NULL;
LPTSTR lpszName, lpszNumber, lpszDeviceID;
LPTSTR lpszHold, szLineBuffer = NULL;
PGROUP pGroup;

dwNumAgents = GetPrivateProfileInt(SZGENERAL,
SZNUMAGENTS,
0,
SZINIFILE);

dwNumGroups = GetPrivateProfileInt(SZGENERAL,
SZNUMGROUPS,
0,
SZINIFILE);

ppAgents = (PAGENT *)ACDAlloc(sizeof(PAGENT) * dwNumAgents);
szLineBuffer = (LPTSTR)ACDAlloc(512 * sizeof(WCHAR));

if (!ppAgents || !szLineBuffer)
{
ACDFree(ppAgents);
ACDFree(szLineBuffer);
return FALSE;
}

lpszHold = szLineBuffer;

for (dwCount = 0; dwCount < dwNumAgents; dwCount++)
{
wsprintf(szAgentLabel,
TEXT("%s%lu"),
SZAGENT,
dwCount);

GetPrivateProfileString(SZAGENTS,
szAgentLabel,
TEXT(""),
szLineBuffer,
512,
SZINIFILE);

lpszName = (LPTSTR)szLineBuffer;

while (szLineBuffer && *szLineBuffer)
{
if (*szLineBuffer == TEXT(','))
{
*szLineBuffer = TEXT('\0');
szLineBuffer++;
break;
}

szLineBuffer++;
}

lpszNumber = (LPTSTR)szLineBuffer;

while (szLineBuffer && *szLineBuffer)
{
if (*szLineBuffer == TEXT(','))
{
*szLineBuffer = TEXT('\0');
szLineBuffer++;
dwID = _wtol(szLineBuffer);
dwID = GetDeviceID(dwID);

ppAgents[dwCount] = AddAgent(lpszName,
lpszNumber,
dwID);

break;
}

szLineBuffer++;
}

}

for (dwCount = 0; dwCount < dwNumGroups; dwCount++)
{
wsprintf(szGroupLabel,
TEXT("%s%lu"),
SZGROUP,
dwCount);

GetPrivateProfileString(SZGROUPS,
szGroupLabel,
TEXT(""),
szLineBuffer,
512,
SZINIFILE);

lpszName = (LPTSTR)szLineBuffer;

while (szLineBuffer && *szLineBuffer)
{
if (*szLineBuffer == TEXT(','))
{
*szLineBuffer = TEXT('\0');
szLineBuffer++;
lpszDeviceID = szLineBuffer;
break;
}

szLineBuffer++;
}

while (szLineBuffer && *szLineBuffer)
{
if (*szLineBuffer == TEXT(','))
{
*szLineBuffer = TEXT('\0');
szLineBuffer++;

dwAddress = _wtol(szLineBuffer);

break;
}

szLineBuffer++;
}

dwID = _wtol(lpszDeviceID);
dwID = GetDeviceID(dwID);


pGroup = AddGroup(lpszName,
dwID,
dwAddress);

if (!pGroup)
{
continue;
}

for (dwCount2 = 0; dwCount2 < dwNumAgents; dwCount2++)
{
wsprintf(szAgentLabel,
TEXT("%s%lu"),
SZAGENT,
dwCount2);

if (GetPrivateProfileString(szGroupLabel,
szAgentLabel,
TEXT(""),
szLineBuffer,
512,
SZINIFILE) != 0)
{
InsertIntoGroupList(pGroup,
ppAgents[dwCount2]);
}

} // for dwcount2

} // for dwcount

ACDFree(ppAgents);
ACDFree(lpszHold);

DoGroupView();

return TRUE;
}