summaryrefslogtreecommitdiffstats
path: root/private/tapi/dev/apps/acd
diff options
context:
space:
mode:
Diffstat (limited to 'private/tapi/dev/apps/acd')
-rw-r--r--private/tapi/dev/apps/acd/acdsmpl.c2495
-rw-r--r--private/tapi/dev/apps/acd/acdsmpl.h192
-rw-r--r--private/tapi/dev/apps/acd/acdsmpl.rc158
-rw-r--r--private/tapi/dev/apps/acd/acdtapi.c1208
-rw-r--r--private/tapi/dev/apps/acd/acdutils.c817
-rw-r--r--private/tapi/dev/apps/acd/clntapp.cpp123
-rw-r--r--private/tapi/dev/apps/acd/makefile29
-rw-r--r--private/tapi/dev/apps/acd/makefile.sdk35
-rw-r--r--private/tapi/dev/apps/acd/resource.h43
-rw-r--r--private/tapi/dev/apps/acd/sources59
10 files changed, 5159 insertions, 0 deletions
diff --git a/private/tapi/dev/apps/acd/acdsmpl.c b/private/tapi/dev/apps/acd/acdsmpl.c
new file mode 100644
index 000000000..8601bf63f
--- /dev/null
+++ b/private/tapi/dev/apps/acd/acdsmpl.c
@@ -0,0 +1,2495 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// 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;
+}
+
+
+
diff --git a/private/tapi/dev/apps/acd/acdsmpl.h b/private/tapi/dev/apps/acd/acdsmpl.h
new file mode 100644
index 000000000..10e93dbbe
--- /dev/null
+++ b/private/tapi/dev/apps/acd/acdsmpl.h
@@ -0,0 +1,192 @@
+#ifndef _ACDSMPL_
+#define _ACDSMPL_
+
+#include <windows.h>
+#include <commdlg.h>
+#include <commctrl.h>
+#include <tapi.h>
+
+///////////////////////////////////////////////////////////////////////////
+//
+// STRUCTURES
+//
+///////////////////////////////////////////////////////////////////////////
+
+typedef struct _tagLISTITEM;
+
+typedef struct
+{
+ DWORD dwState;
+ DWORD dwNextState;
+ DWORD dwActivity;
+
+} ADDRESSINFO, * PADDRESSINFO;
+
+typedef struct _tagAGENT
+{
+ DWORD dwKey;
+ DWORD dwSize;
+ struct _tagAGENT * pNext;
+ struct _tagAGENT * pPrev;
+ LPTSTR lpszName;
+ LPTSTR lpszNumber;
+ HTREEITEM hItem;
+ DWORD dwDeviceID;
+ DWORD dwPermID;
+ HLINE hLine;
+ DWORD dwNumAddresses;
+ PADDRESSINFO pAddressInfo;
+
+} AGENT, * PAGENT;
+
+typedef struct _tagGROUP
+{
+ DWORD dwKey;
+ DWORD dwSize;
+ struct _tagGROUP * pNext;
+ struct _tagGROUP * pPrev;
+ LPTSTR lpszName;
+ HTREEITEM hItem;
+ HLINE hLine;
+ DWORD dwDeviceID;
+ DWORD dwAddress;
+ struct _tagLISTITEM * pAgentList;
+
+} GROUP, * PGROUP;
+
+typedef struct _tagGENERICSTRUCT
+{
+ DWORD dwKey;
+ DWORD dwSize;
+ struct _tagGENERICSTRUCT * pNext;
+ struct _tagGENERICSTRUCT * pPrev;
+
+} GENERICSTRUCT, * PGENERICSTRUCT;
+
+typedef struct _tagLISTITEM
+{
+ DWORD dwKey;
+ DWORD dwSize;
+ struct _tagLISTITEM * pNext;
+ struct _tagLISTITEM * pPrev;
+ PAGENT pAgent;
+ BOOL bLoggedIn;
+ DWORD dwAddress;
+
+} LISTITEM, * PLISTITEM;
+
+
+typedef struct _tagACDGLOBALS
+{
+ PAGENT pAgents;
+ PGROUP pGroups;
+ DWORD dwNumAgents;
+ DWORD dwNumGroups;
+ LPDWORD pdwPermIDs;
+ HINSTANCE hInstance;
+ HLINEAPP hLineApp;
+ DWORD dwNumDevs;
+ HWND hMainWnd;
+ HWND hTreeWnd;
+ HWND hLogWnd;
+ BOOL bGroupView;
+ DWORD dwBarLocation;
+ HTREEITEM hAgentParent;
+ HTREEITEM hGroupParent;
+ HTREEITEM hTreeItemWithMenu;
+
+} ACDGLOBALS, * LPACDGLOBALS;
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// PROTOTYPES
+//
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+// memory
+LPVOID ACDAlloc(DWORD dwSize);
+
+void ACDFree(LPVOID pBuf);
+
+LPVOID ACDReAlloc(LPVOID pBuf,
+ DWORD dwSize);
+
+// tapi utils
+LINEADDRESSCAPS * LineGetAddressCaps (HLINEAPP hLineApp,
+ DWORD dwDeviceID,
+ DWORD dwAddressID);
+
+LINECALLINFO * LineGetCallInfo (HCALL hCall);
+
+LINEDEVCAPS * LineGetDevCaps (HLINEAPP hLineApp,
+ DWORD dwDeviceID);
+
+VARSTRING * LineGetID (HLINE hLine,
+ DWORD dwAddressID,
+ HCALL hCall,
+ DWORD dwSelect,
+ LPCTSTR lpszDeviceClass);
+
+LINECALLSTATUS * LineGetCallStatus (HCALL hCall);
+
+// list utils
+BOOL InsertStruct(PGENERICSTRUCT * ppRoot,
+ PGENERICSTRUCT pStruct);
+
+BOOL DeleteStruct(PGENERICSTRUCT * ppRoot,
+ PGENERICSTRUCT pStruct);
+
+PGROUP AddGroup(LPTSTR lpszName,
+ DWORD dwDeviceID,
+ DWORD dwAddress);
+
+PAGENT AddAgent(LPTSTR lpszName,
+ LPTSTR lpszNumber,
+ DWORD dwDeviceID);
+
+BOOL DeleteAgent(PAGENT pAgent);
+
+BOOL DeleteGroup(PGROUP pGroup);
+
+BOOL InsertIntoGroupList(PGROUP pGroup,
+ PAGENT pAgent);
+
+BOOL RemoveFromGroupList(PGROUP pGroup,
+ PAGENT pAgent);
+
+DWORD GetDeviceID(DWORD dwPermID);
+
+PAGENT GetAgentFromhLine(HLINE hLine);
+
+PAGENT GetAgentFromName(LPTSTR lpszName);
+
+PLISTITEM IsAgentInList(PLISTITEM pList,
+ PAGENT pAgent);
+
+///////////////////////////////////////////////////////////////////////////
+//
+// DEFINES
+//
+///////////////////////////////////////////////////////////////////////////
+
+#define TOTALACTIVITIES 10
+#define NUMGROUPENTRIES 10
+#define NAMESIZE 128
+
+// structure keys
+#define GROUPROOTKEY 'GRRT'
+#define AGENTROOTKEY 'AGRT'
+#define AGENTKEY 'AGNT'
+#define GROUPKEY 'GROU'
+#define LISTKEY 'LIST'
+
+
+// window control defines
+#define SIZEBAR 3
+#define WINDOWSCALEFACTOR 15
+
+#define SZAPPNAME TEXT("ACD Sample")
+
+#endif
+
diff --git a/private/tapi/dev/apps/acd/acdsmpl.rc b/private/tapi/dev/apps/acd/acdsmpl.rc
new file mode 100644
index 000000000..f3cc9d10e
--- /dev/null
+++ b/private/tapi/dev/apps/acd/acdsmpl.rc
@@ -0,0 +1,158 @@
+#include <windows.h>
+#include <commctrl.h>
+#include "resource.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_MAINDLG DIALOGEX 0, 0, 420, 230
+STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION |
+ WS_SYSMENU | WS_THICKFRAME
+CAPTION "ACD Sample"
+MENU IDR_MAINMENU
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "",IDC_TREEWND,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES |
+ WS_BORDER | WS_TABSTOP | TVS_DISABLEDRAGDROP ,7,7,125,216,
+ WS_EX_CLIENTEDGE
+ EDITTEXT IDC_EDITWND,139,7,274,216,ES_AUTOHSCROLL | ES_READONLY |
+ ES_WANTRETURN
+END
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ADD DIALOG DISCARDABLE 0, 0, 148, 134
+STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+CAPTION "Add Group"
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_NAME,7,17,134,14,ES_AUTOHSCROLL
+ COMBOBOX IDC_LINECOMBO,7,50,134,54,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ COMBOBOX IDC_ADDRESSCOMBO,7,85,134,51,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ DEFPUSHBUTTON "OK",IDOK,7,113,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,91,113,50,14
+ LTEXT "&Name",IDC_STATIC,7,7,20,8
+ LTEXT "Line",IDC_STATIC,7,39,14,8
+ LTEXT "Address",IDC_STATIC,7,74,26,8
+END
+
+IDD_ADDAGENT DIALOG DISCARDABLE 0, 0, 146, 137
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Add Agent"
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_NAME,7,20,132,14,ES_AUTOHSCROLL
+ COMBOBOX IDC_LINECOMBO,7,54,132,83,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ EDITTEXT IDC_DESTADDRESS,7,90,132,14,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,116,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,89,116,50,14
+ LTEXT "Agent Name",IDC_STATIC,7,8,82,8
+ LTEXT "Line",IDC_STATIC,7,42,14,8
+ LTEXT "Transfer Number",IDC_STATIC,7,75,54,8
+END
+
+
+IDD_ADDTOLIST DIALOG DISCARDABLE 0, 0, 222, 142
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Add To"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,41,121,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,127,121,50,14
+ LISTBOX IDC_LIST1,7,23,68,87,LBS_SORT | LBS_NOINTEGRALHEIGHT |
+ WS_VSCROLL | WS_TABSTOP
+ LISTBOX IDC_LIST2,147,23,68,87,LBS_SORT | LBS_NOINTEGRALHEIGHT |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "&Add >>>",IDC_ADD,84,41,50,14
+ PUSHBUTTON "<<< &Remove",IDC_REMOVE,84,78,50,14
+ LTEXT "Static",IDC_STATICNOTINLIST,7,7,98,8
+ LTEXT "Static",IDC_STATICINLIST,147,7,68,8
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MAINMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Open", ID_FILE_OPEN
+ MENUITEM "&New", ID_FILE_NEW
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", ID_FILE_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "Add &Agent", ID_EDIT_ADDAGENT
+ MENUITEM "Add &Group", ID_EDIT_ADDGROUP
+ END
+ POPUP "&View"
+ BEGIN
+ MENUITEM "&Group View", ID_VIEW_GROUP
+ MENUITEM "&Agent View", ID_VIEW_AGENT
+ END
+END
+
+////////////////////////////////////////////////////////////////////
+//
+// Version Info
+//
+
+//#if WINNT
+//#include <ntverp.h>
+//#else
+//#include <version.h>
+//#endif
+#include <winver.h>
+
+
+#ifdef RC_INVOKED
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 4,00,01,001
+PRODUCTVERSION 4,00,01,001
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEFLAGS (VS_FF_PRERELEASE|VS_FF_DEBUG)
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE VFT2_UNKNOWN
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0" /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */
+ BEGIN
+ VALUE "CompanyName", "Microsoft Corporation\0"
+ VALUE "FileDescription", "TAPI ACD Sample Application\0"
+ VALUE "FileVersion", "1.0\0"
+ VALUE "InternalName", "ACDSmple\0"
+ VALUE "LegalCopyright", "Copyright (C) Microsoft Corp. 1996\0"
+ VALUE "LegalTrademarks", "Microsoft(R) is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation\0"
+ VALUE "ProductName", "ACDSmpl\0"
+ VALUE "ProductVersion", "1.0\0"
+
+ END
+
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 0x04B0
+ END
+END
+
+#endif
+
+
diff --git a/private/tapi/dev/apps/acd/acdtapi.c b/private/tapi/dev/apps/acd/acdtapi.c
new file mode 100644
index 000000000..1a03899d5
--- /dev/null
+++ b/private/tapi/dev/apps/acd/acdtapi.c
@@ -0,0 +1,1208 @@
+///////////////////////////////////////////////////////////////////////////////////
+//
+// ACDTAPI.C
+//
+// This file handles all tapi functionality in the ACD sample
+//
+//
+//
+////////////////////////////////////////////////////////////////////////////////////
+#include <windows.h>
+#include <tapi.h>
+#include "acdsmpl.h"
+
+VOID CALLBACK LineCallback (DWORD hDevice,
+ DWORD dwMsg,
+ DWORD dwCallbackInstance,
+ DWORD dwParam1,
+ DWORD dwParam2,
+ DWORD dwParam3);
+
+
+#define LogTapiError(__lResult__, __szString__)
+#define LogError(__szString__)
+
+extern ACDGLOBALS g;
+
+
+////////////////////////////////////////////////////////////////////////////////////
+//
+// BOOL InitializeTapi()
+//
+// Whatever is needed to init TAPI for the application. This is called
+// before the main window is created.
+//
+////////////////////////////////////////////////////////////////////////////////////
+BOOL InitializeTapi()
+{
+ DWORD dwAPIVersion;
+ LINEINITIALIZEEXPARAMS exparams;
+ LONG lResult;
+ DWORD i;
+ LPLINEDEVCAPS pLDC;
+
+
+ // fill in lineinitex parameters
+ exparams.dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS);
+ exparams.dwOptions = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW;
+
+ dwAPIVersion = TAPI_CURRENT_VERSION;
+
+ // line init
+ if ((lResult = lineInitializeEx(&g.hLineApp,
+ g.hInstance,
+ LineCallback,
+ SZAPPNAME,
+ &g.dwNumDevs,
+ &dwAPIVersion,
+ &exparams)) < 0)
+ {
+ LogTapiError(lResult, "lineInitializeEx");
+ return FALSE;
+ }
+
+ // if there are no tapi devices, should probably
+ // not continue
+ if (g.dwNumDevs == 0)
+ {
+ LogError("No TAPI devices installed");
+ lineShutdown(g.hLineApp);
+ return FALSE;
+ }
+
+ // need to get the permanent device IDs to map from
+ // an .ini file being read in
+ g.pdwPermIDs = (LPDWORD)ACDAlloc(g.dwNumDevs * sizeof(DWORD));
+
+ if (!g.pdwPermIDs)
+ {
+ return FALSE;
+ }
+
+ for (i = 0; i < g.dwNumDevs; i++)
+ {
+ pLDC = LineGetDevCaps(g.hLineApp,
+ i);
+
+ if (pLDC)
+ {
+ g.pdwPermIDs[i] = pLDC->dwPermanentLineID;
+ ACDFree(pLDC);
+ }
+ }
+
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// BOOL CleanUp()
+//
+// Called while shutting down. free memory, close down tapi
+//
+//////////////////////////////////////////////////////////////////////
+BOOL CleanUp()
+{
+ PAGENT pAgent, pAgentNext;
+ PGROUP pGroup, pGroupNext;
+
+ // remove agents
+ pAgent = g.pAgents;
+ while(pAgent)
+ {
+ pAgentNext = pAgent->pNext;
+ DeleteAgent(pAgent);
+ pAgent = pAgentNext;
+ }
+
+ // remove groups
+ pGroup = g.pGroups;
+ while (pGroup)
+ {
+ pGroupNext = pGroup->pNext;
+ DeleteGroup(pGroup);
+ pGroup = pGroupNext;
+ }
+
+ // free id array
+ ACDFree(g.pdwPermIDs);
+
+ // shutdown
+ lineShutdown(g.hLineApp);
+
+ return TRUE;
+
+}
+////////////////////////////////////////////////////////////////////////////////
+//
+// LRESULT MakeGroupList(PAGENT pAgent,
+// LPLINEAGENTGROUPLIST pGroupList)
+//
+// Creates a LINEAGENTGROUPLIST for pAgent - group that the agent
+// is allowed to log into
+// Assumption: don't care about address for group list
+//
+////////////////////////////////////////////////////////////////////////////////
+LRESULT MakeGroupList(PAGENT pAgent,
+ LPLINEAGENTGROUPLIST pGroupList)
+{
+ PGROUP pGroup;
+ DWORD dwTotalSizeNeeded, dwNameOffset, dwNumEntries;
+ LPLINEAGENTGROUPENTRY pEntry;
+ LPTSTR pName;
+
+
+ pGroup = g.pGroups;
+ dwTotalSizeNeeded = sizeof(LINEAGENTGROUPLIST);
+ pGroupList->dwNumEntries = 0;
+ dwNumEntries = 0;
+
+ // walk list of groups
+ while (pGroup)
+ {
+ if (IsAgentInList(pGroup->pAgentList,
+ pAgent))
+ // if found the agent, add the group to the group list
+ {
+ // incrememt number of entries
+ dwNumEntries++;
+
+ // add to total size needed
+ dwTotalSizeNeeded += sizeof(LINEAGENTGROUPENTRY);
+ dwTotalSizeNeeded += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
+ }
+
+ pGroup = pGroup->pNext;
+ }
+
+ pGroupList->dwNeededSize = dwTotalSizeNeeded;
+
+ if (pGroupList->dwTotalSize < dwTotalSizeNeeded)
+ {
+ pGroupList->dwUsedSize = sizeof(LINEAGENTGROUPLIST);
+
+ return 0;
+// return LINEERR_STRUCTURETOOSMALL;
+ }
+
+ pGroupList->dwNumEntries = dwNumEntries;
+
+ // set the list info
+ pGroupList->dwListSize = sizeof(LINEAGENTGROUPENTRY) * pGroupList->dwNumEntries;
+ pGroupList->dwListOffset = sizeof(LINEAGENTGROUPLIST);
+
+ // get the first agentgroup entry struct
+ pEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset);
+
+ dwNameOffset = pGroupList->dwListOffset + pGroupList->dwListSize;
+ pGroup = g.pGroups;
+
+ // loop through the groups again, and fill in the structure
+ while (pGroup)
+ {
+ if (IsAgentInList(pGroup->pAgentList,
+ pAgent))
+ {
+ // ID is just PGROUP
+ pEntry->GroupID.dwGroupID1 = (DWORD)pGroup;
+ pEntry->GroupID.dwGroupID2 = 0;
+ pEntry->GroupID.dwGroupID3 = 0;
+ pEntry->GroupID.dwGroupID4 = 0;
+
+ // set name of group
+ pName = (LPTSTR)(((LPBYTE)pGroupList) + dwNameOffset);
+
+ pEntry->dwNameSize = (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
+ pEntry->dwNameOffset = dwNameOffset;
+ lstrcpy(pName,
+ pGroup->lpszName);
+
+ dwNameOffset += pEntry->dwNameSize;
+
+ // get next entry
+ pEntry++;
+ }
+
+ pGroup = pGroup->pNext;
+ }
+
+ pGroupList->dwUsedSize = dwTotalSizeNeeded;
+
+ return 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////
+//
+// LRESULT SetGroupList()
+//
+// Sets the groups that the agent is logged into.
+// This does not change the groups that the agent _can_ log into
+//
+////////////////////////////////////////////////////////////////////////////
+LRESULT SetGroupList(PAGENT pAgent,
+ DWORD dwAddress,
+ LPLINEAGENTGROUPLIST pGroupList)
+{
+ LPLINEAGENTGROUPENTRY pGroupEntry;
+ PLISTITEM pListEntry;
+ DWORD i;
+ PGROUP * ppGroups = NULL;
+ PGROUP pGroup;
+
+ ppGroups = (PGROUP*)ACDAlloc(sizeof(PGROUP) * pGroupList->dwNumEntries);
+
+ // get to the group entry struct
+ pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset);
+
+ // loop through all entries
+ for (i = 0; i < pGroupList->dwNumEntries; i++)
+ {
+ // get the group in entry
+ // NOTE! NOTE! NOTE!
+ // should protect here against bad pointers !!!
+ pGroup = (PGROUP)pGroupEntry->GroupID.dwGroupID1;
+
+ if (pGroup->dwKey != GROUPKEY)
+ {
+ return LINEERR_INVALAGENTGROUP;
+ }
+
+ pListEntry = pGroup->pAgentList;
+
+ // walk list of agents in that group
+ if (!IsAgentInList(pGroup->pAgentList,
+ pAgent))
+ {
+ ACDFree(ppGroups);
+ return LINEERR_INVALAGENTGROUP;
+ }
+
+ // save group for easy access
+ ppGroups[i] = pGroup;
+
+ // get the next entry (after the variable portion of
+ // the previous entry struct)
+ pGroupEntry++;
+ }
+
+ // now we know that the groups to be set are valid
+ // walk through the list of groups again, and
+ // set the status to logged in/ not logged in
+ // for every group that the agent is a member of
+
+ pGroup = g.pGroups;
+
+ // walk list of all groups
+ while (pGroup)
+ {
+ if (pListEntry = IsAgentInList(pGroup->pAgentList,
+ pAgent))
+ {
+ // default to not logged in
+ pListEntry->bLoggedIn = FALSE;
+
+ // loop through groups being set
+ for (i = 0; i < pGroupList->dwNumEntries; i++)
+ {
+ // if this group is in list, set agent to logged in
+ if (pGroup == ppGroups[i])
+ {
+ pListEntry->bLoggedIn = TRUE;
+ // assumption: agent can only log into a group on one address.
+ pListEntry->dwAddress = dwAddress;
+ break;
+ }
+
+ } // for
+
+ } // agent in list
+
+ // next group
+ pGroup = pGroup->pNext;
+
+ } // while
+
+
+ ACDFree(ppGroups);
+
+ return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// BOOL MakeAgentActivityList()
+//
+// Creates a LINEAGENTACTIVITYLIST for pAgent
+//
+// for the sample, just generic names are used
+// "Activity 1", "Activity 2"....
+//
+/////////////////////////////////////////////////////////////////////////
+LRESULT MakeAgentActivityList(PAGENT pAgent,
+ LPLINEAGENTACTIVITYLIST pActivityList)
+{
+ TCHAR szBuffer[64];
+ DWORD dwTotalSize, dwNameOffset, i, dwNumEntries;
+ LPTSTR pName;
+ LPLINEAGENTACTIVITYENTRY pEntry;
+
+ // init
+ dwTotalSize = sizeof(LINEAGENTACTIVITYLIST);
+ pActivityList->dwNumEntries = 0;
+ dwNumEntries = 0;
+
+ // just a static list of activities
+ for (i = 0; i < TOTALACTIVITIES; i++)
+ {
+ dwNumEntries++;
+
+ // create a name
+ wsprintf(szBuffer, TEXT("Activity %lu"), i);
+
+ // determine size of this entry
+ dwTotalSize += sizeof(LINEAGENTACTIVITYENTRY);
+ dwTotalSize += (lstrlen(szBuffer) + 1) * sizeof(TCHAR);
+ }
+
+ pActivityList->dwNeededSize = dwTotalSize;
+
+ // verify size
+ if (pActivityList->dwTotalSize < dwTotalSize)
+ {
+ pActivityList->dwUsedSize = sizeof(LINEAGENTACTIVITYLIST);
+
+ return 0;
+// return LINEERR_STRUCTURETOOSMALL;
+ }
+
+ pActivityList->dwNumEntries = dwNumEntries;
+
+ // set list stuff
+ pActivityList->dwListSize = sizeof(LINEAGENTACTIVITYENTRY) * pActivityList->dwNumEntries;
+ pActivityList->dwListOffset = sizeof(LINEAGENTACTIVITYLIST);
+
+ // get first activityentry
+ pEntry = (LPLINEAGENTACTIVITYENTRY)(((LPBYTE)pActivityList) + pActivityList->dwListOffset);
+ dwNameOffset = pActivityList->dwListOffset + pActivityList->dwListSize;
+
+ // loop through activities again
+ for (i = 0; i < TOTALACTIVITIES; i++)
+ {
+ // fill in members
+ pEntry->dwID = i;
+
+ // create a name
+ wsprintf(szBuffer, TEXT("Activity %lu"), i);
+
+ pName = (LPTSTR)(((LPBYTE)pActivityList) + dwNameOffset);
+
+ pEntry->dwNameSize = (lstrlen(szBuffer) + 1) * sizeof(TCHAR);
+ pEntry->dwNameOffset = dwNameOffset;
+ lstrcpy(pName,
+ szBuffer);
+
+ dwNameOffset += pEntry->dwNameSize;
+
+ pEntry++;
+
+ } // for
+
+ // fill in used size
+ pActivityList->dwUsedSize = dwTotalSize;
+
+ return 0;
+}
+
+
+#define DWAGENTFEATURES LINEAGENTFEATURE_SETAGENTGROUP | \
+ LINEAGENTFEATURE_SETAGENTSTATE | \
+ LINEAGENTFEATURE_SETAGENTACTIVITY | \
+ LINEAGENTFEATURE_GETAGENTACTIVITYLIST | \
+ LINEAGENTFEATURE_GETAGENTGROUP
+
+#define DWSTATES LINEAGENTSTATE_LOGGEDOFF | \
+ LINEAGENTSTATE_NOTREADY | \
+ LINEAGENTSTATE_READY | \
+ LINEAGENTSTATE_BUSYACD | \
+ LINEAGENTSTATE_BUSYINCOMING | \
+ LINEAGENTSTATE_BUSYOUTBOUND | \
+ LINEAGENTSTATE_BUSYOTHER | \
+ LINEAGENTSTATE_WORKINGAFTERCALL | \
+ LINEAGENTSTATE_UNKNOWN | \
+ LINEAGENTSTATE_UNAVAIL
+
+#define DWNEXTSTATES LINEAGENTSTATE_LOGGEDOFF | \
+ LINEAGENTSTATE_NOTREADY | \
+ LINEAGENTSTATE_READY | \
+ LINEAGENTSTATE_BUSYACD | \
+ LINEAGENTSTATE_BUSYINCOMING | \
+ LINEAGENTSTATE_BUSYOUTBOUND | \
+ LINEAGENTSTATE_BUSYOTHER | \
+ LINEAGENTSTATE_WORKINGAFTERCALL | \
+ LINEAGENTSTATE_UNKNOWN | \
+ LINEAGENTSTATE_UNAVAIL
+
+#define DWSTATUSMESSAGES LINEAGENTSTATUS_GROUP | \
+ LINEAGENTSTATUS_STATE | \
+ LINEAGENTSTATUS_NEXTSTATE | \
+ LINEAGENTSTATUS_ACTIVITY | \
+ LINEAGENTSTATUS_ACTIVITYLIST | \
+ LINEAGENTSTATUS_GROUPLIST | \
+ LINEAGENTSTATUS_CAPSCHANGE | \
+ LINEAGENTSTATUS_VALIDSTATES | \
+ LINEAGENTSTATUS_VALIDNEXTSTATES
+
+
+////////////////////////////////////////////////////////////////////
+//
+// BOOL IsValidState(DWORD dwState)
+//
+////////////////////////////////////////////////////////////////////
+BOOL IsValidState(DWORD dwState)
+{
+ if (!dwState)
+ {
+ return TRUE;
+ }
+
+ if ((dwState) & (dwState - 1))
+ {
+ // more than one bit set
+ return FALSE;
+ }
+
+ // make sure it's one of the valid states
+ return (dwState & DWSTATES);
+
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// BOOL IsValidNextState(DWORD dwState)
+//
+////////////////////////////////////////////////////////////////////
+BOOL IsValidNextState(DWORD dwState)
+{
+ if (!dwState)
+ {
+ return TRUE;
+ }
+
+ if ((dwState) & (dwState - 1))
+ {
+ // more than one bit set
+ return FALSE;
+ }
+
+ // make sure it's one of the valid states
+ return (dwState & DWNEXTSTATES);
+
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// BOOL IsValidActivityID(DWORD dwActivityID)
+//
+///////////////////////////////////////////////////////////////////////
+BOOL IsValidActivityID(DWORD dwActivityID)
+{
+ return (dwActivityID <= TOTALACTIVITIES);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// LRESULT MakeAgentCaps(PAGENT pAgent,
+// LPLINEAGENTCAPS pAgentCaps)
+//
+// Creates a LINEAGENTCAPS for pAgent
+// Features/states/messages are hardcoded
+// for this example
+//
+////////////////////////////////////////////////////////////////////////
+LRESULT MakeAgentCaps(PAGENT pAgent,
+ LPLINEAGENTCAPS pAgentCaps)
+{
+ DWORD dwStringSize;
+
+ dwStringSize = (lstrlen(SZAPPNAME) + 1) * sizeof(TCHAR);
+
+ pAgentCaps->dwNeededSize = sizeof(LINEAGENTCAPS) + dwStringSize;
+
+ if (pAgentCaps->dwTotalSize < pAgentCaps->dwNeededSize)
+ {
+ pAgentCaps->dwUsedSize = sizeof(LINEAGENTCAPS);
+ return 0;
+// return LINEERR_STRUCTURETOOSMALL;
+ }
+
+
+ pAgentCaps->dwAgentHandlerInfoSize = dwStringSize;
+ pAgentCaps->dwAgentHandlerInfoOffset = sizeof(LINEAGENTCAPS);
+
+ pAgentCaps->dwCapsVersion = TAPI_CURRENT_VERSION;
+
+ // these features are hardcoded here.
+ // a real implementation may set specific features
+ // per agent or line or address
+ pAgentCaps->dwFeatures = DWAGENTFEATURES;
+ pAgentCaps->dwStates = DWSTATES;
+ pAgentCaps->dwNextStates = DWNEXTSTATES;
+ pAgentCaps->dwMaxNumGroupEntries = NUMGROUPENTRIES;
+ pAgentCaps->dwAgentStatusMessages = DWSTATUSMESSAGES;
+
+ // no extensions
+ pAgentCaps->dwNumAgentExtensionIDs = 0;
+ pAgentCaps->dwAgentExtensionIDListSize = 0;
+ pAgentCaps->dwAgentExtensionIDListOffset = 0;
+
+
+ pAgentCaps->dwUsedSize = pAgentCaps->dwNeededSize;
+
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// LRESULT GetAgentStatus()
+//
+// Creates a LINEAGENTSTATUS for pAgent
+//
+//////////////////////////////////////////////////////////////////////////
+LRESULT GetAgentStatus(PAGENT pAgent,
+ DWORD dwAddress,
+ LPLINEAGENTSTATUS pAgentStatus)
+{
+ PGROUP pGroup;
+ LPLINEAGENTGROUPENTRY pGroupEntry;
+ DWORD dwTotalSize, dwNameOffset, dwCount;
+ TCHAR szActivityName[NAMESIZE];
+ PGROUP * ppGroups;
+ PLISTITEM pEntry;
+
+ // init total size
+ dwTotalSize = sizeof(LINEAGENTSTATUS);
+
+ if (dwAddress >= pAgent->dwNumAddresses)
+ {
+ return LINEERR_INVALADDRESSID;
+ }
+
+ // set know members
+ // for valid states / next states / agent features, just setting it to
+ // generic stuff. a real implementation may want to set these
+ // field depending on current state / agent / hline
+ pAgentStatus->dwState = pAgent->pAddressInfo[dwAddress].dwState;
+ pAgentStatus->dwNextState = pAgent->pAddressInfo[dwAddress].dwNextState;
+ pAgentStatus->dwActivityID = pAgent->pAddressInfo[dwAddress].dwActivity;
+ pAgentStatus->dwAgentFeatures = DWAGENTFEATURES;
+ pAgentStatus->dwValidStates = DWSTATES;
+ pAgentStatus->dwValidNextStates = DWNEXTSTATES;
+
+ // create the activity name
+ wsprintf(szActivityName, TEXT("Activity %lu"), pAgent->pAddressInfo[dwAddress].dwActivity);
+ dwTotalSize += (lstrlen(szActivityName) + 1) * sizeof(TCHAR);
+
+ ppGroups = (PGROUP *)ACDAlloc(sizeof(PGROUP) * g.dwNumGroups);
+
+ pGroup = g.pGroups;
+
+ pAgentStatus->dwNumEntries = 0;
+
+ // walk list of groups
+ while (pGroup)
+ {
+ pEntry = pGroup->pAgentList;
+
+ // walk each agent in each group
+ while (pEntry)
+ {
+ if (pEntry->pAgent == pAgent)
+ {
+ if ((!pEntry->bLoggedIn) ||
+ (pEntry->dwAddress != dwAddress))
+ {
+ break;
+ }
+
+ // save group
+ ppGroups[pAgentStatus->dwNumEntries] = pGroup;
+
+ // adjust total size / entries
+ pAgentStatus->dwNumEntries++;
+ dwTotalSize += sizeof(LINEAGENTGROUPENTRY);
+ dwTotalSize += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
+
+ break;
+ }
+
+ pEntry = pEntry->pNext;
+
+ } // while (pEntry)
+
+ pGroup = pGroup->pNext;
+
+ } // while (pGroup)
+
+ // set needed size
+ pAgentStatus->dwNeededSize = dwTotalSize;
+
+ // do we have enough room?
+ if (pAgentStatus->dwTotalSize < dwTotalSize)
+ {
+ // if not, return
+ pAgentStatus->dwUsedSize = sizeof(LINEAGENTSTATUS);
+ ACDFree(ppGroups);
+
+ return 0;
+// return LINEERR_STRUCTURETOOSMALL;
+ }
+
+
+ // set the group entries...
+
+ // first get the offset to the first entry
+ pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pAgentStatus) + sizeof(LINEAGENTSTATUS));
+ pAgentStatus->dwGroupListOffset = sizeof(LINEAGENTSTATUS);
+
+ // figure out where the names can go (after all the fixed structures)
+ dwNameOffset = sizeof(LINEAGENTSTATUS) +
+ sizeof(LINEAGENTGROUPENTRY) * pAgentStatus->dwNumEntries;
+
+ // loop through all the group that the agent is logged into
+ for (dwCount = 0; dwCount < pAgentStatus->dwNumEntries; dwCount++)
+ {
+ // set the it (just the pGroup)
+ pGroupEntry->GroupID.dwGroupID1 = (DWORD)ppGroups[dwCount];
+ pGroupEntry->GroupID.dwGroupID2 = 0;
+ pGroupEntry->GroupID.dwGroupID3 = 0;
+ pGroupEntry->GroupID.dwGroupID4 = 0;
+
+ // set name size and offset
+ pGroupEntry->dwNameSize = (lstrlen(ppGroups[dwCount]->lpszName) + 1) * sizeof(TCHAR);
+ pGroupEntry->dwNameOffset = dwNameOffset;
+
+ // copy name
+ lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset),
+ ppGroups[dwCount]->lpszName);
+
+ // fix the name offset
+ dwNameOffset += pGroupEntry->dwNameSize;
+
+ // next entry
+ pGroupEntry++;
+
+ }
+
+ pAgentStatus->dwGroupListSize = dwNameOffset - pAgentStatus->dwGroupListOffset;
+
+ // put the activity name at the end
+ pAgentStatus->dwActivitySize = (lstrlen(szActivityName) + 1) * sizeof(TCHAR);
+ pAgentStatus->dwActivityOffset = dwNameOffset;
+
+ lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset),
+ szActivityName);
+
+
+ ACDFree(ppGroups);
+
+ pAgentStatus->dwUsedSize = pAgentStatus->dwNeededSize;
+ // return success
+ return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// LRESULT SetAgentState()
+//
+// Sets the current and next state for pAgent
+// on that specific address
+//
+//
+/////////////////////////////////////////////////////////////////////
+LRESULT SetAgentState(PAGENT pAgent,
+ DWORD dwAddress,
+ DWORD dwState,
+ DWORD dwNextState)
+{
+ // make sure valid
+ if ((!IsValidState(dwState)) ||
+ (!IsValidNextState(dwNextState)))
+ {
+ return LINEERR_INVALAGENTSTATE;
+ }
+
+ // check address
+ if (dwAddress >= pAgent->dwNumAddresses)
+ {
+ return LINEERR_INVALADDRESSID;
+ }
+
+ // set the state if specified
+ if (dwState)
+ {
+ pAgent->pAddressInfo[dwAddress].dwState = dwState;
+ }
+
+ if (dwNextState)
+ {
+ pAgent->pAddressInfo[dwAddress].dwNextState = dwNextState;
+ }
+
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// LRESULT SetAgentActivity()
+//
+// Sets the activity for pAgent
+//
+///////////////////////////////////////////////////////////////////////////////
+LRESULT SetAgentActivity(PAGENT pAgent,
+ DWORD dwAddress,
+ DWORD dwActivityID)
+{
+ if (dwAddress >= pAgent->dwNumAddresses)
+ {
+ return LINEERR_INVALADDRESSID;
+ }
+
+ if (!IsValidActivityID(dwActivityID))
+ {
+ return LINEERR_INVALAGENTACTIVITY;
+ }
+
+ pAgent->pAddressInfo[dwAddress].dwActivity = dwActivityID;
+
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// BOOL HandleOffering(HCALL hCall)
+//
+// Handles a LINECALLSTATE_OFFERING message
+// Determines the group associated with the address
+// that the call came in on, finds an available
+// agent in that group, and transfers the call to that
+// agent.
+//
+////////////////////////////////////////////////////////////////////////////////
+BOOL HandleOffering(HCALL hCall)
+{
+ LPLINECALLINFO pLCI;
+ PGROUP pGroup;
+ PLISTITEM pEntry;
+ LONG lResult;
+
+ pLCI = LineGetCallInfo(hCall);
+
+ if (!pLCI)
+ {
+ return FALSE;
+ }
+
+ pGroup = g.pGroups;
+
+ // find the group that this call came in on
+ // walk all the groups
+ while (pGroup)
+ {
+ // if the line and address match, it's the
+ // correct address
+ if ((pGroup->hLine == pLCI->hLine) &&
+ (pGroup->dwAddress == pLCI->dwAddressID))
+ {
+ break;
+ }
+
+ pGroup = pGroup->pNext;
+ }
+
+ // couldn't find the group
+ if (!pGroup)
+ {
+ // error!
+ ACDFree(pLCI);
+ return FALSE;
+ }
+
+ // OK - found the group that this call is for. Now transfer to
+ // an agent that is available.
+ pEntry = pGroup->pAgentList;
+
+ while (pEntry)
+ {
+ if (pEntry->bLoggedIn &&
+ (pEntry->pAgent->pAddressInfo[pEntry->dwAddress].dwState ==
+ LINEAGENTSTATE_READY))
+ {
+ // found someone
+ // doing a blind transfer here
+ // other implementations may need to
+ // do lineSetupTransfer / lineDial / lineCompleteTransfer
+ if (lResult = lineBlindTransfer(hCall,
+ (LPCWSTR)pEntry->pAgent->lpszNumber,
+ 0))
+ {
+ //LogTapiError(TEXT("lineBlindTransfer"), lResult);
+ // don't break - try the next agent
+ }
+ else
+ {
+ // set the state to reflect that
+ // a call is being handled
+ SetAgentState(pEntry->pAgent,
+ pEntry->dwAddress,
+ LINEAGENTSTATE_BUSYACD,
+ LINEAGENTSTATE_READY);
+
+ break;
+ }
+
+ }
+
+ pEntry = pEntry->pNext;
+ }
+
+ if (!pEntry)
+ {
+ // couldn't find an available agent
+
+ // NOTE! NOTE! NOTE! NOTE! NOTE!
+ // something should be done here with this call. put into
+ // a queue on hold or something. For this sample, we are just
+ // ignoring it
+ }
+
+ ACDFree(pLCI);
+
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// BOOL HandleIdle(HCALL hCall)
+//
+// Handles LINECALLSTATE_IDLE
+// Should always always always deallocate when
+// getting an IDLE message. Also, determine if this is a call
+// that we know about and set the agent state appropriatly
+//
+//////////////////////////////////////////////////////////////////////////
+BOOL HandleIdle(HCALL hCall)
+{
+ LPLINECALLINFO pLCI;
+ PAGENT pAgent;
+
+ pLCI = LineGetCallInfo(hCall);
+
+ // always deallocate the call
+ lineDeallocateCall(hCall);
+
+ if (!pLCI)
+ {
+ return FALSE;
+ }
+
+ // get the agent associated with the line
+ pAgent = GetAgentFromhLine(pLCI->hLine);
+
+ if (!pAgent)
+ {
+ ACDFree(pLCI);
+ return FALSE;
+ }
+
+
+ // set that agent to their next state
+ // Assumption: only calls that the ACD app know about
+ // occur. For example, if an agent made an outgoing call
+ // and it transitioned to idle, this code would still be executed.
+ // May make more sense to not handle NextState in the ACD app, and let
+ // the client app handle it.
+ SetAgentState(pAgent,
+ pLCI->dwAddressID,
+ pAgent->pAddressInfo[pLCI->dwAddressID].dwNextState,
+ 0);
+
+ ACDFree(pLCI);
+
+ return TRUE;
+
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+//
+// void HandleLineProxyRequest(HLINE hLine,
+// LPLINEPROXYREQUEST pProxyRequest)
+//
+// Handles LINE_PROXYREQUEST message
+// Just dispatches to appropriate functions
+//
+////////////////////////////////////////////////////////////////////////////
+void HandleLineProxyRequest(HLINE hLine,
+ LPLINEPROXYREQUEST pProxyRequest)
+{
+ PAGENT pAgent;
+ LRESULT lResult;
+
+ pAgent = GetAgentFromName((LPTSTR)(((LPBYTE)pProxyRequest) +
+ pProxyRequest->dwClientUserNameOffset));
+
+ if (!pAgent)
+ {
+ lineProxyResponse(hLine,
+ pProxyRequest,
+ LINEERR_INVALAGENTID);
+
+ return;
+ }
+
+ switch (pProxyRequest->dwRequestType)
+ {
+ case LINEPROXYREQUEST_SETAGENTGROUP:
+
+ lResult = SetGroupList(pAgent,
+ pProxyRequest->SetAgentGroup.dwAddressID,
+ &pProxyRequest->SetAgentGroup.GroupList);
+
+ lineProxyResponse(hLine,
+ pProxyRequest,
+ lResult);
+
+ return;
+
+ case LINEPROXYREQUEST_SETAGENTSTATE:
+
+ lResult = SetAgentState(pAgent,
+ pProxyRequest->SetAgentState.dwAddressID,
+ pProxyRequest->SetAgentState.dwAgentState,
+ pProxyRequest->SetAgentState.dwNextAgentState);
+
+ lineProxyResponse(hLine,
+ pProxyRequest,
+ lResult);
+
+ break;
+
+ case LINEPROXYREQUEST_SETAGENTACTIVITY:
+
+ lResult = SetAgentActivity(pAgent,
+ pProxyRequest->SetAgentActivity.dwAddressID,
+ pProxyRequest->SetAgentActivity.dwActivityID);
+
+ lineProxyResponse(hLine,
+ pProxyRequest,
+ lResult);
+
+ break;
+
+ case LINEPROXYREQUEST_GETAGENTSTATUS:
+
+ lResult = GetAgentStatus(pAgent,
+ pProxyRequest->GetAgentStatus.dwAddressID,
+ &pProxyRequest->GetAgentStatus.AgentStatus);
+
+ lineProxyResponse(hLine,
+ pProxyRequest,
+ lResult);
+
+ break;
+
+ case LINEPROXYREQUEST_GETAGENTCAPS:
+
+ if ((hLine == pAgent->hLine) &&
+ (pProxyRequest->GetAgentCaps.dwAddressID < pAgent->dwNumAddresses))
+ {
+ lResult = MakeAgentCaps(pAgent,
+ &pProxyRequest->GetAgentCaps.AgentCaps);
+ }
+ else
+ {
+ lResult = LINEERR_BADDEVICEID;
+ }
+
+ lineProxyResponse(hLine,
+ pProxyRequest,
+ lResult);
+ break;
+
+ case LINEPROXYREQUEST_GETAGENTACTIVITYLIST:
+
+ lResult = MakeAgentActivityList(pAgent,
+ &pProxyRequest->GetAgentActivityList.ActivityList);
+
+ lineProxyResponse(hLine,
+ pProxyRequest,
+ lResult);
+
+ break;
+
+ case LINEPROXYREQUEST_GETAGENTGROUPLIST:
+
+ lResult = MakeGroupList(pAgent,
+ &pProxyRequest->GetAgentGroupList.GroupList);
+
+ lineProxyResponse(hLine,
+ pProxyRequest,
+ lResult);
+ return;
+
+
+ }
+ return;
+}
+
+/////////////////////////////////////////////////////////////
+//
+// void HandleLineCallState(DWORD dwDevice,
+//
+// Handles callstate messages we are interested in
+//
+/////////////////////////////////////////////////////////////
+void HandleLineCallState(DWORD dwDevice,
+ DWORD dwParam1,
+ DWORD dwParam2,
+ DWORD dwParam3)
+{
+ switch (dwParam1)
+ {
+ case LINECALLSTATE_OFFERING:
+ {
+ LPLINECALLSTATUS pLCS;
+
+ // get the call privilege.
+ // NOTE: the new LINE_APPNEWCALL message notifies applications
+ // of their priv for new calls not created by app
+ pLCS = LineGetCallStatus((HCALL)dwDevice);
+ if (!pLCS)
+ {
+ break;
+ }
+
+ if (pLCS->dwCallPrivilege & LINECALLPRIVILEGE_OWNER)
+ {
+ HandleOffering((HCALL)dwDevice);
+ }
+
+ ACDFree(pLCS);
+
+ break;
+ }
+ case LINECALLSTATE_CONNECTED:
+ break;
+
+ case LINECALLSTATE_DISCONNECTED:
+ break;
+
+ case LINECALLSTATE_IDLE:
+
+ HandleIdle((HCALL)dwDevice);
+
+ break;
+
+ case LINECALLSTATE_BUSY:
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// TAPI message handlers. For this sample, they don't
+// do anything
+///////////////////////////////////////////////////////////////////////
+void HandleLineDevState(DWORD dwParam1,
+ DWORD dwParam2,
+ DWORD dwParam3)
+{
+}
+
+
+void HandleLineReply(DWORD dwParam1,
+ DWORD dwParam2,
+ DWORD dwParam3)
+{
+}
+void HandleLineCallInfo(DWORD dwParam1,
+ DWORD dwParam2,
+ DWORD dwParam3)
+{
+}
+
+void HandleLineClose(DWORD dwParam1,
+ DWORD dwParam2,
+ DWORD dwParam3)
+{
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+//
+// LineCallback() - TAPI callback function
+//
+//////////////////////////////////////////////////////////////////////////////////
+VOID CALLBACK LineCallback (DWORD hDevice,
+ DWORD dwMsg,
+ DWORD dwCallbackInstance,
+ DWORD dwParam1,
+ DWORD dwParam2,
+ DWORD dwParam3)
+{
+ switch(dwMsg)
+ {
+ case LINE_PROXYREQUEST:
+ HandleLineProxyRequest((HLINE) hDevice,
+ (LPLINEPROXYREQUEST)dwParam1);
+ return;
+
+ case LINE_LINEDEVSTATE:
+ HandleLineDevState(dwParam1,
+ dwParam2,
+ dwParam3);
+ return;
+ case LINE_REPLY:
+ HandleLineReply(dwParam1,
+ dwParam2,
+ dwParam3);
+ return;
+ case LINE_CALLSTATE:
+ HandleLineCallState(hDevice,
+ dwParam1,
+ dwParam2,
+ dwParam3);
+ return;
+ case LINE_CALLINFO:
+ HandleLineCallInfo(dwParam1,
+ dwParam2,
+ dwParam3);
+ return;
+ case LINE_CLOSE:
+ HandleLineClose(dwParam1,
+ dwParam2,
+ dwParam3);
+ return;
+
+ default:
+ return;
+ }
+}
+
+
diff --git a/private/tapi/dev/apps/acd/acdutils.c b/private/tapi/dev/apps/acd/acdutils.c
new file mode 100644
index 000000000..4406c554e
--- /dev/null
+++ b/private/tapi/dev/apps/acd/acdutils.c
@@ -0,0 +1,817 @@
+//////////////////////////////////////////////////////////////////
+//
+// ACDUTILS.C
+//
+// Some utility functions used in ACDSMPL
+//
+//////////////////////////////////////////////////////////////////
+
+
+
+#include <windows.h>
+#include "acdsmpl.h"
+
+extern ACDGLOBALS g;
+
+//////////////////////////////////////////////////////////////////
+//
+// LPVOID ACDAlloc(DWORD dwSize)
+//
+//////////////////////////////////////////////////////////////////
+LPVOID ACDAlloc(DWORD dwSize)
+{
+ LPVOID pBuf;
+
+ pBuf = GlobalAlloc(GPTR, dwSize);
+
+ return pBuf;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+// void ACDFree(LPVOID pBuf)
+//
+///////////////////////////////////////////////////////////////////
+void ACDFree(LPVOID pBuf)
+{
+ if (pBuf)
+ GlobalFree(pBuf);
+}
+
+///////////////////////////////////////////////////////////////////
+//
+// LPVOID ACDReAlloc()
+//
+///////////////////////////////////////////////////////////////////
+LPVOID ACDReAlloc(LPVOID pBuf,
+ DWORD dwSize)
+{
+ if (pBuf)
+ {
+ pBuf = GlobalReAlloc(pBuf,
+ dwSize,
+ GMEM_MOVEABLE);
+ }
+
+ return pBuf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// BOOL InsertStruct(PGENERICSTRUCT * ppRoot,
+// PGENERICSTRUCT pStruct)
+//
+// Adds a structure to the end of a linked list
+//
+///////////////////////////////////////////////////////////////////////////////
+BOOL InsertStruct(PGENERICSTRUCT * ppRoot,
+ PGENERICSTRUCT pStruct)
+{
+ PGENERICSTRUCT pHold = NULL;
+
+ if (!*ppRoot)
+ {
+ *ppRoot = pStruct;
+ }
+ else
+ {
+ pHold = *ppRoot;
+ }
+
+ if (pHold)
+ {
+ while (pHold->pNext)
+ {
+ pHold = pHold->pNext;
+ }
+
+ pHold->pNext = pStruct;
+ pStruct->pPrev = pHold;
+ }
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////
+//
+// BOOL DeleteStruct
+// Delete structure
+//
+// Delete a structure from a linked list and
+// frees memory associated with it.
+//
+//////////////////////////////////////////////////////////////
+BOOL DeleteStruct(PGENERICSTRUCT * ppRoot,
+ PGENERICSTRUCT pStruct)
+{
+ if (pStruct->pPrev)
+ {
+ pStruct->pPrev->pNext = pStruct->pNext;
+ }
+ else
+ {
+ *ppRoot = pStruct->pNext;
+ }
+
+ if (pStruct->pNext)
+ {
+ pStruct->pNext->pPrev = pStruct->pPrev;
+ }
+
+ ACDFree(pStruct);
+
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////
+//
+// PGROUP AddGroup
+//
+// Adds a group to the global group list
+//
+//////////////////////////////////////////////////////////////
+PGROUP AddGroup(LPTSTR lpszName,
+ DWORD dwDeviceID,
+ DWORD dwAddress)
+{
+ PGROUP pGroup, pHold = NULL;
+ LONG lResult;
+
+ // alloc memory
+ pGroup = (PGROUP)ACDAlloc(sizeof(GROUP));
+
+ if (!pGroup)
+ {
+ return NULL;
+ }
+
+ pGroup->lpszName = (LPTSTR)ACDAlloc((lstrlen(lpszName) + 1) * sizeof(TCHAR));
+
+ // bail if there is a problem
+ if (!pGroup->lpszName)
+ {
+ ACDFree(pGroup);
+ return NULL;
+ }
+
+ // init stuff
+ lstrcpy(pGroup->lpszName, lpszName);
+
+ pGroup->dwKey = GROUPKEY;
+ pGroup->dwSize = sizeof(GROUP);
+ pGroup->dwDeviceID = dwDeviceID;
+
+
+ // open the line in owner mode, so we can
+ // get the incoming calls
+ lResult = lineOpen(g.hLineApp,
+ dwDeviceID,
+ &pGroup->hLine,
+ TAPI_CURRENT_VERSION,
+ 0,
+ 0,
+ LINECALLPRIVILEGE_OWNER,
+ LINEMEDIAMODE_INTERACTIVEVOICE,
+ NULL);
+
+ if (lResult < 0)
+ {
+// LogTapiError(lResult, "lineOpen line %lu", dwDeviceID);
+ ACDFree(pGroup->lpszName);
+ ACDFree(pGroup);
+
+ return NULL;
+ }
+
+ // insert into global list
+ InsertStruct((PGENERICSTRUCT *)&g.pGroups,
+ (PGENERICSTRUCT)pGroup);
+
+ // increment
+ g.dwNumGroups++;
+
+ return pGroup;
+}
+
+//////////////////////////////////////////////////////////////
+//
+// PAGENT AddAgent
+//
+// Adds an agent to the global agent list
+//
+// NOTE: There is a ton of verification type stuff that can
+// be put in here for a real implementation. For example,
+// might want to restrict a user to a single line, or restrict
+// a single line to have one person.
+//
+//////////////////////////////////////////////////////////////
+PAGENT AddAgent(LPTSTR lpszName,
+ LPTSTR lpszNumber,
+ DWORD dwDeviceID)
+{
+ PAGENT pAgent, pHold = NULL;
+ LPLINEDEVCAPS pLDC;
+ LPLINECALLPARAMS pLCP;
+ LONG lResult;
+ LPDWORD pdwProxyRequests;
+
+ // alloc memory
+ pAgent = (PAGENT)ACDAlloc(sizeof(AGENT));
+
+ if (!pAgent)
+ {
+ return NULL;
+ }
+
+ pAgent->lpszName = (LPTSTR)ACDAlloc((lstrlen(lpszName) + 1) * sizeof(TCHAR));
+ pAgent->lpszNumber = (LPTSTR)ACDAlloc((lstrlen(lpszNumber) + 1) * sizeof(TCHAR));
+
+ // bail if there is a problem
+ if (!pAgent->lpszName || !pAgent->lpszNumber)
+ {
+ ACDFree(pAgent);
+ return NULL;
+ }
+
+
+ // init stuff
+ lstrcpy(pAgent->lpszName, lpszName);
+ lstrcpy(pAgent->lpszNumber, lpszNumber);
+
+ pAgent->dwKey = AGENTKEY;
+ pAgent->dwSize = sizeof(AGENT);
+ pAgent->dwDeviceID = dwDeviceID;
+ pAgent->dwPermID = g.pdwPermIDs[dwDeviceID];
+
+ // insert into global agent list
+ InsertStruct((PGENERICSTRUCT *)&g.pAgents,
+ (PGENERICSTRUCT)pAgent);
+
+ // lineOpen is where the application lets TAPI know that it is a Proxy Request handler
+ // for this line. The LINEOPENOPTION_PROXY is added to the privileges. Also,
+ // the dev specific portion of LINECALLPARAMS contains the proxy request constants
+ // that indicate which requests this app can handle
+
+ // This sample handles 7 types of proxy requests - all the ones that are defined
+ // except for AGENTSPECIFIC
+ pLCP = (LPLINECALLPARAMS)ACDAlloc(sizeof(LINECALLPARAMS) + 7*sizeof(DWORD));
+
+ pLCP->dwTotalSize = sizeof(LINECALLPARAMS) + 7*sizeof(DWORD);
+ pLCP->dwDevSpecificOffset = sizeof(LINECALLPARAMS);
+ pLCP->dwDevSpecificSize = sizeof(DWORD) * 7;
+
+ pdwProxyRequests = (LPDWORD)((LPBYTE)pLCP + sizeof(LINECALLPARAMS));
+ // each constant is in a DWORD at the end of LINECALLPARAMS
+ *pdwProxyRequests++ = LINEPROXYREQUEST_SETAGENTGROUP;
+ *pdwProxyRequests++ = LINEPROXYREQUEST_SETAGENTSTATE;
+ *pdwProxyRequests++ = LINEPROXYREQUEST_SETAGENTACTIVITY;
+ *pdwProxyRequests++ = LINEPROXYREQUEST_GETAGENTSTATUS;
+ *pdwProxyRequests++ = LINEPROXYREQUEST_GETAGENTCAPS;
+ *pdwProxyRequests++ = LINEPROXYREQUEST_GETAGENTACTIVITYLIST;
+ *pdwProxyRequests = LINEPROXYREQUEST_GETAGENTGROUPLIST;
+
+ lResult = lineOpen(g.hLineApp,
+ dwDeviceID,
+ &pAgent->hLine,
+ TAPI_CURRENT_VERSION,
+ 0,
+ 0,
+ LINEOPENOPTION_PROXY | LINECALLPRIVILEGE_MONITOR,
+ LINEMEDIAMODE_INTERACTIVEVOICE,
+ pLCP);
+
+ ACDFree(pLCP);
+
+ if (lResult)
+ {
+ //
+ ACDFree(pAgent->lpszName);
+ ACDFree(pAgent);
+
+ }
+
+ pLDC = LineGetDevCaps(g.hLineApp,
+ pAgent->dwDeviceID);
+
+ if (!pLDC)
+ {
+ return FALSE;
+ }
+
+ // alloc memory for address specific info
+ pAgent->pAddressInfo = (PADDRESSINFO)ACDAlloc(sizeof(ADDRESSINFO) * pLDC->dwNumAddresses);
+ pAgent->dwNumAddresses = pLDC->dwNumAddresses;
+
+ ACDFree(pLDC);
+
+ // increment number of agents
+ g.dwNumAgents++;
+
+ return pAgent;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// BOOL DeleteAgent(PAGENT pAgent)
+//
+// Frees all memory associated with pAgent, removes
+// agent from group lists, and remove pAgent from
+// global agent list
+//
+//////////////////////////////////////////////////////////////////////////
+BOOL DeleteAgent(PAGENT pAgent)
+{
+ PGROUP pGroup;
+
+
+ lineClose(pAgent->hLine);
+
+ // free name
+ ACDFree(pAgent->lpszName);
+ ACDFree(pAgent->lpszNumber);
+
+ // free address info
+ ACDFree(pAgent->pAddressInfo);
+
+ pGroup = g.pGroups;
+
+ // walk through groups and remove from
+ // group list if in group list
+ while (pGroup)
+ {
+ if (IsAgentInList(pGroup->pAgentList,
+ pAgent))
+ {
+ RemoveFromGroupList(pGroup,
+ pAgent);
+ }
+
+ pGroup = pGroup->pNext;
+
+ }
+
+ // finally, remove pAgent from global list
+ DeleteStruct((PGENERICSTRUCT *)&g.pAgents,
+ (PGENERICSTRUCT)pAgent);
+
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// BOOL DeleteGroup(PGROUP pGroup)
+//
+// Frees memory assocated with pGroup, and removes the structure from
+// the global list
+//
+//////////////////////////////////////////////////////////////////////////
+BOOL DeleteGroup(PGROUP pGroup)
+{
+ PLISTITEM pList, pListNext;
+
+ lineClose(pGroup->hLine);
+
+ ACDFree(pGroup->lpszName);
+
+ pList = pGroup->pAgentList;
+
+ while (pList)
+ {
+ pListNext = pList->pNext;
+ ACDFree(pList);
+ pList = pListNext;
+ }
+
+ DeleteStruct((PGENERICSTRUCT *)&g.pGroups,
+ (PGENERICSTRUCT)pGroup);
+
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// BOOL InsertIntoGroupList(PGROUP pGroup,
+// PAGENT pAgent)
+//
+// Insert an agent in a group
+//
+//////////////////////////////////////////////////////////////////////////
+BOOL InsertIntoGroupList(PGROUP pGroup,
+ PAGENT pAgent)
+{
+ PLISTITEM pListItem;
+
+ pListItem = (PLISTITEM)ACDAlloc(sizeof(LISTITEM));
+
+ pListItem->dwKey = LISTKEY;
+ pListItem->dwSize = sizeof(LISTITEM);
+ pListItem->pAgent = pAgent;
+
+ InsertStruct((PGENERICSTRUCT *)&pGroup->pAgentList,
+ (PGENERICSTRUCT)pListItem);
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// BOOL RemoveFromGroupList(PGROUP pGroup,
+//
+// remove an agent from a group's list
+//
+//////////////////////////////////////////////////////////////////////////
+BOOL RemoveFromGroupList(PGROUP pGroup,
+ PAGENT pAgent)
+{
+ PLISTITEM pList;
+
+ pList = pGroup->pAgentList;
+
+ while (pList)
+ {
+ if (pList->pAgent == pAgent)
+ {
+ break;
+ }
+
+ pList = pList->pNext;
+ }
+
+ if (!pList)
+ {
+ return FALSE;
+ }
+
+ DeleteStruct((PGENERICSTRUCT *)&pGroup->pAgentList,
+ (PGENERICSTRUCT)pList);
+
+ return TRUE;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//PLISTITEM IsAgentInList(PLISTITEM pList,
+// PAGENT pAgent)
+//
+////////////////////////////////////////////////////////////////////////////////
+PLISTITEM IsAgentInList(PLISTITEM pList,
+ PAGENT pAgent)
+{
+ while (pList)
+ {
+ if (pList->pAgent == pAgent)
+ {
+ return pList;
+ }
+
+ pList = pList->pNext;
+ }
+
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// PAGENT GetAgentFromName(LPTSTR lpszName)
+//
+////////////////////////////////////////////////////////////////////////////////
+PAGENT GetAgentFromName(LPTSTR lpszName)
+{
+ PAGENT pHold;
+
+ pHold = g.pAgents;
+
+ while (pHold)
+ {
+ if (!lstrcmpi(pHold->lpszName,
+ lpszName))
+ {
+ return pHold;
+ }
+
+ pHold = pHold->pNext;
+ }
+
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// PAGENT GetAgentFromhLine(HLINE hLine)
+//
+///////////////////////////////////////////////////////////////////////////////
+PAGENT GetAgentFromhLine(HLINE hLine)
+{
+ PAGENT pAgent;
+
+ pAgent = g.pAgents;
+
+ while (pAgent)
+ {
+ if (pAgent->hLine == hLine)
+ {
+ return pAgent;
+ }
+
+ pAgent = pAgent->pNext;
+ }
+
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// DWORD GetDeviceID(DWORD dwPermID)
+//
+///////////////////////////////////////////////////////////////////////////////
+DWORD GetDeviceID(DWORD dwPermID)
+{
+ DWORD dwCount;
+
+ for (dwCount = 0; dwCount < g.dwNumDevs; dwCount++)
+ {
+ if (g.pdwPermIDs[dwCount] == dwPermID)
+ {
+ return dwCount;
+ }
+ }
+
+ return (DWORD)-1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// **************TAPI WRAPPER FUNCTIONS**************
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// LineGetAddressCaps()
+//
+///////////////////////////////////////////////////////////////////////////////
+LINEADDRESSCAPS * LineGetAddressCaps (HLINEAPP hLineApp,
+ DWORD dwDeviceID,
+ DWORD dwAddressID)
+{
+ LONG lRetVal;
+ LINEADDRESSCAPS * pLineAddressCaps;
+ static DWORD dwMaxNeededSize = sizeof(LINEADDRESSCAPS);
+
+ // Allocate an initial block of memory for the LINEADDRESSCAPS structure,
+ // which may or may not be big enough to hold all of the information.
+ //
+ pLineAddressCaps = ACDAlloc(dwMaxNeededSize);
+
+ for (;;)
+ {
+ if (pLineAddressCaps == NULL)
+ {
+ return NULL;
+ }
+ pLineAddressCaps->dwTotalSize = dwMaxNeededSize;
+
+ // Try (or retry) to get the LINEADDRESSCAPS information
+ //
+ lRetVal = lineGetAddressCaps(hLineApp,
+ dwDeviceID,
+ dwAddressID,
+ TAPI_CURRENT_VERSION,
+ 0,
+ pLineAddressCaps);
+ if (lRetVal < 0)
+ {
+ ACDFree((HLOCAL)pLineAddressCaps);
+ return NULL;
+ }
+
+ // If the currently allocated LINEADDRESSCAPS memory block was big
+ // enough, we're all done, else we need to realloc the memory block
+ // and try again.
+ //
+ if (pLineAddressCaps->dwNeededSize <= dwMaxNeededSize)
+ {
+ return pLineAddressCaps;
+ }
+ else
+ {
+ dwMaxNeededSize = pLineAddressCaps->dwNeededSize;
+ pLineAddressCaps = ACDReAlloc((HLOCAL)pLineAddressCaps,
+ dwMaxNeededSize);
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// LineGetCallInfo()
+//
+///////////////////////////////////////////////////////////////////////////////
+LINECALLINFO * LineGetCallInfo (HCALL hCall)
+{
+ LONG lRetVal;
+ LINECALLINFO * pLineCallInfo;
+ static DWORD dwMaxNeededSize = sizeof(LINECALLINFO);
+
+ // Allocate an initial block of memory for the LINECALLINFO structure,
+ // which may or may not be big enough to hold all of the information.
+ //
+ pLineCallInfo = ACDAlloc(dwMaxNeededSize);
+
+ for (;;)
+ {
+ if (pLineCallInfo == NULL)
+ {
+ return NULL;
+ }
+ pLineCallInfo->dwTotalSize = dwMaxNeededSize;
+
+ // Try (or retry) to get the LINECALLINFO information
+ //
+ lRetVal = lineGetCallInfo(hCall,
+ pLineCallInfo);
+ if (lRetVal < 0)
+ {
+ ACDFree((HLOCAL)pLineCallInfo);
+ return NULL;
+ }
+
+ // If the currently allocated LINECALLINFO memory block was big
+ // enough, we're all done, else we need to realloc the memory block
+ // and try again.
+ //
+ if (pLineCallInfo->dwNeededSize <= dwMaxNeededSize)
+ {
+ return pLineCallInfo;
+ }
+ else
+ {
+ dwMaxNeededSize = pLineCallInfo->dwNeededSize;
+ pLineCallInfo = ACDReAlloc((HLOCAL)pLineCallInfo,
+ dwMaxNeededSize);
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// LineGetDevCaps()
+//
+///////////////////////////////////////////////////////////////////////////////
+LINEDEVCAPS * LineGetDevCaps (HLINEAPP hLineApp,
+ DWORD dwDeviceID)
+{
+ LONG lRetVal;
+ LINEDEVCAPS * pLineDevCaps;
+ static DWORD dwMaxNeededSize = sizeof(LINEDEVCAPS);
+
+ pLineDevCaps = ACDAlloc(dwMaxNeededSize);
+ for (;;)
+ {
+ if (pLineDevCaps == NULL)
+ {
+ return NULL;
+ }
+ pLineDevCaps->dwTotalSize = dwMaxNeededSize;
+ lRetVal = lineGetDevCaps(hLineApp,
+ dwDeviceID,
+ TAPI_CURRENT_VERSION,
+ 0,
+ pLineDevCaps);
+ if (lRetVal < 0)
+ {
+ ACDFree((HLOCAL)pLineDevCaps);
+ return NULL;
+ }
+ if (pLineDevCaps->dwNeededSize <= dwMaxNeededSize)
+ {
+ return pLineDevCaps;
+ }
+ else
+ {
+ dwMaxNeededSize = pLineDevCaps->dwNeededSize;
+ pLineDevCaps = ACDReAlloc((HLOCAL)pLineDevCaps,
+ dwMaxNeededSize);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// LineGetID()
+//
+///////////////////////////////////////////////////////////////////////////////
+VARSTRING * LineGetID (HLINE hLine,
+ DWORD dwAddressID,
+ HCALL hCall,
+ DWORD dwSelect,
+ LPCTSTR lpszDeviceClass)
+{
+ LONG lRetVal;
+ VARSTRING * pVarString;
+ static DWORD dwMaxNeededSize = sizeof(VARSTRING);
+
+ // Allocate an initial block of memory for the VARSTRING structure,
+ // which may or may not be big enough to hold all of the information.
+ //
+ pVarString = ACDAlloc(dwMaxNeededSize);
+
+ for (;;)
+ {
+ if (pVarString == NULL)
+ {
+ return NULL;
+ }
+ pVarString->dwTotalSize = dwMaxNeededSize;
+
+ // Try (or retry) to get the VARSTRING information
+ //
+ lRetVal = lineGetID(hLine,
+ dwAddressID,
+ hCall,
+ dwSelect,
+ pVarString,
+ lpszDeviceClass);
+ if (lRetVal < 0)
+ {
+ ACDFree(pVarString);
+ return NULL;
+ }
+
+ // If the currently allocated VARSTRING memory block was big
+ // enough, we're all done, else we need to realloc the memory block
+ // and try again.
+ //
+ if (pVarString->dwNeededSize <= dwMaxNeededSize)
+ {
+ return pVarString;
+ }
+ else
+ {
+ dwMaxNeededSize = pVarString->dwNeededSize;
+ pVarString = ACDReAlloc((HLOCAL)pVarString,
+ dwMaxNeededSize);
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// LineGetCallStatus()
+//
+///////////////////////////////////////////////////////////////////////////////
+LINECALLSTATUS * LineGetCallStatus (HCALL hCall)
+{
+ LONG lRetVal;
+ LINECALLSTATUS * pLineCallStatus;
+ static DWORD dwMaxNeededSize = sizeof(LINECALLSTATUS);
+
+ // Allocate an initial block of memory for the LINECALLSTATUS structure,
+ // which may or may not be big enough to hold all of the information.
+ //
+ pLineCallStatus = ACDAlloc(dwMaxNeededSize);
+
+ while (TRUE)
+ {
+ if (pLineCallStatus == NULL)
+ {
+ return NULL;
+ }
+ pLineCallStatus->dwTotalSize = dwMaxNeededSize;
+
+ // Try (or retry) to get the LINECALLSTATUS information
+ //
+ lRetVal = lineGetCallStatus(hCall,
+ pLineCallStatus);
+ if (lRetVal < 0)
+ {
+ ACDFree((HLOCAL)pLineCallStatus);
+ return NULL;
+ }
+
+ // If the currently allocated LINECALLSTATUS memory block was big
+ // enough, we're all done, else we need to realloc the memory block
+ // and try again.
+ //
+ if (pLineCallStatus->dwNeededSize <= dwMaxNeededSize)
+ {
+ return pLineCallStatus;
+ }
+ else
+ {
+ dwMaxNeededSize = pLineCallStatus->dwNeededSize;
+ pLineCallStatus = ACDReAlloc((HLOCAL)pLineCallStatus,
+ dwMaxNeededSize);
+ }
+ }
+}
+
diff --git a/private/tapi/dev/apps/acd/clntapp.cpp b/private/tapi/dev/apps/acd/clntapp.cpp
new file mode 100644
index 000000000..7f1732750
--- /dev/null
+++ b/private/tapi/dev/apps/acd/clntapp.cpp
@@ -0,0 +1,123 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+//
+//
+//////////////////////////////////////////////////////////////////////////////
+#include <windows.h>
+
+//////////////////////////////////////////////////////////////////////////////
+// PROTOTYPES
+//////////////////////////////////////////////////////////////////////////////
+static BOOL CreateMainWindow (int nCmdShow);
+
+static LRESULT CALLBACK MainWndProc (HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam);
+
+//////////////////////////////////////////////////////////////////////////////
+// GLOBALS
+//////////////////////////////////////////////////////////////////////////////
+HINSTANCE ghInstance;
+HWND ghMainWnd;
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// WinMain()
+//
+//////////////////////////////////////////////////////////////////////////////
+
+int WINAPI WinMain (HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpszCmdLine,
+ int nCmdShow)
+{
+ MSG msg;
+
+ ghInstance = hInstance;
+
+ if (!CreateMainWindow(nCmdShow))
+ {
+ return 0;
+ }
+
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ return 1;
+}
+
+
+//*****************************************************************************
+// CreateMainWindow()
+//*****************************************************************************
+
+BOOL CreateMainWindow (int nCmdShow)
+{
+ WNDCLASS wc;
+ static char szClassName[] = "TapiClientWndClass";
+
+ wc.style = 0;
+ wc.lpfnWndProc = MainWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = ghInstance;
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = szClassName;
+
+
+ if (!RegisterClass(&wc))
+ {
+ return FALSE;
+ }
+
+ ghMainWnd = CreateWindow(szClassName,
+ "Tapi Client App",
+ WS_OVERLAPPEDWINDOW,
+ 0,
+ 0,
+ GetSystemMetrics(SM_CXSCREEN)/2,
+ GetSystemMetrics(SM_CYSCREEN)/2,
+ NULL,
+ NULL,
+ ghInstance,
+ NULL);
+
+ if (ghMainWnd == NULL)
+ {
+ return FALSE;
+ }
+
+ ShowWindow(ghMainWnd, nCmdShow);
+ UpdateWindow(ghMainWnd);
+ return TRUE;
+}
+
+
+//*****************************************************************************
+// MainWndProc()
+//*****************************************************************************
+
+LRESULT CALLBACK MainWndProc (HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+ return 0;
+}
+
diff --git a/private/tapi/dev/apps/acd/makefile b/private/tapi/dev/apps/acd/makefile
new file mode 100644
index 000000000..f1084966b
--- /dev/null
+++ b/private/tapi/dev/apps/acd/makefile
@@ -0,0 +1,29 @@
+!if "$(OS)" == "Windows_NT"
+
+!INCLUDE $(NTMAKEENV)\makefile.def
+
+!else
+
+##############################################################################
+#
+# Dialer.exe Make file
+#
+##############################################################################
+
+#Ok, we're doing a Win9x build.
+
+ROOT=..\..\..\..\..
+
+VERSIONLIST=debug retail
+
+DEPENDNAME=depend.mk
+
+COMMONMKFILE=makefile.def
+
+IS_OEM=TRUE
+IS_32 = TRUE
+
+!include $(ROOT)\dev\master.mk
+
+!endif
+
diff --git a/private/tapi/dev/apps/acd/makefile.sdk b/private/tapi/dev/apps/acd/makefile.sdk
new file mode 100644
index 000000000..0d3c5a56e
--- /dev/null
+++ b/private/tapi/dev/apps/acd/makefile.sdk
@@ -0,0 +1,35 @@
+# Define NODEBUG to build ACDSMPL without debugging information.
+# Define UNICODE to build ACDSMPL with UNICODE characters.
+
+TARGETOS=WINNT
+
+!include <ntwin32.mak>
+
+unicode = -DUNICODE
+
+cflags=$(cflags) -DWIN32_LEAN_AND_MEAN
+
+all: acdsmpl.exe
+
+# Update the resource if necessary
+
+acdsmpl.res: acdsmpl.rc resource.h
+ $(rc) $(rcvars) -r acdsmpl.rc
+
+# Update the object files if necessary
+
+acdsmpl.obj: acdsmpl.c resource.h acdsmpl.h
+ $(cc) $(cdebug) $(cflags) $(cvars) $(unicode) acdsmpl.c
+
+acdtapi.obj: acdtapi.c resource.h acdsmpl.h
+ $(cc) $(cdebug) $(cflags) $(cvars) $(unicode) acdtapi.c
+
+acdutils.obj: acdutils.c resource.h acdsmpl.h
+ $(cc) $(cdebug) $(cflags) $(cvars) $(unicode) acdutils.c
+
+# Update the executable file if necessary, and if so, add the resource back in.
+
+acdsmpl.exe: acdsmpl.obj acdtapi.obj acdutils.obj acdsmpl.res makefile.sdk
+ $(link) $(linkdebug) $(guiflags) -out:acdsmpl.exe\
+ acdsmpl.obj acdtapi.obj acdutils.obj acdsmpl.res\
+ version.lib tapi32.lib comctl32.lib $(guilibsmt)
diff --git a/private/tapi/dev/apps/acd/resource.h b/private/tapi/dev/apps/acd/resource.h
new file mode 100644
index 000000000..5499d6d80
--- /dev/null
+++ b/private/tapi/dev/apps/acd/resource.h
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////
+//
+// resource.h
+//
+////////////////////////////////////////////////////////////////////////
+#define IDD_MAINDLG 101
+#define IDD_ADD 102
+#define IDD_ADDTOLIST 103
+#define IDD_ADDAGENT 104
+#define IDC_TREEWND 1000
+#define IDC_EDITWND 1001
+#define IDM_NEWGROUP 1002
+#define IDM_NEWAGENT 1003
+#define IDM_GROUPPROPERTIES 1004
+#define IDM_GROUPDELETE 1005
+#define IDM_AGENTPROPERTIES 1006
+#define IDM_AGENTDELETE 1007
+#define IDM_SIGNIN 1008
+#define IDC_NAME 1009
+#define IDC_NUMBER 1010
+#define IDC_LIST1 1011
+#define IDC_LIST2 1012
+#define IDC_ADD 1013
+#define IDC_REMOVE 1014
+#define IDM_GROUPADDTOLIST 1015
+#define IDM_AGENTADDTOLIST 1016
+#define IDC_STATICNOTINLIST 1017
+#define IDC_STATICINLIST 1018
+#define IDR_MAINMENU 1019
+#define ID_FILE_OPEN 1020
+#define ID_FILE_EXIT 1021
+#define ID_FILE_NEW 1022
+#define ID_EDIT_ADDAGENT 1023
+#define ID_EDIT_ADDGROUP 1024
+#define IDC_LINECOMBO 1025
+#define IDC_ADDRESSCOMBO 1026
+#define IDM_GROUPAGENTSTATUS 1027
+#define ID_VIEW_GROUP 1028
+#define ID_VIEW_AGENT 1029
+#define IDC_DESTADDRESS 1030
+
+#define IDC_STATIC -1
+
diff --git a/private/tapi/dev/apps/acd/sources b/private/tapi/dev/apps/acd/sources
new file mode 100644
index 000000000..9412ca2ac
--- /dev/null
+++ b/private/tapi/dev/apps/acd/sources
@@ -0,0 +1,59 @@
+!IF 0
+
+Copyright (c) 1989-1993 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+Author:
+
+ John Rogers (JohnRo) 25-Oct-1991
+
+Notes:
+
+ Commented description of this file is in \nt\public\oak\bin\sources.tpl
+
+Revision History:
+
+!ENDIF
+
+
+MAJORCOMP=net
+MINORCOMP=tapi
+
+TARGETNAME=acdsmpl
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=PROGRAM
+
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\tapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comctl32.lib
+
+INCLUDES=.;$(BASEDIR)\public\sdk\inc
+
+USE_CRTDLL=1
+
+SOURCES=acdsmpl.c \
+ acdtapi.c \
+ acdutils.c \
+ acdsmpl.rc
+
+C_DEFINES=-DWINVER=0x0400 -DUNICODE
+
+UMTYPE=windows
+
+UMENTRY=winmain
+
+!IFNDEF 386_WARNING_LEVEL
+386_WARNING_LEVEL=/W3
+!ENDIF